tqsl-2.5.1/0000775000076400007640000000000013563135525012522 5ustar rmurphyrmurphytqsl-2.5.1/README0000664000076400007640000003116513563135525013410 0ustar rmurphyrmurphyThis is the TrustedQSL project, which provides tools for digitally signing Amateur Radio QSO records. src: Source code and documentation for tqsllib, the TrustedQSL library. apps: Source code for "tqsl" and other sample applications. html: Various legacy documents This document describes the changes to TrustedQSL since version 1.13 and explains how applications can use TQSL in their applications. Command Line Changes -------------------- Many applications use the 'tqsl' application in command line mode to sign log files. There were several new capabilities added to command line operation in 1.14. The first is that tqsl can now automatically sign and upload a log to the LoTW site for the user. This allows your application to simply write an adif file which is then processed and uploaded to LoTW without requiring the application to read the output file and either upload it or tell your user to upload it. The command line parser in 1.14 was rewritten and is less forgiving of improperly formatted command lines. Command Line Options -------------------- The following summarizes the command line options and what they do: Usage: tqsl [-a ] [-b ] [-e ] [-d] [-l ] [-s] [-o ] [-u] [-x] [-p ] [-q] [-n] [-v] [-h] [-t ] [-c ] [Input ADIF or Cabrillo log file to sign] The following command line options may be specified on the command line: -a Specify dialog action - abort, all, compliant or ask This option instructs TQSL on how to handle QSOs that do not appear to be valid. There are many potential causes for invalid QSOs. Examples include QSOs with dates outside the valid range for the certificate being used, QSOs with invalid amateur callsigns, duplicate QSOs, and attempts to sign with an expired certificate. This option specifies how tqsl should handle these exceptions. Using "-a ask" instructs tqsl to use a dialog to ask the user how to proceed. This is the default behavior if "-a" is not provided on the command line. Using "-a abort" instructs tqsl to issue an error message when an exception QSO is processed and immediately abort signing. Using "-a compliant" instructs tqsl to sign the QSOs which are compliant (not duplicates, in date range, and with valid callsigns) and ignore any exception QSOs. This is the recommended behavior for command line applications but is not the default action for compatibility reasons. Using "-a all" instructs tqsl to process all QSOs, ignoring duplicates and invalid callsigns. QSOs outside the range of valid dates for the selected station certificate will not be signed, as they would not be accepted by Logbook. -b Set begin date for the QSO date filter. -e Set end date for the QSL date filter. These options filter QSOs being signed to those after the begin date and before the end date. If neither of these are supplied, then no filtering will be performed. These values will override any date range entries provided by the user. This implies that "-d" (suppress date range dialog) should be used with -b or -e. -d Suppress date range dialog This option instructs tqsl to not ask the user to select a range of dates for processing QSOs. If this is used, all QSOs in the input file will be selected for processing. Command line tools will usually include this option to suppress tqsl dialogs. However, this means that the logging program is responsible for filtering QSOs before delivering them to tqsl. -l Select Station Location. This option selects a station location. This is used for signing logs or in conjunction with the "-s" option to define a location for editing. -s Edit (if used with -l) or create Station Location This option can be used to create a new Station Location (-s without -l) or to edit an existing Station Location (when both -s and -l are provided). -o Output file name (defaults to input name minus extension plus .tq8) This option instructs tqsl where the signed output file will be stored. If it is not provided, the output file will be written to the same location as the input file with the extension changed to ".tq8" -u Upload after signing instead of saving This option instructs tqsl to upload the log file after it is successfully signed. -q Operate in batch mode, not menu-driven mode. -x Operate in batch mode, not menu-driven mode. If -x or -q are included on the command line, tqsl suppresses user dialogs and sends error messages to standard error. A logging application is expected to read this file and possibly display the contents to the user so they can see the results of the command action. If these options are not included, a calling application cannot distinguish between a successful signing and one where a user cancels the signing. -n Look for updated (new) versions of key files. If -n is given on the command line, tqsl checks for new versions of the tqsl program, an updated tqsl configuration file, and verifies that any user certificates are not about to expire. If any of these circumstances exist, the user is prompted to perform the required updates. When the check is completed, tqsl exits. This command line option can not be combined with any other command line options as it only performs an update check and does not sign any logs submitted with the command. -p Password for the signing key This option allows an application to provide the password for the private key that will be used to sign the log file. -v Display the version information and exit -h Display command line help These options allow the user to display the version number of tqsl or to obtain help on the command line usage. -t Open a diagnostic trace file at startup When supplied, this option enables diagnostic tracing at startup, opening the supplied file to record TQSL operations details. This is useful for debugging purposes. -c Use the given callsign when signing a log This option allows a logging program to specify what callsign to use for a log signing operation. This will override the callsign associated with the selected station location, if any. Command Line Usage ------------------ An application that uses the command line invokes the tqsl binary, optionally providing a set of options that dictate how tqsl operates. Normally, such an application should include the "-x" or "-q" options to indicate to tqsl that application popups should be suppressed. Errors discovered during the signing process are sent to the standard error file. Callers would normally indicate where those messages should be sent by adding "2> file.txt" to the command line used to run tqsl. This directs the shell (Windows or Unix) to write the error messages to that file. When operated in "batch" mode (i.e. -x or -q used), tqsl provides information that the calling program can use to determine if the signing operation succeeded. The first way is by capturing tqsl's exit status code. This provides information on success or failure using the following values: 0 - Success: all QSOs submitted were signed and saved or uploaded 1 - Cancelled by user 2 - The log was rejected by the LoTW server 3 - The response from the LoTW server was unexpected 4 - An error occurred in tqsl 5 - An error occurred in tqsllib (invalid filename, bad file format) 6 - Unable to open input file 7 - Unable to open output file 8 - No QSOs were processed because some QSOs were duplicates or out of date range (no QSOs written) 9 - Some QSOs were processed, and some QSOs were ignored because they were duplicates or out of date range (some QSOs written) 10 - Command syntax error 11 - LoTW network connnection failed (no network or LoTW is unreachable) 12 - Unknown error 13 - The TQSL duplicates database is locked. This exit status is also written to stderr in a format that can be parsed by the calling application. The last output from tqsl will be of the format hh:mm:ss AM|PM Final Status: Description (code) (For cases where the language is not English, this will be duplicated - first in the local language, then in English.) The first two fields are a timestamp, the words "Final Status:" always appears. Following that is a short descriptive message giving the exit status. The last thing on the line is the numeric exit code (as above) in parenthesis. Examples of output follows: 05:57:39 PM: Warning: Signing cancelled 05:57:39 PM: No records output 05:57:39 PM: Final Status: cancelled by user (1) 06:05:56 PM: /home/rmurphy/k1mu.adi: 414 QSO records were duplicates 06:05:56 PM: /home/rmurphy/k1mu.adi: wrote 1 records to /home/rmurphy/k1mu.tq8 06:05:56 PM: /home/rmurphy/k1mu.tq8 is ready to be emailed or uploaded. Note: TQSL assumes that this file will be uploaded to LoTW. Resubmitting these QSOs will cause them to be reported as duplicates. 06:05:56 PM: Final Status: Some QSOs were duplicates or out of date range (9) An example usage for signing a log would be tqsl -q -l "K1MU home" -p "Insecure" -a compliant -u -d k1mu.adi 2>temp.txt This indicates quiet mode (-q), selects a station location and a password, indicates that only compliant QSOs will be written (-a), uploads to LoTW (-u), suppresses date popups (-d), provides an input file (k1mu.adi), and finally writes log messages to temp.txt. The logging program would read and process that log once tqsl is done. An application would add "-o" to indicate where tqsl should write the signed log if "-u" (upload) is not provided. Command line applications are strongly encouraged to add "-a=compliant" to their invocations of tqsl, and to consider storing and displaying the log messages to their users. Application Changes ------------------- Some logging applications directly call tqsllib functions to sign log files. The application programming interface (API) to tqsllib has not changed in ways that introduce incompatibilities, but there are additional API calls which are necessary for applications to allow duplicate QSO processing to work properly. Normally, an application will call tqsl_beginCabrilloConverter() or tqsl_beginADIFConverter to begin signing a log file. After the converter is created by those calls, the application should then call tqsl_setConverterAllowDuplicates(conv, false) which tells tqsllib that duplicate processing should be enabled. If you do not call tqsl_setConverterAllowDuplicates, the library will assume that duplicates should be permitted (for compatibility reasons), which may cause unnecessary QSOs to be uploaded. If duplicate suppression is enabled, there is a new error return from tqsl_getConverterGABBI that indicates duplicate QSOs. In this case, tQSL_Error is set to TQSL_DUPLICATE_QSO. Software may need to be modified to handle this new result and act appropriately (ignore it, or abort the signing operation.) After successful processing of a log, an application should call either tqsl_convertCommit(conv) or tqsl_convertRollBack(conv) prior to calling tqsl_endConverter() to signal that a log conversion has completed. tqsl_converterCommit() indicates to tqsllib that the log has been successfully processed and that the QSOs should be added to the duplicate detection database. Calling tqsl_converterRollBack() indicates to tqsllib that the log has not been successfully processed and that the QSO records should not be added to the duplicate database. Simply adding the necessary call before the converter is closed is enough to bring the application up to date. change tqsl_endConverter(&conv) to tqsl_converterCommit(conv); tqsl_endConverter(&conv); Using tqsllib ------------- A minimal set of calls to permit an application to sign a log is the following. Of course, error checking should be performed for each call. tqsl_getStationLocation(&loc, location_name); tqsl_getLocationCallSign(loc, callsign, sizeof callsign); tqsl_getLocationDXCCEntity(loc, &dxcc); tqsl_selectCertificates(&certlist, &ncerts, callsign, dxcc); tqsl_beginADIFConverter(&conv, input_file, certlist, ncerts, loc); tqsl_setConverterAllowDuplicates(conv, false); tqsl_setConverterAppName(conv, "myAppName"); (tell tqsllib the name of your application) while (cp = tqsl_getConverterGABBI(conv) != 0) write the string pointed to by "cp" to your file tqsl_converterCommit(conv); tqsl_endConverter(&conv); tqsl_endStationLocationCapture(&loc); The tq8 files created by tqsl are compressed using zlib functions. You can also submit uncompressed files using a .tq7 extension. tqsl-2.5.1/win32-msi/0000775000076400007640000000000013563134442014247 5ustar rmurphyrmurphytqsl-2.5.1/win32-msi/tqsl-install/0000775000076400007640000000000013563134442016676 5ustar rmurphyrmurphytqsl-2.5.1/win32-msi/tqsl-install/update-helpfrag.cmd0000664000076400007640000000024413563134442022433 0ustar rmurphyrmurphy"C:\Program Files (x86)\WiX Toolset v3.11\bin\heat.exe" dir ..\..\apps\help -cg "HelpDir" -dr "APPLICATIONFOLDER" -indent 2 -out helpfrag.wxs -var "var.helpSrc" -agtqsl-2.5.1/win32-msi/tqsl-install/tqsl-install.wixproj0000664000076400007640000000503313563134442022752 0ustar rmurphyrmurphy Debug x86 3.7 1dfa6e6b-1725-4fa3-81d7-64d8a82cb824 2.0 tqsl-2.5.1 Package $(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets bin\$(Configuration)\ obj\$(Configuration)\ Debug -b "..\..\bin\Release" -b "..\..\apps" -b "..\..\src" -b "..\..\src\Release" -b "..\..\apps\Release" bin\$(Configuration)\ obj\$(Configuration)\ -b "..\..\bin\Release" -b "..\..\apps" -b "..\..\src" -b "..\..\src\Release" -b "..\..\..\apps\Release" $(WixExtDir)\WixUIExtension.dll WixUIExtension tqsl-2.5.1/win32-msi/tqsl-install/tqsl-install.sln0000664000076400007640000000151613563134442022046 0ustar rmurphyrmurphy Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2012 Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "tqsl-install", "tqsl-install.wixproj", "{1DFA6E6B-1725-4FA3-81D7-64D8A82CB824}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {1DFA6E6B-1725-4FA3-81D7-64D8A82CB824}.Debug|x86.ActiveCfg = Debug|x86 {1DFA6E6B-1725-4FA3-81D7-64D8A82CB824}.Debug|x86.Build.0 = Debug|x86 {1DFA6E6B-1725-4FA3-81D7-64D8A82CB824}.Release|x86.ActiveCfg = Release|x86 {1DFA6E6B-1725-4FA3-81D7-64D8A82CB824}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal tqsl-2.5.1/win32-msi/tqsl-install/langfrags.wxs0000664000076400007640000001774513563134442021423 0ustar rmurphyrmurphy tqsl-2.5.1/win32-msi/tqsl-install/helpfrag.wxs0000664000076400007640000007407613563134442021247 0ustar rmurphyrmurphy tqsl-2.5.1/win32-msi/tqsl-install/Product.wxs0000664000076400007640000002170413563134442021065 0ustar rmurphyrmurphy NOT Version9X INSTALLDESKTOPSHORTCUT ISINNOINSTALLED NEWERFOUND notepad.exe CLONEXIT AND NOT Installed QSONEXIT AND NOT Installed tqsl-2.5.1/win32-msi/tqsl-install/MyWixUI_InstallDir.wxs0000664000076400007640000001110713563134442023101 0ustar rmurphyrmurphy 1 "1"]]> 1 NOT Installed Installed AND PATCH 1 LicenseAccepted = "1" 1 1 NOT WIXUI_DONTVALIDATEPATH "1"]]> (ISINNOINSTALLED) AND (WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID="1") (NOT ISINNOINSTALLED) AND (WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID="1") 1 1 NOT Installed Installed AND NOT PATCH Installed AND PATCH 1 1 1 1 tqsl-2.5.1/win32-msi/tqsl-install/MyInstallDirDlg.wxs0000664000076400007640000000501413563134442022443 0ustar rmurphyrmurphy 1 tqsl-2.5.1/win32-msi/tqsl-install/MyExitDialog.wxs0000664000076400007640000000730713563134442022007 0ustar rmurphyrmurphy tqsl-2.5.1/win32-msi/tqsl-install/AlertUninstall.wxs0000664000076400007640000000210213563134442022375 0ustar rmurphyrmurphy 1 1 1 tqsl-2.5.1/win32-msi/tqsl-install/.gitignore0000664000076400007640000000001213563134442020657 0ustar rmurphyrmurphybin/ obj/ tqsl-2.5.1/toolset-to-vc08.py0000664000076400007640000000120213563134442015753 0ustar rmurphyrmurphyVCXPROJ=".vcxproj" v2012_PLATFORM_TOOLSET="v110" v2008_PLATFORM_TOOLSET="v90" from os import walk, path if __name__=="__main__": count=0 for root, dirs, files in walk("."): for file in files: if file.endswith(VCXPROJ): print "Replacing in", path.join(root, file), "...", with open(path.join(root, file), 'r') as proj: s=proj.read() s=s.replace(v2012_PLATFORM_TOOLSET, v2008_PLATFORM_TOOLSET) with open(path.join(root, file), 'w') as proj: proj.write(s) print "done" count+=1 print "Replaced toolset in", count, "project files"tqsl-2.5.1/src/0000775000076400007640000000000013563134442013306 5ustar rmurphyrmurphytqsl-2.5.1/src/xml.h0000664000076400007640000001416713563134442014270 0ustar rmurphyrmurphy/*************************************************************************** xml.h - description ------------------- begin : Fri Aug 9 2002 copyright : (C) 2002 by ARRL author : Jon Bloom email : jbloom@arrl.org revision : $Id$ ***************************************************************************/ #ifndef __xml_h #define __xml_h #include #include #include #include #include using std::pair; using std::string; using std::ostream; using std::map; using std::multimap; using std::vector; namespace tqsllib { class XMLElement; typedef multimap XMLElementList; typedef map XMLElementAttributeList; /** Encapsulates an XML element * * An XMLElement comprises a name, the enclosed text, an optional set of * attributes, and an optional list of contained elements. * * Having a list of contained elements allows construction of the XML * document tree structure. In most cases, the structure will be populated * by a call to parseFile(). */ class XMLElement { public: XMLElement() : _name(""), _text(""), _pretext("") {} /// Constructor initializes element name explicit XMLElement(const string& name) : _text(""), _pretext("") { _name = name; } /// Clear the element of all data void clear(); /// Set the element name void setElementName(const string& name) { _name = name; } /// Get the element name string getElementName() const { return _name; } /// Set an attribute. /** Attributes are treated as unique key/value pairs. */ void setAttribute(const string& key, const string& value); /// Get an attribute by its key. /** Returns a pair where: * * \li \c getAttribute().first := the attribute's value * \li \c getAttribute().second := a bool, true if the atrtribute key exists */ pair getAttribute(const string& key); /// Add an element to the list of contained subelements XMLElementList::iterator addElement(XMLElement* element); XMLElementAttributeList& getAttributeList() { return _attributes; } XMLElementList& getElementList() { return _elements; } /// Parse an XML file and add its element tree to this element int parseFile(const char *filename); #define XML_PARSE_NO_ERROR 0 #define XML_PARSE_SYSTEM_ERROR 1 #define XML_PARSE_SYNTAX_ERROR 2 /// Parse an XML string and add its element tree to this element int parseString(const char *xmlstring); /// Get the first attribute of the element /** Provides the attribute key and value. Returns \c false if the * element contains no attributes */ bool getFirstAttribute(string& key, string& attr); /// Get the next attribute of the element /** Should be called only after calling getFirstAttribute and getting * a return value of \c true. * Provides the attribute key and value. Returns \c false if the * element contains no more attributes */ bool getNextAttribute(string& key, string& attr); /// Get the first contained element named \c name. /** Returns \c false if the element contains no elements named \c name */ bool getFirstElement(const string& name, XMLElement&); /// Get the first contained element. /** Returns \c false if the element contains no elements */ bool getFirstElement(XMLElement&); /// Get the next contained element. /** Should be called only after calling getFirstElement and getting * a return value of \c true. If the getFirstElement that takes an * element name was called, getNextElement will return \c false when there * are no more elements of that name in the element list. * * Returns \c false if the element contains no more elements */ bool getNextElement(XMLElement&); /// Set the contained text string void setText(const string& s) { _text = s; } /// Get the contained text string. /** Note that this string comprises the text contained in this * element only, not any text contained in elements on the * element list; they each have their own contained text. */ string getText() const { return _text; } void setPretext(const string& s) { _pretext = s; } string getPretext() const { return _pretext; } private: static void xml_start(void *data, const XML_Char *name, const XML_Char **atts); static void xml_end(void *data, const XML_Char *name); static void xml_text(void *data, const XML_Char *text, int len); string _name, _text, _pretext; XMLElementAttributeList _attributes; XMLElementList _elements; vector _parsingStack; XMLElementList::iterator _iter; bool _iterByName; string _iterName; XMLElementAttributeList::iterator _aiter; }; inline void XMLElement::clear() { _name = _text = _pretext = _iterName = ""; _attributes.clear(); _elements.clear(); _parsingStack.clear(); } inline void XMLElement::setAttribute(const string& key, const string& value) { _attributes[key] = value; } inline XMLElementList::iterator XMLElement::addElement(XMLElement* element) { XMLElementList::iterator it = _elements.insert(make_pair(element->getElementName(), element)); return it; } inline bool XMLElement::getFirstElement(XMLElement& element) { _iterByName = false; _iter = _elements.begin(); return getNextElement(element); } inline bool XMLElement::getFirstElement(const string& name, XMLElement& element) { _iterName = name; _iterByName = true; _iter = _elements.find(_iterName); return getNextElement(element); } inline bool XMLElement::getNextElement(XMLElement& element) { if (_iter == _elements.end()) return false; if (_iterByName && _iter->second->getElementName() != _iterName) return false; element = *_iter->second; ++_iter; return true; } inline bool XMLElement::getFirstAttribute(string& key, string& attr) { _aiter = _attributes.begin(); return getNextAttribute(key, attr); } inline bool XMLElement::getNextAttribute(string& key, string& attr) { if (_aiter == _attributes.end()) return false; key = _aiter->first; attr = _aiter->second; ++_aiter; return true; } ostream& operator<< (ostream& stream, XMLElement& el); } // namespace tqsllib #endif // __xml_h tqsl-2.5.1/src/xml.cpp0000664000076400007640000001170713563134442014620 0ustar rmurphyrmurphy/*************************************************************************** xml.cpp - description ------------------- begin : Fri Aug 9 2002 copyright : (C) 2002 by ARRL author : Jon Bloom email : jbloom@arrl.org revision : $Id$ ***************************************************************************/ #define TQSLLIB_DEF #include "xml.h" #ifdef _WIN32 #include "tqsllib.h" #include #endif #include #include #include #include #include #include using std::pair; using std::string; using std::ostream; using std::map; namespace tqsllib { pair XMLElement::getAttribute(const string& key) { string s; XMLElementAttributeList::iterator pos; pos = _attributes.find(key); pair rval; if (pos == _attributes.end()) { rval.second = false; } else { rval.first = pos->second; rval.second = true; } return rval; } void XMLElement::xml_start(void *data, const XML_Char *name, const XML_Char **atts) { XMLElement *el = reinterpret_cast(data); XMLElement *new_el = new XMLElement(name); //cout << "Element: " << name << endl; for (int i = 0; atts[i]; i += 2) { new_el->setAttribute(atts[i], atts[i+1]); } if (el->_parsingStack.empty()) { el->_parsingStack.push_back(el->addElement(new_el)); } else { new_el->setPretext(el->_parsingStack.back()->second->getText()); el->_parsingStack.back()->second->setText(""); el->_parsingStack.push_back(el->_parsingStack.back()->second->addElement(new_el)); } } void XMLElement::xml_end(void *data, const XML_Char *name) { XMLElement *el = reinterpret_cast(data); if (!(el->_parsingStack.empty())) el->_parsingStack.pop_back(); } void XMLElement::xml_text(void *data, const XML_Char *text, int len) { XMLElement *el = reinterpret_cast(data); el->_parsingStack.back()->second->_text.append(text, len); } int XMLElement::parseString(const char *xmlstring) { XML_Parser xp = XML_ParserCreate(0); XML_SetUserData(xp, reinterpret_cast(this)); XML_SetStartElementHandler(xp, &XMLElement::xml_start); XML_SetEndElementHandler(xp, &XMLElement::xml_end); XML_SetCharacterDataHandler(xp, &XMLElement::xml_text); _parsingStack.clear(); // Process the XML if (XML_Parse(xp, xmlstring, strlen(xmlstring), 1) == 0) { XML_ParserFree(xp); return XML_PARSE_SYNTAX_ERROR; } XML_ParserFree(xp); return XML_PARSE_NO_ERROR; } int XMLElement::parseFile(const char *filename) { gzFile in = NULL; #ifdef _WIN32 wchar_t* fn = utf8_to_wchar(filename); int fd = _wopen(fn, _O_RDONLY|_O_BINARY); free_wchar(fn); if (fd != -1) in = gzdopen(fd, "rb"); #else in = gzopen(filename, "rb"); #endif if (!in) return XML_PARSE_SYSTEM_ERROR; // Failed to open file char buf[256]; XML_Parser xp = XML_ParserCreate(0); XML_SetUserData(xp, reinterpret_cast(this)); XML_SetStartElementHandler(xp, &XMLElement::xml_start); XML_SetEndElementHandler(xp, &XMLElement::xml_end); XML_SetCharacterDataHandler(xp, &XMLElement::xml_text); _parsingStack.clear(); int rcount; while ((rcount = gzread(in, buf, sizeof buf)) > 0) { // Process the XML if (XML_Parse(xp, buf, rcount, 0) == 0) { gzclose(in); XML_ParserFree(xp); return XML_PARSE_SYNTAX_ERROR; } } gzclose(in); bool rval = (rcount == 0); if (rval) rval = (XML_Parse(xp, "", 0, 1) != 0); XML_ParserFree(xp); return (rval ? XML_PARSE_NO_ERROR : XML_PARSE_SYNTAX_ERROR); } static struct { char c; const char *ent; } xml_entity_table[] = { { '"', """ }, { '\'', "'" }, { '>', ">" }, { '<', "<" } }; static string xml_entities(const string& s) { string ns = s; string::size_type idx = 0; while ((idx = ns.find('&', idx)) != string::npos) { ns.replace(idx, 1, "&"); idx++; } for (int i = 0; i < static_cast((sizeof xml_entity_table / sizeof xml_entity_table[0])); i++) { while ((idx = ns.find(xml_entity_table[i].c)) != string::npos) ns.replace(idx, 1, xml_entity_table[i].ent); } return ns; } /* Stream out an XMLElement as XML text */ ostream& operator<< (ostream& stream, XMLElement& el) { bool ok; XMLElement subel; if (el.getElementName() != "") { stream << "<" << el.getElementName(); string key, val; bool ok = el.getFirstAttribute(key, val); while (ok) { stream << " " << key << "=\"" << xml_entities(val) << "\""; ok = el.getNextAttribute(key, val); } if (el.getText() == "" && !el.getFirstElement(subel)) { stream << " />"; return stream; } else { stream << ">"; } } ok = el.getFirstElement(subel); while (ok) { string s = subel.getPretext(); if (s != "") stream << xml_entities(s); stream << subel; ok = el.getNextElement(subel); } if (el.getText() != "") stream << xml_entities(el.getText()); if (el.getElementName() != "") stream << ""; return stream; } } // namespace tqsllib tqsl-2.5.1/src/winstrdefs.h0000664000076400007640000000046413563134442015653 0ustar rmurphyrmurphy#ifndef WINSTRDEFS_H #define WINSTRDEFS_H #if defined(_WIN32) || defined(_WIN64) #define snprintf _snprintf #define vsnprintf _vsnprintf #define strcasecmp _stricmp #define strncasecmp _strnicmp #define unlink _unlink #define strdup _strdup #define fileno _fileno #endif #endif//WINSTRDEFS_H tqsl-2.5.1/src/windirent.h0000664000076400007640000006074213563134442015473 0ustar rmurphyrmurphy/* * dirent.h - dirent API for Microsoft Visual Studio * * Copyright (C) 2006-2012 Toni Ronkko * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * ``Software''), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL TONI RONKKO BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * * Version 1.13, Dec 12 2012, Toni Ronkko * Use traditional 8+3 file name if the name cannot be represented in the * default ANSI code page. Now compiles again with MSVC 6.0. Thanks to * Konstantin Khomoutov for testing. * * Version 1.12.1, Oct 1 2012, Toni Ronkko * Bug fix: renamed wide-character DIR structure _wDIR to _WDIR (with * capital W) in order to maintain compatibility with MingW. * * Version 1.12, Sep 30 2012, Toni Ronkko * Define PATH_MAX and NAME_MAX. Added wide-character variants _wDIR, * _wdirent, _wopendir(), _wreaddir(), _wclosedir() and _wrewinddir(). * Thanks to Edgar Buerkle and Jan Nijtmans for ideas and code. * * Do not include windows.h. This allows dirent.h to be integrated more * easily into programs using winsock. Thanks to Fernando Azaldegui. * * Version 1.11, Mar 15, 2011, Toni Ronkko * Defined FILE_ATTRIBUTE_DEVICE for MSVC 6.0. * * Version 1.10, Aug 11, 2010, Toni Ronkko * Added d_type and d_namlen fields to dirent structure. The former is * especially useful for determining whether directory entry represents a * file or a directory. For more information, see * http://www.delorie.com/gnu/docs/glibc/libc_270.html * * Improved conformance to the standards. For example, errno is now set * properly on failure and assert() is never used. Thanks to Peter Brockam * for suggestions. * * Fixed a bug in rewinddir(): when using relative directory names, change * of working directory no longer causes rewinddir() to fail. * * Version 1.9, Dec 15, 2009, John Cunningham * Added rewinddir member function * * Version 1.8, Jan 18, 2008, Toni Ronkko * Using FindFirstFileA and WIN32_FIND_DATAA to avoid converting string * between multi-byte and unicode representations. This makes the * code simpler and also allows the code to be compiled under MingW. Thanks * to Azriel Fasten for the suggestion. * * Mar 4, 2007, Toni Ronkko * Bug fix: due to the strncpy_s() function this file only compiled in * Visual Studio 2005. Using the new string functions only when the * compiler version allows. * * Nov 2, 2006, Toni Ronkko * Major update: removed support for Watcom C, MS-DOS and Turbo C to * simplify the file, updated the code to compile cleanly on Visual * Studio 2005 with both unicode and multi-byte character strings, * removed rewinddir() as it had a bug. * * Aug 20, 2006, Toni Ronkko * Removed all remarks about MSVC 1.0, which is antiqued now. Simplified * comments by removing SGML tags. * * May 14 2002, Toni Ronkko * Embedded the function definitions directly to the header so that no * source modules need to be included in the Visual Studio project. Removed * all the dependencies to other projects so that this header file can be * used independently. * * May 28 1998, Toni Ronkko * First version. *****************************************************************************/ #ifndef DIRENT_H #define DIRENT_H #if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && defined(_M_IX86) # define _X86_ #endif #include #include #include #include #include #include #include #include #include #include #include /* Indicates that d_type field is available in dirent structure */ #define _DIRENT_HAVE_D_TYPE /* Indicates that d_namlen field is available in dirent structure */ #define _DIRENT_HAVE_D_NAMLEN /* Entries missing from MSVC 6.0 */ #if !defined(FILE_ATTRIBUTE_DEVICE) # define FILE_ATTRIBUTE_DEVICE 0x40 #endif /* File type and permission flags for stat() */ #if !defined(S_IFMT) # define S_IFMT _S_IFMT /* File type mask */ #endif #if !defined(S_IFDIR) # define S_IFDIR _S_IFDIR /* Directory */ #endif #if !defined(S_IFCHR) # define S_IFCHR _S_IFCHR /* Character device */ #endif #if !defined(S_IFFIFO) # define S_IFFIFO _S_IFFIFO /* Pipe */ #endif #if !defined(S_IFREG) # define S_IFREG _S_IFREG /* Regular file */ #endif #if !defined(S_IREAD) # define S_IREAD _S_IREAD /* Read permission */ #endif #if !defined(S_IWRITE) # define S_IWRITE _S_IWRITE /* Write permission */ #endif #if !defined(S_IEXEC) # define S_IEXEC _S_IEXEC /* Execute permission */ #endif #if !defined(S_IFIFO) # define S_IFIFO _S_IFIFO /* Pipe */ #endif #if !defined(S_IFBLK) # define S_IFBLK 0 /* Block device */ #endif #if !defined(S_IFLNK) # define S_IFLNK 0 /* Link */ #endif #if !defined(S_IFSOCK) # define S_IFSOCK 0 /* Socket */ #endif #if defined(_MSC_VER) # define S_IRUSR S_IREAD /* Read user */ # define S_IWUSR S_IWRITE /* Write user */ # define S_IXUSR 0 /* Execute user */ # define S_IRGRP 0 /* Read group */ # define S_IWGRP 0 /* Write group */ # define S_IXGRP 0 /* Execute group */ # define S_IROTH 0 /* Read others */ # define S_IWOTH 0 /* Write others */ # define S_IXOTH 0 /* Execute others */ #endif /* Maximum length of file name */ #if !defined(PATH_MAX) # define PATH_MAX MAX_PATH #endif #if !defined(FILENAME_MAX) # define FILENAME_MAX MAX_PATH #endif #if !defined(NAME_MAX) # define NAME_MAX FILENAME_MAX #endif /* File type flags for d_type */ #define DT_UNKNOWN 0 #define DT_REG S_IFREG #define DT_DIR S_IFDIR #define DT_FIFO S_IFIFO #define DT_SOCK S_IFSOCK #define DT_CHR S_IFCHR #define DT_BLK S_IFBLK /* Macros for converting between st_mode and d_type */ #define IFTODT(mode) ((mode) & S_IFMT) #define DTTOIF(type) (type) /* * File type macros. Note that block devices, sockets and links cannot be * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are * only defined for compatibility. These macros should always return false * on Windows. */ #define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) #define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) #define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) #define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) #define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) #define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) #define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) /* Return the exact length of d_namlen without zero terminator */ #define _D_EXACT_NAMLEN(p) ((p)->d_namlen) /* Return number of bytes needed to store d_namlen */ #define _D_ALLOC_NAMLEN(p) (PATH_MAX + 1) #ifdef __cplusplus extern "C" { #endif /* Wide-character version */ struct _wdirent { long d_ino; /* Always zero */ unsigned short d_reclen; /* Structure size */ size_t d_namlen; /* Length of name without \0 */ int d_type; /* File type */ wchar_t d_name[PATH_MAX + 1]; /* File name */ }; typedef struct _wdirent _wdirent; struct _WDIR { struct _wdirent ent; /* Current directory entry */ WIN32_FIND_DATAW data; /* Private file data */ int cached; /* True if data is valid */ HANDLE handle; /* Win32 search handle */ wchar_t *patt; /* Initial directory name */ }; typedef struct _WDIR _WDIR; static _WDIR *_wopendir(const wchar_t *dirname); static struct _wdirent *_wreaddir(_WDIR *dirp); static int _wclosedir(_WDIR *dirp); static void _wrewinddir(_WDIR* dirp); /* For compatibility with Symbian */ #define wdirent _wdirent #define WDIR _WDIR #define wopendir _wopendir #define wreaddir _wreaddir #define wclosedir _wclosedir #define wrewinddir _wrewinddir /* Multi-byte character versions */ struct dirent { long d_ino; /* Always zero */ unsigned short d_reclen; /* Structure size */ size_t d_namlen; /* Length of name without \0 */ int d_type; /* File type */ char d_name[PATH_MAX + 1]; /* File name */ }; typedef struct dirent dirent; struct DIR { struct dirent ent; struct _WDIR *wdirp; }; typedef struct DIR DIR; static DIR *opendir(const char *dirname); static struct dirent *readdir(DIR *dirp); static int closedir(DIR *dirp); static void rewinddir(DIR* dirp); /* Internal utility functions */ static WIN32_FIND_DATAW *dirent_first(_WDIR *dirp); static WIN32_FIND_DATAW *dirent_next(_WDIR *dirp); static int dirent_mbstowcs_s( size_t *pReturnValue, wchar_t *wcstr, size_t sizeInWords, const char *mbstr, size_t count); static int dirent_wcstombs_s( size_t *pReturnValue, char *mbstr, size_t sizeInBytes, const wchar_t *wcstr, size_t count); static void dirent_set_errno(int error); /* * Open directory stream DIRNAME for read and return a pointer to the * internal working area that is used to retrieve individual directory * entries. */ static _WDIR* _wopendir(const wchar_t *dirname) { _WDIR *dirp = NULL; int error; /* Must have directory name */ if (dirname == NULL || dirname[0] == '\0') { dirent_set_errno(ENOENT); return NULL; } /* Allocate new _WDIR structure */ dirp = reinterpret_cast<_WDIR*>(malloc(sizeof(struct _WDIR))); if (dirp != NULL) { DWORD n; /* Reset _WDIR structure */ dirp->handle = INVALID_HANDLE_VALUE; dirp->patt = NULL; dirp->cached = 0; /* Compute the length of full path plus zero terminator */ n = GetFullPathNameW(dirname, 0, NULL, NULL); /* Allocate room for absolute directory name and search pattern */ dirp->patt = reinterpret_cast(malloc(sizeof(wchar_t) * n + 16)); if (dirp->patt) { /* * Convert relative directory name to an absolute one. This * allows rewinddir() to function correctly even when current * working directory is changed between opendir() and rewinddir(). */ n = GetFullPathNameW(dirname, n, dirp->patt, NULL); if (n > 0) { wchar_t *p; /* Append search pattern \* to the directory name */ p = dirp->patt + n; if (dirp->patt < p) { switch (p[-1]) { case '\\': case '/': case ':': /* Directory ends in path separator, e.g. c:\temp\ */ /*NOP*/ break; default: /* Directory name doesn't end in path separator */ *p++ = '\\'; } } *p++ = '*'; *p = '\0'; /* Open directory stream and retrieve the first entry */ if (dirent_first(dirp)) { /* Directory stream opened successfully */ error = 0; } else { /* Cannot retrieve first entry */ error = 1; dirent_set_errno(ENOENT); } } else { /* Cannot retrieve full path name */ dirent_set_errno(ENOENT); error = 1; } } else { /* Cannot allocate memory for search pattern */ error = 1; } } else { /* Cannot allocate _WDIR structure */ error = 1; } /* Clean up in case of error */ if (error && dirp) { _wclosedir(dirp); dirp = NULL; } return dirp; } /* * Read next directory entry. The directory entry is returned in dirent * structure in the d_name field. Individual directory entries returned by * this function include regular files, sub-directories, pseudo-directories * "." and ".." as well as volume labels, hidden files and system files. */ static struct _wdirent* _wreaddir(_WDIR *dirp) { WIN32_FIND_DATAW *datap; struct _wdirent *entp; /* Read next directory entry */ datap = dirent_next(dirp); if (datap) { size_t n; DWORD attr; /* Pointer to directory entry to return */ entp = &dirp->ent; /* * Copy file name as wide-character string. If the file name is too * long to fit in to the destination buffer, then truncate file name * to PATH_MAX characters and zero-terminate the buffer. */ n = 0; while (n < PATH_MAX && datap->cFileName[n] != 0) { entp->d_name[n] = datap->cFileName[n]; n++; } dirp->ent.d_name[n] = 0; /* Length of file name excluding zero terminator */ entp->d_namlen = n; /* File type */ attr = datap->dwFileAttributes; if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { entp->d_type = DT_CHR; } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { entp->d_type = DT_DIR; } else { entp->d_type = DT_REG; } /* Reset dummy fields */ entp->d_ino = 0; entp->d_reclen = sizeof(struct _wdirent); } else { /* Last directory entry read */ entp = NULL; } return entp; } /* * Close directory stream opened by opendir() function. This invalidates the * DIR structure as well as any directory entry read previously by * _wreaddir(). */ static int _wclosedir(_WDIR *dirp) { int ok; if (dirp) { /* Release search handle */ if (dirp->handle != INVALID_HANDLE_VALUE) { FindClose(dirp->handle); dirp->handle = INVALID_HANDLE_VALUE; } /* Release search pattern */ if (dirp->patt) { free(dirp->patt); dirp->patt = NULL; } /* Release directory structure */ free(dirp); ok = /*success*/0; } else { /* Invalid directory stream */ dirent_set_errno(EBADF); ok = /*failure*/-1; } return ok; } /* * Rewind directory stream such that _wreaddir() returns the very first * file name again. */ static void _wrewinddir(_WDIR* dirp) { if (dirp) { /* Release existing search handle */ if (dirp->handle != INVALID_HANDLE_VALUE) { FindClose(dirp->handle); } /* Open new search handle */ dirent_first(dirp); } } /* Get first directory entry (internal) */ static WIN32_FIND_DATAW* dirent_first(_WDIR *dirp) { WIN32_FIND_DATAW *datap; /* Open directory and retrieve the first entry */ dirp->handle = FindFirstFileW(dirp->patt, &dirp->data); if (dirp->handle != INVALID_HANDLE_VALUE) { /* a directory entry is now waiting in memory */ datap = &dirp->data; dirp->cached = 1; } else { /* Failed to re-open directory: no directory entry in memory */ dirp->cached = 0; datap = NULL; } return datap; } /* Get next directory entry (internal) */ static WIN32_FIND_DATAW* dirent_next(_WDIR *dirp) { WIN32_FIND_DATAW *p; /* Get next directory entry */ if (dirp->cached != 0) { /* A valid directory entry already in memory */ p = &dirp->data; dirp->cached = 0; } else if (dirp->handle != INVALID_HANDLE_VALUE) { /* Get the next directory entry from stream */ if (FindNextFileW(dirp->handle, &dirp->data) != FALSE) { /* Got a file */ p = &dirp->data; } else { /* The very last entry has been processed or an error occured */ FindClose(dirp->handle); dirp->handle = INVALID_HANDLE_VALUE; p = NULL; } } else { /* End of directory stream reached */ p = NULL; } return p; } /* * Open directory stream using plain old C-string. */ static DIR* opendir(const char *dirname) { struct DIR *dirp; int error; /* Must have directory name */ if (dirname == NULL || dirname[0] == '\0') { dirent_set_errno(ENOENT); return NULL; } /* Allocate memory for DIR structure */ dirp = reinterpret_cast(malloc(sizeof(struct DIR))); if (dirp) { wchar_t wname[PATH_MAX + 1]; size_t n; /* Convert directory name to wide-character string */ error = dirent_mbstowcs_s( &n, wname, PATH_MAX + 1, dirname, PATH_MAX); if (!error) { /* Open directory stream using wide-character name */ dirp->wdirp = _wopendir(wname); if (dirp->wdirp) { /* Directory stream opened */ error = 0; } else { /* Failed to open directory stream */ error = 1; } } else { /* * Cannot convert file name to wide-character string. This * occurs if the string contains invalid multi-byte sequences or * the output buffer is too small to contain the resulting * string. */ error = 1; } } else { /* Cannot allocate DIR structure */ error = 1; } /* Clean up in case of error */ if (error && dirp) { free(dirp); dirp = NULL; } return dirp; } /* * Read next directory entry. * * When working with text consoles, please note that file names returned by * readdir() are represented in the default ANSI code page while any output to * console is typically formatted on another code page. Thus, non-ASCII * characters in file names will not usually display correctly on console. The * problem can be fixed in two ways: (1) change the character set of console * to 1252 using chcp utility and use Lucida Console font, or (2) use * _cprintf function when writing to console. The _cprinf() will re-encode * ANSI strings to the console code page so many non-ASCII characters will * display correcly. */ static struct dirent* readdir(DIR *dirp) { WIN32_FIND_DATAW *datap; struct dirent *entp; /* Read next directory entry */ datap = dirent_next(dirp->wdirp); if (datap) { size_t n; int error; /* Attempt to convert file name to multi-byte string */ error = dirent_wcstombs_s( &n, dirp->ent.d_name, MAX_PATH + 1, datap->cFileName, MAX_PATH); /* * If the file name cannot be represented by a multi-byte string, * then attempt to use old 8+3 file name. This allows traditional * Unix-code to access some file names despite of unicode * characters, although file names may seem unfamiliar to the user. * * Be ware that the code below cannot come up with a short file * name unless the file system provides one. At least * VirtualBox shared folders fail to do this. */ if (error && datap->cAlternateFileName[0] != '\0') { error = dirent_wcstombs_s( &n, dirp->ent.d_name, MAX_PATH + 1, datap->cAlternateFileName, sizeof(datap->cAlternateFileName) / sizeof(datap->cAlternateFileName[0])); } if (!error) { DWORD attr; /* Initialize directory entry for return */ entp = &dirp->ent; /* Length of file name excluding zero terminator */ entp->d_namlen = n - 1; /* File attributes */ attr = datap->dwFileAttributes; if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { entp->d_type = DT_CHR; } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { entp->d_type = DT_DIR; } else { entp->d_type = DT_REG; } /* Reset dummy fields */ entp->d_ino = 0; entp->d_reclen = sizeof(struct dirent); } else { /* * Cannot convert file name to multi-byte string so construct * an errornous directory entry and return that. Note that * we cannot return NULL as that would stop the processing * of directory entries completely. */ entp = &dirp->ent; entp->d_name[0] = '?'; entp->d_name[1] = '\0'; entp->d_namlen = 1; entp->d_type = DT_UNKNOWN; entp->d_ino = 0; entp->d_reclen = 0; } } else { /* No more directory entries */ entp = NULL; } return entp; } /* * Close directory stream. */ static int closedir(DIR *dirp) { int ok; if (dirp) { /* Close wide-character directory stream */ ok = _wclosedir(dirp->wdirp); dirp->wdirp = NULL; /* Release multi-byte character version */ free(dirp); } else { /* Invalid directory stream */ dirent_set_errno(EBADF); ok = /*failure*/-1; } return ok; } /* * Rewind directory stream to beginning. */ static void rewinddir( DIR* dirp) { /* Rewind wide-character string directory stream */ _wrewinddir(dirp->wdirp); } /* Convert multi-byte string to wide character string */ static int dirent_mbstowcs_s( size_t *pReturnValue, wchar_t *wcstr, size_t sizeInWords, const char *mbstr, size_t count) { int error; #if defined(_MSC_VER) && _MSC_VER >= 1400 /* Microsoft Visual Studio 2005 or later */ error = mbstowcs_s(pReturnValue, wcstr, sizeInWords, mbstr, count); #else /* Older Visual Studio or non-Microsoft compiler */ size_t n; /* Convert to wide-character string */ n = mbstowcs(wcstr, mbstr, count); if (n < sizeInWords) { /* Zero-terminate output buffer */ if (wcstr) { wcstr[n] = 0; } /* Length of resuting multi-byte string WITH zero terminator */ if (pReturnValue) { *pReturnValue = n + 1; } /* Success */ error = 0; } else { /* Could not convert string */ error = 1; } #endif return error; } /* Convert wide-character string to multi-byte string */ static int dirent_wcstombs_s( size_t *pReturnValue, char *mbstr, size_t sizeInBytes, const wchar_t *wcstr, size_t count) { int error; #if defined(_MSC_VER) && _MSC_VER >= 1400 /* Microsoft Visual Studio 2005 or later */ error = wcstombs_s(pReturnValue, mbstr, sizeInBytes, wcstr, count); #else /* Older Visual Studio or non-Microsoft compiler */ size_t n; /* Convert to multi-byte string */ n = wcstombs(mbstr, wcstr, count); if (n < sizeInBytes) { /* Zero-terminate output buffer */ if (mbstr) { mbstr[n] = '\0'; } /* Lenght of resulting multi-bytes string WITH zero-terminator */ if (pReturnValue) { *pReturnValue = n + 1; } /* Success */ error = 0; } else { /* Cannot convert string */ error = 1; } #endif return error; } /* Set errno variable */ static void dirent_set_errno(int error) { #if defined(_MSC_VER) /* Microsoft Visual Studio */ _set_errno(error); #else /* Non-Microsoft compiler */ errno = error; #endif } #ifdef __cplusplus } #endif #endif /*DIRENT_H*/ tqsl-2.5.1/src/tqsllib2.def0000664000076400007640000004716513563134442015537 0ustar rmurphyrmurphyLIBRARY tqsllib2.dll EXPORTS _tqsl_getSelectedCertificate@12 tqsl_getSelectedCertificate@12=_tqsl_getSelectedCertificate@12 tqsl_getSelectedCertificate=_tqsl_getSelectedCertificate@12 _tqsl_getNumStationLocationCapturePages@8 tqsl_getNumStationLocationCapturePages@8=_tqsl_getNumStationLocationCapturePages@8 tqsl_getNumStationLocationCapturePages=_tqsl_getNumStationLocationCapturePages@8 _tqsl_adifGetError@4 tqsl_adifGetError@4=_tqsl_adifGetError@4 tqsl_adifGetError=_tqsl_adifGetError@4 _tqsl_adifMakeField@24 tqsl_adifMakeField@24=_tqsl_adifMakeField@24 tqsl_adifMakeField=_tqsl_adifMakeField@24 _tqsl_beginADIF@8 tqsl_beginADIF@8=_tqsl_beginADIF@8 tqsl_beginADIF=_tqsl_beginADIF@8 _tqsl_beginADIFConverter@20 tqsl_beginADIFConverter@20=_tqsl_beginADIFConverter@20 tqsl_beginADIFConverter=_tqsl_beginADIFConverter@20 _tqsl_beginCabrillo@8 tqsl_beginCabrillo@8=_tqsl_beginCabrillo@8 tqsl_beginCabrillo=_tqsl_beginCabrillo@8 _tqsl_beginCabrilloConverter@20 tqsl_beginCabrilloConverter@20=_tqsl_beginCabrilloConverter@20 tqsl_beginCabrilloConverter=_tqsl_beginCabrilloConverter@20 _tqsl_beginSigning@16 tqsl_beginSigning@16=_tqsl_beginSigning@16 tqsl_beginSigning=_tqsl_beginSigning@16 _tqsl_cabrilloGetError@4 tqsl_cabrilloGetError@4=_tqsl_cabrilloGetError@4 tqsl_cabrilloGetError=_tqsl_cabrilloGetError@4 _tqsl_checkSigningStatus@4 tqsl_checkSigningStatus@4=_tqsl_checkSigningStatus@4 tqsl_checkSigningStatus=_tqsl_checkSigningStatus@4 _tqsl_clearADIFModes@0 tqsl_clearADIFModes@0=_tqsl_clearADIFModes@0 tqsl_clearADIFModes=_tqsl_clearADIFModes@0 _tqsl_clearCabrilloMap@0 tqsl_clearCabrilloMap@0=_tqsl_clearCabrilloMap@0 tqsl_clearCabrilloMap=_tqsl_clearCabrilloMap@0 _tqsl_compareDates@8 tqsl_compareDates@8=_tqsl_compareDates@8 tqsl_compareDates=_tqsl_compareDates@8 _tqsl_convertDateToText@12 tqsl_convertDateToText@12=_tqsl_convertDateToText@12 tqsl_convertDateToText=_tqsl_convertDateToText@12 _tqsl_convertTimeToText@12 tqsl_convertTimeToText@12=_tqsl_convertTimeToText@12 tqsl_convertTimeToText=_tqsl_convertTimeToText@12 _tqsl_converterCommit@4 tqsl_converterCommit@4=_tqsl_converterCommit@4 tqsl_converterCommit=_tqsl_converterCommit@4 _tqsl_converterRollBack@4 tqsl_converterRollBack@4=_tqsl_converterRollBack@4 tqsl_converterRollBack=_tqsl_converterRollBack@4 _tqsl_createCertRequest@16 tqsl_createCertRequest@16=_tqsl_createCertRequest@16 tqsl_createCertRequest=_tqsl_createCertRequest@16 _tqsl_decodeBase64@12 tqsl_decodeBase64@12=_tqsl_decodeBase64@12 tqsl_decodeBase64=_tqsl_decodeBase64@12 _tqsl_deleteCertificate@4 tqsl_deleteCertificate@4=_tqsl_deleteCertificate@4 tqsl_deleteCertificate=_tqsl_deleteCertificate@4 _tqsl_deleteStationLocation@4 tqsl_deleteStationLocation@4=_tqsl_deleteStationLocation@4 tqsl_deleteStationLocation=_tqsl_deleteStationLocation@4 _tqsl_encodeBase64@16 tqsl_encodeBase64@16=_tqsl_encodeBase64@16 tqsl_encodeBase64=_tqsl_encodeBase64@16 _tqsl_endADIF@4 tqsl_endADIF@4=_tqsl_endADIF@4 tqsl_endADIF=_tqsl_endADIF@4 _tqsl_endCabrillo@4 tqsl_endCabrillo@4=_tqsl_endCabrillo@4 tqsl_endCabrillo=_tqsl_endCabrillo@4 _tqsl_endConverter@4 tqsl_endConverter@4=_tqsl_endConverter@4 tqsl_endConverter=_tqsl_endConverter@4 _tqsl_endSigning@4 tqsl_endSigning@4=_tqsl_endSigning@4 tqsl_endSigning=_tqsl_endSigning@4 _tqsl_endStationLocationCapture@4 tqsl_endStationLocationCapture@4=_tqsl_endStationLocationCapture@4 tqsl_endStationLocationCapture=_tqsl_endStationLocationCapture@4 _tqsl_exportPKCS12File@12 tqsl_exportPKCS12File@12=_tqsl_exportPKCS12File@12 tqsl_exportPKCS12File=_tqsl_exportPKCS12File@12 _tqsl_freeCertificate@4 tqsl_freeCertificate@4=_tqsl_freeCertificate@4 tqsl_freeCertificate=_tqsl_freeCertificate@4 _tqsl_getADIFField@24 tqsl_getADIFField@24=_tqsl_getADIFField@24 tqsl_getADIFField=_tqsl_getADIFField@24 _tqsl_getADIFLine@8 tqsl_getADIFLine@8=_tqsl_getADIFLine@8 tqsl_getADIFLine=_tqsl_getADIFLine@8 _tqsl_getADIFMode@12 tqsl_getADIFMode@12=_tqsl_getADIFMode@12 tqsl_getADIFMode=_tqsl_getADIFMode@12 _tqsl_getBand@20 tqsl_getBand@20=_tqsl_getBand@20 tqsl_getBand=_tqsl_getBand@20 _tqsl_getCabrilloContest@12 tqsl_getCabrilloContest@12=_tqsl_getCabrilloContest@12 tqsl_getCabrilloContest=_tqsl_getCabrilloContest@12 _tqsl_getCabrilloField@12 tqsl_getCabrilloField@12=_tqsl_getCabrilloField@12 tqsl_getCabrilloField=_tqsl_getCabrilloField@12 _tqsl_getCabrilloFreqType@8 tqsl_getCabrilloFreqType@8=_tqsl_getCabrilloFreqType@8 tqsl_getCabrilloFreqType=_tqsl_getCabrilloFreqType@8 _tqsl_getCabrilloLine@8 tqsl_getCabrilloLine@8=_tqsl_getCabrilloLine@8 tqsl_getCabrilloLine=_tqsl_getCabrilloLine@8 _tqsl_getCabrilloMapEntry@12 tqsl_getCabrilloMapEntry@12=_tqsl_getCabrilloMapEntry@12 tqsl_getCabrilloMapEntry=_tqsl_getCabrilloMapEntry@12 _tqsl_getCabrilloRecordText@4 tqsl_getCabrilloRecordText@4=_tqsl_getCabrilloRecordText@4 tqsl_getCabrilloRecordText=_tqsl_getCabrilloRecordText@4 _tqsl_getCertificateAROName@12 tqsl_getCertificateAROName@12=_tqsl_getCertificateAROName@12 tqsl_getCertificateAROName=_tqsl_getCertificateAROName@12 _tqsl_getCertificateCallSign@12 tqsl_getCertificateCallSign@12=_tqsl_getCertificateCallSign@12 tqsl_getCertificateCallSign=_tqsl_getCertificateCallSign@12 _tqsl_getCertificateDXCCEntity@8 tqsl_getCertificateDXCCEntity@8=_tqsl_getCertificateDXCCEntity@8 tqsl_getCertificateDXCCEntity=_tqsl_getCertificateDXCCEntity@8 _tqsl_getCertificateEmailAddress@12 tqsl_getCertificateEmailAddress@12=_tqsl_getCertificateEmailAddress@12 tqsl_getCertificateEmailAddress=_tqsl_getCertificateEmailAddress@12 _tqsl_getCertificateEncoded@12 tqsl_getCertificateEncoded@12=_tqsl_getCertificateEncoded@12 tqsl_getCertificateEncoded=_tqsl_getCertificateEncoded@12 _tqsl_getCertificateIssuer@12 tqsl_getCertificateIssuer@12=_tqsl_getCertificateIssuer@12 tqsl_getCertificateIssuer=_tqsl_getCertificateIssuer@12 _tqsl_getCertificateIssuerOrganization@12 tqsl_getCertificateIssuerOrganization@12=_tqsl_getCertificateIssuerOrganization@12 tqsl_getCertificateIssuerOrganization=_tqsl_getCertificateIssuerOrganization@12 _tqsl_getCertificateIssuerOrganizationalUnit@12 tqsl_getCertificateIssuerOrganizationalUnit@12=_tqsl_getCertificateIssuerOrganizationalUnit@12 tqsl_getCertificateIssuerOrganizationalUnit=_tqsl_getCertificateIssuerOrganizationalUnit@12 _tqsl_getCertificateKeyOnly@8 tqsl_getCertificateKeyOnly@8=_tqsl_getCertificateKeyOnly@8 tqsl_getCertificateKeyOnly=_tqsl_getCertificateKeyOnly@8 _tqsl_getCertificateNotAfterDate@8 tqsl_getCertificateNotAfterDate@8=_tqsl_getCertificateNotAfterDate@8 tqsl_getCertificateNotAfterDate=_tqsl_getCertificateNotAfterDate@8 _tqsl_getCertificateNotBeforeDate@8 tqsl_getCertificateNotBeforeDate@8=_tqsl_getCertificateNotBeforeDate@8 tqsl_getCertificateNotBeforeDate=_tqsl_getCertificateNotBeforeDate@8 _tqsl_getCertificatePrivateKeyType@4 tqsl_getCertificatePrivateKeyType@4=_tqsl_getCertificatePrivateKeyType@4 tqsl_getCertificatePrivateKeyType=_tqsl_getCertificatePrivateKeyType@4 _tqsl_getCertificateQSONotAfterDate@8 tqsl_getCertificateQSONotAfterDate@8=_tqsl_getCertificateQSONotAfterDate@8 tqsl_getCertificateQSONotAfterDate=_tqsl_getCertificateQSONotAfterDate@8 _tqsl_getCertificateQSONotBeforeDate@8 tqsl_getCertificateQSONotBeforeDate@8=_tqsl_getCertificateQSONotBeforeDate@8 tqsl_getCertificateQSONotBeforeDate=_tqsl_getCertificateQSONotBeforeDate@8 _tqsl_getCertificateRequestAddress1@12 tqsl_getCertificateRequestAddress1@12=_tqsl_getCertificateRequestAddress1@12 tqsl_getCertificateRequestAddress1=_tqsl_getCertificateRequestAddress1@12 _tqsl_getCertificateRequestAddress2@12 tqsl_getCertificateRequestAddress2@12=_tqsl_getCertificateRequestAddress2@12 tqsl_getCertificateRequestAddress2=_tqsl_getCertificateRequestAddress2@12 _tqsl_getCertificateRequestCity@12 tqsl_getCertificateRequestCity@12=_tqsl_getCertificateRequestCity@12 tqsl_getCertificateRequestCity=_tqsl_getCertificateRequestCity@12 _tqsl_getCertificateRequestCountry@12 tqsl_getCertificateRequestCountry@12=_tqsl_getCertificateRequestCountry@12 tqsl_getCertificateRequestCountry=_tqsl_getCertificateRequestCountry@12 _tqsl_getCertificateRequestPostalCode@12 tqsl_getCertificateRequestPostalCode@12=_tqsl_getCertificateRequestPostalCode@12 tqsl_getCertificateRequestPostalCode=_tqsl_getCertificateRequestPostalCode@12 _tqsl_getCertificateRequestState@12 tqsl_getCertificateRequestState@12=_tqsl_getCertificateRequestState@12 tqsl_getCertificateRequestState=_tqsl_getCertificateRequestState@12 _tqsl_getCertificateSerial@8 tqsl_getCertificateSerial@8=_tqsl_getCertificateSerial@8 tqsl_getCertificateSerial=_tqsl_getCertificateSerial@8 _tqsl_getCertificateSerialExt@12 tqsl_getCertificateSerialExt@12=_tqsl_getCertificateSerialExt@12 tqsl_getCertificateSerialExt=_tqsl_getCertificateSerialExt@12 _tqsl_getCertificateSerialLength@4 tqsl_getCertificateSerialLength@4=_tqsl_getCertificateSerialLength@4 tqsl_getCertificateSerialLength=_tqsl_getCertificateSerialLength@4 _tqsl_getConfigVersion@8 tqsl_getConfigVersion@8=_tqsl_getConfigVersion@8 tqsl_getConfigVersion=_tqsl_getConfigVersion@8 _tqsl_getConverterCert@8 tqsl_getConverterCert@8=_tqsl_getConverterCert@8 tqsl_getConverterCert=_tqsl_getConverterCert@8 _tqsl_getConverterGABBI@4 tqsl_getConverterGABBI@4=_tqsl_getConverterGABBI@4 tqsl_getConverterGABBI=_tqsl_getConverterGABBI@4 _tqsl_getConverterLine@8 tqsl_getConverterLine@8=_tqsl_getConverterLine@8 tqsl_getConverterLine=_tqsl_getConverterLine@8 _tqsl_getConverterRecordText@4 tqsl_getConverterRecordText@4=_tqsl_getConverterRecordText@4 tqsl_getConverterRecordText=_tqsl_getConverterRecordText@4 _tqsl_getDXCCEntity@12 tqsl_getDXCCEntity@12=_tqsl_getDXCCEntity@12 tqsl_getDXCCEntity=_tqsl_getDXCCEntity@12 _tqsl_getDXCCEntityName@8 tqsl_getDXCCEntityName@8=_tqsl_getDXCCEntityName@8 tqsl_getDXCCEntityName=_tqsl_getDXCCEntityName@8 _tqsl_getDXCCZoneMap@8 tqsl_getDXCCZoneMap@8=_tqsl_getDXCCZoneMap@8 tqsl_getDXCCZoneMap=_tqsl_getDXCCZoneMap@8 _tqsl_getErrorString@0 tqsl_getErrorString@0=_tqsl_getErrorString@0 tqsl_getErrorString=_tqsl_getErrorString@0 _tqsl_getErrorString_v@4 tqsl_getErrorString_v@4=_tqsl_getErrorString_v@4 tqsl_getErrorString_v=_tqsl_getErrorString_v@4 _tqsl_getGABBItCERT@8 tqsl_getGABBItCERT@8=_tqsl_getGABBItCERT@8 tqsl_getGABBItCERT=_tqsl_getGABBItCERT@8 _tqsl_getGABBItCONTACT@16 tqsl_getGABBItCONTACT@16=_tqsl_getGABBItCONTACT@16 tqsl_getGABBItCONTACT=_tqsl_getGABBItCONTACT@16 _tqsl_getGABBItCONTACTData@24 tqsl_getGABBItCONTACTData@24=_tqsl_getGABBItCONTACTData@24 tqsl_getGABBItCONTACTData=_tqsl_getGABBItCONTACTData@24 _tqsl_getGABBItSTATION@12 tqsl_getGABBItSTATION@12=_tqsl_getGABBItSTATION@12 tqsl_getGABBItSTATION=_tqsl_getGABBItSTATION@12 _tqsl_getLocationCallSign@12 tqsl_getLocationCallSign@12=_tqsl_getLocationCallSign@12 tqsl_getLocationCallSign=_tqsl_getLocationCallSign@12 _tqsl_getLocationDXCCEntity@8 tqsl_getLocationDXCCEntity@8=_tqsl_getLocationDXCCEntity@8 tqsl_getLocationDXCCEntity=_tqsl_getLocationDXCCEntity@8 _tqsl_getLocationFieldChanged@12 tqsl_getLocationFieldChanged@12=_tqsl_getLocationFieldChanged@12 tqsl_getLocationFieldChanged=_tqsl_getLocationFieldChanged@12 _tqsl_getLocationFieldCharData@16 tqsl_getLocationFieldCharData@16=_tqsl_getLocationFieldCharData@16 tqsl_getLocationFieldCharData=_tqsl_getLocationFieldCharData@16 _tqsl_getLocationFieldDataGABBI@16 tqsl_getLocationFieldDataGABBI@16=_tqsl_getLocationFieldDataGABBI@16 tqsl_getLocationFieldDataGABBI=_tqsl_getLocationFieldDataGABBI@16 _tqsl_getLocationFieldDataGABBISize@12 tqsl_getLocationFieldDataGABBISize@12=_tqsl_getLocationFieldDataGABBISize@12 tqsl_getLocationFieldDataGABBISize=_tqsl_getLocationFieldDataGABBISize@12 _tqsl_getLocationFieldDataLabel@16 tqsl_getLocationFieldDataLabel@16=_tqsl_getLocationFieldDataLabel@16 tqsl_getLocationFieldDataLabel=_tqsl_getLocationFieldDataLabel@16 _tqsl_getLocationFieldDataLabelSize@12 tqsl_getLocationFieldDataLabelSize@12=_tqsl_getLocationFieldDataLabelSize@12 tqsl_getLocationFieldDataLabelSize=_tqsl_getLocationFieldDataLabelSize@12 _tqsl_getLocationFieldDataLength@12 tqsl_getLocationFieldDataLength@12=_tqsl_getLocationFieldDataLength@12 tqsl_getLocationFieldDataLength=_tqsl_getLocationFieldDataLength@12 _tqsl_getLocationFieldDataType@12 tqsl_getLocationFieldDataType@12=_tqsl_getLocationFieldDataType@12 tqsl_getLocationFieldDataType=_tqsl_getLocationFieldDataType@12 _tqsl_getLocationFieldFlags@12 tqsl_getLocationFieldFlags@12=_tqsl_getLocationFieldFlags@12 tqsl_getLocationFieldFlags=_tqsl_getLocationFieldFlags@12 _tqsl_getLocationFieldIndex@12 tqsl_getLocationFieldIndex@12=_tqsl_getLocationFieldIndex@12 tqsl_getLocationFieldIndex=_tqsl_getLocationFieldIndex@12 _tqsl_getLocationFieldInputType@12 tqsl_getLocationFieldInputType@12=_tqsl_getLocationFieldInputType@12 tqsl_getLocationFieldInputType=_tqsl_getLocationFieldInputType@12 _tqsl_getLocationFieldIntData@12 tqsl_getLocationFieldIntData@12=_tqsl_getLocationFieldIntData@12 tqsl_getLocationFieldIntData=_tqsl_getLocationFieldIntData@12 _tqsl_getLocationFieldListItem@20 tqsl_getLocationFieldListItem@20=_tqsl_getLocationFieldListItem@20 tqsl_getLocationFieldListItem=_tqsl_getLocationFieldListItem@20 _tqsl_getMaxSignatureSize@8 tqsl_getMaxSignatureSize@8=_tqsl_getMaxSignatureSize@8 tqsl_getMaxSignatureSize=_tqsl_getMaxSignatureSize@8 _tqsl_getMode@12 tqsl_getMode@12=_tqsl_getMode@12 tqsl_getMode=_tqsl_getMode@12 _tqsl_getNumBand@4 tqsl_getNumBand@4=_tqsl_getNumBand@4 tqsl_getNumBand=_tqsl_getNumBand@4 _tqsl_getNumDXCCEntity@4 tqsl_getNumDXCCEntity@4=_tqsl_getNumDXCCEntity@4 tqsl_getNumDXCCEntity=_tqsl_getNumDXCCEntity@4 _tqsl_getNumLocationField@8 tqsl_getNumLocationField@8=_tqsl_getNumLocationField@8 tqsl_getNumLocationField=_tqsl_getNumLocationField@8 _tqsl_getNumLocationFieldListItems@12 tqsl_getNumLocationFieldListItems@12=_tqsl_getNumLocationFieldListItems@12 tqsl_getNumLocationFieldListItems=_tqsl_getNumLocationFieldListItems@12 _tqsl_getNumMode@4 tqsl_getNumMode@4=_tqsl_getNumMode@4 tqsl_getNumMode=_tqsl_getNumMode@4 _tqsl_getNumPropagationMode@4 tqsl_getNumPropagationMode@4=_tqsl_getNumPropagationMode@4 tqsl_getNumPropagationMode=_tqsl_getNumPropagationMode@4 _tqsl_getNumProviders@4 tqsl_getNumProviders@4=_tqsl_getNumProviders@4 tqsl_getNumProviders=_tqsl_getNumProviders@4 _tqsl_getNumSatellite@4 tqsl_getNumSatellite@4=_tqsl_getNumSatellite@4 tqsl_getNumSatellite=_tqsl_getNumSatellite@4 _tqsl_getNumStationLocations@8 tqsl_getNumStationLocations@8=_tqsl_getNumStationLocations@8 tqsl_getNumStationLocations=_tqsl_getNumStationLocations@8 _tqsl_getPropagationMode@12 tqsl_getPropagationMode@12=_tqsl_getPropagationMode@12 tqsl_getPropagationMode=_tqsl_getPropagationMode@12 _tqsl_getProvider@8 tqsl_getProvider@8=_tqsl_getProvider@8 tqsl_getProvider=_tqsl_getProvider@8 _tqsl_getSatellite@20 tqsl_getSatellite@20=_tqsl_getSatellite@20 tqsl_getSatellite=_tqsl_getSatellite@20 _tqsl_getSerialFromTQSLFile@8 tqsl_getSerialFromTQSLFile@8=_tqsl_getSerialFromTQSLFile@8 tqsl_getSerialFromTQSLFile=_tqsl_getSerialFromTQSLFile@8 _tqsl_getStationLocation@8 tqsl_getStationLocation@8=_tqsl_getStationLocation@8 tqsl_getStationLocation=_tqsl_getStationLocation@8 _tqsl_getStationLocationCallSign@16 tqsl_getStationLocationCallSign@16=_tqsl_getStationLocationCallSign@16 tqsl_getStationLocationCallSign=_tqsl_getStationLocationCallSign@16 _tqsl_getStationLocationCaptureName@12 tqsl_getStationLocationCaptureName@12=_tqsl_getStationLocationCaptureName@12 tqsl_getStationLocationCaptureName=_tqsl_getStationLocationCaptureName@12 _tqsl_getStationLocationCapturePage@8 tqsl_getStationLocationCapturePage@8=_tqsl_getStationLocationCapturePage@8 tqsl_getStationLocationCapturePage=_tqsl_getStationLocationCapturePage@8 _tqsl_getStationLocationName@16 tqsl_getStationLocationName@16=_tqsl_getStationLocationName@16 tqsl_getStationLocationName=_tqsl_getStationLocationName@16 _tqsl_getVersion@8 tqsl_getVersion@8=_tqsl_getVersion@8 tqsl_getVersion=_tqsl_getVersion@8 _tqsl_hasNextStationLocationCapture@8 tqsl_hasNextStationLocationCapture@8=_tqsl_hasNextStationLocationCapture@8 tqsl_hasNextStationLocationCapture=_tqsl_hasNextStationLocationCapture@8 _tqsl_hasPrevStationLocationCapture@8 tqsl_hasPrevStationLocationCapture@8=_tqsl_hasPrevStationLocationCapture@8 tqsl_hasPrevStationLocationCapture=_tqsl_hasPrevStationLocationCapture@8 _tqsl_importPKCS12File@24 tqsl_importPKCS12File@24=_tqsl_importPKCS12File@24 tqsl_importPKCS12File=_tqsl_importPKCS12File@24 _tqsl_importTQSLFile@12 tqsl_importTQSLFile@12=_tqsl_importTQSLFile@12 tqsl_importTQSLFile=_tqsl_importTQSLFile@12 _tqsl_init@0 tqsl_init@0=_tqsl_init@0 tqsl_init=_tqsl_init@0 _tqsl_initDate@8 tqsl_initDate@8=_tqsl_initDate@8 tqsl_initDate=_tqsl_initDate@8 _tqsl_initStationLocationCapture@4 tqsl_initStationLocationCapture@4=_tqsl_initStationLocationCapture@4 tqsl_initStationLocationCapture=_tqsl_initStationLocationCapture@4 _tqsl_initTime@8 tqsl_initTime@8=_tqsl_initTime@8 tqsl_initTime=_tqsl_initTime@8 _tqsl_isDateNull@4 tqsl_isDateNull@4=_tqsl_isDateNull@4 tqsl_isDateNull=_tqsl_isDateNull@4 _tqsl_isDateValid@4 tqsl_isDateValid@4=_tqsl_isDateValid@4 tqsl_isDateValid=_tqsl_isDateValid@4 _tqsl_isTimeValid@4 tqsl_isTimeValid@4=_tqsl_isTimeValid@4 tqsl_isTimeValid=_tqsl_isTimeValid@4 _tqsl_nextStationLocationCapture@4 tqsl_nextStationLocationCapture@4=_tqsl_nextStationLocationCapture@4 tqsl_nextStationLocationCapture=_tqsl_nextStationLocationCapture@4 _tqsl_prevStationLocationCapture@4 tqsl_prevStationLocationCapture@4=_tqsl_prevStationLocationCapture@4 tqsl_prevStationLocationCapture=_tqsl_prevStationLocationCapture@4 _tqsl_saveStationLocationCapture@8 tqsl_saveStationLocationCapture@8=_tqsl_saveStationLocationCapture@8 tqsl_saveStationLocationCapture=_tqsl_saveStationLocationCapture@8 _tqsl_selectCertificates@28 tqsl_selectCertificates@28=_tqsl_selectCertificates@28 tqsl_selectCertificates=_tqsl_selectCertificates@28 _tqsl_setADIFConverterDateFilter@12 tqsl_setADIFConverterDateFilter@12=_tqsl_setADIFConverterDateFilter@12 tqsl_setADIFConverterDateFilter=_tqsl_setADIFConverterDateFilter@12 _tqsl_setADIFMode@8 tqsl_setADIFMode@8=_tqsl_setADIFMode@8 tqsl_setADIFMode=_tqsl_setADIFMode@8 _tqsl_setCabrilloMapEntry@12 tqsl_setCabrilloMapEntry@12=_tqsl_setCabrilloMapEntry@12 tqsl_setCabrilloMapEntry=_tqsl_setCabrilloMapEntry@12 _tqsl_setConverterAllowBadCall@8 tqsl_setConverterAllowBadCall@8=_tqsl_setConverterAllowBadCall@8 tqsl_setConverterAllowBadCall=_tqsl_setConverterAllowBadCall@8 _tqsl_setConverterAllowDuplicates@8 tqsl_setConverterAllowDuplicates@8=_tqsl_setConverterAllowDuplicates@8 tqsl_setConverterAllowDuplicates=_tqsl_setConverterAllowDuplicates@8 _tqsl_setDirectory@4 tqsl_setDirectory@4=_tqsl_setDirectory@4 tqsl_setDirectory=_tqsl_setDirectory@4 _tqsl_setLocationFieldCharData@12 tqsl_setLocationFieldCharData@12=_tqsl_setLocationFieldCharData@12 tqsl_setLocationFieldCharData=_tqsl_setLocationFieldCharData@12 _tqsl_setLocationFieldIndex@12 tqsl_setLocationFieldIndex@12=_tqsl_setLocationFieldIndex@12 tqsl_setLocationFieldIndex=_tqsl_setLocationFieldIndex@12 _tqsl_setLocationFieldIntData@12 tqsl_setLocationFieldIntData@12=_tqsl_setLocationFieldIntData@12 tqsl_setLocationFieldIntData=_tqsl_setLocationFieldIntData@12 _tqsl_setStationLocationCaptureName@8 tqsl_setStationLocationCaptureName@8=_tqsl_setStationLocationCaptureName@8 tqsl_setStationLocationCaptureName=_tqsl_setStationLocationCaptureName@8 _tqsl_setStationLocationCapturePage@8 tqsl_setStationLocationCapturePage@8=_tqsl_setStationLocationCapturePage@8 tqsl_setStationLocationCapturePage=_tqsl_setStationLocationCapturePage@8 _tqsl_signDataBlock@20 tqsl_signDataBlock@20=_tqsl_signDataBlock@20 tqsl_signDataBlock=_tqsl_signDataBlock@20 _tqsl_signQSORecord@20 tqsl_signQSORecord@20=_tqsl_signQSORecord@20 tqsl_signQSORecord=_tqsl_signQSORecord@20 _tqsl_updateStationLocationCapture@4 tqsl_updateStationLocationCapture@4=_tqsl_updateStationLocationCapture@4 tqsl_updateStationLocationCapture=_tqsl_updateStationLocationCapture@4 _tqsl_verifyDataBlock@20 tqsl_verifyDataBlock@20=_tqsl_verifyDataBlock@20 tqsl_verifyDataBlock=_tqsl_verifyDataBlock@20 tQSL_ADIF_Error DATA tQSL_Cabrillo_Error DATA tQSL_CustomError DATA tQSL_Errno DATA tQSL_Error DATA tQSL_ErrorFile DATA tqsl-2.5.1/src/tqsllib.spec.in0000664000076400007640000000276413563134442016252 0ustar rmurphyrmurphySummary: The TrustedQSL library Name: tqsllib Version: @VERSION@ Release: 2 Copyright: Custom BSD-like Group: Development/Libraries Source: tqsllib-%{version}.tar.gz BuildRoot: /var/tmp/%{name}-buildroot Requires: openssl expat zlib BuildPrereq: openssl-devel expat-devel zlib-devel %package devel Summary: The TrustedQSL Library development tools Group: System/Libraries %description The TrustedQSL library is used for generating digitally signed QSO records (records of Amateur Radio contacts). This package contains the library and configuration files needed to run TrustedQSL applications. %description devel The TrustedQSL library is used for generating digitally signed QSO records (records of Amateur Radio contacts). This package contains the header files needed to build TrustedQSL applications as well as a static tqsllib library and API documentation. %prep %setup -q -n tqsllib-%{version} %build # use --disable-docs because RPM handles docs itself cmake -DCMAKE_INSTALL_PREFIX=/usr . make %install make install %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root) %doc LICENSE ChangeLog /usr/lib/libtqsllib.so /usr/lib/libtqsllib.so.1 /usr/lib/libtqsllib.so.1.0.0 /usr/share/tqsl/config.xml /usr/bin/dumptqsldata %files devel %defattr(-,root,root) %doc LICENSE ChangeLog doxygen/html /usr/lib/libtqsllib.a /usr/lib/libtqsllib.la /usr/include/tqsllib.h /usr/include/tqslerrno.h /usr/include/cabrillo.h /usr/include/adif.h /usr/include/tqslconvert.h %post /sbin/ldconfig %postun /sbin/ldconfig tqsl-2.5.1/src/tqsllib.rc.in0000664000076400007640000000210313563134442015707 0ustar rmurphyrmurphy#include 1 VERSIONINFO FILEVERSION @TQSLLIB_VERSION_MAJOR@, @TQSLLIB_VERSION_MINOR@, 0, 0 PRODUCTVERSION @TQSLLIB_VERSION_MAJOR@, @TQSLLIB_VERSION_MINOR@, 0, 0 FILEFLAGSMASK 0 FILEFLAGS VS_FFI_FILEFLAGSMASK FILEOS VOS__WINDOWS32 FILETYPE VFT_DLL BEGIN BLOCK "StringFileInfo" BEGIN // Language type = U.S. English(0x0409) and Character Set = Windows, Multilingual(0x04e4) BLOCK "040904E4" // Matches VarFileInfo Translation hex value. BEGIN VALUE "CompanyName", "American Radio Relay League, Inc.\000" VALUE "FileDescription", "TrustedQSL Library\000" VALUE "FileVersion", "@TQSLLIB_VERSION@\000" VALUE "InternalName", "TQSLLIB\000" VALUE "LegalCopyright", "Copyright 2019 American Radio Relay League.\000" VALUE "OriginalFilename", "tqsllib@DLLVER@.dll\000" VALUE "ProductName", "TQSLLIB\000" VALUE "ProductVersion", "@TQSLLIB_VERSION@\000" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x0409, 0x04e4 // U.S. English(0x0409) & Windows Multilingual(0x04e4) 1252 END END tqsl-2.5.1/src/tqsllib.h0000664000076400007640000016076113563134442015144 0ustar rmurphyrmurphy/*************************************************************************** tqsllib.h - description ------------------- begin : Mon May 20 2002 copyright : (C) 2002 by ARRL author : Jon Bloom email : jbloom@arrl.org revision : $Id: tqsllib.h,v 1.14 2013/03/01 13:26:44 k1mu Exp $ ***************************************************************************/ #ifndef TQSLLIB_H #define TQSLLIB_H #if defined(_WIN32) && !defined(TQSL_NODLL) #ifdef TQSLLIB_DEF #define DLLEXPORT __declspec(dllexport) #define DLLEXPORTDATA __declspec(dllexport) #define CALLCONVENTION __stdcall #else #define DLLEXPORT __declspec(dllimport) #define DLLEXPORTDATA __declspec(dllimport) #define CALLCONVENTION __stdcall #endif #else #define DLLEXPORT ///< Symbol exports - Windows only #define DLLEXPORTDATA ///< Symbol exports - Windows only #define CALLCONVENTION ///< Symbol exports - Windows only #endif #include "adif.h" #include "cabrillo.h" /** \file * tQSL library functions. */ /* Sizes */ #define TQSL_MAX_PATH_LEN 256 ///< Max length of a FS path #define TQSL_PASSWORD_MAX 80 ///< Max password length #define TQSL_NAME_ELEMENT_MAX 256 ///< Max Org name length #define TQSL_CALLSIGN_MAX 13 ///< Max callsign length #define TQSL_CRQ_NAME_MAX 60 ///< Max length of request name #define TQSL_CRQ_ADDR_MAX 80 ///< Max length of request addr #define TQSL_CRQ_CITY_MAX 80 ///< Max length of request city #define TQSL_CRQ_STATE_MAX 80 ///< Max length of request state #define TQSL_CRQ_POSTAL_MAX 20 ///< Max length of request zip #define TQSL_CRQ_COUNTRY_MAX 80 ///< Max length of req entity #define TQSL_CRQ_EMAIL_MAX 180 ///< Max length of req email #define TQSL_BAND_MAX 6 ///< Max length of a band name #define TQSL_MODE_MAX 16 ///< Max length of a mode name #define TQSL_FREQ_MAX 20 ///< Max length of a frequency #define TQSL_SATNAME_MAX 20 ///< Max length of a sat name #define TQSL_PROPMODE_MAX 20 ///< Max length of a prop mode #define TQSL_CERT_CB_USER 0 ///< Callback is for user cert #define TQSL_CERT_CB_CA 1 ///< Callback is for CA cert #define TQSL_CERT_CB_ROOT 2 ///< Callback is for root cert #define TQSL_CERT_CB_PKEY 3 ///< Callback is for private key #define TQSL_CERT_CB_CONFIG 4 ///< Callback for config file #define TQSL_CERT_CB_CERT_TYPE(x) ((x) & 0xf) ///< Type of the cert #define TQSL_CERT_CB_MILESTONE 0 ///< New certificate #define TQSL_CERT_CB_RESULT 0x10 ///< Cert import result #define TQSL_CERT_CB_CALL_TYPE(x) ((x) & TQSL_CERT_CB_RESULT) ///< Callback type #define TQSL_CERT_CB_PROMPT 0 ///< Callback prompt #define TQSL_CERT_CB_DUPLICATE 0x100 ///< Dupe cert callback #define TQSL_CERT_CB_ERROR 0x200 ///< Error import callback #define TQSL_CERT_CB_LOADED 0x300 ///< Cert loaded callback #define TQSL_CERT_CB_RESULT_TYPE(x) ((x) & 0x0f00) ///< Result type mask typedef void * tQSL_Cert; ///< Opaque certificate type typedef void * tQSL_Location; ///< Opaque location type typedef char * tQSL_StationDataEnc; ///< Opaque station data type /** Struct that holds y-m-d */ typedef struct { int year; ///< Numeric year int month; ///< Numeric month int day; ///< Numeric day } tQSL_Date; /** Struct that holds h-m-s */ typedef struct { int hour; ///< Time hour field int minute; ///< Time minute field int second; ///< Time seconds field } tQSL_Time; /** Certificate provider data */ typedef struct tqsl_provider_st { char organizationName[TQSL_NAME_ELEMENT_MAX+1]; ///< Provider name char organizationalUnitName[TQSL_NAME_ELEMENT_MAX+1]; ///< Provider unit char emailAddress[TQSL_NAME_ELEMENT_MAX+1]; ///< Provider e-mail char url[TQSL_NAME_ELEMENT_MAX+1]; ///< Provider URL } TQSL_PROVIDER; /** Certificate request data */ typedef struct tqsl_cert_req_st { ///< Cert request data char providerName[TQSL_NAME_ELEMENT_MAX+1]; ///< Provider name char providerUnit[TQSL_NAME_ELEMENT_MAX+1]; ///< Provider unit char callSign[TQSL_CALLSIGN_MAX+1]; ///< Callsign char name[TQSL_CRQ_NAME_MAX+1]; ///< Name char address1[TQSL_CRQ_ADDR_MAX+1]; ///< Address 1 char address2[TQSL_CRQ_ADDR_MAX+1]; ///< Address 2 char city[TQSL_CRQ_CITY_MAX+1]; ///< City char state[TQSL_CRQ_STATE_MAX+1]; ///< State char postalCode[TQSL_CRQ_POSTAL_MAX+1]; ///< Postal Code char country[TQSL_CRQ_COUNTRY_MAX+1]; ///< Country char emailAddress[TQSL_CRQ_EMAIL_MAX+1]; ///< e-mail int dxccEntity; ///< DXCC Entity code tQSL_Date qsoNotBefore; ///< QSOs not before date tQSL_Date qsoNotAfter; ///< QSOs not after date char password[TQSL_PASSWORD_MAX+1]; ///< Password tQSL_Cert signer; ///< Signing cert char renew; ///< Rewewal reference } TQSL_CERT_REQ; /** QSO data */ typedef struct { char callsign[TQSL_CALLSIGN_MAX+1]; ///< QSO callsign char band[TQSL_BAND_MAX+1]; ///< QSO band char mode[TQSL_MODE_MAX+1]; ///< QSO mode char submode[TQSL_MODE_MAX+1]; ///< QSO submode tQSL_Date date; ///< QSO date tQSL_Time time; ///< QSO time char freq[TQSL_FREQ_MAX+1]; ///< QSO frequency char rxfreq[TQSL_FREQ_MAX+1]; ///< QSO receive frequency char rxband[TQSL_BAND_MAX+1]; ///< QSO RX band char propmode[TQSL_PROPMODE_MAX+1]; ///< QSO prop mode char satname[TQSL_SATNAME_MAX+1]; ///< QSO satellite name bool callsign_set; ///< QSO specifies a call worked bool mode_set; ///< QSO specifies a mode bool band_set; ///< QSO specifies a band or frequency bool date_set; ///< QSO specifies a date bool time_set; ///< QSO specifies a time } TQSL_QSO_RECORD; /// Base directory for tQSL library working files. DLLEXPORTDATA extern const char *tQSL_BaseDir; DLLEXPORTDATA extern const char *tQSL_RsrcDir; #ifdef __cplusplus extern "C" { #endif /** \defgroup Util Utility API */ /** @{ */ /// Error code from most recent tQSL library call. /** * The values for the error code are defined in tqslerrno.h */ DLLEXPORTDATA extern int tQSL_Error; /// The ADIF error code DLLEXPORTDATA extern TQSL_ADIF_GET_FIELD_ERROR tQSL_ADIF_Error; /// The ADIF error code DLLEXPORTDATA extern TQSL_CABRILLO_ERROR_TYPE tQSL_Cabrillo_Error; /// File name of file giving error. (May be empty.) DLLEXPORTDATA extern char tQSL_ErrorFile[256]; /// Custom error message string DLLEXPORTDATA extern char tQSL_CustomError[256]; /// System errno - stored when tQSL_Error == TQSL_SYSTEM_ERROR DLLEXPORTDATA extern int tQSL_Errno; /// Callsign used in import - used for missing public key error DLLEXPORTDATA extern char tQSL_ImportCall[256]; /// Serial number of recent certificate import DLLEXPORTDATA extern long tQSL_ImportSerial; /// Diagnostic log file DLLEXPORTDATA extern FILE* tQSL_DiagFile; /** Initialize the tQSL library * * This function should be called prior to calling any other library functions. */ DLLEXPORT int CALLCONVENTION tqsl_init(); /** Set the directory where the TQSL files are kept. * May be called either before of after tqsl_init(), but should be called * before calling any other functions in the library. * * Note that this is purely optional. The library will figure out an * approriate directory if tqsl_setDirectory isn't called. Unless there is * some particular need to set the directory explicitly, programs should * refrain from doing so. */ DLLEXPORT int CALLCONVENTION tqsl_setDirectory(const char *dir); /** Gets the error string for the current tQSL library error and resets the error status. * See tqsl_getErrorString_v(). */ DLLEXPORT const char* CALLCONVENTION tqsl_getErrorString(); /** Gets the error string corresponding to the given error number. * The error string is available only until the next call to * tqsl_getErrorString_v or tqsl_getErrorString. */ DLLEXPORT const char* CALLCONVENTION tqsl_getErrorString_v(int err); /** Encode a block of data into Base64 text. * * \li \c data = block of data to encode * \li \c datalen = length of \c data in bytes * \li \c output = pointer to output buffer * \li \c outputlen = size of output buffer in bytes */ DLLEXPORT int CALLCONVENTION tqsl_encodeBase64(const unsigned char *data, int datalen, char *output, int outputlen); /** Decode Base64 text into binary data. * * \li \c input = NUL-terminated text string of Base64-encoded data * \li \c data = pointer to output buffer * \li \c datalen = pointer to int containing the size of the output buffer in bytes * * Places the number of resulting data bytes into \c *datalen. */ DLLEXPORT int CALLCONVENTION tqsl_decodeBase64(const char *input, unsigned char *data, int *datalen); /** Initialize a tQSL_Date object from a date string. * * The date string must be YYYY-MM-DD or YYYYMMDD format. * * Returns 0 on success, nonzero on failure */ DLLEXPORT int CALLCONVENTION tqsl_initDate(tQSL_Date *date, const char *str); /** Initialize a tQSL_Time object from a time string. * * The time string must be HH[:]MM[[:]SS] format. * * Returns 0 on success, nonzero on failure */ DLLEXPORT int CALLCONVENTION tqsl_initTime(tQSL_Time *time, const char *str); /** Compare two tQSL_Date objects. * * Returns: * - -1 if \c a < \c b * * - 0 if \c a == \c b * * - 1 if \c a > \c b */ DLLEXPORT int CALLCONVENTION tqsl_compareDates(const tQSL_Date *a, const tQSL_Date *b); /** Calculate the number of days between two tQSL_Date objects. * * Returns a positive result if the first date is earlier, otherwise * negative. */ DLLEXPORT int CALLCONVENTION tqsl_subtractDates(const tQSL_Date *a, const tQSL_Date *b, int *diff); /** Converts a tQSL_Date object to a YYYY-MM-DD string. * * Returns a pointer to \c buf or NULL on error */ DLLEXPORT char* CALLCONVENTION tqsl_convertDateToText(const tQSL_Date *date, char *buf, int bufsiz); /** Test whether a tQSL_Date contains a valid date value * * Returns 1 if the date is valid */ DLLEXPORT int CALLCONVENTION tqsl_isDateValid(const tQSL_Date *d); /** Test whether a tQSL_Date is empty (contains all zeroes) * * Returns 1 if the date is null */ DLLEXPORT int CALLCONVENTION tqsl_isDateNull(const tQSL_Date *d); /** Test whether a tQSL_Time contains a valid time value * * Returns 1 if the time is valid */ DLLEXPORT int CALLCONVENTION tqsl_isTimeValid(const tQSL_Time *t); /** Converts a tQSL_Time object to a HH:MM:SSZ string. * * Returns a pointer to \c buf or NULL on error */ DLLEXPORT char* CALLCONVENTION tqsl_convertTimeToText(const tQSL_Time *time, char *buf, int bufsiz); /** Returns the library version. \c major and/or \c minor may be NULL. */ DLLEXPORT int CALLCONVENTION tqsl_getVersion(int *major, int *minor); /** Returns the configuration-file version. \c major and/or \c minor may be NULL. */ DLLEXPORT int CALLCONVENTION tqsl_getConfigVersion(int *major, int *minor); /** @} */ /** \defgroup CertStuff Certificate Handling API * * Certificates are managed by manipulating \c tQSL_Cert objects. A \c tQSL_Cert * contains: * * \li The identity of the organization that issued the certificate (the "issuer"). * \li The name and call sign of the amateur radio operator (ARO). * \li The DXCC entity number for which this certificate is valid. * \li The range of QSO dates for which this certificate can be used. * \li The resources needed to digitally sign and verify QSO records. * * The certificate management process consists of: * * \li Applying for a certificate. Certificate requests are produced via the * tqsl_createCertRequest() function, which produces a certificate-request * file to send to the issuer. * \li Importing the certificate file received from the issuer into the local * "certificate store," a directory managed by the tQSL library, via * tqsl_importTQSLFile(). * \li Selecting an appropriate certificate to use to sign a QSO record via * tqsl_selectCertificates(). */ /** @{ */ #define TQSL_SELECT_CERT_WITHKEYS 1 ///< Private keys only (no cert) #define TQSL_SELECT_CERT_EXPIRED 2 ///< Include expired certs #define TQSL_SELECT_CERT_SUPERCEDED 4 ///< Include superseded certs /** Get a list of certificates * * Selects a set of certificates from the user's certificate store * based on optional selection criteria. The function produces a * list of tQSL_Cert objects. * * \li \c certlist - Pointer to a variable that is set by the * function to point to the list of tQSL_Cert objects. * \li \c ncerts - Pointer to an int that is set to the number * of objects in the \c certlist list. * \li \c callsign - Optional call sign to match. * \li \c date - Optional QSO date string in ISO format. Only certs * that have a QSO date range that encompasses this date will be * returned. * \li \c issuer - Optional issuer (DN) string to match. * \li \c flag - OR of \c TQSL_SELECT_CERT_EXPIRED (include expired * certs), \c TQSL_SELECT_CERT_SUPERCEDED and \c TQSL_SELECT_CERT_WITHKEYS * (keys that don't have associated certs will be returned). * * Returns 0 on success, nonzero on failure. * * Each of the tQSL_Cert objects in the list should be freed * by calling tqsl_freeCertificate(). tqsl_freeCertificateList() is a better * function to use for that as it also frees the allocated array that * holds the certificate pointers. * */ DLLEXPORT int CALLCONVENTION tqsl_selectCertificates(tQSL_Cert **certlist, int *ncerts, const char *callsign, int dxcc, const tQSL_Date *date, const TQSL_PROVIDER *issuer, int flag); /** Get a list of authority certificates * * Selects a set of certificates from the root or authorities certificate stores * The function produces a list of tQSL_Cert objects. * * Each of the tQSL_Cert objects in the list should be freed * by calling tqsl_freeCertificate(). tqsl_freeCertificateList() is a better * function to use for that as it also frees the allocated array that * holds the certificate pointers. * */ DLLEXPORT int CALLCONVENTION tqsl_selectCACertificates(tQSL_Cert **certlist, int *ncerts, const char *type); /** Get a particulat certificate from the list returnded by * tqsl_selectCertificates. This function exists principally * to make it easier for VB programs to access the list of * certificates. * * It is the caller's responsibility to ensure that 0 <= idx < ncerts * (where ncerts is the value returned by tqsl_selectCertificates) */ DLLEXPORT int CALLCONVENTION tqsl_getSelectedCertificate(tQSL_Cert *cert, const tQSL_Cert **certlist, int idx); /** Find out if the "certificate" is expired */ DLLEXPORT int CALLCONVENTION tqsl_isCertificateExpired(tQSL_Cert cert, int *status); /** Find out if the "certificate" is superceded */ DLLEXPORT int CALLCONVENTION tqsl_isCertificateSuperceded(tQSL_Cert cert, int *status); /** Find out if the "certificate" is just a key pair. */ DLLEXPORT int CALLCONVENTION tqsl_getCertificateKeyOnly(tQSL_Cert cert, int *keyonly); /** Get the encoded certificate for inclusion in a GABBI file. */ DLLEXPORT int CALLCONVENTION tqsl_getCertificateEncoded(tQSL_Cert cert, char *buf, int bufsiz); /** Get the encoded private key for inclusion in a backup file. */ DLLEXPORT int CALLCONVENTION tqsl_getKeyEncoded(tQSL_Cert cert, char *buf, int bufsiz); /** Import a base64 encoded certificate and private key from a backup file. */ DLLEXPORT int CALLCONVENTION tqsl_importKeyPairEncoded(const char *callsign, const char *type, const char *keybuf, const char *certbuf); /** Get the issuer's serial number of the certificate. */ DLLEXPORT int CALLCONVENTION tqsl_getCertificateSerial(tQSL_Cert cert, long *serial); /** Get the issuer's serial number of the certificate as a hexadecimal string. * Needed for certs with long serial numbers (typically root certs). */ DLLEXPORT int CALLCONVENTION tqsl_getCertificateSerialExt(tQSL_Cert cert, char *serial, int serialsiz); /** Get the length of the issuer's serial number of the certificate as it will be * returned by tqsl_getCertificateSerialExt. */ DLLEXPORT int CALLCONVENTION tqsl_getCertificateSerialLength(tQSL_Cert cert); /** Get the issuer (DN) string from a tQSL_Cert. * * \li \c cert - a tQSL_Cert object, normally one returned from * tqsl_selectCertificates() * \li \c buf - Buffer to hold the returned string. * \li \c bufsiz - Size of \c buf. * * Returns 0 on success, nonzero on failure. */ DLLEXPORT int CALLCONVENTION tqsl_getCertificateIssuer(tQSL_Cert cert, char *buf, int bufsiz); /** Get the issuer's organization name from a tQSL_Cert. * * \li \c cert - a tQSL_Cert object, normally one returned from * tqsl_selectCertificates() * \li \c buf - Buffer to hold the returned string. * \li \c bufsiz - Size of \c buf. * * Returns 0 on success, nonzero on failure. */ DLLEXPORT int CALLCONVENTION tqsl_getCertificateIssuerOrganization(tQSL_Cert cert, char *buf, int bufsiz); /** Get the issuer's organizational unit name from a tQSL_Cert. * * \li \c cert - a tQSL_Cert object, normally one returned from * tqsl_selectCertificates() * \li \c buf - Buffer to hold the returned string. * \li \c bufsiz - Size of \c buf. * * Returns 0 on success, nonzero on failure. */ DLLEXPORT int CALLCONVENTION tqsl_getCertificateIssuerOrganizationalUnit(tQSL_Cert cert, char *buf, int bufsiz); /** Get the ARO call sign string from a tQSL_Cert. * * \li \c cert - a tQSL_Cert object, normally one returned from * tqsl_selectCertificates() * \li \c buf - Buffer to hold the returned string. * \li \c bufsiz - Size of \c buf. * * Returns 0 on success, nonzero on failure. */ DLLEXPORT int CALLCONVENTION tqsl_getCertificateCallSign(tQSL_Cert cert, char *buf, int bufsiz); /** Get the ARO name string from a tQSL_Cert. * * \li \c cert - a tQSL_Cert object, normally one returned from * tqsl_selectCertificates() * \li \c buf - Buffer to hold the returned string. * \li \c bufsiz - Size of \c buf. * * Returns 0 on success, nonzero on failure. */ DLLEXPORT int CALLCONVENTION tqsl_getCertificateAROName(tQSL_Cert cert, char *buf, int bufsiz); /** Get the email address from a tQSL_Cert. * * \li \c cert - a tQSL_Cert object, normally one returned from * tqsl_selectCertificates() * \li \c buf - Buffer to hold the returned string. * \li \c bufsiz - Size of \c buf. * * Returns 0 on success, nonzero on failure. */ DLLEXPORT int CALLCONVENTION tqsl_getCertificateEmailAddress(tQSL_Cert cert, char *buf, int bufsiz); /** Get the QSO not-before date from a tQSL_Cert. * * \li \c cert - a tQSL_Cert object, normally one returned from * tqsl_selectCertificates() * \li \c date - Pointer to a tQSL_Date struct to hold the returned date. * * Returns 0 on success, nonzero on failure. */ DLLEXPORT int CALLCONVENTION tqsl_getCertificateQSONotBeforeDate(tQSL_Cert cert, tQSL_Date *date); /** Get the QSO not-after date from a tQSL_Cert. * * \li \c cert - a tQSL_Cert object, normally one returned from * tqsl_selectCertificates() * \li \c date - Pointer to a tQSL_Date struct to hold the returned date. * * Returns 0 on success, nonzero on failure. */ DLLEXPORT int CALLCONVENTION tqsl_getCertificateQSONotAfterDate(tQSL_Cert cert, tQSL_Date *date); /** Get the certificate's not-before date from a tQSL_Cert. * * \li \c cert - a tQSL_Cert object, normally one returned from * tqsl_selectCertificates() * \li \c date - Pointer to a tQSL_Date struct to hold the returned date. * * Returns 0 on success, nonzero on failure. */ DLLEXPORT int CALLCONVENTION tqsl_getCertificateNotBeforeDate(tQSL_Cert cert, tQSL_Date *date); /** Get the certificate's not-after date from a tQSL_Cert. * * \li \c cert - a tQSL_Cert object, normally one returned from * tqsl_selectCertificates() * \li \c date - Pointer to a tQSL_Date struct to hold the returned date. * * Returns 0 on success, nonzero on failure. */ DLLEXPORT int CALLCONVENTION tqsl_getCertificateNotAfterDate(tQSL_Cert cert, tQSL_Date *date); /** Get the DXCC entity number from a tQSL_Cert. * * \li \c cert - a tQSL_Cert object, normally one returned from * tqsl_selectCertificates() * \li \c dxcc - Pointer to an int to hold the returned date. * * Returns 0 on success, nonzero on failure. */ DLLEXPORT int CALLCONVENTION tqsl_getCertificateDXCCEntity(tQSL_Cert cert, int *dxcc); /** Get the first address line from the certificate request used in applying * for a tQSL_Cert certificate. * * \li \c cert - a tQSL_Cert object, normally one returned from * tqsl_selectCertificates() * \li \c buf - Buffer to hold the returned string. * \li \c bufsiz - Size of \c buf. * * Returns 0 on success, nonzero on failure. */ DLLEXPORT int CALLCONVENTION tqsl_getCertificateRequestAddress1(tQSL_Cert cert, char *str, int bufsiz); /** Get the second address line from the certificate request used in applying * for a tQSL_Cert certificate. * * \li \c cert - a tQSL_Cert object, normally one returned from * tqsl_selectCertificates() * \li \c buf - Buffer to hold the returned string. * \li \c bufsiz - Size of \c buf. * * Returns 0 on success, nonzero on failure. */ DLLEXPORT int CALLCONVENTION tqsl_getCertificateRequestAddress2(tQSL_Cert cert, char *str, int bufsiz); /** Get the city from the certificate request used in applying * for a tQSL_Cert certificate. * * \li \c cert - a tQSL_Cert object, normally one returned from * tqsl_selectCertificates() * \li \c buf - Buffer to hold the returned string. * \li \c bufsiz - Size of \c buf. * * Returns 0 on success, nonzero on failure. */ DLLEXPORT int CALLCONVENTION tqsl_getCertificateRequestCity(tQSL_Cert cert, char *str, int bufsiz); /** Get the state from the certificate request used in applying * for a tQSL_Cert certificate. * * \li \c cert - a tQSL_Cert object, normally one returned from * tqsl_selectCertificates() * \li \c buf - Buffer to hold the returned string. * \li \c bufsiz - Size of \c buf. * * Returns 0 on success, nonzero on failure. */ DLLEXPORT int CALLCONVENTION tqsl_getCertificateRequestState(tQSL_Cert cert, char *str, int bufsiz); /** Get the postal (ZIP) code from the certificate request used in applying * for a tQSL_Cert certificate. * * \li \c cert - a tQSL_Cert object, normally one returned from * tqsl_selectCertificates() * \li \c buf - Buffer to hold the returned string. * \li \c bufsiz - Size of \c buf. * * Returns 0 on success, nonzero on failure. */ DLLEXPORT int CALLCONVENTION tqsl_getCertificateRequestPostalCode(tQSL_Cert cert, char *str, int bufsiz); /** Get the country from the certificate request used in applying * for a tQSL_Cert certificate. * * \li \c cert - a tQSL_Cert object, normally one returned from * tqsl_selectCertificates() * \li \c buf - Buffer to hold the returned string. * \li \c bufsiz - Size of \c buf. * * Returns 0 on success, nonzero on failure. */ DLLEXPORT int CALLCONVENTION tqsl_getCertificateRequestCountry(tQSL_Cert cert, char *str, int bufsiz); #define TQSL_PK_TYPE_ERR 0 ///< Error retrieving private key #define TQSL_PK_TYPE_NONE 1 ///< No private key #define TQSL_PK_TYPE_UNENC 2 ///< Private key is not encrypted #define TQSL_PK_TYPE_ENC 3 ///< Private key is encrypted /** Determine the nature of the private key associated with a * certificate. * * \li \c cert - a tQSL_Cert object, normally one returned from * tqsl_selectCertificates() * * Returns one of the following values: * * \li \c TQSL_PK_TYPE_ERR - An error occurred. Use tqsl_getErrorString() to examine. * \li \c TQSL_PK_TYPE_NONE - No matching private key was found. * \li \c TQSL_PK_TYPE_UNENC - The matching private key is unencrypted. * \li \c TQSL_PK_TYPE_ENC - The matching private key is encrypted * (password protected). */ DLLEXPORT int CALLCONVENTION tqsl_getCertificatePrivateKeyType(tQSL_Cert cert); /** Free the memory used by the tQSL_Cert. Once this function is called, * \c cert should not be used again in any way. */ DLLEXPORT void CALLCONVENTION tqsl_freeCertificate(tQSL_Cert cert); /** Free the memory used by a certificate list. The allocated list * of tQSL_Certs are freed and the pointer array is freed. * Once this function is called, the \c list or the \c cert * should not be used again in any way. */ DLLEXPORT void CALLCONVENTION tqsl_freeCertificateList(tQSL_Cert* list, int ncerts); #define TQSL_CERT_STATUS_UNK 0 ///< Status is unknown #define TQSL_CERT_STATUS_SUP 1 ///< Certificate is superceded #define TQSL_CERT_STATUS_EXP 2 ///< Certificate is expired #define TQSL_CERT_STATUS_OK 3 ///< Certificate is valid #define TQSL_CERT_STATUS_INV 4 ///< Invalid serial number /** Determine the status of a callsign certificate * \li \c serial - the serial number of the certificate * tqsl_selectCertificates() * \li \c status - an integer to receive the certificate status * * Returns one of the following values: * * \li \c TQSL_CERT_STATUS_UNK - An error occurred and the status is unknown * \li \c TQSL_CERT_STATUS_SUP - The certificate has been superceded * \li \c TQSL_CERT_STATUS_EXP - The certificate has expired * \li \c TQSL_CERT_STATUS_OK - The certificate is valid * \li \c TQSL_CERT_STATUS_INV - The serial number supplied is invalid * */ DLLEXPORT int CALLCONVENTION tqsl_getCertificateStatus(long serial); /** Store the status of a callsign certificate * \li \c serial - serial number of the certificate * \li \c status - the status value to store. */ DLLEXPORT int CALLCONVENTION tqsl_setCertificateStatus(long serial, const char *status); /* int tqsl_checkCertificate(tQSL_Cert); */ /** Import a Gabbi cert file received from a CA * * The callback, \c cb, will be called whenever a certificate is ready * to be imported: * * cb(type, message); * * \c type has several fields that can be accessed via macros: * * \c TQSL_CERT_CB_CALL_TYPE(type) := \c TQSL_CERT_CB_MILESTONE | \c TQSL_CERT_CB_RESULT * * \c TQSL_CERT_CB_CERT_TYPE(type) := \c TQSL_CERT_CB_ROOT | \c TQSL_CERT_CB_CA | \c TQSL_CERT_CB_USER * * \c TQSL_CERT_CB_RESULT_TYPE(type) := \c TQSL_CERT_CB_PROMPT | \c TQSL_CERT_CB_WARNING | \c TQSL_CERT_CB_ERROR * * \c TQSL_CERT_CB_RESULT_TYPE() is meaningful only if \c TQSL_CERT_CB_CALL_TYPE() == \c TQSL_CERT_CB_RESULT */ DLLEXPORT int CALLCONVENTION tqsl_importTQSLFile(const char *file, int(*cb)(int type, const char *message, void *userdata), void *user); /** Get the serial for the first user cert from a .tq6 file * used to support asking the user to save their cert after import * \li \c file is the path to the file * \li \c serial is where the serial number is returned * * Returns 0 on success, nonzero on failure. * */ DLLEXPORT int CALLCONVENTION tqsl_getSerialFromTQSLFile(const char *file, long *serial); /** Get the number of certificate providers known to tqsllib. */ DLLEXPORT int CALLCONVENTION tqsl_getNumProviders(int *n); /** Get the information for a certificate provider. * * \li \c idx is the index, 0 <= idx < tqsl_getNumProviders() */ DLLEXPORT int CALLCONVENTION tqsl_getProvider(int idx, TQSL_PROVIDER *provider); /** Create a certificate-request Gabbi file. * * The \c req parameter must be properly populated with the required fields. * * If \c req->password is NULL and \c cb is not NULL, the callback will be * called to acquire the password. Otherwise \c req->password will be used as * the password. If the password is NULL or an empty string the generated * private key will be stored unencrypted. * * If req->signer is not zero and the signing certificate requires a password, * the password may be in req->signer_password, else signer_pwcb is called. */ DLLEXPORT int CALLCONVENTION tqsl_createCertRequest(const char *filename, TQSL_CERT_REQ *req, int(*pwcb)(char *pwbuf, int pwsize, void *userdata), void *user); /** Save a key pair and certificates to a file in PKCS12 format. * * The tQSL_Cert must be initialized for signing (see tqsl_beginSigning()) * if the user certificate is being exported. * * The supplied \c p12password is used to encrypt the PKCS12 data. */ DLLEXPORT int CALLCONVENTION tqsl_exportPKCS12File(tQSL_Cert cert, const char *filename, const char *p12password); /** Save a key pair and certificates to a Base64 string in PKCS12 format. * * The tQSL_Cert must be initialized for signing (see tqsl_beginSigning()) * if the user certificate is being exported. * * The supplied \c p12password is used to encrypt the PKCS12 data. */ DLLEXPORT int CALLCONVENTION tqsl_exportPKCS12Base64(tQSL_Cert cert, char *base64, int b64len, const char *p12password); /** Load certificates and a private key from a PKCS12 file. */ DLLEXPORT int CALLCONVENTION tqsl_importPKCS12File(const char *filename, const char *p12password, const char *password, int (*pwcb)(char *buf, int bufsiz, void *userdata), int(*cb)(int type , const char *message, void *userdata), void *user); /** Load certificates and a private key from a Base64 encoded PKCS12 string. */ DLLEXPORT int CALLCONVENTION tqsl_importPKCS12Base64(const char *base64, const char *p12password, const char *password, int (*pwcb)(char *buf, int bufsiz, void *userdata), int(*cb)(int type , const char *message, void *userdata), void *user); /** Get the list of restorable station locations. */ DLLEXPORT int CALLCONVENTION tqsl_getDeletedCallsignCertificates(char ***calls, int *ncall, const char *filter); /** Free the list of restorable Callsign Certificates. */ DLLEXPORT void CALLCONVENTION tqsl_freeDeletedCertificateList(char **list, int nloc); /** Restore a deleted callsign certificate by callsign. */ DLLEXPORT int CALLCONVENTION tqsl_restoreCallsignCertificate(const char *callsign); /** Delete a certificate and private key */ DLLEXPORT int CALLCONVENTION tqsl_deleteCertificate(tQSL_Cert cert); /** @} */ /** \defgroup Sign Signing API * * The Signing API uses a tQSL_Cert (see \ref CertStuff) to digitally * sign a block of data. */ /** @{ */ /** Initialize the tQSL_Cert object for use in signing. * * This produces an unencrypted copy of the private key in memory. * * if \c password is not NULL, it must point to the password to use to decrypt * the private key. If \c password is NULL and \c pwcb is not NULL, \c pwcb * is called to get the password. If the private key is encrypted and both * \c password and \c pwcb are NULL, or if the supplied password fails to * decrypt the key, a TQSL_PASSWORD_ERROR error is returned. * * \c pwcb parameters: \c pwbuf is a pointer to a buffer of \c pwsize chars. * The buffer should be NUL-terminated. */ DLLEXPORT int CALLCONVENTION tqsl_beginSigning(tQSL_Cert cert, char *password, int(*pwcb)(char *pwbuf, int pwsize, void *userdata), void *user); /** Test whether the tQSL_Cert object is initialized for signing. * * Returns 0 if initialized. Sets tQSL_Error to TQSL_SIGNINIT_ERROR if not. */ DLLEXPORT int CALLCONVENTION tqsl_checkSigningStatus(tQSL_Cert cert); /** Get the maximum size of a signature block that will be produced * when the tQSL_Cert is used to sign data. (Note that the size of the * signature block is unaffected by the size of the data block being signed.) */ DLLEXPORT int CALLCONVENTION tqsl_getMaxSignatureSize(tQSL_Cert cert, int *sigsize); /** Sign a data block. * * tqsl_beginSigning() must have been called for * the tQSL_Cert object before calling this function. */ DLLEXPORT int CALLCONVENTION tqsl_signDataBlock(tQSL_Cert cert, const unsigned char *data, int datalen, unsigned char *sig, int *siglen); /** Verify a signed data block. * * tqsl_beginSigning() need \em not have been called. */ DLLEXPORT int CALLCONVENTION tqsl_verifyDataBlock(tQSL_Cert cert, const unsigned char *data, int datalen, unsigned char *sig, int siglen); /** Sign a single QSO record * * tqsl_beginSigning() must have been called for * the tQSL_Cert object before calling this function. * * \c loc must be a valid tQSL_Location object. See \ref Data. */ DLLEXPORT int CALLCONVENTION tqsl_signQSORecord(tQSL_Cert cert, tQSL_Location loc, TQSL_QSO_RECORD *rec, unsigned char *sig, int *siglen); /** Terminate signing operations for this tQSL_Cert object. * * This zero-fills the unencrypted private key in memory. */ DLLEXPORT int CALLCONVENTION tqsl_endSigning(tQSL_Cert cert); /** @} */ /** \defgroup Data Data API * * The Data API is used to form data into TrustedQSL records. A TrustedQSL record * consists of a station record and a QSO record. Together, the two records * fully describe one station's end of the QSO -- just as a paper QSL card does. * * The station record contains the callsign and geographic location of the * station submitting the QSO record. The library manages the station records. * The tqsl_xxxStationLocationCapture functions are used to generate and save * a station record. The intent is to provide an interface that makes a step-by-step * system (such as a GUI "wizard") easily implemented. * * The tqsl_getStationLocation() function is used to retrieve station records. * * With the necessary station location available, a signed GABBI output file can * be generated using the tqsl_getGABBIxxxxx functions: * * \li tqsl_getGABBItCERT() - Returns a GABBI tCERT record for the given tQSL_Cert * \li tqsl_getGABBItSTATION() - Returns a GABBI tSTATION record for the given * tQSL_Location * \li tqsl_getGABBItCONTACT() - Returns a GABBI tCONTACT record for the given * TQSL_QSO_RECORD, using the given tQSL_Cert and tQSL_Location. * \li tqsl_getGABBItCONTACTData() - Returns a GABBI tCONTACT record and the * SIGNDATA for the given TQSL_QSO_RECORD, using the given tQSL_Cert and * tQSL_Location. * * The GABBI format requires that the tCERT record contain an integer identifier * that is unique within the GABBI file. Similarly, each tSTATION record must * contain a unique identifier. Aditionally, the tSTATION record must reference * the identifier of a preceding tCERT record. Finally, each tCONTACT record must * reference a preceding tSTATION record. (A GABBI processor uses these identifiers * and references to tie the station and contact records together and to verify * their signature via the certificate.) It is the responsibility of the caller * to supply these identifiers and to ensure that the supplied references match * the tQSL_Cert and tQSL_Location used to create the referenced GABBI records. * * Station Location Generation * * The station-location generation process involves determining the values * for a number of station-location parameters. Normally this * will be done by prompting the user for the values. The responses given * by the user may determine which later fields are required. For example, * if the user indicates that the DXCC entity is UNITED STATES, a later * field would ask for the US state. This field would not be required if the * DXCC entity were not in the US. * * To accommodate the dynamic nature of the field requirements, the fields * are ordered such that dependent fields are queried after the field(s) * on which they depend. To make this process acceptable in a GUI * system, the fields are grouped into pages, where multiple fields may * be displayed on the same page. The grouping is such that which fields * are within the page is not dependent on any of the values of the * fields within the page. That is, a page of fields contains the same * fields no matter what value any of the fields contains. (However, * the \em values of fields within the page can depend on the values * of fields that precede them in the page.) * * Here is a brief overview of the sequence of events involved in * generating a station location interactively, one field at a time: * * 1) Call tqsl_initStationLocationCapture() (new location) or tqsl_getStationLocation() * (existing location). * * 2) For \c field from 0 to tqsl_getNumLocationField(): * \li Display the field label [tqsl_getLocationFieldDataLabel()] * \li Get the field content from the user. This can be a selection * from a list, an entered integer or an entered character string, * depending on the value returned by tqsl_getLocationFieldInputType(). * * 3) If tqsl_hasNextStationLocationCapture() returns 1, call * tqsl_nextStationLocationCapture() and go back to step 2. * * In the case of a GUI system, you'll probably want to display the * fields in pages. The sequence of events is a bit different: * * 1) Call tqsl_initStationLocationCapture() (new location) or tqsl_getStationLocation() * (existing location). * * 2) For \c field from 0 to tqsl_getNumLocationField(), * \li Display the field label [tqsl_getLocationFieldDataLabel()] * \li Display the field-input control This can be a list-selection * or an entered character string or integer, depending on the value * returned by tqsl_getLocationFieldInputType(). * * 3) Each time the user changes a field, call tqsl_updateStationLocationCapture(). * This may change the allowable selection for fields that follow the field * the user changed, so the control for each of those fields should be updated * as in step 2. * * 4) Once the user has completed entries for the page, if * tqsl_hasNextStationLocationCapture() returns 1, call * tqsl_nextStationLocationCapture() and go back to step 2. * * N.B. The first two fields in the station-location capture process are * always call sign and DXCC entity, in that order. As a practical matter, these * two fields must match the corresponding fields in the available certificates. * The library will therefore constrain the values of these fields to match * what's available in the certificate store. See \ref CertStuff. */ /** @{ */ /* Location field input types */ #define TQSL_LOCATION_FIELD_TEXT 1 ///< Text type input field #define TQSL_LOCATION_FIELD_DDLIST 2 ///< Dropdown list input field #define TQSL_LOCATION_FIELD_LIST 3 ///< List type input field #define TQSL_LOCATION_FIELD_BADZONE 4 ///< Used to return zone selection errors /* Location field data types */ #define TQSL_LOCATION_FIELD_CHAR 1 ///< Character field #define TQSL_LOCATION_FIELD_INT 2 ///< Integer field /** Begin the process of generating a station record */ DLLEXPORT int CALLCONVENTION tqsl_initStationLocationCapture(tQSL_Location *locp); /** Release the station-location resources. This should be called for * any tQSL_Location that was initialized via tqsl_initStationLocationCapture() * or tqsl_getStationLocation() */ DLLEXPORT int CALLCONVENTION tqsl_endStationLocationCapture(tQSL_Location *locp); /** Update the pages based on the currently selected settings. */ DLLEXPORT int CALLCONVENTION tqsl_updateStationLocationCapture(tQSL_Location loc); /** Return the number of station location capture pages. */ DLLEXPORT int CALLCONVENTION tqsl_getNumStationLocationCapturePages(tQSL_Location loc, int *npages); /** Get the current page number */ DLLEXPORT int CALLCONVENTION tqsl_getStationLocationCapturePage(tQSL_Location loc, int *page); /** Set the current page number. * Typically, the page number will be 1 (the starting page) or a value * obtained from tqsl_getStationLocationCapturePage(). */ DLLEXPORT int CALLCONVENTION tqsl_setStationLocationCapturePage(tQSL_Location loc, int page); /** Set the certificate flags used in a location page. * This is used to enable expired certs (or disable). */ DLLEXPORT int CALLCONVENTION tqsl_setStationLocationCertFlags(tQSL_Location loc, int flags); /** Advance the page to the next one in the page sequence */ DLLEXPORT int CALLCONVENTION tqsl_nextStationLocationCapture(tQSL_Location loc); /** Return the page to the previous one in the page sequence. */ DLLEXPORT int CALLCONVENTION tqsl_prevStationLocationCapture(tQSL_Location loc); /** Returns 1 (in rval) if there is a next page */ DLLEXPORT int CALLCONVENTION tqsl_hasNextStationLocationCapture(tQSL_Location loc, int *rval); /** Returns 1 (in rval) if there is a previous page */ DLLEXPORT int CALLCONVENTION tqsl_hasPrevStationLocationCapture(tQSL_Location loc, int *rval); /** Save the station location data. Note that the name must have been * set via tqsl_setStationLocationCaptureName if this is a new * station location. If the \c overwrite parameter is zero and a * station location of that name is already in existance, an error * occurs with tQSL_Error set to TQSL_NAME_EXISTS. */ DLLEXPORT int CALLCONVENTION tqsl_saveStationLocationCapture(tQSL_Location loc, int overwrite); /** Get the name of the station location */ DLLEXPORT int CALLCONVENTION tqsl_getStationLocationCaptureName(tQSL_Location loc, char *namebuf, int bufsiz); /** Set the name of the station location */ DLLEXPORT int CALLCONVENTION tqsl_setStationLocationCaptureName(tQSL_Location loc, const char *name); /** Get the number of saved station locations */ DLLEXPORT int CALLCONVENTION tqsl_getNumStationLocations(tQSL_Location loc, int *nloc); /** Get the name of the specified (by \c idx) saved station location */ DLLEXPORT int CALLCONVENTION tqsl_getStationLocationName(tQSL_Location loc, int idx, char *buf, int bufsiz); /** Get the call sign from the station location */ DLLEXPORT int CALLCONVENTION tqsl_getStationLocationCallSign(tQSL_Location loc, int idx, char *buf, int bufsiz); /** Get a named field from the station location */ DLLEXPORT int CALLCONVENTION tqsl_getStationLocationField(tQSL_Location locp, const char *name, char *namebuf, int bufsize); /** Retrieve a saved station location. * Once finished wih the station location, tqsl_endStationLocationCapture() * should be called to release resources. */ DLLEXPORT int CALLCONVENTION tqsl_getStationLocation(tQSL_Location *loc, const char *name); /** Get any errors returned from parsing the selected station location. * This should be called after tqsl_getStationLocation to determine if * any of the existing fields failed validation. Currently only zone * data is validated here, but future validations for things like * properly formatted grid squares is likely. */ DLLEXPORT int CALLCONVENTION tqsl_getStationLocationErrors(tQSL_Location loc, char *buf, int bufsiz); /** Return the contents of the station data file as a byte stream. * The caller is required to tqsl_freeStationDataEnc() this pointer when done with it. */ DLLEXPORT int CALLCONVENTION tqsl_getStationDataEnc(tQSL_StationDataEnc *sdata); /** Free the pointer returned by tqsl_getStationDataEnc(tQSL_StationDataEnc*) */ DLLEXPORT int CALLCONVENTION tqsl_freeStationDataEnc(tQSL_StationDataEnc sdata); /** Merge saved location data with existing */ DLLEXPORT int CALLCONVENTION tqsl_mergeStationLocations(const char *locdata); /** Remove the stored station location by name. */ DLLEXPORT int CALLCONVENTION tqsl_deleteStationLocation(const char *name); /** Restore the deleted station location by name. */ DLLEXPORT int CALLCONVENTION tqsl_restoreStationLocation(const char *name); /** Get the list of restorable station locations. */ DLLEXPORT int CALLCONVENTION tqsl_getDeletedStationLocations(char ***locp, int *nloc); /** Free the list of restorable station locations. */ DLLEXPORT void CALLCONVENTION tqsl_freeDeletedLocationList(char **list, int nloc); /** Get the number of fields on the current station location page */ DLLEXPORT int CALLCONVENTION tqsl_getNumLocationField(tQSL_Location loc, int *numf); /** Get the number of characters in the label for the specified field */ DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataLabelSize(tQSL_Location loc, int field_num, int *rval); /** Get the label for the specified field */ DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataLabel(tQSL_Location loc, int field_num, char *buf, int bufsiz); /** Get the size of the GABBI name of the specified field */ DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataGABBISize(tQSL_Location loc, int field_num, int *rval); /** Get the GABBI name of the specified field */ DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataGABBI(tQSL_Location loc, int field_num, char *buf, int bufsiz); /** Get the input type of the input field. * * \c type will be one of TQSL_LOCATION_FIELD_TEXT, TQSL_LOCATION_FIELD_DDLIST * or TQSL_LOCATION_FIELD_LIST */ /** Get the number of fields on the current station location page */ DLLEXPORT int CALLCONVENTION tqsl_getNumLocationField(tQSL_Location loc, int *numf); /** Get the number of characters in the label for the specified field */ DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataLabelSize(tQSL_Location loc, int field_num, int *rval); /** Get the label for the specified field */ DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataLabel(tQSL_Location loc, int field_num, char *buf, int bufsiz); /** Get the size of the GABBI name of the specified field */ DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataGABBISize(tQSL_Location loc, int field_num, int *rval); /** Get the GABBI name of the specified field */ DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataGABBI(tQSL_Location loc, int field_num, char *buf, int bufsiz); /** Get the input type of the input field. * * \c type will be one of TQSL_LOCATION_FIELD_TEXT, TQSL_LOCATION_FIELD_DDLIST * or TQSL_LOCATION_FIELD_LIST */ DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldInputType(tQSL_Location loc, int field_num, int *type); /** Get the data type of the input field. * * \c type will be either TQSL_LOCATION_FIELD_CHAR or TQSL_LOCATION_FIELD_INT */ DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataType(tQSL_Location loc, int field_num, int *type); /** Get the flags for the input field. * * \c flags will be either * TQSL_LOCATION_FIELD_UPPER Field is to be uppercased on input * TQSL_LOCATION_FIELD_MUSTSEL Value must be selected * TQSL_LOCATION_FIELD_SELNXT Value must be selected to allow Next/Finish * */ DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldFlags(tQSL_Location loc, int field_num, int *flags); /** Get the length of the input field data. */ DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataLength(tQSL_Location loc, int field_num, int *rval); /** Get the character data from the specified field. * * If the field input type (see tqsl_getLocationFieldInputType()) is * TQSL_LOCATION_FIELD_DDLIST or TQSL_LOCATION_FIELD_LIST, this will * return the text of the selected item. */ DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldCharData(tQSL_Location loc, int field_num, char *buf, int bufsiz); /** Get the integer data from the specified field. * * This is only meaningful if the field data type (see tqsl_getLocationFieldDataType()) * is TQSL_LOCATION_FIELD_INT. */ DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldIntData(tQSL_Location loc, int field_num, int *dat); /** If the field input type (see tqsl_getLocationFieldInputType()) is * TQSL_LOCATION_FIELD_DDLIST or TQSL_LOCATION_FIELD_LIST, gets the * index of the selected list item. */ DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldIndex(tQSL_Location loc, int field_num, int *dat); /** Get the number of items in the specified field's pick list. */ DLLEXPORT int CALLCONVENTION tqsl_getNumLocationFieldListItems(tQSL_Location loc, int field_num, int *rval); /** Get the text of a specified item of a specified field */ DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldListItem(tQSL_Location loc, int field_num, int item_idx, char *buf, int bufsiz); /** Set the text data of a specified field. */ DLLEXPORT int CALLCONVENTION tqsl_setLocationFieldCharData(tQSL_Location loc, int field_num, const char *buf); /** Set the integer data of a specified field. */ DLLEXPORT int CALLCONVENTION tqsl_setLocationFieldIntData(tQSL_Location loc, int field_num, int dat); /** If the field input type (see tqsl_getLocationFieldInputType()) is * TQSL_LOCATION_FIELD_DDLIST or TQSL_LOCATION_FIELD_LIST, sets the * index of the selected list item. */ DLLEXPORT int CALLCONVENTION tqsl_setLocationFieldIndex(tQSL_Location loc, int field_num, int dat); /** Get the \e changed status of a field. The changed flag is set to 1 if the * field's pick list was changed during the last call to tqsl_updateStationLocationCapture * or zero if the list was not changed. */ DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldChanged(tQSL_Location loc, int field_num, int *changed); /** Get the call sign from the station location. */ DLLEXPORT int CALLCONVENTION tqsl_getLocationCallSign(tQSL_Location loc, char *buf, int bufsiz); /** Set the call sign for the station location. */ DLLEXPORT int CALLCONVENTION tqsl_setLocationCallSign(tQSL_Location loc, const char *buf); /** Get the DXCC entity from the station location. */ DLLEXPORT int CALLCONVENTION tqsl_getLocationDXCCEntity(tQSL_Location loc, int *dxcc); /** Get the QSO details in canonical form. */ DLLEXPORT int CALLCONVENTION tqsl_getLocationQSODetails(tQSL_Location locp, char *buf, int buflen); /** Get the station location details in canonical form. */ DLLEXPORT int CALLCONVENTION tqsl_getLocationStationDetails(tQSL_Location locp, char *buf, int buflen); /** Save the json results for a given callsign location Detail. */ DLLEXPORT int CALLCONVENTION tqsl_saveCallsignLocationInfo(const char *callsign, const char *json); /** Retrieve the json results for a given callsign location Detail. */ DLLEXPORT int CALLCONVENTION tqsl_getCallsignLocationInfo(const char *callsign, char **buf); /** Get the number of DXCC entities in the master DXCC list. */ DLLEXPORT int CALLCONVENTION tqsl_getNumDXCCEntity(int *number); /** Get a DXCC entity from the list of DXCC entities by its index. */ DLLEXPORT int CALLCONVENTION tqsl_getDXCCEntity(int index, int *number, const char **name); /** Get the name of a DXCC Entity by its DXCC number. */ DLLEXPORT int CALLCONVENTION tqsl_getDXCCEntityName(int number, const char **name); /** Get the zonemap of a DXCC Entity by its DXCC number. */ DLLEXPORT int CALLCONVENTION tqsl_getDXCCZoneMap(int number, const char **zonemap); /** Get the start date of a DXCC Entity by its DXCC number. */ DLLEXPORT int CALLCONVENTION tqsl_getDXCCStartDate(int number, tQSL_Date *d); /** Get the end date of a DXCC Entity by its DXCC number. */ DLLEXPORT int CALLCONVENTION tqsl_getDXCCEndDate(int number, tQSL_Date *d); /** Get the deleted status of a DXCC Entity by its DXCC number. */ DLLEXPORT int CALLCONVENTION tqsl_getDXCCDeleted(int number, int *deleted); /** Get the number of Band entries in the Band list */ DLLEXPORT int CALLCONVENTION tqsl_getNumBand(int *number); /** Get a band by its index. * * \c name - The GAABI name of the band. * \c spectrum - HF | VHF | UHF * \c low - The low end of the band in kHz (HF) or MHz (VHF/UHF) * \c high - The low high of the band in kHz (HF) or MHz (VHF/UHF) * * Note: \c spectrum, \c low and/or \c high may be NULL. */ DLLEXPORT int CALLCONVENTION tqsl_getBand(int index, const char **name, const char **spectrum, int *low, int *high); /** Get the number of Mode entries in the Mode list */ DLLEXPORT int CALLCONVENTION tqsl_getNumMode(int *number); /** Get a mode by its index. * * \c mode - The GAABI mode name * \c group - CW | PHONE | IMAGE | DATA * * Note: \c group may be NULL. */ DLLEXPORT int CALLCONVENTION tqsl_getMode(int index, const char **mode, const char **group); /** Get the number of Propagation Mode entries in the Propagation Mode list */ DLLEXPORT int CALLCONVENTION tqsl_getNumPropagationMode(int *number); /** Get a propagation mode by its index. * * \c name - The GAABI propagation mode name * \c descrip - Text description of the propagation mode * * Note: \c descrip may be NULL. */ DLLEXPORT int CALLCONVENTION tqsl_getPropagationMode(int index, const char **name, const char **descrip); /** Get the number of Satellite entries in the Satellite list */ DLLEXPORT int CALLCONVENTION tqsl_getNumSatellite(int *number); /** Get a satellite by its index. * * \c name - The GAABI satellite name * \c descrip - Text description of the satellite * \c start - The date the satellite entered service * \c end - The last date the satellite was in service * * Note: \c descrip, start and/or end may be NULL. */ DLLEXPORT int CALLCONVENTION tqsl_getSatellite(int index, const char **name, const char **descrip, tQSL_Date *start, tQSL_Date *end); /** Clear the map of Cabrillo contests. */ DLLEXPORT int CALLCONVENTION tqsl_clearCabrilloMap(); /** Set the mapping of a Cabrillo contest name (as found in the * CONTEST line of a Cabrillo file) to the QSO line call-worked field number * and the contest type. * * \c field can have a value of TQSL_MIN_CABRILLO_MAP_FIELD (cabrillo.h) * or greater. Field number starts at 1. * * \c contest_type must be TQSL_CABRILLO_HF or TQSL_CABRILLO_VHF, * defined in cabrillo.h */ DLLEXPORT int CALLCONVENTION tqsl_setCabrilloMapEntry(const char *contest, int field, int contest_type); /** Get the mapping of a Cabrillo contest name (as found in the * CONTEST line of a Cabrillo file) to a call-worked field number * and the contest type. * * \c fieldnum will be set to 0 if the contest name isn't in the Cabrillo * map. Otherwise it is set to the QSO line field number of the call-worked * field, with field counting starting at 1. * * \c contest_type may be NULL. If not, it is set to the Cabrillo contest * type (TQSL_CABRILLO_HF or TQSL_CABRILLO_VHF), defined in cabrillo.h. */ DLLEXPORT int CALLCONVENTION tqsl_getCabrilloMapEntry(const char *contest, int *fieldnum, int *contest_type); /** Clear the map of ADIF modes */ DLLEXPORT int CALLCONVENTION tqsl_clearADIFModes(); /** Set the mapping of an ADIF mode to a TQSL mode. */ DLLEXPORT int CALLCONVENTION tqsl_setADIFMode(const char *adif_item, const char *mode); /** Map an ADIF mode to its TQSL equivalent. */ DLLEXPORT int CALLCONVENTION tqsl_getADIFMode(const char *adif_item, char *mode, int nmode); /** Map a GABBI mode to its mode/submode pair. */ DLLEXPORT int CALLCONVENTION tqsl_getADIFSubMode(const char *adif_item, char *mode, char *submode, int nmode); /** Get a GABBI record that contains the certificate. * * \c uid is the value for the CERT_UID field * * Returns the NULL pointer on error. * * N.B. On systems that distinguish text-mode files from binary-mode files, * notably Windows, the GABBI records should be written in binary mode. */ DLLEXPORT const char* CALLCONVENTION tqsl_getGABBItCERT(tQSL_Cert cert, int uid); /** Get a GABBI record that contains the Staion Location data. * * \li \c uid is the value for the STATION_UID field. * \li \c certuid is the value of the asociated CERT_UID field. * * Returns the NULL pointer on error. * * N.B. On systems that distinguish text-mode files from binary-mode files, * notably Windows, the GABBI records should be written in binary mode. */ DLLEXPORT const char* CALLCONVENTION tqsl_getGABBItSTATION(tQSL_Location loc, int uid, int certuid); /** Get a GABBI record that contains the QSO data. * * \li \c stationuid is the value of the associated STATION_UID field. * * N.B.: If \c cert is not initialized for signing (see tqsl_beginSigning()) * the function will return with a TQSL_SIGNINIT_ERROR error. * * Returns the NULL pointer on error. * * N.B. On systems that distinguish text-mode files from binary-mode files, * notably Windows, the GABBI records should be written in binary mode. */ DLLEXPORT const char* CALLCONVENTION tqsl_getGABBItCONTACT(tQSL_Cert cert, tQSL_Location loc, TQSL_QSO_RECORD *qso, int stationuid); /** Get a GABBI record that contains the QSO data along with the associated * signdata (QSO data signed to validate the QSO). * * \li \c stationuid is the value of the associated STATION_UID field. * * N.B.: If \c cert is not initialized for signing (see tqsl_beginSigning()) * the function will return with a TQSL_SIGNINIT_ERROR error. * * Returns the NULL pointer on error. * * N.B. On systems that distinguish text-mode files from binary-mode files, * notably Windows, the GABBI records should be written in binary mode. */ DLLEXPORT const char* CALLCONVENTION tqsl_getGABBItCONTACTData(tQSL_Cert cert, tQSL_Location loc, TQSL_QSO_RECORD *qso, int stationuid, char *signdata, int sdlen); /** @} */ /** Output to a diagnostic trace file (if one is opened. * * \li \c name is the name of the function being executed */ DLLEXPORT void CALLCONVENTION tqslTrace(const char *name, const char *format, ...); /** Close the diagnostic trace file (if it is open) */ DLLEXPORT void CALLCONVENTION tqsl_closeDiagFile(void); /** Close the diagnostic trace file (if it is open) */ DLLEXPORT int CALLCONVENTION tqsl_diagFileOpen(void); /** Returns true if the log file is open * */ DLLEXPORT int CALLCONVENTION tqsl_openDiagFile(const char* file); #ifdef _WIN32 DLLEXPORT wchar_t* CALLCONVENTION utf8_to_wchar(const char* str); DLLEXPORT char* CALLCONVENTION wchar_to_utf8(const wchar_t* str, bool forceUTF8); DLLEXPORT void CALLCONVENTION free_wchar(wchar_t* ptr); #endif #ifdef __cplusplus } #endif /* Useful defines */ #define TQSL_MAX_PW_LENGTH 32 ///< Password buffer length #endif /* TQSLLIB_H */ tqsl-2.5.1/src/tqsllib.css0000664000076400007640000000211613563134442015472 0ustar rmurphyrmurphyBODY { font-family: Helvetica,Arial,sans-serif; font-size: small } H1 { text-align: center; font-size: x-large } H2 { font-size: large } CODE { font-size: medium } A.qindex {} A.qindexRef {} A.el { text-decoration: none; font-weight: bold; } A.elRef { font-weight: bold } A.code { text-decoration: none; font-weight: normal; color: #4444ee } A.codeRef { font-weight: normal; color: #4444ee } A:link { color: #0000ff } A:visited { color: #0066cc } DL.el { margin-left: -1cm } DIV.fragment { width: 100%; border: none; background-color: #eeeeee } DIV.ah { background-color: black; margin-bottom: 3; margin-top: 3 } TD.md { background-color: #f2f2ff } DIV.groupHeader { margin-left: 16; margin-top: 12; margin-bottom: 6; font-weight: bold } DIV.groupText { margin-left: 16; font-style: italic; font-size: smaller } FONT.keyword { color: #008000 } FONT.keywordtype { color: #604020 } FONT.keywordflow { color: #e08000 } FONT.comment { color: #800000 } FONT.preprocessor { color: #806020 } FONT.stringliteral { color: #002080 } FONT.charliteral { color: #008080 } TD { font-size: small } tqsl-2.5.1/src/tqsllib.cpp0000664000076400007640000006757013563134442015503 0ustar rmurphyrmurphy/*************************************************************************** tqsllib.c - description ------------------- begin : Mon May 20 2002 copyright : (C) 2002 by ARRL author : Jon Bloom email : jbloom@arrl.org revision : $Id$ ***************************************************************************/ #define TQSLLIB_DEF #include "tqsllib.h" #include #include #include #include #include #ifdef _WIN32 #include #include #include #include #else #include #endif #ifdef __APPLE__ #include #endif #include using std::string; #include #include #include #include "tqslerrno.h" #include "adif.h" #include "winstrdefs.h" #ifdef _WIN32 #define MKDIR(x, y) _wmkdir((x)) #else #define MKDIR(x, y) mkdir((x), (y)) #endif DLLEXPORTDATA int tQSL_Error = 0; DLLEXPORTDATA int tQSL_Errno = 0; DLLEXPORTDATA TQSL_ADIF_GET_FIELD_ERROR tQSL_ADIF_Error; DLLEXPORTDATA const char *tQSL_BaseDir = 0; DLLEXPORTDATA const char *tQSL_RsrcDir = 0; DLLEXPORTDATA char tQSL_ErrorFile[256]; DLLEXPORTDATA char tQSL_CustomError[256]; DLLEXPORTDATA char tQSL_ImportCall[256]; DLLEXPORTDATA long tQSL_ImportSerial = 0; DLLEXPORTDATA FILE* tQSL_DiagFile = 0; #define TQSL_OID_BASE "1.3.6.1.4.1.12348.1." #define TQSL_OID_CALLSIGN TQSL_OID_BASE "1" #define TQSL_OID_QSO_NOT_BEFORE TQSL_OID_BASE "2" #define TQSL_OID_QSO_NOT_AFTER TQSL_OID_BASE "3" #define TQSL_OID_DXCC_ENTITY TQSL_OID_BASE "4" #define TQSL_OID_SUPERCEDED_CERT TQSL_OID_BASE "5" #define TQSL_OID_CRQ_ISSUER_ORGANIZATION TQSL_OID_BASE "6" #define TQSL_OID_CRQ_ISSUER_ORGANIZATIONAL_UNIT TQSL_OID_BASE "7" #define TQSL_OID_CRQ_EMAIL TQSL_OID_BASE "8" #define TQSL_OID_CRQ_ADDRESS1 TQSL_OID_BASE "9" #define TQSL_OID_CRQ_ADDRESS2 TQSL_OID_BASE "10" #define TQSL_OID_CRQ_CITY TQSL_OID_BASE "11" #define TQSL_OID_CRQ_STATE TQSL_OID_BASE "12" #define TQSL_OID_CRQ_POSTAL TQSL_OID_BASE "13" #define TQSL_OID_CRQ_COUNTRY TQSL_OID_BASE "14" static const char *custom_objects[][3] = { { TQSL_OID_CALLSIGN, "AROcallsign", "AROcallsign" }, { TQSL_OID_QSO_NOT_BEFORE, "QSONotBeforeDate", "QSONotBeforeDate" }, { TQSL_OID_QSO_NOT_AFTER, "QSONotAfterDate", "QSONotAfterDate" }, { TQSL_OID_DXCC_ENTITY, "dxccEntity", "dxccEntity" }, { TQSL_OID_SUPERCEDED_CERT, "supercededCertificate", "supercededCertificate" }, { TQSL_OID_CRQ_ISSUER_ORGANIZATION, "tqslCRQIssuerOrganization", "tqslCRQIssuerOrganization" }, { TQSL_OID_CRQ_ISSUER_ORGANIZATIONAL_UNIT, "tqslCRQIssuerOrganizationalUnit", "tqslCRQIssuerOrganizationalUnit" }, { TQSL_OID_CRQ_EMAIL, "tqslCRQEmail", "tqslCRQEmail" }, { TQSL_OID_CRQ_ADDRESS1, "tqslCRQAddress1", "tqslCRQAddress1" }, { TQSL_OID_CRQ_ADDRESS2, "tqslCRQAddress2", "tqslCRQAddress2" }, { TQSL_OID_CRQ_CITY, "tqslCRQCity", "tqslCRQCity" }, { TQSL_OID_CRQ_STATE, "tqslCRQState", "tqslCRQState" }, { TQSL_OID_CRQ_POSTAL, "tqslCRQPostal", "tqslCRQPostal" }, { TQSL_OID_CRQ_COUNTRY, "tqslCRQCountry", "tqslCRQCountry" }, }; static const char *error_strings[] = { "Memory allocation failure", /* TQSL_ALLOC_ERROR */ "Unable to initialize random number generator", /* TQSL_RANDOM_ERROR */ "Invalid argument", /* TQSL_ARGUMENT_ERROR */ "Operator aborted operation", /* TQSL_OPERATOR_ABORT */ "No Certificate Request matches the selected Callsign Certificate",/* TQSL_NOKEY_ERROR */ "Buffer too small", /* TQSL_BUFFER_ERROR */ "Invalid date format", /* TQSL_INVALID_DATE */ "Certificate not initialized for signing", /* TQSL_SIGNINIT_ERROR */ "Password not correct", /* TQSL_PASSWORD_ERROR */ "Expected name", /* TQSL_EXPECTED_NAME */ "Name exists", /* TQSL_NAME_EXISTS */ "Data for this DXCC entity could not be found", /* TQSL_NAME_NOT_FOUND */ "Invalid time format", /* TQSL_INVALID_TIME */ "QSO date is not within the date range specified on your Callsign Certificate", /* TQSL_CERT_DATE_MISMATCH */ "Certificate provider not found", /* TQSL_PROVIDER_NOT_FOUND */ "No callsign certificate for key", /* TQSL_CERT_KEY_ONLY */ "Configuration file cannot be opened", /* TQSL_CONFIG_ERROR */ "The private key for this Callsign Certificate is not present on this computer; you can obtain it by loading a .tbk or .p12 file", /* TQSL_CERT_NOT_FOUND */ "PKCS#12 file not TQSL compatible", /* TQSL_PKCS12_ERROR */ "Callsign Certificate not TQSL compatible", /* TQSL_CERT_TYPE_ERROR */ "Date out of range", /* TQSL_DATE_OUT_OF_RANGE */ "Already Uploaded QSO suppressed", /* TQSL_DUPLICATE_QSO */ "Database error", /* TQSL_DB_ERROR */ "The selected station location could not be found", /* TQSL_LOCATION_NOT_FOUND */ "The selected callsign could not be found", /* TQSL_CALL_NOT_FOUND */ "The TQSL configuration file cannot be parsed", /* TQSL_CONFIG_SYNTAX_ERROR */ "This file can not be processed due to a system error", /* TQSL_FILE_SYSTEM_ERROR */ "The format of this file is incorrect.", /* TQSL_FILE_SYNTAX_ERROR */ "This Callsign Certificate could not be installed", /* TQSL_CERT_ERROR */ }; const char* tqsl_openssl_error(void); #if defined(_WIN32) static int pmkdir(const wchar_t *path, int perm) { wchar_t dpath[TQSL_MAX_PATH_LEN]; wchar_t npath[TQSL_MAX_PATH_LEN]; wchar_t *cp; char *p = wchar_to_utf8(path, true); tqslTrace("pmkdir", "path=%s", p); free(p); int nleft = (sizeof npath / 2) - 1; wcsncpy(dpath, path, (sizeof dpath / 2)); cp = wcstok(dpath, L"/\\"); npath[0] = 0; while (cp) { if (wcslen(cp) > 0 && cp[wcslen(cp)-1] != ':') { wcsncat(npath, L"\\", nleft); nleft--; wcsncat(npath, cp, nleft); nleft -= wcslen(cp); if (MKDIR(npath, perm) != 0 && errno != EEXIST) { tqslTrace("pmkdir", "Error creating %s: %s", npath, strerror(errno)); return 1; } } else { wcsncat(npath, cp, nleft); nleft -= wcslen(cp); } cp = wcstok(NULL, L"/\\"); } return 0; } #else // defined(_WIN32) static int pmkdir(const char *path, int perm) { char dpath[TQSL_MAX_PATH_LEN]; char npath[TQSL_MAX_PATH_LEN]; char *cp; tqslTrace("pmkdir", "path=%s", path); int nleft = sizeof npath - 1; strncpy(dpath, path, sizeof dpath); cp = strtok(dpath, "/\\"); npath[0] = 0; while (cp) { if (strlen(cp) > 0 && cp[strlen(cp)-1] != ':') { strncat(npath, "/", nleft); nleft--; strncat(npath, cp, nleft); nleft -= strlen(cp); if (MKDIR(npath, perm) != 0 && errno != EEXIST) { tqslTrace("pmkdir", "Error creating %s: %s", npath, strerror(errno)); return 1; } } else { strncat(npath, cp, nleft); nleft -= strlen(cp); } cp = strtok(NULL, "/\\"); } return 0; } #endif // defined(_WIN32) static void tqsl_get_rsrc_dir() { tqslTrace("tqsl_get_rsrc_dir", NULL); #ifdef _WIN32 HKEY hkey; DWORD dtype; char wpath[TQSL_MAX_PATH_LEN]; DWORD bsize = sizeof wpath; int wval; if ((wval = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\TrustedQSL", 0, KEY_READ, &hkey)) == ERROR_SUCCESS) { wval = RegQueryValueEx(hkey, "InstallPath", 0, &dtype, (LPBYTE)wpath, &bsize); RegCloseKey(hkey); if (wval == ERROR_SUCCESS) { string p = string(wpath); if (p[p.length() -1] == '\\') p = p.substr(0, p.length() - 1); tQSL_RsrcDir = strdup(p.c_str()); } } #elif defined(__APPLE__) // Get path to config.xml resource from bundle CFBundleRef tqslBundle = CFBundleGetMainBundle(); CFURLRef configXMLURL = CFBundleCopyResourceURL(tqslBundle, CFSTR("config"), CFSTR("xml"), NULL); if (configXMLURL) { CFStringRef pathString = CFURLCopyFileSystemPath(configXMLURL, kCFURLPOSIXPathStyle); CFRelease(configXMLURL); // Convert CFString path to config.xml to string object CFIndex maxStringLengthInBytes = CFStringGetMaximumSizeForEncoding(CFStringGetLength(pathString), kCFStringEncodingUTF8); char *pathCString = static_cast(malloc(maxStringLengthInBytes)); if (pathCString) { CFStringGetCString(pathString, pathCString, maxStringLengthInBytes, kCFStringEncodingASCII); CFRelease(pathString); size_t p; string path = string(pathCString); if ((p = path.find("/config.xml")) != string::npos) { path = path.substr(0, p); } tQSL_RsrcDir = strdup(path.c_str()); free(pathCString); } } #else string p = CONFDIR; if (p[p.length() - 1] == '/') p = p.substr(0, p.length() - 1); tQSL_RsrcDir = strdup(p.c_str()); #endif tqslTrace("tqsl_get_rsrc_dir", "rsrc_path=%s", tQSL_RsrcDir); } DLLEXPORT int CALLCONVENTION tqsl_init() { static char semaphore = 0; unsigned int i; #ifdef _WIN32 static wchar_t path[TQSL_MAX_PATH_LEN * 2]; // lets cin/out/err work in windows // AllocConsole(); // freopen("CONIN$", "r", stdin); // freopen("CONOUT$", "w", stdout); // freopen("CONOUT$", "w", stderr); DWORD bsize = sizeof path; int wval; #else static char path[TQSL_MAX_PATH_LEN]; #endif #if !defined(_WIN32) && !defined(__APPLE__) // Work around ill-considered decision by Fedora to stop allowing // certificates with MD5 signatures setenv("OPENSSL_ENABLE_MD5_VERIFY", "1", 0); #endif /* OpenSSL API tends to change between minor version numbers, so make sure * we're using the right version */ #if OPENSSL_VERSION_NUMBER >= 0x10100000L unsigned long SSLver = OpenSSL_version_num(); #else long SSLver = SSLeay(); #endif int SSLmajor = (SSLver >> 28) & 0xff; int SSLminor = (SSLver >> 20) & 0xff; int TQSLmajor = (OPENSSL_VERSION_NUMBER >> 28) & 0xff; int TQSLminor = (OPENSSL_VERSION_NUMBER >> 20) & 0xff; if (SSLmajor != TQSLmajor || (SSLminor != TQSLminor && (SSLmajor != 9 && SSLminor != 7 && TQSLminor == 6))) { tqslTrace("tqsl_init", "version error - ssl %d.%d", SSLmajor, SSLminor); tQSL_Error = TQSL_OPENSSL_VERSION_ERROR; return 1; } ERR_clear_error(); tqsl_getErrorString(); /* Clear the error status */ if (semaphore) return 0; #if OPENSSL_VERSION_NUMBER < 0x10100000L ERR_load_crypto_strings(); OpenSSL_add_all_algorithms(); #endif for (i = 0; i < (sizeof custom_objects / sizeof custom_objects[0]); i++) { if (OBJ_create(custom_objects[i][0], custom_objects[i][1], custom_objects[i][2]) == 0) { tqslTrace("tqsl_init", "Error making custom objects: %s", tqsl_openssl_error()); tQSL_Error = TQSL_OPENSSL_ERROR; return 1; } } if (tQSL_RsrcDir == NULL) { tqsl_get_rsrc_dir(); } if (tQSL_BaseDir == NULL) { #if defined(_WIN32) wchar_t *wcp; if ((wcp = _wgetenv(L"TQSLDIR")) != NULL && *wcp != '\0') { wcsncpy(path, wcp, sizeof path); #else char *cp; if ((cp = getenv("TQSLDIR")) != NULL && *cp != '\0') { strncpy(path, cp, sizeof path); #endif } else { #if defined(_WIN32) wval = SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, path); if (wval != ERROR_SUCCESS) wcsncpy(path, L"C:", sizeof path); wcsncat(path, L"\\TrustedQSL", sizeof path - wcslen(path) - 1); #elif defined(LOTW_SERVER) strncpy(path, "/var/lotw/tqsl", sizeof path); #else // some unix flavor if (getenv("HOME") != NULL) { strncpy(path, getenv("HOME"), sizeof path); strncat(path, "/", sizeof path - strlen(path)-1); strncat(path, ".tqsl", sizeof path - strlen(path)-1); } else { strncpy(path, ".tqsl", sizeof path); } #endif } if (pmkdir(path, 0700)) { #if defined(_WIN32) char *p = wchar_to_utf8(path, false); strncpy(tQSL_ErrorFile, p, sizeof tQSL_ErrorFile); #else strncpy(tQSL_ErrorFile, path, sizeof tQSL_ErrorFile); #endif tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; #if defined(_WIN32) tqslTrace("tqsl_init", "Error creating working path %s: %s", p, strerror(errno)); free(p); #else tqslTrace("tqsl_init", "Error creating working path %s: %s", path, strerror(errno)); #endif return 1; } FILE *test; #if defined(_WIN32) tQSL_BaseDir = wchar_to_utf8(path, true); wcsncat(path, L"\\tmp.tmp", sizeof path - wcslen(path) - 1); if ((test = _wfopen(path, L"wb")) == NULL) { tQSL_Errno = errno; char *p = wchar_to_utf8(path, false); snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "Unable to create files in the TQSL working directory (%s): %m", p); tQSL_Error = TQSL_CUSTOM_ERROR; return 1; } fclose(test); _wunlink(path); #else if (tQSL_BaseDir) free (const_cast(tQSL_BaseDir)); tQSL_BaseDir = strdup(path); strncat(path, "/tmp.tmp", sizeof path -strlen(path) - 1); if ((test = fopen(path, "wb")) == NULL) { tQSL_Errno = errno; snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "Unable to create files in the TQSL working directory (%s): %m", tQSL_BaseDir); tQSL_Error = TQSL_CUSTOM_ERROR; return 1; } fclose(test); unlink(path); #endif } semaphore = 1; return 0; } DLLEXPORT int CALLCONVENTION tqsl_setDirectory(const char *dir) { static char path[TQSL_MAX_PATH_LEN]; if (strlen(dir) >= TQSL_MAX_PATH_LEN) { tQSL_Error = TQSL_BUFFER_ERROR; return 1; } strncpy(path, dir, sizeof path); tQSL_BaseDir = path; return 0; } DLLEXPORT const char* CALLCONVENTION tqsl_getErrorString_v(int err) { static char buf[256]; unsigned long openssl_err; int adjusted_err; if (err == 0) return "NO ERROR"; if (err == TQSL_CUSTOM_ERROR) { if (tQSL_CustomError[0] == 0) { return "Unknown custom error"; } else { strncpy(buf, tQSL_CustomError, sizeof buf); return buf; } } if (err == TQSL_DB_ERROR && tQSL_CustomError[0] != 0) { snprintf(buf, sizeof buf, "Database Error: %s", tQSL_CustomError); return buf; } if (err == TQSL_SYSTEM_ERROR || err == TQSL_FILE_SYSTEM_ERROR) { if (strlen(tQSL_ErrorFile) > 0) { snprintf(buf, sizeof buf, "System error: %s : %s", tQSL_ErrorFile, strerror(tQSL_Errno)); tQSL_ErrorFile[0] = '\0'; } else { snprintf(buf, sizeof buf, "System error: %s", strerror(tQSL_Errno)); } return buf; } if (err == TQSL_FILE_SYNTAX_ERROR) { if (strlen(tQSL_ErrorFile) > 0) { snprintf(buf, sizeof buf, "File syntax error: %s", tQSL_ErrorFile); tQSL_ErrorFile[0] = '\0'; } else { strncpy(buf, "File syntax error", sizeof buf); } return buf; } if (err == TQSL_OPENSSL_ERROR) { openssl_err = ERR_get_error(); strncpy(buf, "OpenSSL error: ", sizeof buf); if (openssl_err) ERR_error_string_n(openssl_err, buf + strlen(buf), sizeof buf - strlen(buf)-1); else strncat(buf, "[error code not available]", sizeof buf - strlen(buf)-1); return buf; } if (err == TQSL_ADIF_ERROR) { buf[0] = 0; if (strlen(tQSL_ErrorFile) > 0) { snprintf(buf, sizeof buf, "%s: %s", tQSL_ErrorFile, tqsl_adifGetError(tQSL_ADIF_Error)); tQSL_ErrorFile[0] = '\0'; } else { snprintf(buf, sizeof buf, "%s", tqsl_adifGetError(tQSL_ADIF_Error)); } return buf; } if (err == TQSL_CABRILLO_ERROR) { buf[0] = 0; if (strlen(tQSL_ErrorFile) > 0) { snprintf(buf, sizeof buf, "%s: %s", tQSL_ErrorFile, tqsl_cabrilloGetError(tQSL_Cabrillo_Error)); tQSL_ErrorFile[0] = '\0'; } else { snprintf(buf, sizeof buf, "%s", tqsl_cabrilloGetError(tQSL_Cabrillo_Error)); } return buf; } if (err == TQSL_OPENSSL_VERSION_ERROR) { #if OPENSSL_VERSION_NUMBER >= 0x10100000L unsigned long SSLver = OpenSSL_version_num(); #else long SSLver = SSLeay(); #endif snprintf(buf, sizeof buf, "Incompatible OpenSSL Library version %d.%d.%d; expected %d.%d.%d", static_cast(SSLver >> 28) & 0xff, static_cast(SSLver >> 20) & 0xff, static_cast(SSLver >> 12) & 0xff, static_cast(OPENSSL_VERSION_NUMBER >> 28) & 0xff, static_cast(OPENSSL_VERSION_NUMBER >> 20) & 0xff, static_cast(OPENSSL_VERSION_NUMBER >> 12) & 0xff); return buf; } if (err == TQSL_CERT_NOT_FOUND && tQSL_ImportCall[0] != '\0') { snprintf(buf, sizeof buf, "The private key for callsign %s serial %ld is not present on this computer; you can obtain it by loading a .tbk or .p12 file", tQSL_ImportCall, tQSL_ImportSerial); tQSL_ImportCall[0] = '\0'; return buf; } adjusted_err = err - TQSL_ERROR_ENUM_BASE; if (adjusted_err < 0 || adjusted_err >= static_cast(sizeof error_strings / sizeof error_strings[0])) { snprintf(buf, sizeof buf, "Invalid error code: %d", err); return buf; } return error_strings[adjusted_err]; } DLLEXPORT const char* CALLCONVENTION tqsl_getErrorString() { const char *cp; cp = tqsl_getErrorString_v(tQSL_Error); tQSL_Error = TQSL_NO_ERROR; tQSL_Errno = 0; tQSL_ErrorFile[0] = 0; tQSL_CustomError[0] = 0; return cp; } DLLEXPORT int CALLCONVENTION tqsl_encodeBase64(const unsigned char *data, int datalen, char *output, int outputlen) { BIO *bio = NULL, *bio64 = NULL; int n; char *memp; int rval = 1; if (data == NULL || output == NULL) { tQSL_Error = TQSL_ARGUMENT_ERROR; tqslTrace("tqsl_encodeBase64", "arg err data=0x%lx, output=0x%lx", data, output); return rval; } if ((bio = BIO_new(BIO_s_mem())) == NULL) { tqslTrace("tqsl_encodeBase64", "BIO_new err %s", tqsl_openssl_error()); goto err; } if ((bio64 = BIO_new(BIO_f_base64())) == NULL) { tqslTrace("tqsl_encodeBase64", "BIO_new64 err %s", tqsl_openssl_error()); goto err; } bio = BIO_push(bio64, bio); if (BIO_write(bio, data, datalen) < 1) { tqslTrace("tqsl_encodeBase64", "BIO_write err %s", tqsl_openssl_error()); goto err; } if (BIO_flush(bio) != 1) { tqslTrace("tqsl_encodeBase64", "BIO_flush err %s", tqsl_openssl_error()); goto err; } n = BIO_get_mem_data(bio, &memp); if (n > outputlen-1) { tqslTrace("tqsl_encodeBase64", "buffer has %d, avail %d", n, outputlen); tQSL_Error = TQSL_BUFFER_ERROR; goto end; } memcpy(output, memp, n); output[n] = 0; BIO_free_all(bio); bio = NULL; rval = 0; goto end; err: tQSL_Error = TQSL_OPENSSL_ERROR; end: if (bio != NULL) BIO_free_all(bio); return rval; } DLLEXPORT int CALLCONVENTION tqsl_decodeBase64(const char *input, unsigned char *data, int *datalen) { BIO *bio = NULL, *bio64 = NULL; int n; int rval = 1; if (input == NULL || data == NULL || datalen == NULL) { tqslTrace("tqsl_decodeBase64", "arg error input=0x%lx, data=0x%lx, datalen=0x%lx", input, data, datalen); tQSL_Error = TQSL_ARGUMENT_ERROR; return rval; } if ((bio = BIO_new_mem_buf(const_cast(input), strlen(input))) == NULL) { tqslTrace("tqsl_decodeBase64", "BIO_new_mem_buf err %s", tqsl_openssl_error()); goto err; } BIO_set_mem_eof_return(bio, 0); if ((bio64 = BIO_new(BIO_f_base64())) == NULL) { tqslTrace("tqsl_decodeBase64", "BIO_new err %s", tqsl_openssl_error()); goto err; } bio = BIO_push(bio64, bio); n = BIO_read(bio, data, *datalen); if (n < 0) { tqslTrace("tqsl_decodeBase64", "BIO_read error %s", tqsl_openssl_error()); goto err; } if (BIO_ctrl_pending(bio) != 0) { tqslTrace("tqsl_decodeBase64", "ctrl_pending err %s", tqsl_openssl_error()); tQSL_Error = TQSL_BUFFER_ERROR; goto end; } *datalen = n; rval = 0; goto end; err: tQSL_Error = TQSL_OPENSSL_ERROR; end: if (bio != NULL) BIO_free_all(bio); return rval; } /* Convert a tQSL_Date field to an ISO-format date string */ DLLEXPORT char* CALLCONVENTION tqsl_convertDateToText(const tQSL_Date *date, char *buf, int bufsiz) { char lbuf[10]; int len; char *cp = buf; int bufleft = bufsiz-1; if (date == NULL || buf == NULL) { tQSL_Error = TQSL_ARGUMENT_ERROR; if (buf) buf[0] = '\0'; return NULL; } if (date->year < 1 || date->year > 9999 || date->month < 1 || date->month > 12 || date->day < 1 || date->day > 31) { buf[0] = '\0'; return NULL; } len = snprintf(lbuf, sizeof lbuf, "%04d-", date->year); strncpy(cp, lbuf, bufleft); cp += len; bufleft -= len; len = snprintf(lbuf, sizeof lbuf, "%02d-", date->month); if (bufleft > 0) strncpy(cp, lbuf, bufleft); cp += len; bufleft -= len; len = snprintf(lbuf, sizeof lbuf, "%02d", date->day); if (bufleft > 0) strncpy(cp, lbuf, bufleft); bufleft -= len; if (bufleft < 0) return NULL; buf[bufsiz-1] = '\0'; return buf; } DLLEXPORT int CALLCONVENTION tqsl_isDateValid(const tQSL_Date *d) { static int mon_days[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; if (d == NULL) { tQSL_Error = TQSL_ARGUMENT_ERROR; return 0; } if (d->year < 1 || d->year > 9999) return 0; if (d->month < 1 || d->month > 12) return 0; if (d->day < 1 || d->day > 31) return 0; mon_days[2] = ((d->year % 4) == 0 && ((d->year % 100) != 0 || (d->year % 400) == 0)) ? 29 : 28; if (d->day > mon_days[d->month]) return 0; return 1; } DLLEXPORT int CALLCONVENTION tqsl_isDateNull(const tQSL_Date *d) { if (d == NULL) { tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } return (d->year == 0 && d->month == 0 && d->day == 0) ? 1 : 0; } /* Convert a tQSL_Time field to an ISO-format date string */ DLLEXPORT char* CALLCONVENTION tqsl_convertTimeToText(const tQSL_Time *time, char *buf, int bufsiz) { char lbuf[10]; int len; char *cp = buf; int bufleft = bufsiz-1; if (time == NULL || buf == NULL) { tQSL_Error = TQSL_ARGUMENT_ERROR; return NULL; } if (!tqsl_isTimeValid(time)) return NULL; len = snprintf(lbuf, sizeof lbuf, "%02d:", time->hour); strncpy(cp, lbuf, bufleft); cp += len; bufleft -= len; len = snprintf(lbuf, sizeof lbuf, "%02d:", time->minute); if (bufleft > 0) strncpy(cp, lbuf, bufleft); cp += len; bufleft -= len; len = snprintf(lbuf, sizeof lbuf, "%02d", time->second); if (bufleft > 0) strncpy(cp, lbuf, bufleft); cp += len; bufleft -= len; if (bufleft > 0) strncpy(cp, "Z", bufleft); bufleft -= 1; if (bufleft < 0) return NULL; buf[bufsiz-1] = '\0'; return buf; } DLLEXPORT int CALLCONVENTION tqsl_isTimeValid(const tQSL_Time *t) { if (t == NULL) { tQSL_Error = TQSL_ARGUMENT_ERROR; return 0; } if (t->hour < 0 || t->hour > 23) return 0; if (t->minute < 0 || t->minute > 59) return 0; if (t->second < 0 || t->second > 59) return 0; return 1; } /* Compare two tQSL_Date values, returning -1, 0, 1 */ DLLEXPORT int CALLCONVENTION tqsl_compareDates(const tQSL_Date *a, const tQSL_Date *b) { if (a == NULL || b == NULL) { tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (a->year < b->year) return -1; if (a->year > b->year) return 1; if (a->month < b->month) return -1; if (a->month > b->month) return 1; if (a->day < b->day) return -1; if (a->day > b->day) return 1; return 0; } // Return the number of days for a given year/month (January=1) static int days_per_month(int year, int month) { switch (month) { case 2: if ((((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0)) { return 29; } else { return 28; } case 4: case 6: case 9: case 11: return 30; default: return 31; } return 0; } // Return the julian day number for a given date. // One-based year/month/day static int julian_day(int year, int month, int day) { int jday = 0; for (int mon = 1; mon < month; mon ++) { jday += days_per_month(year, mon); } jday += day; return jday; } /* Calculate the difference between two tQSL_Date values */ DLLEXPORT int CALLCONVENTION tqsl_subtractDates(const tQSL_Date *a, const tQSL_Date *b, int *diff) { if (a == NULL || b == NULL || diff == NULL) { tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } tQSL_Date first = *a; tQSL_Date last = *b; int mult = 1; // Ensure that the first is earliest if (tqsl_compareDates(&last, &first) < 0) { first = *b; last = *a; mult = -1; } int delta = 0; for (; first.year < last.year; first.year++) { int fday = julian_day(first.year, first.month, first.day); int fend = julian_day(first.year, 12, 31); delta += (fend - fday + 1); // days until next 1 Jan first.month = 1; first.day = 1; } // Now the years are the same - calculate delta int fjulian = julian_day(first.year, first.month, first.day); int ljulian = julian_day(last.year, last.month, last.day); delta += (ljulian - fjulian); *diff = (delta * mult); // Swap sign if necessary return 0; } /* Fill a tQSL_Date struct with the date from a text string */ DLLEXPORT int CALLCONVENTION tqsl_initDate(tQSL_Date *date, const char *str) { const char *cp; if (date == NULL) { tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (str == NULL) { date->year = date->month = date->day = 0; return 0; } if ((cp = strchr(str, '-')) != NULL) { /* Parse YYYY-MM-DD */ date->year = strtol(str, NULL, 10); cp++; date->month = strtol(cp, NULL, 10); cp = strchr(cp, '-'); if (cp == NULL) goto err; cp++; date->day = strtol(cp, NULL, 10); } else if (strlen(str) == 8) { /* Parse YYYYMMDD */ char frag[10]; strncpy(frag, str, 4); frag[4] = 0; date->year = strtol(frag, NULL, 10); strncpy(frag, str+4, 2); frag[2] = 0; date->month = strtol(frag, NULL, 10); date->day = strtol(str+6, NULL, 10); } else { /* Invalid ISO date string */ goto err; } if (date->year < 1 || date->year > 9999) goto err; if (date->month < 1 || date->month > 12) goto err; if (date->day < 1 || date->day > 31) goto err; return 0; err: tQSL_Error = TQSL_INVALID_DATE; return 1; } /* Fill a tQSL_Time struct with the time from a text string */ DLLEXPORT int CALLCONVENTION tqsl_initTime(tQSL_Time *time, const char *str) { const char *cp; int parts[3]; int i; if (time == NULL) { tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } time->hour = time->minute = time->second = 0; if (str == NULL) return 0; if (strlen(str) < 3) { tQSL_Error = TQSL_INVALID_TIME; return 1; } parts[0] = parts[1] = parts[2] = 0; for (i = 0, cp = str; i < static_cast(sizeof parts / sizeof parts[0]); i++) { if (strlen(cp) < 2) break; if (!isdigit(*cp) || !isdigit(*(cp+1))) goto err; if (i == 0 && strlen(str) == 3) { // Special case: HMM -- no colons, one-digit hour parts[i] = *cp - '0'; ++cp; } else { parts[i] = (*cp - '0') * 10 + *(cp+1) - '0'; cp += 2; } if (*cp == ':') cp++; } if (parts[0] < 0 || parts[0] > 23) goto err; if (parts[1] < 0 || parts[1] > 59) goto err; if (parts[2] < 0 || parts[2] > 59) goto err; time->hour = parts[0]; time->minute = parts[1]; time->second = parts[2]; return 0; err: tQSL_Error = TQSL_INVALID_TIME; return 1; } DLLEXPORT int CALLCONVENTION tqsl_getVersion(int *major, int *minor) { if (major) *major = TQSLLIB_VERSION_MAJOR; if (minor) *minor = TQSLLIB_VERSION_MINOR; return 0; } #ifdef _WIN32 DLLEXPORT wchar_t* CALLCONVENTION utf8_to_wchar(const char* str) { wchar_t* buffer; int needed = MultiByteToWideChar(CP_UTF8, 0, str, -1, 0, 0); buffer = static_cast(malloc(needed*sizeof(wchar_t) + 4)); if (!buffer) return NULL; MultiByteToWideChar(CP_UTF8, 0, str, -1, &buffer[0], needed); return buffer; } DLLEXPORT char* CALLCONVENTION wchar_to_utf8(const wchar_t* str, bool forceUTF8) { char* buffer; int needed = WideCharToMultiByte(forceUTF8 ? CP_UTF8 : CP_ACP, 0, str, -1, 0, 0, NULL, NULL); buffer = static_cast(malloc(needed + 2)); if (!buffer) return NULL; WideCharToMultiByte(forceUTF8 ? CP_UTF8 : CP_ACP, 0, str, -1, &buffer[0], needed, NULL, NULL); return buffer; } DLLEXPORT void CALLCONVENTION free_wchar(wchar_t* ptr) { free(ptr); } #endif DLLEXPORT void CALLCONVENTION tqslTrace(const char *name, const char *format, ...) { va_list ap; if (!tQSL_DiagFile) return; time_t t = time(0); char timebuf[50]; strncpy(timebuf, ctime(&t), sizeof timebuf); timebuf[strlen(timebuf) - 1] = '\0'; // Strip the newline if (!format) { fprintf(tQSL_DiagFile, "%s %s\r\n", timebuf, name); fflush(tQSL_DiagFile); return; } else { if (name) { fprintf(tQSL_DiagFile, "%s %s: ", timebuf, name); } } va_start(ap, format); vfprintf(tQSL_DiagFile, format, ap); va_end(ap); fprintf(tQSL_DiagFile, "\r\n"); fflush(tQSL_DiagFile); } DLLEXPORT void CALLCONVENTION tqsl_closeDiagFile(void) { if (tQSL_DiagFile) fclose(tQSL_DiagFile); tQSL_DiagFile = NULL; } DLLEXPORT int CALLCONVENTION tqsl_diagFileOpen(void) { return tQSL_DiagFile != NULL; } DLLEXPORT int CALLCONVENTION tqsl_openDiagFile(const char *fname) { #ifdef _WIN32 wchar_t* lfn = utf8_to_wchar(fname); tQSL_DiagFile = _wfopen(lfn, L"wb"); free_wchar(lfn); #else tQSL_DiagFile = fopen(fname, "wb"); #endif return (tQSL_DiagFile == NULL); } const char* tqsl_openssl_error(void) { static char buf[256]; int openssl_err; openssl_err = ERR_peek_error(); if (openssl_err) ERR_error_string_n(openssl_err, buf, sizeof buf); else strncpy(buf, "[error code not available]", sizeof buf); return buf; } tqsl-2.5.1/src/tqsllib-doc.h0000664000076400007640000000264413563134442015702 0ustar rmurphyrmurphy/*************************************************************************** tqsllib-doc.h - description ------------------- begin : Tue Jun 4 2002 copyright : (C) 2002 by ARRL author : Jon Bloom email : jbloom@arrl.org revision : $Id$ ***************************************************************************/ /** \mainpage * * The TrustedQSL library API is divided into several groups: * * \li \ref CertStuff - Request, load and retrieve digital certificates * \li \ref Data - Manage station-location data and produce signed data records * \li \ref Convert - Convert and sign ADIF and Cabrillo log files * \li \ref Util - Functions to operate on objects, set system parameters, and report errors * \li \ref Sign - Low-level digital signing * \li \ref ADIF - Low-level parsing and creation of ADIF files * \li \ref Cabrillo - Low-level parsing of Cabrillo files. * * Most of the library functions return an integer value that is * zero if there is no error and 1 if there is an error. The specific * error can be determined by examining #tQSL_Error and, possibly, * #tQSL_ADIF_Error, #tQSL_Cabrillo_Error, #tQSL_ErrorFile and * #tQSL_CustomError. The tqsl_getErrorString() and tqsl_getErrorString_v() * functions can be used to get error text strings. * */ tqsl-2.5.1/src/tqslexc.h0000664000076400007640000000173113563134442015144 0ustar rmurphyrmurphy/*************************************************************************** tqslexc.h - description ------------------- begin : Sat Dec 14 2002 copyright : (C) 2002 by ARRL author : Jon Bloom email : jbloom@arrl.org revision : $Id$ ***************************************************************************/ #ifndef __tqslexc_h #define __tqslexc_h #include #include #include "tqsllib.h" using std::exception; using std::string; class myexc : public exception { public: explicit myexc(const string& err) : exception() { _err = err; } myexc(const myexc& x) { _err = x._err; } virtual const char *what() const throw () { return _err.c_str(); } virtual ~myexc() throw() {} private: string _err; }; class tqslexc : public myexc { public: tqslexc() : myexc(tqsl_getErrorString()) {} }; #endif // __tqslexc_h tqsl-2.5.1/src/tqslerrno.h0000664000076400007640000000557013563134442015517 0ustar rmurphyrmurphy/*************************************************************************** tqslerrno.h - description ------------------- begin : Tue May 28 2002 copyright : (C) 2002 by ARRL author : Jon Bloom email : jbloom@arrl.org revision : $Id$ ***************************************************************************/ #ifndef __TQSLERRNO_H #define __TQSLERRNO_H /** \file * #tQSL_Error values */ #define TQSL_NO_ERROR 0 ///< No error #define TQSL_SYSTEM_ERROR 1 ///< System Error #define TQSL_OPENSSL_ERROR 2 ///< Error in OpenSSL calls #define TQSL_ADIF_ERROR 3 ///< ADIF Errors #define TQSL_CUSTOM_ERROR 4 ///< Custom errors - output to tQSL_CustomError #define TQSL_CABRILLO_ERROR 5 ///< Cabrillo handler error #define TQSL_OPENSSL_VERSION_ERROR 6 ///< OpenSSL version obsolete #define TQSL_ERROR_ENUM_BASE 16 ///< Base for enumerated errors #define TQSL_ALLOC_ERROR 16 ///< Memory allocation error #define TQSL_RANDOM_ERROR 17 ///< Error initializing random number generator #define TQSL_ARGUMENT_ERROR 18 ///< Invalid arguments #define TQSL_OPERATOR_ABORT 19 ///< Aborted by operator #define TQSL_NOKEY_ERROR 20 ///< No key available #define TQSL_BUFFER_ERROR 21 ///< Insufficient buffer space #define TQSL_INVALID_DATE 22 ///< Date string invalid #define TQSL_SIGNINIT_ERROR 23 ///< Error initializing signing routine #define TQSL_PASSWORD_ERROR 24 ///< Invalid password #define TQSL_EXPECTED_NAME 25 ///< Name expected but not supplied #define TQSL_NAME_EXISTS 26 ///< Entity name exists already #define TQSL_NAME_NOT_FOUND 27 ///< Entity name does not exist #define TQSL_INVALID_TIME 28 ///< Time format is invalid #define TQSL_CERT_DATE_MISMATCH 29 ///< Certificate date mismatch #define TQSL_PROVIDER_NOT_FOUND 30 ///< Certificate provider unknown #define TQSL_CERT_KEY_ONLY 31 ///< No signed public key is installed #define TQSL_CONFIG_ERROR 32 ///< There is an error in the configuration file #define TQSL_CERT_NOT_FOUND 33 ///< The certificate could not be found #define TQSL_PKCS12_ERROR 34 ///< There is an error parsing the .p12 file #define TQSL_CERT_TYPE_ERROR 35 ///< The certificate type is invalid #define TQSL_DATE_OUT_OF_RANGE 36 ///< The date is out of the valid range #define TQSL_DUPLICATE_QSO 37 ///< This QSO is already uploaded #define TQSL_DB_ERROR 38 ///< The dupe database could not be accessed #define TQSL_LOCATION_NOT_FOUND 39 ///< The station location is invalid #define TQSL_CALL_NOT_FOUND 40 ///< The callsign could not be located #define TQSL_CONFIG_SYNTAX_ERROR 41 ///< The config file has a syntax error #define TQSL_FILE_SYSTEM_ERROR 42 ///< There was a file system I/O error #define TQSL_FILE_SYNTAX_ERROR 43 ///< The file format is invalid #define TQSL_CERT_ERROR 44 ///< Callsign certificate could not be installed #endif /* __TQSLERRNO_H */ tqsl-2.5.1/src/tqslconvert.h0000664000076400007640000001626513563134442016055 0ustar rmurphyrmurphy/*************************************************************************** convert.h - description ------------------- begin : Sun Nov 17 2002 copyright : (C) 2002 by ARRL author : Jon Bloom email : jbloom@arrl.org revision : $Id$ ***************************************************************************/ #ifndef __tqslconvert_h #define __tqslconvert_h #include "tqsllib.h" /** \file * tQSL library converter functions. */ /** \defgroup Convert Converter API * * The Converter API provides the capability of converting Cabrillo * and ADIF files to GABBI output. */ /** @{ */ typedef void * tQSL_Converter; //!< Opaque converter type used by applications //!< to access conversion functions //!< #ifdef __cplusplus extern "C" { #endif /** Create a simple converter object * * Allocates resources for converting logs and processing duplicate records. */ DLLEXPORT int CALLCONVENTION tqsl_beginConverter(tQSL_Converter *convp); /** Initiates the conversion process for an ADIF file. * * \c certs and \c ncerts define a set of certificates that are available to the * converter for signing records. Typically, this list will be obtained by * calling tqsl_selectCertificates(). * * tqsl_endConverter() should be called to free the resources when the conversion * is finished. */ DLLEXPORT int CALLCONVENTION tqsl_beginADIFConverter(tQSL_Converter *conv, const char *filename, tQSL_Cert *certs, int ncerts, tQSL_Location loc); /** Initiates the conversion process for a Cabrillo file. * * \c certs and \c ncerts define a set of certificates that are available to the * converter for signing records. Typically, this list will be obtained by * calling tqsl_selectCertificates(). * * tqsl_endConverter() should be called to free the resources when the conversion * is finished. */ DLLEXPORT int CALLCONVENTION tqsl_beginCabrilloConverter(tQSL_Converter *conv, const char *filename, tQSL_Cert *certs, int ncerts, tQSL_Location loc); /** End the conversion process by freeing the used resources. */ DLLEXPORT int CALLCONVENTION tqsl_endConverter(tQSL_Converter *conv); /** Configure the converter to allow (allow != 0) or disallow (allow == 0) * nonamateur call signs in the CALL field. (Note: the test for * validity is fairly trivial and will allow some nonamateur calls to * get through, but it does catch most common errors.) * * \c allow defaults to 0 when tqsl_beginADIFConverter or * tqsl_beginCabrilloConverter is called. */ DLLEXPORT int CALLCONVENTION tqsl_setConverterAllowBadCall(tQSL_Converter conv, int allow); /** Configure the converter to allow (allow != 0) or disallow (allow == 0) * duplicate QSOs in a signed log. * Duplicate detection is done using QSO details, location details, and * certificate serial number. * * \c allow defaults to 1 for backwards compatibility when tqsl_beginADIFConverter or * tqsl_beginCabrilloConverter is called. */ DLLEXPORT int CALLCONVENTION tqsl_setConverterAllowDuplicates(tQSL_Converter convp, int allow); /** Specify the name of the application using the conversion library. * This is output in a header record in the exported log file. * Call this before calling tqsl_getConverterGABBI. * * \c app is a c string containing the application name. */ DLLEXPORT int CALLCONVENTION tqsl_setConverterAppName(tQSL_Converter convp, const char *app); /** Roll back insertions into the duplicates database. * * This is called when cancelling creating a log, and causes any records * added to the duplicates database to be removed so re-processing that * log does not cause the records to be mis-marked as duplicates. */ DLLEXPORT int CALLCONVENTION tqsl_converterRollBack(tQSL_Converter convp); /** Commits insertions into the duplicates database. * * This is called when a log is created normally and without issue, and so * the presumption is that we are "done" with these QSOs. */ DLLEXPORT int CALLCONVENTION tqsl_converterCommit(tQSL_Converter convp); /** Bulk read the duplicate DB records * * This is called to retrieve the QSO records from the dupe database. * It returns the key/value pair upon each call. * Return -1 for end of file, 0 for success, 1 for errors. */ DLLEXPORT int CALLCONVENTION tqsl_getDuplicateRecords(tQSL_Converter convp, char *key, char *data, int keylen); /** Bulk read the duplicate DB records * * This is called to retrieve the QSO records from the dupe database. * It returns the key/value pair upon each call. * Return -1 for end of file, 0 for success, 1 for errors. * V2 expects a 256 byte buffer for the "data" string. */ DLLEXPORT int CALLCONVENTION tqsl_getDuplicateRecordsV2(tQSL_Converter convp, char *key, char *data, int keylen); /** Bulk write duplicate DB records * * This is called to store a QSO record into the dupe database. * * Return -1 for duplicate insertion, 0 for success, 1 for errors. */ DLLEXPORT int CALLCONVENTION tqsl_putDuplicateRecord(tQSL_Converter convp, const char *key, const char *data, int keylen); /** Set QSO date filtering in the converter. * * If \c start points to a valid date, QSOs prior to that date will be ignored * by the converter. Similarly, if \c end points to a valid date, QSOs after * that date will be ignored. Either or both may be NULL (or point to an * invalid date) to disable date filtering for the respective range. */ DLLEXPORT int CALLCONVENTION tqsl_setADIFConverterDateFilter(tQSL_Converter conv, tQSL_Date *start, tQSL_Date *end); /** This is the main converter function. It returns a single GABBI * record. * * Returns the NULL pointer on error or EOF. (Test tQSL_Error to determine which.) * * tQSL_Error is set to TQSL_DATE_OUT_OF_RANGE if QSO date range checking * is active and the QSO date is outside the specified range. * This is a non-fatal error. * * tQSL_Error is set to TQSL_DUPLICATE_QSO if the QSO has already been * processed on the current computer. * * N.B. On systems that distinguish text-mode files from binary-mode files, * notably Windows, the GABBI records should be written in binary mode. * * N.B. If the selected certificate has not been initialized for signing via * tqsl_beginSigning(), this function will return a TQSL_SIGNINIT_ERROR. * The cert that caused the error can be obtained via tqsl_getConverterCert(), * initialized for signing, and then this function can be called again. No * data records will be lost in this process. */ DLLEXPORT const char* CALLCONVENTION tqsl_getConverterGABBI(tQSL_Converter conv); /** Get the certificate used to sign the most recent QSO record. */ DLLEXPORT int CALLCONVENTION tqsl_getConverterCert(tQSL_Converter conv, tQSL_Cert *certp); /** Get the input-file line number last read by the converter, starting * at line 1. */ DLLEXPORT int CALLCONVENTION tqsl_getConverterLine(tQSL_Converter conv, int *lineno); /** Get the text of the last record read by the converter. * * Returns NULL on error. */ DLLEXPORT const char* CALLCONVENTION tqsl_getConverterRecordText(tQSL_Converter conv); /** @} */ #ifdef __cplusplus } #endif #endif /* __tqslconvert_h */ tqsl-2.5.1/src/tqslconvert.cpp0000664000076400007640000015240013563134442016400 0ustar rmurphyrmurphy /*************************************************************************** tqslconvert.cpp - description ------------------- begin : Sun Nov 17 2002 copyright : (C) 2002 by ARRL author : Jon Bloom email : jbloom@arrl.org revision : $Id$ ***************************************************************************/ #define TQSLLIB_DEF #include "tqsllib.h" #include "tqslconvert.h" #include #include #include #include #include "tqslerrno.h" #include #include #include #include #ifdef USE_LMDB #include #define db_strerror mdb_strerror #else #include #endif #include //#include #ifndef _WIN32 #include #include #else #include #include "windirent.h" #endif #include "winstrdefs.h" using std::set; using std::string; static bool checkCallSign(const string& call); namespace tqsllib { class TQSL_CONVERTER { public: TQSL_CONVERTER(); ~TQSL_CONVERTER(); void clearRec(); int sentinel; // FILE *file; tQSL_ADIF adif; tQSL_Cabrillo cab; tQSL_Cert *certs; int ncerts; tQSL_Location loc; TQSL_QSO_RECORD rec; bool rec_done; int cert_idx; int base_idx; bool need_station_rec; bool *certs_used; bool allow_bad_calls; set modes; set bands; set propmodes; set satellites; string rec_text; tQSL_Date start, end; bool db_open; #ifdef USE_LMDB MDB_dbi seendb; MDB_env* dbenv; MDB_txn* txn; MDB_cursor* cursor; #else DB *seendb; DB_ENV* dbenv; DB_TXN* txn; DBC* cursor; #endif char *dbpath; FILE* errfile; char serial[512]; char callsign[64]; bool allow_dupes; bool need_ident_rec; char *appName; }; inline TQSL_CONVERTER::TQSL_CONVERTER() : sentinel(0x4445) { // file = 0; adif = 0; cab = 0; cert_idx = -1; base_idx = 1; certs_used = 0; need_station_rec = false; rec_done = true; allow_bad_calls = false; allow_dupes = true; //by default, don't change existing behavior (also helps with commit) memset(&rec, 0, sizeof rec); memset(&start, 0, sizeof start); memset(&end, 0, sizeof end); db_open = false; #ifndef USE_LMDB seendb = NULL; #endif dbpath = NULL; dbenv = NULL; txn = NULL; cursor = NULL; errfile = NULL; memset(&serial, 0, sizeof serial); memset(&callsign, 0, sizeof callsign); appName = NULL; need_ident_rec = true; // Init the band data const char *val; int n = 0; tqsl_getNumBand(&n); for (int i = 0; i < n; i++) { val = 0; tqsl_getBand(i, &val, 0, 0, 0); if (val) bands.insert(val); } // Init the mode data n = 0; tqsl_getNumMode(&n); for (int i = 0; i < n; i++) { val = 0; tqsl_getMode(i, &val, 0); if (val) modes.insert(val); } // Init the propagation mode data n = 0; tqsl_getNumPropagationMode(&n); for (int i = 0; i < n; i++) { val = 0; tqsl_getPropagationMode(i, &val, 0); if (val) propmodes.insert(val); } // Init the satellite data n = 0; tqsl_getNumSatellite(&n); for (int i = 0; i < n; i++) { val = 0; tqsl_getSatellite(i, &val, 0, 0, 0); if (val) satellites.insert(val); } } inline TQSL_CONVERTER::~TQSL_CONVERTER() { clearRec(); // if (file) // fclose(file); tqsl_endADIF(&adif); if (certs_used) delete[] certs_used; sentinel = 0; } inline void TQSL_CONVERTER::clearRec() { memset(&rec, 0, sizeof rec); rec_text = ""; } #define CAST_TQSL_CONVERTER(x) ((tqsllib::TQSL_CONVERTER *)(x)) } // namespace tqsllib using tqsllib::TQSL_CONVERTER; static char * fix_freq(const char *in) { static char out[128]; const char *p = in; bool decimal = false; char *o = out; while (*p) { if (*p == '.') { if (decimal) { p++; continue; } decimal = true; } *o++ = *p++; } *o = '\0'; return out; } static char * tqsl_strtoupper(char *str) { for (char *cp = str; *cp != '\0'; cp++) *cp = toupper(*cp); return str; } static TQSL_CONVERTER * check_conv(tQSL_Converter conv) { if (tqsl_init()) return 0; if (conv == 0 || CAST_TQSL_CONVERTER(conv)->sentinel != 0x4445) return 0; return CAST_TQSL_CONVERTER(conv); } static tqsl_adifFieldDefinitions adif_qso_record_fields[] = { { "CALL", "", TQSL_ADIF_RANGE_TYPE_NONE, TQSL_CALLSIGN_MAX, 0, 0, NULL }, { "BAND", "", TQSL_ADIF_RANGE_TYPE_NONE, TQSL_BAND_MAX, 0, 0, NULL }, { "MODE", "", TQSL_ADIF_RANGE_TYPE_NONE, TQSL_MODE_MAX, 0, 0, NULL }, { "SUBMODE", "", TQSL_ADIF_RANGE_TYPE_NONE, TQSL_MODE_MAX, 0, 0, NULL }, { "QSO_DATE", "", TQSL_ADIF_RANGE_TYPE_NONE, 10, 0, 0, NULL }, { "TIME_ON", "", TQSL_ADIF_RANGE_TYPE_NONE, 10, 0, 0, NULL }, { "FREQ", "", TQSL_ADIF_RANGE_TYPE_NONE, TQSL_FREQ_MAX, 0, 0, NULL }, { "FREQ_RX", "", TQSL_ADIF_RANGE_TYPE_NONE, TQSL_FREQ_MAX, 0, 0, NULL }, { "BAND_RX", "", TQSL_ADIF_RANGE_TYPE_NONE, TQSL_BAND_MAX, 0, 0, NULL }, { "SAT_NAME", "", TQSL_ADIF_RANGE_TYPE_NONE, TQSL_SATNAME_MAX, 0, 0, NULL }, { "PROP_MODE", "", TQSL_ADIF_RANGE_TYPE_NONE, TQSL_PROPMODE_MAX, 0, 0, NULL }, { "eor", "", TQSL_ADIF_RANGE_TYPE_NONE, 0, 0, 0, NULL }, }; DLLEXPORT int CALLCONVENTION tqsl_beginConverter(tQSL_Converter *convp) { tqslTrace("tqsl_beginConverter", NULL); if (tqsl_init()) return 0; if (!convp) { tqslTrace("tqsl_beginConverter", "convp=NULL"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } TQSL_CONVERTER *conv = new TQSL_CONVERTER(); *convp = conv; return 0; } DLLEXPORT int CALLCONVENTION tqsl_beginADIFConverter(tQSL_Converter *convp, const char *filename, tQSL_Cert *certs, int ncerts, tQSL_Location loc) { tqslTrace("tqsl_beginADIFConverter", NULL); if (tqsl_init()) return 0; if (!convp || !filename) { tqslTrace("tqsl_beginADIFConverter", "arg err convp=0x%lx filename=0x%lx certs=0x%lx", convp, filename, certs); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } tQSL_ADIF adif; if (tqsl_beginADIF(&adif, filename)) { tqslTrace("tqsl_beginADIFConverter", "tqsl_beginADIF fail %d", tQSL_Error); return 1; } TQSL_CONVERTER *conv = new TQSL_CONVERTER(); conv->adif = adif; conv->certs = certs; conv->ncerts = ncerts; if (ncerts > 0) { conv->certs_used = new bool[ncerts]; for (int i = 0; i < ncerts; i++) conv->certs_used[i] = false; } conv->loc = loc; *convp = conv; return 0; } DLLEXPORT int CALLCONVENTION tqsl_beginCabrilloConverter(tQSL_Converter *convp, const char *filename, tQSL_Cert *certs, int ncerts, tQSL_Location loc) { tqslTrace("tqsl_beginCabrilloConverter", NULL); if (tqsl_init()) return 0; if (!convp || !filename) { tQSL_Error = TQSL_ARGUMENT_ERROR; tqslTrace("tqsl_beginCabrilloConverter", "arg error convp=0x%lx, filename=0x%lx, certs=0x%lx", convp, filename, certs); return 1; } tQSL_Cabrillo cab; if (tqsl_beginCabrillo(&cab, filename)) { tqslTrace("tqsl_beginCabrilloConverter", "tqsl_beginCabrillo fail %d", tQSL_Error); return 1; } TQSL_CONVERTER *conv = new TQSL_CONVERTER(); conv->cab = cab; conv->certs = certs; conv->ncerts = ncerts; if (ncerts > 0) { conv->certs_used = new bool[ncerts]; for (int i = 0; i < ncerts; i++) conv->certs_used[i] = false; } conv->loc = loc; *convp = conv; return 0; } DLLEXPORT int CALLCONVENTION tqsl_endConverter(tQSL_Converter *convp) { tqslTrace("tqsl_endConverter", NULL); if (!convp || CAST_TQSL_CONVERTER(*convp) == 0) return 0; TQSL_CONVERTER* conv; if ((conv = check_conv(*convp))) { #ifdef USE_LMDB if (conv->txn) mdb_txn_abort(conv->txn); #else if (conv->txn) conv->txn->abort(conv->txn); #endif if (conv->db_open) { #ifdef USE_LMDB mdb_dbi_close(conv->dbenv, conv->seendb); #else conv->seendb->compact(conv->seendb, NULL, NULL, NULL, NULL, 0, NULL); conv->seendb->close(conv->seendb, 0); #endif } conv->db_open = false; if (conv->dbenv) { #ifdef USE_LMDB mdb_env_close(conv->dbenv); #else char **unused; conv->dbenv->txn_checkpoint(conv->dbenv, 0, 0, 0); conv->dbenv->log_archive(conv->dbenv, &unused, DB_ARCH_REMOVE); conv->dbenv->close(conv->dbenv, 0); #endif } // close files and clean up converters, if any if (conv->adif) tqsl_endADIF(&conv->adif); if (conv->cab) tqsl_endCabrillo(&conv->cab); if (conv->dbpath) free(conv->dbpath); if (conv->errfile) fclose(conv->errfile); } if (conv->appName) free(conv->appName); if (CAST_TQSL_CONVERTER(*convp)->sentinel == 0x4445) delete CAST_TQSL_CONVERTER(*convp); *convp = 0; return 0; } static unsigned char * adif_allocate(size_t size) { return new unsigned char[size]; } static int find_matching_cert(TQSL_CONVERTER *conv) { int i; for (i = 0; i < conv->ncerts; i++) { tQSL_Date cdate; if (tqsl_getCertificateQSONotBeforeDate(conv->certs[i], &cdate)) return -1; if (tqsl_compareDates(&(conv->rec.date), &cdate) < 0) continue; if (tqsl_getCertificateQSONotAfterDate(conv->certs[i], &cdate)) return -1; if (tqsl_compareDates(&(conv->rec.date), &cdate) > 0) continue; return i; } return -1; } static const char *notypes[] = { "D", "T", "M", "N", "C", "" }; static const char * tqsl_infer_band(const char* infreq) { char *oldlocale = setlocale(LC_NUMERIC, "C"); double freq = atof(infreq); setlocale(LC_NUMERIC, oldlocale); double freq_khz = freq * 1000.0; int nband = 0; tqsl_getNumBand(&nband); for (int i = 0; i < nband; i++) { const char *name; const char *spectrum; int low, high; if (tqsl_getBand(i, &name, &spectrum, &low, &high)) break; bool match = false; if (!strcmp(spectrum, "HF")) { // Allow for cases where loggers that don't log the // real frequency. if (low == 10100) low = 10000; else if (low == 18068) low = 18000; else if (low == 24890) low = 24000; if (freq_khz >= low && freq_khz <= high) { match = true; } } else { if (freq >= low && freq <= high) match = true; if (freq >= low && high == 0) match = true; } if (match) return name; } return ""; } DLLEXPORT int CALLCONVENTION tqsl_setADIFConverterDateFilter(tQSL_Converter convp, tQSL_Date *start, tQSL_Date *end) { TQSL_CONVERTER *conv; tqslTrace("tqsl_setADIFConverterDateFilter", NULL); if (!(conv = check_conv(convp))) return 1; if (start == NULL) conv->start.year = conv->start.month = conv->start.day = 0; else conv->start = *start; if (end == NULL) conv->end.year = conv->end.month = conv->end.day = 0; else conv->end = *end; return 0; } // Remove the dupes db files static void remove_db(const char *path) { tqslTrace("remove_db", "path=%s", path); #ifdef _WIN32 wchar_t* wpath = utf8_to_wchar(path); _WDIR *dir = _wopendir(wpath); free_wchar(wpath); #else DIR *dir = opendir(path); #endif if (dir != NULL) { #ifdef USE_LMDB #ifdef _WIN32 struct _wdirent *ent = NULL; while ((ent = _wreaddir(dir)) != NULL) { if (!wcscmp(ent->d_name, L"data.mdb") || !wcscmp(ent->d_name, L"lock.mdb")) { #else struct dirent *ent = NULL; while ((ent = readdir(dir)) != NULL) { if (!strcmp(ent->d_name, "data.mdb") || !strcmp(ent->d_name, "lock.mdb")) { #endif #else // USE_LMDB #ifdef _WIN32 struct _wdirent *ent = NULL; while ((ent = _wreaddir(dir)) != NULL) { if (!wcscmp(ent->d_name, L"duplicates.db") || !wcsncmp(ent->d_name, L"log.", 4) || !wcsncmp(ent->d_name, L"__db.", 5)) { #else struct dirent *ent = NULL; while ((ent = readdir(dir)) != NULL) { if (!strcmp(ent->d_name, "duplicates.db") || !strncmp(ent->d_name, "log.", 4) || !strncmp(ent->d_name, "__db.", 5)) { #endif #endif // USE_LMDB string fname = path; int rstat; #ifdef _WIN32 char dname[TQSL_MAX_PATH_LEN]; wcstombs(dname, ent->d_name, TQSL_MAX_PATH_LEN); fname = fname + "/" + dname; wchar_t* wfname = utf8_to_wchar(fname.c_str()); tqslTrace("remove_db", "unlinking %s", fname.c_str()); rstat = _wunlink(wfname); free_wchar(wfname); #else fname = fname + "/" + ent->d_name; tqslTrace("remove_db", "unlinking %s", fname.c_str()); rstat = unlink(fname.c_str()); #endif if (rstat < 0) { tqslTrace("remove_db", "can't unlink %s: %s", fname.c_str(), strerror(errno)); } } } #ifdef _WIN32 _wclosedir(dir); #else closedir(dir); #endif } return; } #if !defined(_WIN32) && !defined(USE_LMDB) // Callback method for the dbenv->failchk() call // Used to determine if the given pid/tid is // alive. static int isalive(DB_ENV *env, pid_t pid, db_threadid_t tid, uint32_t flags) { int alive = 0; if (pid == getpid()) { alive = 1; } else if (kill(pid, 0) == 0) { alive = 1; } else if (errno == EPERM) { alive = 1; } return alive; } #endif // _WIN32 // Open the duplicates database #ifdef USE_LMDB static bool open_db(TQSL_CONVERTER *conv, bool readonly) { bool dbinit_cleanup = false; int dbret; bool triedRemove = false; bool triedDelete = false; string fixedpath = tQSL_BaseDir; //must be first because of gotos size_t found = fixedpath.find('\\'); tqslTrace("open_db", "path=%s", fixedpath.c_str()); //bdb complains about \\s in path on windows... while (found != string::npos) { fixedpath.replace(found, 1, "/"); found = fixedpath.find('\\'); } conv->dbpath = strdup(fixedpath.c_str()); #ifndef _WIN32 // Clean up junk in that directory DIR *dir = opendir(fixedpath.c_str()); if (dir != NULL) { struct dirent *ent; while ((ent = readdir(dir)) != NULL) { if (ent->d_name[0] == '.') continue; struct stat s; // If it's a symlink pointing to itself, remove it. string fname = fixedpath + "/" + ent->d_name; if (stat(fname.c_str(), &s)) { if (errno == ELOOP) { #ifdef _WIN32 _wunlink(ConvertFromUtf8ToUtf16(fname.c_str())); #else unlink(fname.c_str()); #endif } } } closedir(dir); } #endif string logpath = fixedpath + "/dberr.log"; #ifdef _WIN32 wchar_t* wlogpath = utf8_to_wchar(logpath.c_str()); conv->errfile = _wfopen(wlogpath, L"wb"); free_wchar(wlogpath); #else conv->errfile = fopen(logpath.c_str(), "wb"); #endif reopen: // Try to open the database while (true) { if (!conv->dbenv) { // Create the database environment handle if ((dbret = mdb_env_create(&conv->dbenv))) { // can't make env handle tqslTrace("open_db", "mdb_env_create error %s", mdb_strerror(dbret)); if (conv->errfile) fprintf(conv->errfile, "mdb_env_create error %s\n", mdb_strerror(dbret)); dbinit_cleanup = true; goto dbinit_end; } tqslTrace("open_db", "dbenv=0x%lx", conv->dbenv); } mdb_env_set_maxdbs(conv->dbenv, 2); mdb_env_set_maxreaders(conv->dbenv, 2); mdb_env_set_mapsize(conv->dbenv, 1024 * 1024 * 1024); // Now open the database tqslTrace("open_db", "Opening the database at %s", conv->dbpath); if ((dbret = mdb_env_open(conv->dbenv, conv->dbpath, 0, 0600))) { tqslTrace("open_db", "dbenv->open %s error %s", conv->dbpath, mdb_strerror(dbret)); if (conv->errfile) fprintf(conv->errfile, "opening DB %s returns status %s\n", conv->dbpath, mdb_strerror(dbret)); // can't open environment - try to delete it and try again. tqslTrace("open_db", "Environment open fail, triedRemove=%d", triedRemove); if (!triedRemove) { // Remove the dross tqslTrace("open_db", "Removing environment"); conv->dbenv = NULL; triedRemove = true; if (conv->errfile) fprintf(conv->errfile, "About to retry after removing the environment\n"); tqslTrace("open_db", "About to retry after removing the environment"); continue; } tqslTrace("open_db", "Retry attempt after removing the environment failed"); if (conv->errfile) { fprintf(conv->errfile, "Retry attempt after removing the environment failed.\n"); } // can't open environment and cleanup efforts failed. mdb_env_close(conv->dbenv); conv->dbenv = NULL; // this can't be recovered dbinit_cleanup = true; tqslTrace("open_db", "can't fix. abandoning."); remove_db(fixedpath.c_str()); goto dbinit_end; } break; // Opened OK. } tqslTrace("open_db", "starting transaction, readonly=%d", readonly); if ((dbret = mdb_txn_begin(conv->dbenv, NULL, readonly ? MDB_RDONLY : 0, &conv->txn))) { // can't start a txn tqslTrace("open_db", "can't create txn %s", mdb_strerror(dbret)); if (conv->errfile) fprintf(conv->errfile, "Can't create transaction: %s\n", mdb_strerror(dbret)); dbinit_cleanup = true; goto dbinit_end; } tqslTrace("open_db", "opening database now"); if ((dbret = mdb_dbi_open(conv->txn, NULL, 0, &conv->seendb))) { if (dbret == MDB_NOTFOUND) { tqslTrace("open_db", "DB not found, making a new one"); dbret = mdb_dbi_open(conv->txn, NULL, MDB_CREATE, &conv->seendb); } if (dbret) { // can't open the db tqslTrace("open_db", "create failed with %s errno %d", mdb_strerror(dbret), errno); if (conv->errfile) fprintf(conv->errfile, "create failed with %s errno %d", mdb_strerror(dbret), errno); dbinit_cleanup = true; goto dbinit_end; } } dbinit_end: if (dbinit_cleanup) { tqslTrace("open_db", "DB open failed, triedDelete=%d", triedDelete); tQSL_Error = TQSL_DB_ERROR; tQSL_Errno = errno; strncpy(tQSL_CustomError, mdb_strerror(dbret), sizeof tQSL_CustomError); tqslTrace("open_db", "Error opening db: %s", tQSL_CustomError); if (conv->txn) mdb_txn_abort(conv->txn); conv->txn = NULL; if (conv->db_open) { mdb_dbi_close(conv->dbenv, conv->seendb); conv->db_open = false; } if (conv->dbenv) { if (conv->dbpath) { free(conv->dbpath); conv->dbpath = NULL; } mdb_drop(conv->txn, conv->seendb, 1); mdb_env_close(conv->dbenv); } if (conv->cursor) mdb_cursor_close(conv->cursor); if (conv->errfile) fclose(conv->errfile); conv->dbenv = NULL; conv->cursor = NULL; conv->errfile = NULL; // Handle case where the database is just broken #ifndef DB_RUNRECOVERY #define DB_RUNRECOVERY -30973 #endif if ((dbret == EINVAL || dbret == DB_RUNRECOVERY) && !triedDelete) { tqslTrace("open_db", "EINVAL/RUNRECOVERY. Removing db"); remove_db(fixedpath.c_str()); triedDelete = true; goto reopen; } conv->db_open = false; return false; } conv->db_open = true; return true; } #else // USE_LMDB static bool open_db(TQSL_CONVERTER *conv, bool readonly) { bool dbinit_cleanup = false; int dbret; bool triedRemove = false; bool triedDelete = false; int envflags = DB_INIT_TXN|DB_INIT_LOG|DB_INIT_MPOOL|DB_RECOVER|DB_REGISTER|DB_CREATE; string fixedpath = tQSL_BaseDir; //must be first because of gotos size_t found = fixedpath.find('\\'); tqslTrace("open_db", "path=%s", fixedpath.c_str()); //bdb complains about \\s in path on windows... while (found != string::npos) { fixedpath.replace(found, 1, "/"); found = fixedpath.find('\\'); } conv->dbpath = strdup(fixedpath.c_str()); #ifndef _WIN32 // Clean up junk in that directory DIR *dir = opendir(fixedpath.c_str()); if (dir != NULL) { struct dirent *ent; while ((ent = readdir(dir)) != NULL) { if (ent->d_name[0] == '.') continue; struct stat s; // If it's a symlink pointing to itself, remove it. string fname = fixedpath + "/" + ent->d_name; if (stat(fname.c_str(), &s)) { if (errno == ELOOP) { #ifdef _WIN32 _wunlink(ConvertFromUtf8ToUtf16(fname.c_str())); #else unlink(fname.c_str()); #endif } } } closedir(dir); } #endif string logpath = fixedpath + "/dberr.log"; #ifdef _WIN32 wchar_t* wlogpath = utf8_to_wchar(logpath.c_str()); conv->errfile = _wfopen(wlogpath, L"wb"); free_wchar(wlogpath); #else conv->errfile = fopen(logpath.c_str(), "wb"); #endif reopen: // Try to open the database while (true) { if (!conv->dbenv) { // Create the database environment handle if ((dbret = db_env_create(&conv->dbenv, 0))) { // can't make env handle tqslTrace("open_db", "db_env_create error %s", db_strerror(dbret)); dbinit_cleanup = true; goto dbinit_end; } tqslTrace("open_db", "dbenv=0x%lx", conv->dbenv); if (conv->errfile) { conv->dbenv->set_errfile(conv->dbenv, conv->errfile); conv->dbenv->set_verbose(conv->dbenv, DB_VERB_RECOVERY, 1); } // Enable stale lock removal conv->dbenv->set_thread_count(conv->dbenv, 8); #ifndef _WIN32 conv->dbenv->set_isalive(conv->dbenv, isalive); #endif // Log files default to 10 Mb each. We don't need nearly that much. if (conv->dbenv->set_lg_max) conv->dbenv->set_lg_max(conv->dbenv, 256 * 1024); // Allocate additional locking resources - some have run out with // the default 1000 locks if (conv->dbenv->set_lk_max_locks) conv->dbenv->set_lk_max_locks(conv->dbenv, 20000); if (conv->dbenv->set_lk_max_objects) conv->dbenv->set_lk_max_objects(conv->dbenv, 20000); } // Now open the database tqslTrace("open_db", "Opening the database at %s", conv->dbpath); if ((dbret = conv->dbenv->open(conv->dbenv, conv->dbpath, envflags, 0600))) { int db_errno = errno; tqslTrace("open_db", "dbenv->open %s error %s", conv->dbpath, db_strerror(dbret)); if (conv->errfile) fprintf(conv->errfile, "opening DB %s returns status %s\n", conv->dbpath, db_strerror(dbret)); // Can't open the database - maybe try private? if ((dbret == EACCES || dbret == EROFS) || (dbret == EINVAL && errno == dbret)) { if (!(envflags & DB_PRIVATE)) { envflags |= DB_PRIVATE; continue; } } // can't open environment - try to delete it and try again. tqslTrace("open_db", "Environment open fail, triedRemove=%d", triedRemove); if (!triedRemove) { // Remove the dross tqslTrace("open_db", "Removing environment"); conv->dbenv->remove(conv->dbenv, conv->dbpath, DB_FORCE); conv->dbenv = NULL; triedRemove = true; if (conv->errfile) fprintf(conv->errfile, "About to retry after removing the environment\n"); tqslTrace("open_db", "About to retry after removing the environment"); continue; } tqslTrace("open_db", "Retry attempt after removing the environment failed"); if (conv->errfile) { fprintf(conv->errfile, "Retry attempt after removing the environment failed.\n"); } // EINVAL means that the database is corrupted to the point // where it can't be opened. Remove it and try again. if ((dbret == EINVAL || db_errno == EINVAL) && !triedDelete) { tqslTrace("open_db", "EINVAL. Removing db"); conv->dbenv->close(conv->dbenv, 0); conv->dbenv = NULL; remove_db(fixedpath.c_str()); triedDelete = true; continue; } // can't open environment and cleanup efforts failed. conv->dbenv->close(conv->dbenv, 0); conv->dbenv = NULL; // this can't be recovered dbinit_cleanup = true; tqslTrace("open_db", "can't fix. abandoning."); remove_db(fixedpath.c_str()); goto dbinit_end; } break; // Opened OK. } #ifndef _WIN32 // isalive() method doesn't exist for WIN32. // Stale lock removal tqslTrace("open_db", "Removing stale locks"); dbret = conv->dbenv->failchk(conv->dbenv, 0); if (dbret && conv->errfile) { fprintf(conv->errfile, "lock removal for DB %s returns status %s\n", conv->dbpath, db_strerror(dbret)); } #endif tqslTrace("open_db", "calling db_create"); if ((dbret = db_create(&conv->seendb, conv->dbenv, 0))) { // can't create db dbinit_cleanup = true; tqslTrace("open_db", "Can't create db"); goto dbinit_end; } #ifndef DB_TXN_BULK #define DB_TXN_BULK 0 #endif tqslTrace("open_db", "starting transaction, readonly=%d", readonly); if (!readonly && (dbret = conv->dbenv->txn_begin(conv->dbenv, NULL, &conv->txn, DB_TXN_BULK))) { // can't start a txn tqslTrace("open_db", "can't create txn %s", db_strerror(dbret)); dbinit_cleanup = true; goto dbinit_end; } // Probe the database type tqslTrace("open_db", "opening database now"); if ((dbret = conv->seendb->open(conv->seendb, conv->txn, "duplicates.db", NULL, DB_UNKNOWN, 0, 0600))) { if (dbret == ENOENT) { tqslTrace("open_db", "DB not found, making a new one"); dbret = conv->seendb->open(conv->seendb, conv->txn, "duplicates.db", NULL, DB_HASH, DB_CREATE, 0600); } if (dbret) { // can't open the db tqslTrace("open_db", "create failed with %s errno %d", db_strerror(dbret), errno); dbinit_cleanup = true; goto dbinit_end; } } DBTYPE type; conv->seendb->get_type(conv->seendb, &type); tqslTrace("open_db", "type=%d", type); if (type == DB_BTREE) { tqslTrace("open_db", "BTREE type. Converting."); // Have to convert the database. string dumpfile = fixedpath + "/dupedump.txt"; #ifdef _WIN32 wchar_t* wdumpfile = utf8_to_wchar(dumpfile.c_str()); FILE *dmp = _wfopen(wdumpfile, L"wb+"); free_wchar(wdumpfile); #else FILE *dmp = fopen(dumpfile.c_str(), "wb+"); #endif if (!dmp) { tqslTrace("open_db", "Error opening dump file %s: %s", dumpfile.c_str(), strerror(errno)); dbinit_cleanup = true; goto dbinit_end; } if (!conv->cursor) { #ifndef DB_CURSOR_BULK #define DB_CURSOR_BULK 0 #endif int err = conv->seendb->cursor(conv->seendb, conv->txn, &conv->cursor, DB_CURSOR_BULK); if (err) { strncpy(tQSL_CustomError, db_strerror(err), sizeof tQSL_CustomError); tQSL_Error = TQSL_DB_ERROR; tQSL_Errno = errno; tqslTrace("open_db", "Error setting cursor for old DB: %s", err); dbinit_cleanup = true; goto dbinit_end; } } DBT dbkey, dbdata; char duprec[512]; while (1) { memset(&dbkey, 0, sizeof dbkey); memset(&dbdata, 0, sizeof dbdata); int status = conv->cursor->c_get(conv->cursor, &dbkey, &dbdata, DB_NEXT); if (DB_NOTFOUND == status) { break; // No more records } if (status != 0) { strncpy(tQSL_CustomError, db_strerror(status), sizeof tQSL_CustomError); tQSL_Error = TQSL_DB_ERROR; tQSL_Errno = errno; tqslTrace("open_db", "Error reading for dump: %s", db_strerror(status)); dbinit_cleanup = true; goto dbinit_end; } memcpy(duprec, dbkey.data, dbkey.size); duprec[dbkey.size] = '\0'; fprintf(dmp, "%s\n", duprec); } conv->cursor->close(conv->cursor); if (conv->txn) conv->txn->commit(conv->txn, 0); conv->seendb->close(conv->seendb, 0); conv->db_open = false; conv->dbenv->remove(conv->dbenv, conv->dbpath, DB_FORCE); conv->dbenv->close(conv->dbenv, 0); conv->cursor = NULL; conv->seendb = NULL; conv->dbenv = NULL; // Remove the old dupe db tqslTrace("open_db", "Removing old format db"); remove_db(fixedpath.c_str()); // Now create the new database if ((dbret = db_env_create(&conv->dbenv, 0))) { // can't make env handle tqslTrace("open_db", "Can't make db handle: %s", db_strerror(dbret)); dbinit_cleanup = true; goto dbinit_end; } if (conv->errfile) conv->dbenv->set_errfile(conv->dbenv, conv->errfile); if (conv->dbenv->set_lg_max) conv->dbenv->set_lg_max(conv->dbenv, 256 * 1024); if (conv->dbenv->set_lk_max_locks) conv->dbenv->set_lk_max_locks(conv->dbenv, 20000); if (conv->dbenv->set_lk_max_objects) conv->dbenv->set_lk_max_objects(conv->dbenv, 20000); if ((dbret = conv->dbenv->open(conv->dbenv, conv->dbpath, envflags, 0600))) { tqslTrace("open_db", "Error opening db: %s", db_strerror(dbret)); if (conv->errfile) fprintf(conv->errfile, "opening DB %s returns status %d\n", conv->dbpath, dbret); dbinit_cleanup = true; goto dbinit_end; } if ((dbret = db_create(&conv->seendb, conv->dbenv, 0))) { // can't create db tqslTrace("open_db", "Error creating db: %s", db_strerror(dbret)); dbinit_cleanup = true; goto dbinit_end; } // Create the new database if ((dbret = conv->seendb->open(conv->seendb, NULL, "duplicates.db", NULL, DB_HASH, DB_CREATE, 0600))) { // can't open the db tqslTrace("open_db", "Error opening new db: %s", db_strerror(dbret)); dbinit_cleanup = true; goto dbinit_end; } fseek(dmp, 0, SEEK_SET); char d[1]= {'D'}; memset(&dbkey, 0, sizeof dbkey); memset(&dbdata, 0, sizeof dbdata); dbdata.data = d; dbdata.size = 1; while (fgets(duprec, sizeof duprec, dmp)) { dbkey.data = duprec; dbkey.size = strlen(duprec) - 1; conv->seendb->put(conv->seendb, NULL, &dbkey, &dbdata, 0); } conv->seendb->close(conv->seendb, 0); conv->dbenv->close(conv->dbenv, 0); goto reopen; } dbinit_end: if (dbinit_cleanup) { tqslTrace("open_db", "DB open failed, triedDelete=%d", triedDelete); tQSL_Error = TQSL_DB_ERROR; tQSL_Errno = errno; strncpy(tQSL_CustomError, db_strerror(dbret), sizeof tQSL_CustomError); tqslTrace("open_db", "Error opening db: %s", tQSL_CustomError); if (conv->txn) conv->txn->abort(conv->txn); if (conv->seendb) conv->seendb->close(conv->seendb, 0); conv->db_open = false; if (conv->dbenv) { if (conv->dbpath) { conv->dbenv->remove(conv->dbenv, conv->dbpath, DB_FORCE); free(conv->dbpath); conv->dbpath = NULL; } conv->dbenv->close(conv->dbenv, 0); } if (conv->cursor) conv->cursor->close(conv->cursor); if (conv->errfile) fclose(conv->errfile); conv->txn = NULL; conv->dbenv = NULL; conv->cursor = NULL; conv->seendb = NULL; conv->errfile = NULL; // Handle case where the database is just broken if (dbret == EINVAL && !triedDelete) { tqslTrace("open_db", "EINVAL. Removing db"); remove_db(fixedpath.c_str()); triedDelete = true; goto reopen; } return false; } conv->db_open = true; return true; } #endif // USE_LMDB DLLEXPORT const char* CALLCONVENTION tqsl_getConverterGABBI(tQSL_Converter convp) { TQSL_CONVERTER *conv; char signdata[1024]; if (!(conv = check_conv(convp))) return 0; if (conv->need_ident_rec) { int major = 0, minor = 0, config_major = 0, config_minor = 0; tqsl_getVersion(&major, &minor); tqsl_getConfigVersion(&config_major, &config_minor); char temp[512]; static char ident[512]; snprintf(temp, sizeof temp, "%s Lib: V%d.%d Config: V%d.%d AllowDupes: %s", conv->appName ? conv->appName : "Unknown", major, minor, config_major, config_minor, conv->allow_dupes ? "true" : "false"); temp[sizeof temp - 1] = '\0'; int len = strlen(temp); snprintf(ident, sizeof ident, "%s\n", len, temp); ident[sizeof ident - 1] = '\0'; conv->need_ident_rec = false; return ident; } if (conv->need_station_rec) { int uid = conv->cert_idx + conv->base_idx; conv->need_station_rec = false; const char *tStation = tqsl_getGABBItSTATION(conv->loc, uid, uid); tqsl_getCertificateSerialExt(conv->certs[conv->cert_idx], conv->serial, sizeof conv->serial); tqsl_getCertificateCallSign(conv->certs[conv->cert_idx], conv->callsign, sizeof conv->callsign); return tStation; } if (!conv->allow_dupes && !conv->db_open) { if (!open_db(conv, false)) { // If can't open dupes DB return 0; } } TQSL_ADIF_GET_FIELD_ERROR stat; if (conv->rec_done) { //cerr << "Getting rec" << endl; conv->rec_done = false; conv->clearRec(); int cstat = 0; int saveErr = 0; if (conv->adif) { while (1) { tqsl_adifFieldResults result; if (tqsl_getADIFField(conv->adif, &result, &stat, adif_qso_record_fields, notypes, adif_allocate)) break; if (stat != TQSL_ADIF_GET_FIELD_SUCCESS && stat != TQSL_ADIF_GET_FIELD_NO_NAME_MATCH) break; if (!strcasecmp(result.name, "eor")) break; if (!strcasecmp(result.name, "CALL") && result.data) { conv->rec.callsign_set = true; strncpy(conv->rec.callsign, reinterpret_cast(result.data), sizeof conv->rec.callsign); } else if (!strcasecmp(result.name, "BAND") && result.data) { conv->rec.band_set = true; strncpy(conv->rec.band, reinterpret_cast(result.data), sizeof conv->rec.band); } else if (!strcasecmp(result.name, "MODE") && result.data) { conv->rec.mode_set = true; strncpy(conv->rec.mode, reinterpret_cast(result.data), sizeof conv->rec.mode); } else if (!strcasecmp(result.name, "SUBMODE") && result.data) { strncpy(conv->rec.submode, reinterpret_cast(result.data), sizeof conv->rec.submode); } else if (!strcasecmp(result.name, "FREQ") && result.data) { conv->rec.band_set = true; strncpy(conv->rec.freq, fix_freq(reinterpret_cast(result.data)), sizeof conv->rec.freq); if (atof(conv->rec.freq) == 0.0) conv->rec.freq[0] = '\0'; } else if (!strcasecmp(result.name, "FREQ_RX") && result.data) { strncpy(conv->rec.rxfreq, fix_freq(reinterpret_cast(result.data)), sizeof conv->rec.rxfreq); if (atof(conv->rec.rxfreq) == 0.0) conv->rec.rxfreq[0] = '\0'; } else if (!strcasecmp(result.name, "BAND_RX") && result.data) { strncpy(conv->rec.rxband, reinterpret_cast(result.data), sizeof conv->rec.rxband); } else if (!strcasecmp(result.name, "SAT_NAME") && result.data) { strncpy(conv->rec.satname, reinterpret_cast(result.data), sizeof conv->rec.satname); } else if (!strcasecmp(result.name, "PROP_MODE") && result.data) { strncpy(conv->rec.propmode, reinterpret_cast(result.data), sizeof conv->rec.propmode); } else if (!strcasecmp(result.name, "QSO_DATE") && result.data) { conv->rec.date_set = true; cstat = tqsl_initDate(&(conv->rec.date), (const char *)result.data); if (cstat) saveErr = tQSL_Error; } else if (!strcasecmp(result.name, "TIME_ON") && result.data) { conv->rec.time_set = true; cstat = tqsl_initTime(&(conv->rec.time), (const char *)result.data); if (cstat) saveErr = tQSL_Error; } if (stat == TQSL_ADIF_GET_FIELD_SUCCESS) { conv->rec_text += string(reinterpret_cast(result.name)) + ": "; if (result.data) conv->rec_text += string(reinterpret_cast(result.data)); conv->rec_text += "\n"; } if (result.data) delete[] result.data; } if (saveErr) { tQSL_Error = saveErr; conv->rec_done = true; return 0; } if (stat == TQSL_ADIF_GET_FIELD_EOF) return 0; if (stat != TQSL_ADIF_GET_FIELD_SUCCESS) { tQSL_ADIF_Error = stat; tQSL_Error = TQSL_ADIF_ERROR; return 0; } // ADIF record is complete. See if we need to infer the BAND fields. if (conv->rec.band[0] == 0) strncpy(conv->rec.band, tqsl_infer_band(conv->rec.freq), sizeof conv->rec.band); if (conv->rec.rxband[0] == 0) strncpy(conv->rec.rxband, tqsl_infer_band(conv->rec.rxfreq), sizeof conv->rec.rxband); } else if (conv->cab) { TQSL_CABRILLO_ERROR_TYPE stat; do { tqsl_cabrilloField field; if (tqsl_getCabrilloField(conv->cab, &field, &stat)) return 0; if (stat == TQSL_CABRILLO_NO_ERROR || stat == TQSL_CABRILLO_EOR) { // Field found if (!strcasecmp(field.name, "CALL")) { conv->rec.callsign_set = true; strncpy(conv->rec.callsign, field.value, sizeof conv->rec.callsign); } else if (!strcasecmp(field.name, "BAND")) { conv->rec.band_set = true; strncpy(conv->rec.band, field.value, sizeof conv->rec.band); } else if (!strcasecmp(field.name, "MODE")) { conv->rec.mode_set = true; strncpy(conv->rec.mode, field.value, sizeof conv->rec.mode); } else if (!strcasecmp(field.name, "FREQ")) { conv->rec.band_set = true; strncpy(conv->rec.freq, field.value, sizeof conv->rec.freq); } else if (!strcasecmp(field.name, "QSO_DATE")) { conv->rec.date_set = true; cstat = tqsl_initDate(&(conv->rec.date), field.value); if (cstat) saveErr = tQSL_Error; } else if (!strcasecmp(field.name, "TIME_ON")) { conv->rec.time_set = true; cstat = tqsl_initTime(&(conv->rec.time), field.value); if (cstat) saveErr = tQSL_Error; } if (conv->rec_text != "") conv->rec_text += "\n"; conv->rec_text += string(field.name) + ": " + field.value; } } while (stat == TQSL_CABRILLO_NO_ERROR); if (saveErr) tQSL_Error = saveErr; if (saveErr || stat != TQSL_CABRILLO_EOR) { conv->rec_done = true; return 0; } } else { tQSL_Error = TQSL_CUSTOM_ERROR; strncpy(tQSL_CustomError, "Converter not initialized", sizeof tQSL_CustomError); tqslTrace("tqsl_getConverterGABBI", "Converter not initialized"); return 0; } } // Does the QSO have the basic required elements? if (!conv->rec.callsign_set) { conv->rec_done = true; snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "Invalid contact - QSO does not specify a Callsign"); tQSL_Error = TQSL_CUSTOM_ERROR; return 0; } if (!conv->rec.band_set) { conv->rec_done = true; snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "Invalid contact - QSO does not specify a band or frequency"); tQSL_Error = TQSL_CUSTOM_ERROR; return 0; } if (!conv->rec.mode_set) { conv->rec_done = true; snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "Invalid contact - QSO does not specify a mode"); tQSL_Error = TQSL_CUSTOM_ERROR; return 0; } if (!conv->rec.date_set) { conv->rec_done = true; snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "Invalid contact - QSO does not specify a date"); tQSL_Error = TQSL_CUSTOM_ERROR; return 0; } if (!conv->rec.time_set) { conv->rec_done = true; snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "Invalid contact - QSO does not specify a time"); tQSL_Error = TQSL_CUSTOM_ERROR; return 0; } // Check QSO date against user-specified date range. if (tqsl_isDateValid(&(conv->rec.date))) { if (tqsl_isDateValid(&(conv->start)) && tqsl_compareDates(&(conv->rec.date), &(conv->start)) < 0) { conv->rec_done = true; tQSL_Error = TQSL_DATE_OUT_OF_RANGE; return 0; } if (tqsl_isDateValid(&(conv->end)) && tqsl_compareDates(&(conv->rec.date), &(conv->end)) > 0) { conv->rec_done = true; tQSL_Error = TQSL_DATE_OUT_OF_RANGE; return 0; } } // Do field value mapping tqsl_strtoupper(conv->rec.callsign); if (!conv->allow_bad_calls) { if (!checkCallSign(conv->rec.callsign)) { conv->rec_done = true; snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "Invalid amateur CALL (%s)", conv->rec.callsign); tQSL_Error = TQSL_CUSTOM_ERROR; return 0; } } tqsl_strtoupper(conv->rec.band); tqsl_strtoupper(conv->rec.rxband); tqsl_strtoupper(conv->rec.mode); tqsl_strtoupper(conv->rec.submode); char val[256] = ""; // Try to find the GABBI mode several ways. val[0] = '\0'; if (conv->rec.submode[0] != '\0') { char modeSub[256]; strncpy(modeSub, conv->rec.mode, sizeof modeSub); size_t left = sizeof modeSub - strlen(modeSub); strncat(modeSub, "%", left); left = sizeof modeSub - strlen(modeSub); strncat(modeSub, conv->rec.submode, left); if (tqsl_getADIFMode(modeSub, val, sizeof val)) { // mode%submode lookup failed // Try just the submode. if (tqsl_getADIFMode(conv->rec.submode, val, sizeof val)) { // bare submode failed if (tqsl_getADIFMode(conv->rec.mode, val, sizeof val)) { val[0] = '\0'; } } } } else { // Just a mode, no submode. Look that up. tqsl_getADIFMode(conv->rec.mode, val, sizeof val); } if (val[0] != '\0') strncpy(conv->rec.mode, val, sizeof conv->rec.mode); // Check field validities if (conv->modes.find(conv->rec.mode) == conv->modes.end()) { conv->rec_done = true; snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "Invalid MODE (%s)", conv->rec.mode); tQSL_Error = TQSL_CUSTOM_ERROR; return 0; } if (conv->bands.find(conv->rec.band) == conv->bands.end()) { conv->rec_done = true; snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "Invalid BAND (%s)", conv->rec.band); tQSL_Error = TQSL_CUSTOM_ERROR; return 0; } if (conv->rec.rxband[0] && (conv->bands.find(conv->rec.rxband) == conv->bands.end())) { conv->rec_done = true; snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "Invalid RX BAND (%s)", conv->rec.rxband); tQSL_Error = TQSL_CUSTOM_ERROR; return 0; } if (conv->rec.freq[0] && strcmp(conv->rec.band, "SUBMM") && strcmp(conv->rec.band, tqsl_infer_band(conv->rec.freq))) { // Have a BAND and FREQ. // Frequency is not in that band - ignore it. conv->rec.freq[0] = '\0'; } if (conv->rec.rxfreq[0] && strcmp(conv->rec.rxband, "SUBMM") && strcmp(conv->rec.rxband, tqsl_infer_band(conv->rec.rxfreq))) { // Have a RX_BAND and RX_FREQ. Frequency is not in that band - ignore it. conv->rec.rxfreq[0] = '\0'; } if (conv->rec.propmode[0] != '\0' && conv->propmodes.find(conv->rec.propmode) == conv->propmodes.end()) { conv->rec_done = true; snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "Invalid PROP_MODE (%s)", conv->rec.propmode); tQSL_Error = TQSL_CUSTOM_ERROR; return 0; } if (conv->rec.satname[0] != '\0' && conv->satellites.find(conv->rec.satname) == conv->satellites.end()) { conv->rec_done = true; snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "Invalid SAT_NAME (%s)", conv->rec.satname); tQSL_Error = TQSL_CUSTOM_ERROR; return 0; } if (!strcmp(conv->rec.propmode, "SAT") && conv->rec.satname[0] == '\0') { conv->rec_done = true; snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "PROP_MODE = 'SAT' but no SAT_NAME"); tQSL_Error = TQSL_CUSTOM_ERROR; return 0; } if (strcmp(conv->rec.propmode, "SAT") && conv->rec.satname[0] != '\0') { conv->rec_done = true; snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "SAT_NAME set but PROP_MODE is not 'SAT'"); tQSL_Error = TQSL_CUSTOM_ERROR; return 0; } // Check cert if (conv->ncerts <= 0) { conv->rec_done = true; tQSL_Error = TQSL_CERT_NOT_FOUND; return 0; } int cidx = find_matching_cert(conv); if (cidx < 0) { conv->rec_done = true; tQSL_Error = TQSL_CERT_DATE_MISMATCH; return 0; } if (cidx != conv->cert_idx) { // Switching certs conv->cert_idx = cidx; if (!conv->certs_used[conv->cert_idx]) { // Need to output tCERT, tSTATION conv->need_station_rec = true; conv->certs_used[conv->cert_idx] = true; return tqsl_getGABBItCERT(conv->certs[conv->cert_idx], conv->cert_idx + conv->base_idx); } } const char *grec = tqsl_getGABBItCONTACTData(conv->certs[conv->cert_idx], conv->loc, &(conv->rec), conv->cert_idx + conv->base_idx, signdata, sizeof(signdata)); if (grec) { conv->rec_done = true; if (!conv->allow_dupes) { char stnloc[128]; char qso[128]; if (tqsl_getLocationStationDetails(conv->loc, stnloc, sizeof stnloc)) { stnloc[0] = '\0'; } if (tqsl_getLocationQSODetails(conv->loc, qso, sizeof qso)) { qso[0] = '\0'; } // Old-style Lookup uses signdata and cert serial number #ifdef USE_LMDB MDB_val dbkey, dbdata; #else DBT dbkey, dbdata; memset(&dbkey, 0, sizeof dbkey); memset(&dbdata, 0, sizeof dbdata); #endif // append signing key serial strncat(signdata, conv->serial, sizeof(signdata) - strlen(signdata)-1); // Updated dupe database entry. Key is formed from // local callsign concatenated with the QSO details char dupekey[128]; snprintf(dupekey, sizeof dupekey, "%s%s", conv->callsign, qso); #ifdef USE_LMDB dbkey.mv_size = strlen(signdata); dbkey.mv_data = signdata; int dbget_err = mdb_get(conv->txn, conv->seendb, &dbkey, &dbdata); #else dbkey.size = strlen(signdata); dbkey.data = signdata; int dbget_err = conv->seendb->get(conv->seendb, conv->txn, &dbkey, &dbdata, 0); #endif if (0 == dbget_err) { //lookup was successful; thus this is a duplicate. tQSL_Error = TQSL_DUPLICATE_QSO; tQSL_CustomError[0] = '\0'; // delete the old record int dbput_err; #ifdef USE_LMDB mdb_del(conv->txn, conv->seendb, &dbkey, &dbdata); // Update this to the current format dbkey.mv_size = strlen(dupekey); dbkey.mv_data = dupekey; dbdata.mv_data = stnloc; dbdata.mv_size = strlen(stnloc); dbput_err = mdb_put(conv->txn, conv->seendb, &dbkey, &dbdata, 0); #else conv->seendb->del(conv->seendb, conv->txn, &dbkey, 0); // Update this to the current format memset(&dbkey, 0, sizeof dbkey); dbkey.size = strlen(dupekey); dbkey.data = dupekey; memset(&dbdata, 0, sizeof dbdata); dbdata.data = stnloc; dbdata.size = strlen(stnloc); dbput_err = conv->seendb->put(conv->seendb, conv->txn, &dbkey, &dbdata, 0); #endif if (0 != dbput_err) { strncpy(tQSL_CustomError, db_strerror(dbput_err), sizeof tQSL_CustomError); tQSL_Error = TQSL_DB_ERROR; return 0; } return 0; #ifdef USE_LMDB } else if (dbget_err != MDB_NOTFOUND) { #else } else if (dbget_err != DB_NOTFOUND) { #endif //non-zero return, but not "not found" - thus error strncpy(tQSL_CustomError, db_strerror(dbget_err), sizeof tQSL_CustomError); tQSL_Error = TQSL_DB_ERROR; return 0; // could be more specific but there's very little the user can do at this point anyway } #ifdef USE_LMDB dbkey.mv_size = strlen(dupekey); dbkey.mv_data = dupekey; dbget_err = mdb_get(conv->txn, conv->seendb, &dbkey, &dbdata); #else memset(&dbkey, 0, sizeof dbkey); memset(&dbdata, 0, sizeof dbdata); dbkey.size = strlen(dupekey); dbkey.data = dupekey; dbget_err = conv->seendb->get(conv->seendb, conv->txn, &dbkey, &dbdata, 0); #endif if (0 == dbget_err) { //lookup was successful; thus this is a duplicate. tQSL_Error = TQSL_DUPLICATE_QSO; // Save the original and new station location details so those can be provided // with an error by the caller #ifdef USE_LMDB char *olddup = reinterpret_cast (malloc(dbdata.mv_size + 2)); memcpy(olddup, dbdata.mv_data, dbdata.mv_size); olddup[dbdata.mv_size] = '\0'; #else char *olddup = reinterpret_cast (malloc(dbdata.size + 2)); memcpy(olddup, dbdata.data, dbdata.size); olddup[dbdata.size] = '\0'; #endif snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "%s|%s", olddup, stnloc); free(olddup); return 0; #ifdef USE_LMDB } else if (dbget_err != MDB_NOTFOUND) { #else } else if (dbget_err != DB_NOTFOUND) { #endif //non-zero return, but not "not found" - thus error strncpy(tQSL_CustomError, db_strerror(dbget_err), sizeof tQSL_CustomError); tQSL_Error = TQSL_DB_ERROR; return 0; // could be more specific but there's very little the user can do at this point anyway } int dbput_err; #ifdef USE_LMDB dbdata.mv_data = stnloc; dbdata.mv_size = strlen(stnloc); dbput_err = mdb_put(conv->txn, conv->seendb, &dbkey, &dbdata, 0); #else memset(&dbdata, 0, sizeof dbdata); dbdata.data = stnloc; dbdata.size = strlen(stnloc); dbput_err = conv->seendb->put(conv->seendb, conv->txn, &dbkey, &dbdata, 0); #endif if (0 != dbput_err) { strncpy(tQSL_CustomError, db_strerror(dbput_err), sizeof tQSL_CustomError); tQSL_Error = TQSL_DB_ERROR; return 0; } } } return grec; } DLLEXPORT int CALLCONVENTION tqsl_getConverterCert(tQSL_Converter convp, tQSL_Cert *certp) { TQSL_CONVERTER *conv; if (!(conv = check_conv(convp))) return 1; if (certp == 0) { tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } *certp = conv->certs[conv->cert_idx]; return 0; } DLLEXPORT int CALLCONVENTION tqsl_getConverterLine(tQSL_Converter convp, int *lineno) { TQSL_CONVERTER *conv; if (!(conv = check_conv(convp))) return 1; if (lineno == 0) { tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (conv->cab) return tqsl_getCabrilloLine(conv->cab, lineno); else if (conv->adif) return tqsl_getADIFLine(conv->adif, lineno); *lineno = 0; return 0; } DLLEXPORT const char* CALLCONVENTION tqsl_getConverterRecordText(tQSL_Converter convp) { TQSL_CONVERTER *conv; if (!(conv = check_conv(convp))) return 0; return conv->rec_text.c_str(); } DLLEXPORT int CALLCONVENTION tqsl_setConverterAllowBadCall(tQSL_Converter convp, int allow) { TQSL_CONVERTER *conv; if (!(conv = check_conv(convp))) return 1; conv->allow_bad_calls = (allow != 0); return 0; } DLLEXPORT int CALLCONVENTION tqsl_setConverterAllowDuplicates(tQSL_Converter convp, int allow) { TQSL_CONVERTER *conv; if (!(conv = check_conv(convp))) return 1; conv->allow_dupes = (allow != 0); return 0; } DLLEXPORT int CALLCONVENTION tqsl_setConverterAppName(tQSL_Converter convp, const char *app) { TQSL_CONVERTER *conv; if (!(conv = check_conv(convp))) return 1; if (!app) { tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } conv->appName = strdup(app); return 0; } DLLEXPORT int CALLCONVENTION tqsl_converterRollBack(tQSL_Converter convp) { TQSL_CONVERTER *conv; tqslTrace("tqsl_converterRollBack", NULL); if (!(conv = check_conv(convp))) return 1; if (!conv->db_open) return 0; if (conv->txn) #ifdef USE_LMDB mdb_txn_abort(conv->txn); #else conv->txn->abort(conv->txn); #endif conv->txn = NULL; return 0; } DLLEXPORT int CALLCONVENTION tqsl_converterCommit(tQSL_Converter convp) { TQSL_CONVERTER *conv; tqslTrace("tqsl_converterCommit", NULL); if (!(conv = check_conv(convp))) return 1; if (!conv->db_open) return 0; if (conv->txn) #ifdef USE_LMDB mdb_txn_commit(conv->txn); #else conv->txn->commit(conv->txn, 0); #endif conv->txn = NULL; return 0; } DLLEXPORT int CALLCONVENTION tqsl_getDuplicateRecords(tQSL_Converter convp, char *key, char *data, int keylen) { TQSL_CONVERTER *conv; if (!(conv = check_conv(convp))) return 1; if (!conv->db_open) { if (!open_db(conv, true)) { // If can't open dupes DB return 1; } } if (!conv->cursor) { #ifdef USE_LMDB int err = mdb_cursor_open(conv->txn, conv->seendb, &conv->cursor); #else int err = conv->seendb->cursor(conv->seendb, conv->txn, &conv->cursor, DB_CURSOR_BULK); #endif if (err) { strncpy(tQSL_CustomError, db_strerror(err), sizeof tQSL_CustomError); tQSL_Error = TQSL_DB_ERROR; tQSL_Errno = errno; return 1; } } #ifdef USE_LMDB MDB_val dbkey, dbdata; int status = mdb_cursor_get(conv->cursor, &dbkey, &dbdata, MDB_NEXT); if (MDB_NOTFOUND == status) { #else DBT dbkey, dbdata; memset(&dbkey, 0, sizeof dbkey); memset(&dbdata, 0, sizeof dbdata); int status = conv->cursor->c_get(conv->cursor, &dbkey, &dbdata, DB_NEXT); if (DB_NOTFOUND == status) { #endif return -1; // No more records } if (status != 0) { strncpy(tQSL_CustomError, db_strerror(status), sizeof tQSL_CustomError); tQSL_Error = TQSL_DB_ERROR; tQSL_Errno = errno; return 1; } #ifdef USE_LMDB memcpy(key, dbkey.mv_data, dbkey.mv_size); key[dbkey.mv_size] = '\0'; if (dbdata.mv_size > 9) dbdata.mv_size = 9; memcpy(data, dbdata.mv_data, dbdata.mv_size); data[dbdata.mv_size] = '\0'; #else memcpy(key, dbkey.data, dbkey.size); key[dbkey.size] = '\0'; if (dbdata.size > 9) dbdata.size = 9; memcpy(data, dbdata.data, dbdata.size); data[dbdata.size] = '\0'; #endif return 0; } DLLEXPORT int CALLCONVENTION tqsl_getDuplicateRecordsV2(tQSL_Converter convp, char *key, char *data, int keylen) { TQSL_CONVERTER *conv; if (!(conv = check_conv(convp))) return 1; if (!conv->db_open) { if (!open_db(conv, true)) { // If can't open dupes DB return 1; } } if (!conv->cursor) { #ifdef USE_LMDB int err = mdb_cursor_open(conv->txn, conv->seendb, &conv->cursor); #else int err = conv->seendb->cursor(conv->seendb, conv->txn, &conv->cursor, DB_CURSOR_BULK); #endif if (err) { strncpy(tQSL_CustomError, db_strerror(err), sizeof tQSL_CustomError); tQSL_Error = TQSL_DB_ERROR; tQSL_Errno = errno; return 1; } } #ifdef USE_LMDB MDB_val dbkey, dbdata; int status = mdb_cursor_get(conv->cursor, &dbkey, &dbdata, MDB_NEXT); if (MDB_NOTFOUND == status) { #else DBT dbkey, dbdata; memset(&dbkey, 0, sizeof dbkey); memset(&dbdata, 0, sizeof dbdata); int status = conv->cursor->c_get(conv->cursor, &dbkey, &dbdata, DB_NEXT); if (DB_NOTFOUND == status) { #endif return -1; // No more records } if (status != 0) { strncpy(tQSL_CustomError, db_strerror(status), sizeof tQSL_CustomError); tQSL_Error = TQSL_DB_ERROR; tQSL_Errno = errno; return 1; } #ifdef USE_LMDB memcpy(key, dbkey.mv_data, dbkey.mv_size); key[dbkey.mv_size] = '\0'; if (dbdata.mv_size > 255) dbdata.mv_size = 255; memcpy(data, dbdata.mv_data, dbdata.mv_size); data[dbdata.mv_size] = '\0'; #else memcpy(key, dbkey.data, dbkey.size); key[dbkey.size] = '\0'; if (dbdata.size > 255) dbdata.size = 255; memcpy(data, dbdata.data, dbdata.size); data[dbdata.size] = '\0'; #endif return 0; } DLLEXPORT int CALLCONVENTION tqsl_putDuplicateRecord(tQSL_Converter convp, const char *key, const char *data, int keylen) { TQSL_CONVERTER *conv; if (!(conv = check_conv(convp))) return 0; if (!conv->db_open) { if (!open_db(conv, false)) { // If can't open dupes DB return 0; } } #ifdef USE_LMDB MDB_val dbkey, dbdata; dbkey.mv_size = keylen; dbkey.mv_data = const_cast(key); dbdata.mv_size = strlen(data); dbdata.mv_data = const_cast(data); int status = mdb_put(conv->txn, conv->seendb, &dbkey, &dbdata, 0); if (MDB_KEYEXIST == status) { return -1; // OK, but already there } #else DBT dbkey, dbdata; memset(&dbkey, 0, sizeof dbkey); memset(&dbdata, 0, sizeof dbdata); dbkey.size = keylen; dbkey.data = const_cast(key); dbdata.size = strlen(data); dbdata.data = const_cast(data); int status = conv->seendb->put(conv->seendb, conv->txn, &dbkey, &dbdata, 0); if (DB_KEYEXIST == status) { return -1; // OK, but already there } #endif if (status != 0) { strncpy(tQSL_CustomError, db_strerror(status), sizeof tQSL_CustomError); tQSL_Error = TQSL_DB_ERROR; tQSL_Errno = errno; return 1; } return 0; } static bool hasValidCallSignChars(const string& call) { // Check for invalid characters if (call.find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/") != string::npos) return false; // Need at least one letter if (call.find_first_of("ABCDEFGHIJKLMNOPQRSTUVWXYZ") == string::npos) return false; // Need at least one number if (call.find_first_of("0123456789") == string::npos) return false; // Invalid callsign patterns // Starting with 0, Q // 1x other than 1A, 1M, 1S string first = call.substr(0, 1); string second = call.substr(1, 1); if (first == "0" || first == "Q" || #ifdef MARK_C7_4Y_INVALID (first == "C" && second == "7") || (first == "4" && second == "Y") || #endif (first == "1" && second != "A" && second != "M" && second != "S")) return false; return true; } static bool checkCallSign(const string& call) { if (!hasValidCallSignChars(call)) return false; if (call.length() < 3) return false; string::size_type idx, newidx; for (idx = 0; idx != string::npos; idx = newidx+1) { string s; newidx = call.find('/', idx); if (newidx == string::npos) s = call.substr(idx); else s = call.substr(idx, newidx - idx); if (s.length() == 0) return false; // Leading or trailing '/' is bad, bad! if (newidx == string::npos) break; } return true; } tqsl-2.5.1/src/station_loc.cpp0000664000076400007640000000552313563134442016335 0ustar rmurphyrmurphy/*************************************************************************** station_loc.cpp - description ------------------- begin : Sat Dec 14 2002 copyright : (C) 2002 by ARRL author : Jon Bloom email : jbloom@arrl.org revision : $Id$ ***************************************************************************/ #ifdef HAVE_CONFIG_H #include "sysconfig.h" #endif #include #include #include #include #include "tqsllib.h" #include "tqslexc.h" using std::string; using std::ios; using std::cerr; using std::cout; using std::endl; int usage() { cerr << "Usage: station_loc callsign [dxcc]" << endl; exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { try { string call, dxcc; if (argc < 2) usage(); call = argv[1]; if (argc > 2) dxcc = argv[2]; if (tqsl_init()) throw tqslexc(); tQSL_Location loc; if (tqsl_initStationLocationCapture(&loc)) throw tqslexc(); if (tqsl_setStationLocationCapturePage(loc, 1)) throw tqslexc(); // We know that the first field of page 1 is always call and the 2nd is DXCC int nfield; tqsl_getNumLocationFieldListItems(loc, 0, &nfield); int i; for (i = 0; i < nfield; i++) { char buf[256]; if (tqsl_getLocationFieldListItem(loc, 0, i, buf, sizeof buf)) throw tqslexc(); if (!strcasecmp(buf, call.c_str())) break; } if (i == nfield) throw myexc(string("Can't init station location for call = ") + call); if (tqsl_setLocationFieldIndex(loc, 0, i)) throw tqslexc(); if (tqsl_updateStationLocationCapture(loc)) throw tqslexc(); if (dxcc != "") { int nfield; tqsl_getNumLocationFieldListItems(loc, 1, &nfield); //cerr << nfield << endl; for (i = 0; i < nfield; i++) { char buf[256]; if (tqsl_setLocationFieldIndex(loc, 1, i)) throw tqslexc(); if (tqsl_getLocationFieldCharData(loc, 1, buf, sizeof buf)) throw tqslexc(); //cerr << buf << endl; if (!strcasecmp(buf, dxcc.c_str())) break; } if (i == nfield) throw myexc(string("Can't init location for DXCC = ") + dxcc); if (tqsl_setLocationFieldIndex(loc, 1, i)) throw tqslexc(); } int dxcc_idx; if (tqsl_getLocationFieldIndex(loc, 1, &dxcc_idx)) throw tqslexc(); char buf[256]; if (tqsl_getLocationFieldListItem(loc, 1, dxcc_idx, buf, sizeof buf)) throw tqslexc(); string lname = call + "_auto"; if (tqsl_setStationLocationCaptureName(loc, lname.c_str())) throw tqslexc(); if (tqsl_saveStationLocationCapture(loc, 1)) throw tqslexc(); tqsl_endStationLocationCapture(&loc); cout << "Wrote station location for " << call << " - " << buf << endl; } catch(exception& x) { cerr << "Aborted: " << x.what() << endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } tqsl-2.5.1/src/openssl_cert.h0000664000076400007640000001327513563134442016167 0ustar rmurphyrmurphy/*************************************************************************** openssl_cert.h - description ------------------- begin : Tue May 14 2002 copyright : (C) 2002 by ARRL author : Jon Bloom email : jbloom@arrl.org revision : $Id$ ***************************************************************************/ #ifndef OPENSSL_CERT_H #define OPENSSL_CERT_H /** \file * OpenSSL X509 certificate interface functions. */ #include #if (OPENSSL_VERSION_NUMBER == 0x10000003L) /* broken header file - fix by override */ #define i2d_ASN1_SET i2d_ASN1_SET_buggy #define d2i_ASN1_SET d2i_ASN1_SET_buggy #define ASN1_seq_unpack ASN1_seq_unpack_buggy #define ASN1_seq_pack ASN1_seq_pack_buggy #include #undef i2d_ASN1_SET #undef d2i_ASN1_SET #undef ASN1_seq_unpack #undef ASN1_seq_pack #ifdef __cplusplus extern "C" { #endif int i2d_ASN1_SET(void *a, unsigned char **pp, i2d_of_void *i2d, int ex_tag, int ex_class, int is_set); void *d2i_ASN1_SET(void *a, const unsigned char **pp, long length, d2i_of_void *d2i, void (*free_func)(void*), int ex_tag, // NOLINT(readability/casting) int ex_class); void *ASN1_seq_unpack(const unsigned char *buf, int len, d2i_of_void *d2i, void (*free_func)(void* dummy)); unsigned char *ASN1_seq_pack(void *safes, i2d_of_void *i2d, unsigned char **buf, int *len); #ifdef __cplusplus } #endif #endif /* buggy openssl header */ #include #undef CLIENT_STATIC #ifndef LOTW_SERVER #define CLIENT_STATIC static #else #define CLIENT_STATIC #endif typedef STACK_OF(X509) TQSL_X509_STACK; typedef struct { char *name_buf; int name_buf_size; char *value_buf; int value_buf_size; } TQSL_X509_NAME_ITEM; namespace tqsllib { typedef enum { ROOTCERT = 0, CACERT, USERCERT } certtype; int tqsl_import_cert(const char *cert, certtype type, int(*cb)(int, const char *, void *), void *); int tqsl_get_pem_serial(const char *pem, long *serial); } // namespace tqsllib #if defined(LOTW_SERVER) || defined(OPENSSL_CERT_SOURCE) #ifdef __cplusplus extern "C" { #endif /// Loads a stack of certificates from the caller-supplied BIO /** See the OpenSSL documentation for background on BIO operations. * * Returns a pointer to an OpenSSL X509 stack, as used by * tqsl_ssl_verify_cert() */ CLIENT_STATIC TQSL_X509_STACK *tqsl_ssl_load_certs_from_BIO(BIO *in); /// Loads a stack of certificates from a file /** See tqsl_ssl_load_certs_from_BIO() */ CLIENT_STATIC TQSL_X509_STACK *tqsl_ssl_load_certs_from_file(const char *filename); /// Verifies a certificate using stacks of certificates /** The user supplies the X509 certificate to verify (the test certificate) * along with two stacks of certificates. The \c cacerts stack is a list * of certificates, one of which was used to sign the test certificate. * The \c rootcerts are considered "trusted." One of them must have been used * to sign either the test certificate itself or the CA cert that signed * the test certificate. * * Returns NULL if the test certificate is valid, othewise returns an error message. */ CLIENT_STATIC const char *tqsl_ssl_verify_cert(X509 *cert, TQSL_X509_STACK *cacerts, TQSL_X509_STACK *rootcerts, int purpose, int (*cb)(int ok, X509_STORE_CTX *ctx), TQSL_X509_STACK **chain = 0); #if 0 /* unused */ /// Get the number of name entries in an X509 name object CLIENT_STATIC int tqsl_get_name_count(X509_NAME *name); /// Retrieve a name entry from an X509 name object by index CLIENT_STATIC int tqsl_get_name_index(X509_NAME *name, int index, TQSL_X509_NAME_ITEM *name_item); #endif /// Retrieve a name entry from an X509 name object by name CLIENT_STATIC int tqsl_get_name_entry(X509_NAME *name, const char *obj_name, TQSL_X509_NAME_ITEM *name_item); #if 0 /* unused */ /// Get the number of name entries in an X509 cert's subject name CLIENT_STATIC int tqsl_cert_get_subject_name_count(X509 *cert); /// Retrieve a name entry from an X509 cert's subject name by index CLIENT_STATIC int tqsl_cert_get_subject_name_index(X509 *cert, int index, TQSL_X509_NAME_ITEM *name_item); #endif /// Retrieve a name entry from an X509 cert's subject name by name CLIENT_STATIC int tqsl_cert_get_subject_name_entry(X509 *cert, const char *obj_name, TQSL_X509_NAME_ITEM *name_item); #if 0 /* unused */ /// Retrieve a name entry date from an X509 cert's subject name by name CLIENT_STATIC int tqsl_cert_get_subject_date(X509 *cert, const char *obj_name, tQSL_Date *date); #endif /// Convert an ASN date CLIENT_STATIC int tqsl_get_asn1_date(const ASN1_TIME *tm, tQSL_Date *date); /// Filter a list (stack) of certs based on (optional) call sign, qso date and issuer criteria /** Returns a (possibly empty) stack of certificates that match the criteria. Returns NULL * on error. * * The returned stack contains \em copies of the certs from the input stack. The input * stack is not altered. */ CLIENT_STATIC TQSL_X509_STACK *tqsl_filter_cert_list(TQSL_X509_STACK *sk, const char *callsign, int dxcc, const tQSL_Date *date, const TQSL_PROVIDER *issuer, int isvalid); CLIENT_STATIC EVP_PKEY *tqsl_new_rsa_key(int nbits); CLIENT_STATIC int tqsl_store_cert(const char *pem, X509 *cert, const char *certfile, int type, bool force, int (*cb)(int, const char *, void *), void *); CLIENT_STATIC int tqsl_write_adif_field(FILE *fp, const char *fieldname, char type, const unsigned char *value, int len); CLIENT_STATIC int tqsl_bio_write_adif_field(BIO *bio, const char *fieldname, char type, const unsigned char *value, int len); #ifdef __cplusplus } #endif #endif /* defined(LOTW_SERVER) || defined(OPENSSL_CERT_SOURCE) */ #endif /* OPENSSL_CERT_H */ tqsl-2.5.1/src/openssl_cert.cpp0000664000076400007640000054160013563134442016520 0ustar rmurphyrmurphy/*************************************************************************** openssl_cert.c - description ------------------- begin : Tue May 14 2002 copyright : (C) 2002 by ARRL author : Jon Bloom email : jbloom@arrl.org revision : $Id$ ***************************************************************************/ /* Routines to massage X.509 certs for tQSL. See openssl_cert.h for overview */ /* 2004-04-10 Fixed tqsl_add_bag_attribute error for OpenSSL > 0.96 (Thanks to Kenji, JJ1BDX for the fix) */ /* Portions liberally copied from OpenSSL's apps source code */ /* ==================================================================== * Copyright (c) 1998-2001 The OpenSSL Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" * * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please contact * openssl-core@openssl.org. * * 5. Products derived from this software may not be called "OpenSSL" * nor may "OpenSSL" appear in their names without prior written * permission of the OpenSSL Project. * * 6. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit (http://www.openssl.org/)" * * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This product includes cryptographic software written by Eric Young * (eay@cryptsoft.com). This product includes software written by Tim * Hudson (tjh@cryptsoft.com). * */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * * This package is an SSL implementation written * by Eric Young (eay@cryptsoft.com). * The implementation was written so as to conform with Netscapes SSL. * * This library is free for commercial and non-commercial use as long as * the following conditions are aheared to. The following conditions * apply to all code found in this distribution, be it the RC4, RSA, * lhash, DES, etc., code; not just the SSL code. The SSL documentation * included with this distribution is covered by the same copyright terms * except that the holder is Tim Hudson (tjh@cryptsoft.com). * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. * If this package is used in a product, Eric Young should be given attribution * as the author of the parts of the library used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * "This product includes cryptographic software written by * Eric Young (eay@cryptsoft.com)" * The word 'cryptographic' can be left out if the rouines from the library * being used are not cryptographic related :-). * 4. If you include any Windows specific code (or a derivative thereof) from * the apps directory (application code) you must include an acknowledgement: * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] */ #define OPENSSL_CERT_SOURCE #define TQSLLIB_DEF #include #include #include #include #include #include #include #ifdef _WIN32 #include #define MKDIR(x, y) _wmkdir(x) #else #define MKDIR(x, y) mkdir(x, y) #include #include #endif #include #include #include #undef X509_NAME //http://www.mail-archive.com/openssl-users@openssl.org/msg59116.html #include #include #include #include #include #include #include /* Ugly workaround for Openssl 1.0 bug per: * http://rt.openssl.org/Ticket/Display.html?user=guest&pass=guest&id=2123 */ #if (OPENSSL_VERSION_NUMBER == 0x10000003L) #define i2d_ASN1_SET i2d_ASN1_SET_buggy #define d2i_ASN1_SET d2i_ASN1_SET_buggy #define ASN1_seq_unpack ASN1_seq_unpack_buggy #define ASN1_seq_pack ASN1_seq_pack_buggy #include #undef i2d_ASN1_SET #undef d2i_ASN1_SET #undef ASN1_seq_unpack #undef ASN1_seq_pack #ifdef __cplusplus extern "C" { #endif int i2d_ASN1_SET(void *a, unsigned char **pp, i2d_of_void *i2d, int ex_tag, int ex_class, int is_set); void *d2i_ASN1_SET(void *a, const unsigned char **pp, long length, d2i_of_void *d2i, void (*free_func)(void* p), int ex_tag, int ex_class); void *ASN1_seq_unpack(const unsigned char *buf, int len, d2i_of_void *d2i, void (*free_func)(void* dummy)); unsigned char *ASN1_seq_pack(void *safes, i2d_of_void *i2d, unsigned char **buf, int *len); #ifdef __cplusplus } #endif #endif // OpenSSL v1.0 // Work with OpenSSL 1.1.0 and later #if OPENSSL_VERSION_NUMBER >= 0x10100000L # define M_PKCS12_bag_type PKCS12_bag_type # define M_PKCS12_cert_bag_type PKCS12_cert_bag_type # define M_PKCS12_crl_bag_type PKCS12_cert_bag_type # define M_PKCS12_certbag2x509 PKCS12_SAFEBAG_get1_cert # define M_PKCS12_decrypt_skey PKCS12_decrypt_skey # define M_PKCS12_unpack_authsafes PKCS12_unpack_authsafes # define M_PKCS12_pack_authsafes PKCS12_pack_authsafes # define PKCS12_get_attr PKCS12_SAFEBAG_get0_attr # define PKCS12_bag_type PKCS12_SAFEBAG_get_nid # define PKCS12_cert_bag_type PKCS12_SAFEBAG_get_bag_nid # define PKCS12_x5092certbag PKCS12_SAFEBAG_create_cert # define PKCS12_x509crl2certbag PKCS12_SAFEBAG_create_crl # define X509_STORE_CTX_trusted_stack X509_STORE_CTX_set0_trusted_stack # define X509_get_notAfter X509_get0_notAfter # define X509_get_notBefore X509_get0_notBefore # define PKCS12_MAKE_SHKEYBAG PKCS12_SAFEBAG_create_pkcs8_encrypt # define X509_V_FLAG_CB_ISSUER_CHECK 0x0 #else # define ASN1_STRING_get0_data ASN1_STRING_data #endif #include #include #include #include #include #include #include "tqsllib.h" #include "tqslerrno.h" #include "xml.h" #include "winstrdefs.h" #ifdef _MSC_VER //is a visual studio compiler #include "windirent.h" #endif #define tqsl_malloc malloc #define tqsl_realloc realloc #define tqsl_calloc calloc #define tqsl_free free #define TQSL_OBJ_TO_API(x) (reinterpret_cast((x))) #define TQSL_API_TO_OBJ(x, type) ((type)(x)) #define TQSL_API_TO_CERT(x) TQSL_API_TO_OBJ((x), tqsl_cert *) #include "openssl_cert.h" using std::vector; using std::map; using std::set; using std::string; using std::ofstream; using std::ios; using std::endl; using std::exception; using tqsllib::XMLElement; using tqsllib::XMLElementList; #ifdef _WIN32 #define TQSL_OPEN_READ L"rb" #define TQSL_OPEN_WRITE L"wb" #define TQSL_OPEN_APPEND L"ab" #else #define TQSL_OPEN_READ "r" #define TQSL_OPEN_WRITE "w" #define TQSL_OPEN_APPEND "a" #endif #if (OPENSSL_VERSION_NUMBER & 0xfffff000) >= 0x10000000L #define uni2asc OPENSSL_uni2asc #define asc2uni OPENSSL_asc2uni #endif static char *tqsl_trim(char *buf); static int tqsl_check_parm(const char *p, const char *parmName); static TQSL_CERT_REQ *tqsl_copy_cert_req(TQSL_CERT_REQ *userreq); static TQSL_CERT_REQ *tqsl_free_cert_req(TQSL_CERT_REQ *req, int seterr); static char *tqsl_make_key_path(const char *callsign, char *path, int size); static int tqsl_make_key_list(vector< map > & keys); static int tqsl_find_matching_key(X509 *cert, EVP_PKEY **keyp, TQSL_CERT_REQ **crq, const char *password, int (*cb)(char *, int, void *), void *); static char *tqsl_make_cert_path(const char *filename, char *path, int size); static char *tqsl_make_backup_path(const char *filename, char *path, int size); static int tqsl_get_cert_ext(X509 *cert, const char *ext, unsigned char *userbuf, int *buflen, int *crit); CLIENT_STATIC int tqsl_get_asn1_date(const ASN1_TIME *tm, tQSL_Date *date); static char *tqsl_sign_base64_data(tQSL_Cert cert, char *b64data); static int fixed_password_callback(char *buf, int bufsiz, int verify, void *userdata); static int prompted_password_callback(char *buf, int bufsiz, int verify, void *userfunc); static int tqsl_check_crq_field(tQSL_Cert cert, char *buf, int bufsiz); static bool safe_strncpy(char *dest, const char *src, int size); static int tqsl_ssl_error_is_nofile(); static int tqsl_unlock_key(const char *pem, EVP_PKEY **keyp, const char *password, int (*cb)(char *, int, void *), void *); static int tqsl_replace_key(const char *callsign, const char *path, map& newfields, int (*cb)(int, const char *, void *), void *userdata); static int tqsl_self_signed_is_ok(int ok, X509_STORE_CTX *ctx); static int tqsl_expired_is_ok(int ok, X509_STORE_CTX *ctx); static int tqsl_clear_deleted(const char *callsign, const char *path, EVP_PKEY *cert_key); static int tqsl_key_exists(const char *callsign, EVP_PKEY *cert_key); static int tqsl_open_key_file(const char *path); static int tqsl_read_key(map& fields); static void tqsl_close_key_file(void); extern const char* tqsl_openssl_error(void); /* Private data structures */ typedef struct { long id; X509 *cert; EVP_PKEY *key; TQSL_CERT_REQ *crq; char *pubkey; char *privkey; unsigned char keyonly; } tqsl_cert; typedef struct { long id; X509 *cert; } tqsl_crq; static tqsl_cert * tqsl_cert_new(); static void tqsl_cert_free(tqsl_cert *p); static int tqsl_cert_check(tqsl_cert *p, bool needcert = true); struct tqsl_loadcert_handler { int type; int (*func)(const char *pem, X509 *x, int(*cb)(int type, const char *, void *), void *); }; static int tqsl_handle_root_cert(const char *, X509 *, int (*cb)(int, const char *, void *), void *); static int tqsl_handle_ca_cert(const char *, X509 *, int (*cb)(int, const char *, void *), void *); static int tqsl_handle_user_cert(const char *, X509 *, int (*cb)(int, const char *, void *), void *); static struct tqsl_loadcert_handler tqsl_loadcert_handlers[] = { { TQSL_CERT_CB_ROOT, &tqsl_handle_root_cert }, { TQSL_CERT_CB_CA, &tqsl_handle_ca_cert }, { TQSL_CERT_CB_USER, &tqsl_handle_user_cert } }; static const char *notypes[] = { "" }; /* static tqsl_adifFieldDefinitions tqsl_cert_file_fields[] = { { "TQSL_CERT", "", TQSL_ADIF_RANGE_TYPE_NONE, 0, 0, 0, NULL, NULL }, { "TQSL_CERT_USER", "", TQSL_ADIF_RANGE_TYPE_NONE, 2000, 0, 0, NULL, &tqsl_load_user_cert }, { "TQSL_CERT_CA", "", TQSL_ADIF_RANGE_TYPE_NONE, 2000, 0, 0, NULL, &tqsl_load_ca_cert }, { "TQSL_CERT_ROOT", "", TQSL_ADIF_RANGE_TYPE_NONE, 2000, 0, 0, NULL, &tqsl_load_root_cert }, }; */ static unsigned char tqsl_static_buf[2001]; static char ImportCall[256]; static unsigned char * tqsl_static_alloc(size_t size) { if (size > sizeof tqsl_static_buf) return NULL; strncpy(reinterpret_cast(tqsl_static_buf), "", sizeof tqsl_static_buf); return tqsl_static_buf; } namespace tqsllib { int tqsl_import_cert(const char *data, certtype type, int(*cb)(int, const char *, void *), void *userdata) { BIO *bio; X509 *cert; int stat; struct tqsl_loadcert_handler *handler = &(tqsl_loadcert_handlers[type]); /* This is a certificate, supposedly. Let's make sure */ tqslTrace("tqsl_import_cert", NULL); bio = BIO_new_mem_buf(reinterpret_cast(const_cast(data)), strlen(data)); if (bio == NULL) { tqslTrace("tqsl_import_cert", "BIO mem buf error %s", tqsl_openssl_error()); tQSL_Error = TQSL_OPENSSL_ERROR; return 1; } cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); BIO_free(bio); if (cert == NULL) { tqslTrace("tqsl_import_cert", "BIO read error, err=%s", tqsl_openssl_error()); tQSL_Error = TQSL_OPENSSL_ERROR; return 1; } /* It's a certificate. Let's try to add it. Any errors will be * reported via the callback (if any) but will not be fatal unless * the callback says so. */ ImportCall[0] = '\0'; tQSL_ImportSerial = 0; stat = (*(handler->func))(data, cert, cb, userdata); X509_free(cert); if (stat) { if (tQSL_Error == TQSL_CERT_ERROR) { return 1; } if (cb != NULL) { stat = (*cb)(handler->type | TQSL_CERT_CB_RESULT | TQSL_CERT_CB_ERROR, tqsl_getErrorString_v(tQSL_Error), userdata); if (stat) { tqslTrace("tqsl_import_cert", "import error %d", tQSL_Error); return 1; } else { tqslTrace("tqsl_import_cert", "import error. Handler suppressed."); } } else { /* No callback -- any errors are fatal */ tqslTrace("tqsl_import_cert", "import error %d", tQSL_Error); return 1; } return stat; } strncpy(tQSL_ImportCall, ImportCall, sizeof tQSL_ImportCall); return 0; } int tqsl_get_pem_serial(const char *pem, long *serial) { BIO *bio; X509 *cert; tqslTrace("tqsl_get_pem_serial", NULL); if (tqsl_init()) return 1; if (pem == NULL || serial == NULL) { tqslTrace("tqsl_get_pem_serial", "arg error pem=0x%lx, serial=0x%lx", pem, serial); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } bio = BIO_new_mem_buf(reinterpret_cast(const_cast(pem)), strlen(pem)); if (bio == NULL) { tqslTrace("tqsl_get_pem_serial", "mem buf error %s", tqsl_openssl_error()); tQSL_Error = TQSL_OPENSSL_ERROR; return 1; } cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); BIO_free(bio); if (cert == NULL) { tqslTrace("tqsl_get_pem_serial", "cert read error %s", tqsl_openssl_error()); tQSL_Error = TQSL_OPENSSL_ERROR; return 1; } *serial = ASN1_INTEGER_get(X509_get_serialNumber(cert)); return 0; } } // namespace tqsllib /********** PUBLIC API FUNCTIONS ***********/ DLLEXPORT int CALLCONVENTION tqsl_createCertRequest(const char *filename, TQSL_CERT_REQ *userreq, int (*pwcb)(char *pwbuf, int pwsize, void *), void *userdata) { TQSL_CERT_REQ *req = NULL; EVP_PKEY *key = NULL; X509_REQ *xr = NULL; X509_NAME *subj = NULL; int nid, len; int rval = 1; FILE *out = NULL; BIO *bio = NULL; const EVP_MD *digest = NULL; char buf[200]; char path[256]; char *cp; const EVP_CIPHER *cipher = NULL; char *password; const char *type; tqslTrace("tqsl_createCertRequest", NULL); if (tqsl_init()) return 1; if (filename == NULL || userreq == NULL) { tqslTrace("tqsl_createCertRequest", "arg error filename=0x%lx, userreq=0x%lx", filename, userreq); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (userreq->signer != NULL && (!tqsl_cert_check(TQSL_API_TO_CERT(userreq->signer)) || TQSL_API_TO_CERT(userreq->signer)->key == NULL)) { tqslTrace("tqsl_createCertRequest", "arg error signer/key"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if ((req = tqsl_copy_cert_req(userreq)) == NULL) { tqslTrace("tqsl_createCertRequest", "Error copying %d", tQSL_Error); goto end; } /* Check parameters for validity */ tqsl_trim(req->providerName); tqsl_trim(req->providerUnit); tqsl_trim(req->name); if (tqsl_check_parm(req->name, "Name")) { tqslTrace("tqsl_createCertRequest", "check_parm Name"); goto end; } tqsl_trim(req->callSign); if (tqsl_check_parm(req->callSign, "Call Sign")) { tqslTrace("tqsl_createCertRequest", "check_parm Call Sign"); goto end; } tqsl_trim(req->address1); if (tqsl_check_parm(req->address1, "Address")) { tqslTrace("tqsl_createCertRequest", "check_parm Address1"); goto end; } tqsl_trim(req->address2); tqsl_trim(req->city); if (tqsl_check_parm(req->city, "City")) { tqslTrace("tqsl_createCertRequest", "check_parm City"); goto end; } tqsl_trim(req->state); tqsl_trim(req->country); if (tqsl_check_parm(req->country, "Country")) { tqslTrace("tqsl_createCertRequest", "check_parm Country"); goto end; } tqsl_trim(req->postalCode); tqsl_trim(req->emailAddress); if (tqsl_check_parm(req->emailAddress, "Email address")) { tqslTrace("tqsl_createCertRequest", "check_parm email"); goto end; } if ((cp = strchr(req->emailAddress, '@')) == NULL || strchr(cp, '.') == NULL) { strncpy(tQSL_CustomError, "Invalid email address", sizeof tQSL_CustomError); tQSL_Error = TQSL_CUSTOM_ERROR; tqslTrace("tqsl_createCertRequest", "check_parm email: %s %s", req->emailAddress, tQSL_CustomError); goto end; } if (!tqsl_isDateValid(&(req->qsoNotBefore))) { strncpy(tQSL_CustomError, "Invalid date (qsoNotBefore)", sizeof tQSL_CustomError); tqslTrace("tqsl_createCertRequest", "check_parm not before: %s %s", req->qsoNotBefore, tQSL_CustomError); tQSL_Error = TQSL_CUSTOM_ERROR; goto end; } if (!tqsl_isDateNull(&(req->qsoNotAfter))) { if (!tqsl_isDateValid(&(req->qsoNotAfter))) { strncpy(tQSL_CustomError, "Invalid date (qsoNotAfter)", sizeof tQSL_CustomError); tqslTrace("tqsl_createCertRequest", "check_parm not after: %s %s", req->qsoNotAfter, tQSL_CustomError); tQSL_Error = TQSL_CUSTOM_ERROR; goto end; } if (tqsl_compareDates(&(req->qsoNotAfter), &(req->qsoNotBefore)) < 0) { strncpy(tQSL_CustomError, "qsoNotAfter date is earlier than qsoNotBefore", sizeof tQSL_CustomError); tqslTrace("tqsl_createCertRequest", "check_parm not after: %s %s", req->qsoNotAfter, tQSL_CustomError); tQSL_Error = TQSL_CUSTOM_ERROR; goto end; } } /* Try opening the output stream */ #ifdef _WIN32 wchar_t* wfilename = utf8_to_wchar(filename); if ((out = _wfopen(wfilename, TQSL_OPEN_WRITE)) == NULL) { free_wchar(wfilename); #else if ((out = fopen(filename, TQSL_OPEN_WRITE)) == NULL) { #endif strncpy(tQSL_ErrorFile, filename, sizeof tQSL_ErrorFile); tqslTrace("tqsl_createCertRequest", "Open file - system error %s", strerror(errno)); tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; goto end; } #ifdef _WIN32 free_wchar(wfilename); #endif if (fputs("\ntQSL certificate request\n\n", out) == EOF) { strncpy(tQSL_ErrorFile, filename, sizeof tQSL_ErrorFile); tqslTrace("tqsl_createCertRequest", "Write request file - system error %s", strerror(errno)); tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; goto end; } tqsl_write_adif_field(out, "eoh", 0, NULL, 0); type = (req->signer != NULL) ? (req->renew ? "TQSL_CRQ_RENEWAL" : "TQSL_CRQ_ADDITIONAL") : "TQSL_CRQ_NEW"; int libmaj, libmin, configmaj, configmin; tqsl_getVersion(&libmaj, &libmin); tqsl_getConfigVersion(&configmaj, &configmin); snprintf(buf, sizeof buf, "Lib: V%d.%d, Config: %d, %d", libmaj, libmin, configmaj, configmin); tqsl_write_adif_field(out, "TQSL_IDENT", 0, (unsigned char *)buf, -1); tqsl_write_adif_field(out, type, 0, NULL, 0); tqsl_write_adif_field(out, "TQSL_CRQ_PROVIDER", 0, (unsigned char *)req->providerName, -1); tqsl_write_adif_field(out, "TQSL_CRQ_PROVIDER_UNIT", 0, (unsigned char *)req->providerUnit, -1); tqsl_write_adif_field(out, "TQSL_CRQ_EMAIL", 0, (unsigned char *)req->emailAddress, -1); tqsl_write_adif_field(out, "TQSL_CRQ_NAME", 0, (unsigned char *)req->name, -1); tqsl_write_adif_field(out, "TQSL_CRQ_ADDRESS1", 0, (unsigned char *)req->address1, -1); tqsl_write_adif_field(out, "TQSL_CRQ_ADDRESS2", 0, (unsigned char *)req->address2, -1); tqsl_write_adif_field(out, "TQSL_CRQ_CITY", 0, (unsigned char *)req->city, -1); tqsl_write_adif_field(out, "TQSL_CRQ_STATE", 0, (unsigned char *)req->state, -1); tqsl_write_adif_field(out, "TQSL_CRQ_POSTAL", 0, (unsigned char *)req->postalCode, -1); tqsl_write_adif_field(out, "TQSL_CRQ_COUNTRY", 0, (unsigned char *)req->country, -1); snprintf(buf, sizeof buf, "%d", req->dxccEntity); tqsl_write_adif_field(out, "TQSL_CRQ_DXCC_ENTITY", 0, (unsigned char *)buf, -1); tqsl_convertDateToText(&(req->qsoNotBefore), buf, sizeof buf); tqsl_write_adif_field(out, "TQSL_CRQ_QSO_NOT_BEFORE", 0, (unsigned char *)buf, -1); if (!tqsl_isDateNull(&(req->qsoNotAfter))) { tqsl_convertDateToText(&(req->qsoNotAfter), buf, sizeof buf); tqsl_write_adif_field(out, "TQSL_CRQ_QSO_NOT_AFTER", 0, (unsigned char *)buf, -1); } /* Generate a new key pair */ if ((key = tqsl_new_rsa_key(1024)) == NULL) { tqslTrace("tqsl_createCertRequest", "key create error %d", tQSL_Error); goto end; } /* Make the X.509 certificate request */ if ((xr = X509_REQ_new()) == NULL) { tqslTrace("tqsl_createCertRequest", "req create error %s", tqsl_openssl_error()); goto err; } if (!X509_REQ_set_version(xr, 0L)) { tqslTrace("tqsl_createCertRequest", "version set error %s", tqsl_openssl_error()); goto err; } subj = X509_REQ_get_subject_name(xr); nid = OBJ_txt2nid("AROcallsign"); if (nid != NID_undef) X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_ASC, (unsigned char *)req->callSign, -1, -1, 0); nid = OBJ_txt2nid("commonName"); if (nid != NID_undef) X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_ASC, (unsigned char *)req->name, -1, -1, 0); nid = OBJ_txt2nid("emailAddress"); if (nid != NID_undef) X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_ASC, (unsigned char *)req->emailAddress, -1, -1, 0); X509_REQ_set_pubkey(xr, key); if ((digest = EVP_sha256()) == NULL) { tqslTrace("tqsl_createCertRequest", "evp_sha256 error %s", tqsl_openssl_error()); goto err; } if (!X509_REQ_sign(xr, key, digest)) { tqslTrace("tqsl_createCertRequest", "req_sign error %s", tqsl_openssl_error()); goto err; } if ((bio = BIO_new(BIO_s_mem())) == NULL) { tqslTrace("tqsl_createCertRequest", "bio_new error %s", tqsl_openssl_error()); goto err; } if (!PEM_write_bio_X509_REQ(bio, xr)) { tqslTrace("tqsl_createCertRequest", "write_bio error %s", tqsl_openssl_error()); goto err; } len = static_cast(BIO_get_mem_data(bio, &cp)); tqsl_write_adif_field(out, "TQSL_CRQ_REQUEST", 0, (unsigned char *)cp, len); if (req->signer != NULL) { char *b64; char ibuf[256]; if ((b64 = tqsl_sign_base64_data(req->signer, cp)) == NULL) { fclose(out); tqslTrace("tqsl_createCertRequest", "tqsl_sign_base64 error %s", tqsl_openssl_error()); goto end; } tqsl_write_adif_field(out, "TQSL_CRQ_SIGNATURE", 0, (unsigned char *)b64, -1); tqsl_getCertificateIssuer(req->signer, ibuf, sizeof ibuf); tqsl_write_adif_field(out, "TQSL_CRQ_SIGNATURE_CERT_ISSUER", 0, (unsigned char *)ibuf, -1); snprintf(ibuf, sizeof ibuf, "%ld", ASN1_INTEGER_get(X509_get_serialNumber(TQSL_API_TO_CERT(req->signer)->cert))); tqsl_write_adif_field(out, "TQSL_CRQ_SIGNATURE_CERT_SERIAL", 0, (unsigned char *)ibuf, -1); } BIO_free(bio); bio = NULL; tqsl_write_adif_field(out, "eor", 0, NULL, 0); if (fclose(out) == EOF) { strncpy(tQSL_ErrorFile, filename, sizeof tQSL_ErrorFile); tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_createCertRequest", "write error %d", errno); goto end; } out = NULL; /* Write the key to the key store */ if (!tqsl_make_key_path(req->callSign, path, sizeof path)) { tqslTrace("tqsl_createCertRequest", "make_key_path error %d", errno); goto end; } #ifdef _WIN32 wchar_t* wpath = utf8_to_wchar(path); if ((out = _wfopen(wpath, TQSL_OPEN_APPEND)) == NULL) { free_wchar(wpath); #else if ((out = fopen(path, TQSL_OPEN_APPEND)) == NULL) { #endif strncpy(tQSL_ErrorFile, path, sizeof tQSL_ErrorFile); tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_createCertRequest", "opening file error %s", strerror(errno)); goto end; } #ifdef _WIN32 free_wchar(wpath); #endif tqsl_write_adif_field(out, "TQSL_CRQ_PROVIDER", 0, (unsigned char *)req->providerName, -1); tqsl_write_adif_field(out, "TQSL_CRQ_PROVIDER_UNIT", 0, (unsigned char *)req->providerUnit, -1); tqsl_write_adif_field(out, "TQSL_CRQ_EMAIL", 0, (unsigned char *)req->emailAddress, -1); tqsl_write_adif_field(out, "TQSL_CRQ_ADDRESS1", 0, (unsigned char *)req->address1, -1); tqsl_write_adif_field(out, "TQSL_CRQ_ADDRESS2", 0, (unsigned char *)req->address2, -1); tqsl_write_adif_field(out, "TQSL_CRQ_CITY", 0, (unsigned char *)req->city, -1); tqsl_write_adif_field(out, "TQSL_CRQ_STATE", 0, (unsigned char *)req->state, -1); tqsl_write_adif_field(out, "TQSL_CRQ_POSTAL", 0, (unsigned char *)req->postalCode, -1); tqsl_write_adif_field(out, "TQSL_CRQ_COUNTRY", 0, (unsigned char *)req->country, -1); tqsl_write_adif_field(out, "CALLSIGN", 0, (unsigned char *)req->callSign, -1); snprintf(buf, sizeof buf, "%d", req->dxccEntity); tqsl_write_adif_field(out, "TQSL_CRQ_DXCC_ENTITY", 0, (unsigned char *)buf, -1); tqsl_convertDateToText(&(req->qsoNotBefore), buf, sizeof buf); tqsl_write_adif_field(out, "TQSL_CRQ_QSO_NOT_BEFORE", 0, (unsigned char *)buf, -1); if (!tqsl_isDateNull(&(req->qsoNotAfter))) { tqsl_convertDateToText(&(req->qsoNotAfter), buf, sizeof buf); tqsl_write_adif_field(out, "TQSL_CRQ_QSO_NOT_AFTER", 0, (unsigned char *)buf, -1); } if ((bio = BIO_new(BIO_s_mem())) == NULL) { tqslTrace("tqsl_createCertRequest", "bio_new error %s", tqsl_openssl_error()); goto err; } password = const_cast(req->password); if (password == NULL && pwcb != NULL) { if ((*pwcb)(buf, TQSL_MAX_PW_LENGTH, userdata)) { tqslTrace("tqsl_createCertRequest", "password abort"); tQSL_Error = TQSL_OPERATOR_ABORT; goto end; } password = buf; } if (password != NULL && *password != '\0') { if ((cipher = EVP_des_ede3_cbc()) == NULL) { tqslTrace("tqsl_createCertRequest", "password error"); goto err; } len = strlen(password); } else { password = NULL; len = 0; } if (!PEM_write_bio_PrivateKey(bio, key, cipher, (unsigned char *)password, len, NULL, NULL)) { tqslTrace("tqsl_createCertRequest", "write priv key error %s", tqsl_openssl_error()); goto err; } len = static_cast(BIO_get_mem_data(bio, &cp)); tqsl_write_adif_field(out, "PRIVATE_KEY", 0, (unsigned char *)cp, len); BIO_free(bio); if ((bio = BIO_new(BIO_s_mem())) == NULL) { tqslTrace("tqsl_createCertRequest", "bio_new error %s", tqsl_openssl_error()); goto err; } if (!PEM_write_bio_PUBKEY(bio, key)) { tqslTrace("tqsl_createCertRequest", "write pubkey %s", tqsl_openssl_error()); goto err; } len = static_cast(BIO_get_mem_data(bio, &cp)); tqsl_write_adif_field(out, "PUBLIC_KEY", 0, (unsigned char *)cp, len); BIO_free(bio); bio = NULL; tqsl_write_adif_field(out, "eor", 0, NULL, 0); if (fclose(out) == EOF) { tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_createCertRequest", "write file error %s", strerror(tQSL_Errno)); goto end; } out = NULL; rval = 0; goto end; err: tQSL_Error = TQSL_OPENSSL_ERROR; end: if (bio != NULL) BIO_free(bio); if (out != NULL) fclose(out); if (xr != NULL) X509_REQ_free(xr); if (key != NULL) EVP_PKEY_free(key); if (req != NULL) tqsl_free_cert_req(req, 0); return rval; } DLLEXPORT int CALLCONVENTION tqsl_getSelectedCertificate(tQSL_Cert *cert, const tQSL_Cert **certlist, int idx) { tqslTrace("tqsl_getSelectedCertificate", NULL); if (tqsl_init()) return 1; if (certlist == NULL || cert == NULL || idx < 0) { tqslTrace("tqsl_getSelectedCertificate", "arg error certlist=0x%lx, cert=0x%lx, idx=%d", certlist, cert, idx); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } *cert = (*certlist)[idx]; return 0; } DLLEXPORT int CALLCONVENTION tqsl_isCertificateExpired(tQSL_Cert cert, int *status) { tqslTrace("tqsl_isCertificateExpired", NULL); if (tqsl_init()) return 1; if (cert == NULL || status == NULL || !tqsl_cert_check(TQSL_API_TO_CERT(cert), false)) { tqslTrace("tqsl_isCertificateExpired", "arg error cert=0x%lx status=0x%lx", cert, status); tQSL_Error = TQSL_ARGUMENT_ERROR; if (status) *status = false; return 1; } int keyonly; if (tqsl_getCertificateKeyOnly(cert, &keyonly) == 0 && keyonly) { *status = false; return 0; } long serial = 0; tqsl_getCertificateSerial(cert, &serial); if (tqsl_getCertificateStatus(serial) == TQSL_CERT_STATUS_EXP) { *status = true; return 0; } *status = false; /* Check for expired */ time_t t = time(0); struct tm *tm = gmtime(&t); tQSL_Date d; d.year = tm->tm_year + 1900; d.month = tm->tm_mon + 1; d.day = tm->tm_mday; const ASN1_TIME *ctm; if ((ctm = X509_get_notAfter(TQSL_API_TO_CERT(cert)->cert)) == NULL) { *status = true; return 0; } else { tQSL_Date cert_na; tqsl_get_asn1_date(ctm, &cert_na); if (tqsl_compareDates(&cert_na, &d) < 0) { *status = true; return 0; } } return 0; } static TQSL_X509_STACK *xcerts = NULL; DLLEXPORT int CALLCONVENTION tqsl_isCertificateSuperceded(tQSL_Cert cert, int *status) { char path[256]; int i; X509 *x = NULL; char *cp; vector< map > keylist; vector< map >::iterator it; set superceded_certs; int len; bool superceded = false; char buf[256]; tqslTrace("tqsl_isCertificateSuperceded", NULL); if (tqsl_init()) return 1; if (cert == NULL || status == NULL || !tqsl_cert_check(TQSL_API_TO_CERT(cert), false)) { tqslTrace("tqsl_isCertificateSuperceded", "arg error cert=0x%lx, status=0x%lx", cert, status); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } *status = false; int keyonly; if (tqsl_getCertificateKeyOnly(cert, &keyonly) == 0 && keyonly) { return 0; } long serial = 0; tqsl_getCertificateSerial(cert, &serial); if (tqsl_getCertificateStatus(serial) == TQSL_CERT_STATUS_SUP) { *status = true; tqslTrace("tqsl_isCertificateSuperceded", "returning true"); return 0; } /* Get the certs from the cert store */ tqsl_make_cert_path("user", path, sizeof path); if (xcerts == NULL) xcerts = tqsl_ssl_load_certs_from_file(path); if (xcerts == NULL) { if (tQSL_Error == TQSL_OPENSSL_ERROR) { tqslTrace("tqsl_isCertificateSuperceded", "openssl error loading certs %d", tQSL_Error); return 1; } } /* Make a list of superceded certs */ for (i = 0; i < sk_X509_num(xcerts); i++) { x = sk_X509_value(xcerts, i); len = sizeof buf-1; if (!tqsl_get_cert_ext(x, "supercededCertificate", (unsigned char *)buf, &len, NULL)) { buf[len] = 0; string sup = buf; superceded_certs.insert(sup); /* Fix - the extension as inserted by ARRL * reads ".../Email=lotw@arrl.org", not * the expected ".../emailAddress=". * save both forms in case this gets * changed at the LoTW site */ size_t pos = sup.find("/Email"); if (pos != string::npos) { sup.replace(pos, 6, "/emailAddress"); superceded_certs.insert(sup); } } } // "supercededCertificate" extension is ; cp = X509_NAME_oneline(X509_get_issuer_name(TQSL_API_TO_CERT(cert)->cert), buf, sizeof(buf)); if (cp == NULL) { superceded = false; tqslTrace("tqsl_isCertificateSuperceded", "returning false"); } else { string sup = buf; sup += ";"; long serial = 0; tqsl_getCertificateSerial(cert, &serial); snprintf(buf, sizeof buf, "%ld", serial); sup += buf; if (superceded_certs.find(sup) != superceded_certs.end()) { tqslTrace("tqsl_isCertificateSuperceded", "returning true"); superceded = true; } } *status = superceded; return 0; } DLLEXPORT int CALLCONVENTION tqsl_selectCertificates(tQSL_Cert **certlist, int *ncerts, const char *callsign, int dxcc, const tQSL_Date *date, const TQSL_PROVIDER *issuer, int flags) { int withkeys = flags & TQSL_SELECT_CERT_WITHKEYS; TQSL_X509_STACK *selcerts = NULL; char path[256]; int i; X509 *x; int rval = 1; tqsl_cert *cp; TQSL_CERT_REQ *crq; BIO *bio = NULL; EVP_PKEY *pubkey = NULL; EVP_PKEY *curkey = NULL; vector< map > keylist; vector< map >::iterator it; bool keyerror = false; int savedError; int savedErrno; tqslTrace("tqsl_selectCertificates", "callsign=%s, dxcc=%d, flags=%d", callsign ? callsign : "NULL", dxcc, flags); if (tqsl_init()) return 1; if (ncerts == NULL) { tqslTrace("tqsl_selectCertificates", "arg error ncerts=0x%lx", ncerts); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } *ncerts = 0; if (certlist) *certlist = NULL; /* Convert the dates to tQSL_Date objects */ if (date && !tqsl_isDateNull(date) && !tqsl_isDateValid(date)) { tqslTrace("tqsl_selectCertificates", "arg error - bad date"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } /* Get the certs from the cert store */ tqsl_make_cert_path("user", path, sizeof path); if (xcerts == NULL) xcerts = tqsl_ssl_load_certs_from_file(path); if (xcerts == NULL) { if (tQSL_Error == TQSL_OPENSSL_ERROR) { tqslTrace("tqsl_selectCertificates", "openssl error"); return 1; } else if (tQSL_Error != TQSL_SYSTEM_ERROR || tQSL_Errno != ENOENT) { // No file tqslTrace("tqsl_selectCertificates", "other error %d", tQSL_Error); return 1; } } else { selcerts = tqsl_filter_cert_list(xcerts, callsign, dxcc, date, issuer, flags); } // Get a list of keys and find any unmatched (no cert) ones if (withkeys) { if (tqsl_make_key_list(keylist)) { keyerror = true; // Remember that an error occurred savedError = tQSL_Error; // but allow the rest of the certs to load savedErrno = tQSL_Errno; tqslTrace("tqsl_selectCertificates", "make_key_list error %d %d", tQSL_Error, tQSL_Errno); } if (xcerts != NULL) { for (i = 0; i < sk_X509_num(xcerts); i++) { x = sk_X509_value(xcerts, i); if ((pubkey = X509_get_pubkey(x)) == NULL) { tqslTrace("tqsl_selectCertificates", "can't get pubkey"); goto err; } for (it = keylist.begin(); it != keylist.end(); it++) { int match = 0; /* Compare the keys */ string& keystr = (*it)["PUBLIC_KEY"]; if ((bio = BIO_new_mem_buf(static_cast(const_cast(keystr.c_str())), keystr.length())) == NULL) { tqslTrace("tqsl_selectCertifcates", "bio_new error %s", tqsl_openssl_error()); goto err; } if ((curkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL)) == NULL) { tqslTrace("tqsl_selectCertificates", "pem_read_bio err %s", tqsl_openssl_error()); goto err; } BIO_free(bio); bio = NULL; if (EVP_PKEY_cmp(curkey, pubkey) == 1) match = 1; EVP_PKEY_free(curkey); curkey = NULL; if (match) { // Remove matched key from list keylist.erase(it); break; } } EVP_PKEY_free(pubkey); pubkey = NULL; } } // Filter keylist for (it = keylist.begin(); it != keylist.end(); ) { if ((*it)["TQSL_CRQ_PROVIDER"] == "") it = keylist.erase(it); else if (callsign && (*it)["CALLSIGN"] != callsign) it = keylist.erase(it); else if (dxcc && strtol((*it)["TQSL_CRQ_DXCC_ENTITY"].c_str(), NULL, 10) != dxcc) it = keylist.erase(it); else if (issuer && (*it)["TQSL_CRQ_PROVIDER"] != issuer->organizationName) it = keylist.erase(it); else if (issuer && (*it)["TQSL_CRQ_PROVIDER_UNIT"] != issuer->organizationalUnitName) it = keylist.erase(it); else it++; } } //cerr << keylist.size() << " unmatched keys" << endl; *ncerts = (selcerts ? sk_X509_num(selcerts) : 0) + keylist.size(); tqslTrace("tqsl_selectCertificates", "ncerts=%d", *ncerts); if (certlist == NULL) // Only want certificate count goto end; *certlist = reinterpret_cast(tqsl_calloc(*ncerts, sizeof(tQSL_Cert))); if (selcerts != NULL) { for (i = 0; i < sk_X509_num(selcerts); i++) { x = sk_X509_value(selcerts, i); if ((cp = tqsl_cert_new()) == NULL) { tqslTrace("tqsl_selectCertificates", "error making new cert - %s", tqsl_openssl_error()); goto end; } cp->cert = X509_dup(x); (*certlist)[i] = TQSL_OBJ_TO_API(cp); } } else { i = 0; } for (it = keylist.begin(); it != keylist.end(); it++) { if ((cp = tqsl_cert_new()) == NULL) { tqslTrace("tqsl_selectCertificates", "error making new cert - %s", tqsl_openssl_error()); goto end; } crq = reinterpret_cast(tqsl_calloc(1, sizeof(TQSL_CERT_REQ))); if (crq != NULL) { tQSL_Error = TQSL_BUFFER_ERROR; if (!safe_strncpy(crq->providerName, (*it)["TQSL_CRQ_PROVIDER"].c_str(), sizeof crq->providerName)) goto end; if (!safe_strncpy(crq->providerUnit, (*it)["TQSL_CRQ_PROVIDER_UNIT"].c_str(), sizeof crq->providerUnit)) goto end; if (!safe_strncpy(crq->callSign, (*it)["CALLSIGN"].c_str(), sizeof crq->callSign)) goto end; if (!safe_strncpy(crq->name, (*it)["TQSL_CRQ_NAME"].c_str(), sizeof crq->name)) goto end; if (!safe_strncpy(crq->emailAddress, (*it)["TQSL_CRQ_EMAIL"].c_str(), sizeof crq->emailAddress)) goto end; if (!safe_strncpy(crq->address1, (*it)["TQSL_CRQ_ADDRESS1"].c_str(), sizeof crq->address1)) goto end; if (!safe_strncpy(crq->address2, (*it)["TQSL_CRQ_ADDRESS2"].c_str(), sizeof crq->address2)) goto end; if (!safe_strncpy(crq->city, (*it)["TQSL_CRQ_CITY"].c_str(), sizeof crq->city)) goto end; if (!safe_strncpy(crq->state, (*it)["TQSL_CRQ_STATE"].c_str(), sizeof crq->state)) goto end; if (!safe_strncpy(crq->postalCode, (*it)["TQSL_CRQ_POSTAL"].c_str(), sizeof crq->postalCode)) goto end; if (!safe_strncpy(crq->country, (*it)["TQSL_CRQ_COUNTRY"].c_str(), sizeof crq->country)) goto end; crq->dxccEntity = strtol((*it)["TQSL_CRQ_DXCC_ENTITY"].c_str(), NULL, 10); tqsl_initDate(&(crq->qsoNotBefore), (*it)["TQSL_CRQ_QSO_NOT_BEFORE"].c_str()); tqsl_initDate(&(crq->qsoNotAfter), (*it)["TQSL_CRQ_QSO_NOT_AFTER"].c_str()); tQSL_Error = 0; } cp->crq = crq; int len = strlen((*it)["PUBLIC_KEY"].c_str()); if (len) { cp->pubkey = new char[len+1]; strncpy(cp->pubkey, (*it)["PUBLIC_KEY"].c_str(), len+1); } len = strlen((*it)["PRIVATE_KEY"].c_str()); if (len) { cp->privkey = new char[len+1]; strncpy(cp->privkey, (*it)["PRIVATE_KEY"].c_str(), len+1); } cp->keyonly = 1; (*certlist)[i++] = TQSL_OBJ_TO_API(cp); } if (keyerror) { // If an error happened with private key scan tQSL_Error = savedError; // Restore the error status from that tQSL_Errno = savedErrno; rval = 1; } else { rval = 0; } goto end; err: tQSL_Error = TQSL_OPENSSL_ERROR; end: if (selcerts != NULL) sk_X509_free(selcerts); if (bio != NULL) BIO_free(bio); if (pubkey != NULL) EVP_PKEY_free(pubkey); if (curkey != NULL) EVP_PKEY_free(curkey); return rval; } DLLEXPORT int CALLCONVENTION tqsl_selectCACertificates(tQSL_Cert **certlist, int *ncerts, const char *type) { TQSL_X509_STACK *cacerts = NULL; int rval = 1; char path[256]; int i; X509 *x; tqsl_cert *cp; vector< map > keylist; vector< map >::iterator it; tqslTrace("tqsl_selectCACertificates", NULL); if (tqsl_init()) return 1; if (certlist == NULL || ncerts == NULL) { tqslTrace("tqsl_selectCACertificates", "arg error certlist=0x%lx, ncerts=0x%lx", certlist, ncerts); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } /* Get the certs from the cert store */ tqsl_make_cert_path(type, path, sizeof path); cacerts = tqsl_ssl_load_certs_from_file(path); if (cacerts == NULL) { if (tQSL_Error == TQSL_OPENSSL_ERROR) { tqslTrace("tqsl_selectCACertificates", "cacerts openssl error"); return 1; } } *ncerts = (cacerts ? sk_X509_num(cacerts) : 0) + keylist.size(); *certlist = reinterpret_cast(tqsl_calloc(*ncerts, sizeof(tQSL_Cert))); if (cacerts != NULL) { for (i = 0; i < sk_X509_num(cacerts); i++) { x = sk_X509_value(cacerts, i); if ((cp = tqsl_cert_new()) == NULL) { tqslTrace("tqsl_selectCACertificates", "cert_new error %s", tqsl_openssl_error()); goto end; } cp->cert = X509_dup(x); (*certlist)[i] = TQSL_OBJ_TO_API(cp); } } rval = 0; end: if (cacerts != NULL) sk_X509_free(cacerts); return rval; } DLLEXPORT int CALLCONVENTION tqsl_getCertificateKeyOnly(tQSL_Cert cert, int *keyonly) { tqslTrace("tqsl_getCertificateKeyOnly", "cert=0x%lx, keyonly=0x%lx", cert, keyonly); if (tqsl_init()) return 1; if (cert == NULL || keyonly == NULL || !tqsl_cert_check(TQSL_API_TO_CERT(cert), false)) { tqslTrace("tqsl_getCertificateKeyOnly", "arg error"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } *keyonly = TQSL_API_TO_CERT(cert)->keyonly; return 0; } DLLEXPORT int CALLCONVENTION tqsl_getCertificateEncoded(tQSL_Cert cert, char *buf, int bufsiz) { BIO *bio = NULL; int len; char *cp; int rval = 1; tqslTrace("tqsl_getCertificateEncoded", NULL); if (tqsl_init()) return 1; if (cert == NULL || buf == NULL || !tqsl_cert_check(TQSL_API_TO_CERT(cert))) { tqslTrace("tqsl_getCertificateEncoded", "arg error cert=0x%lx, buf=0x%lx", cert, buf); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if ((bio = BIO_new(BIO_s_mem())) == NULL) { tqslTrace("tqsl_getCertificateEncoded", "bio_new err %s", tqsl_openssl_error()); goto err; } if (!PEM_write_bio_X509(bio, TQSL_API_TO_CERT(cert)->cert)) { tqslTrace("tqsl_getCertificateEncoded", "pem_write_bio err %s", tqsl_openssl_error()); goto err; } len = static_cast(BIO_get_mem_data(bio, &cp)); if (len < bufsiz) { memcpy(buf, cp, len); buf[len] = 0; } else { tqslTrace("tqsl_getCertificateEncoded", "buffer error %d needed %d there", len, bufsiz); tQSL_Error = TQSL_BUFFER_ERROR; goto end; } rval = 0; goto end; err: tQSL_Error = TQSL_OPENSSL_ERROR; end: if (bio != NULL) BIO_free(bio); return rval; } DLLEXPORT int CALLCONVENTION tqsl_getKeyEncoded(tQSL_Cert cert, char *buf, int bufsiz) { BIO *b64 = NULL; BIO *bio = NULL; BIO *out = NULL; char callsign[40]; long len; char *cp; vector< map > keylist; vector< map >::iterator it; EVP_PKEY *pubkey = NULL; EVP_PKEY *curkey = NULL; tqslTrace("tqsl_getKeyEncoded", NULL); if (tqsl_init()) return 1; if (cert == NULL || buf == NULL || !tqsl_cert_check(TQSL_API_TO_CERT(cert), false)) { tqslTrace("tqsl_getKeyEncoded", "arg error cert=0x%lx, buf=0x%lx", cert, buf); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } tQSL_Error = TQSL_OPENSSL_ERROR; // If it's 'keyonly', then there's no public key - use the one in the cert. if (TQSL_API_TO_CERT(cert)->keyonly) { if (TQSL_API_TO_CERT(cert)->privkey == 0) { tqslTrace("tqsl_getKeyEncoded", "arg error no private key"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } strncpy(callsign, TQSL_API_TO_CERT(cert)->crq->callSign, sizeof callsign); b64 = BIO_new(BIO_f_base64()); out = BIO_new(BIO_s_mem()); out = BIO_push(b64, out); tQSL_Error = TQSL_SYSTEM_ERROR; if (tqsl_bio_write_adif_field(out, "CALLSIGN", 0, (const unsigned char *)callsign, -1)) { tqslTrace("tqsl_getKeyEncoded", "write_adif_field error %d", tQSL_Error); return 1; } if (tqsl_bio_write_adif_field(out, "PRIVATE_KEY", 0, (const unsigned char *)TQSL_API_TO_CERT(cert)->privkey, -1)) { tqslTrace("tqsl_getKeyEncoded", "write_adif_field error %d", tQSL_Error); return 1; } if (tqsl_bio_write_adif_field(out, "PUBLIC_KEY", 0, (const unsigned char *)TQSL_API_TO_CERT(cert)->pubkey, -1)) { tqslTrace("tqsl_getKeyEncoded", "write_adif_field error %d", tQSL_Error); return 1; } char numbuf[10]; snprintf(numbuf, sizeof numbuf, "%d", TQSL_API_TO_CERT(cert)->crq->dxccEntity); if (tqsl_bio_write_adif_field(out, "TQSL_CRQ_DXCC_ENTITY", 0, (const unsigned char *)numbuf, -1)) { tqslTrace("tqsl_getKeyEncoded", "write_adif_field error %d", tQSL_Error); return 1; } if (tqsl_bio_write_adif_field(out, "TQSL_CRQ_PROVIDER", 0, (const unsigned char *)TQSL_API_TO_CERT(cert)->crq->providerName, -1)) { tqslTrace("tqsl_getKeyEncoded", "write_adif_field error %d", tQSL_Error); return 1; } if (tqsl_bio_write_adif_field(out, "TQSL_CRQ_PROVIDER_UNIT", 0, (const unsigned char *)TQSL_API_TO_CERT(cert)->crq->providerUnit, -1)) { tqslTrace("tqsl_getKeyEncoded", "write_adif_field error %d", tQSL_Error); return 1; } if (tqsl_bio_write_adif_field(out, "TQSL_CRQ_EMAIL", 0, (const unsigned char *)TQSL_API_TO_CERT(cert)->crq->emailAddress, -1)) { tqslTrace("tqsl_getKeyEncoded", "write_adif_field error %d", tQSL_Error); return 1; } if (tqsl_bio_write_adif_field(out, "TQSL_CRQ_ADDRESS1", 0, (const unsigned char *)TQSL_API_TO_CERT(cert)->crq->address1, -1)) { tqslTrace("tqsl_getKeyEncoded", "write_adif_field error %d", tQSL_Error); return 1; } if (tqsl_bio_write_adif_field(out, "TQSL_CRQ_ADDRESS2", 0, (const unsigned char *)TQSL_API_TO_CERT(cert)->crq->address2, -1)) { tqslTrace("tqsl_getKeyEncoded", "write_adif_field error %d", tQSL_Error); return 1; } if (tqsl_bio_write_adif_field(out, "TQSL_CRQ_CITY", 0, (const unsigned char *)TQSL_API_TO_CERT(cert)->crq->city, -1)) { tqslTrace("tqsl_getKeyEncoded", "write_adif_field error %d", tQSL_Error); return 1; } if (tqsl_bio_write_adif_field(out, "TQSL_CRQ_STATE", 0, (const unsigned char *)TQSL_API_TO_CERT(cert)->crq->state, -1)) { tqslTrace("tqsl_getKeyEncoded", "write_adif_field error %d", tQSL_Error); return 1; } if (tqsl_bio_write_adif_field(out, "TQSL_CRQ_POSTAL", 0, (const unsigned char *)TQSL_API_TO_CERT(cert)->crq->postalCode, -1)) { tqslTrace("tqsl_getKeyEncoded", "write_adif_field error %d", tQSL_Error); return 1; } if (tqsl_bio_write_adif_field(out, "TQSL_CRQ_COUNTRY", 0, (const unsigned char *)TQSL_API_TO_CERT(cert)->crq->country, -1)) { tqslTrace("tqsl_getKeyEncoded", "write_adif_field error %d", tQSL_Error); return 1; } char datebuf[20]; tqsl_convertDateToText(&(TQSL_API_TO_CERT(cert)->crq->qsoNotAfter), datebuf, sizeof datebuf); if (tqsl_bio_write_adif_field(out, "TQSL_CRQ_QSO_NOT_AFTER", 0, (const unsigned char *)datebuf, -1)) { tqslTrace("tqsl_getKeyEncoded", "write_adif_field error %d", tQSL_Error); tQSL_Error = TQSL_OPENSSL_ERROR; return 1; } tqsl_convertDateToText(&(TQSL_API_TO_CERT(cert)->crq->qsoNotBefore), datebuf, sizeof datebuf); if (tqsl_bio_write_adif_field(out, "TQSL_CRQ_QSO_NOT_BEFORE", 0, (const unsigned char *)datebuf, -1)) { tqslTrace("tqsl_getKeyEncoded", "write_adif_field error %d", tQSL_Error); tQSL_Error = TQSL_OPENSSL_ERROR; return 1; } tqsl_bio_write_adif_field(out, "eor", 0, NULL, 0); if (BIO_flush(out) != 1) { tQSL_Error = TQSL_CUSTOM_ERROR; strncpy(tQSL_CustomError, "Error encoding certificate", sizeof tQSL_CustomError); BIO_free_all(out); tqslTrace("tqsl_getKeyEncoded", "BIO_flush error %s", tqsl_openssl_error()); return 1; } len = BIO_get_mem_data(out, &cp); if (len > bufsiz) { tQSL_Error = TQSL_CUSTOM_ERROR; snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "Private key buffer size %d is too small - %ld needed", bufsiz, len); BIO_free_all(out); tqslTrace("tqsl_getKeyEncoded", "buffer size err: %s", tQSL_CustomError); return 1; } memcpy(buf, cp, len); buf[len] = '\0'; BIO_free_all(out); return 0; } if (tqsl_getCertificateCallSign(cert, callsign, sizeof callsign)) { tqslTrace("tqsl_getKeyEncoded", "Error getting callsign %d", tQSL_Error); return 1; } if (tqsl_make_key_list(keylist)) { tqslTrace("tqsl_getKeyEncoded", "Error making keylist %d", tQSL_Error); tQSL_Error = TQSL_SYSTEM_ERROR; return 1; } if ((pubkey = X509_get_pubkey(TQSL_API_TO_CERT(cert)->cert)) == 0) { tqslTrace("tqsl_getKeyEncoded", "Error getting pubkey %d", tQSL_Error); tQSL_Error = TQSL_OPENSSL_ERROR; return 1; } // Find the matching private key for (it = keylist.begin(); it != keylist.end(); it++) { string& keystr = (*it)["PUBLIC_KEY"]; if ((bio = BIO_new_mem_buf(static_cast(const_cast(keystr.c_str())), keystr.length())) == NULL) { tqslTrace("tqsl_getKeyEncoded", "Error getting buffer %s", tqsl_openssl_error()); tQSL_Error = TQSL_OPENSSL_ERROR; return 1; } if ((curkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL)) == NULL) { BIO_free(bio); tqslTrace("tqsl_getKeyEncoded", "Error reading PUBKEY %s", tqsl_openssl_error()); tQSL_Error = TQSL_OPENSSL_ERROR; return 1; } BIO_free(bio); bio = NULL; if (EVP_PKEY_cmp(curkey, pubkey) == 1) { // This is the matching private key. Let's feed it back. EVP_PKEY_free(curkey); curkey = NULL; EVP_PKEY_free(pubkey); pubkey = NULL; b64 = BIO_new(BIO_f_base64()); out = BIO_new(BIO_s_mem()); out = BIO_push(b64, out); map::iterator mit; for (mit = it->begin(); mit != it->end(); mit++) { if (tqsl_bio_write_adif_field(out, mit->first.c_str(), 0, (const unsigned char *)mit->second.c_str(), -1)) { tQSL_Error = TQSL_SYSTEM_ERROR; tqslTrace("tqsl_getKeyEncoded", "Error writing field %s", tqsl_openssl_error()); return 1; } } tqsl_bio_write_adif_field(out, "eor", 0, NULL, 0); if (BIO_flush(out) != 1) { tQSL_Error = TQSL_CUSTOM_ERROR; tqslTrace("tqsl_getKeyEncoded", "Error flushing write %s", tqsl_openssl_error()); strncpy(tQSL_CustomError, "Error encoding certificate", sizeof tQSL_CustomError); BIO_free_all(out); return 1; } len = BIO_get_mem_data(out, &cp); if (len > bufsiz) { tQSL_Error = TQSL_CUSTOM_ERROR; snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "Private key buffer size %d is too small - %ld needed", bufsiz, len); tqslTrace("tqsl_getKeyEncoded", "Buffer err %s", tQSL_CustomError); BIO_free_all(out); return 1; } memcpy(buf, cp, len); buf[len] = '\0'; BIO_free_all(out); return 0; } else { EVP_PKEY_free(curkey); curkey = NULL; } } if (pubkey != NULL) EVP_PKEY_free(pubkey); tqslTrace("tqsl_getKeyEncoded", "private key not found"); tQSL_Error = TQSL_CUSTOM_ERROR; snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "Private key not found for callsign %s", callsign); return 1; // Private key not found } DLLEXPORT int CALLCONVENTION tqsl_importKeyPairEncoded(const char *callsign, const char *type, const char *keybuf, const char *certbuf) { BIO *in = NULL; BIO *b64 = NULL; BIO *pub = NULL; X509 *cert; char path[TQSL_MAX_PATH_LEN]; char temppath[TQSL_MAX_PATH_LEN]; char biobuf[4096]; int cb = 0; map fields; void* userdata = NULL; tqslTrace("tqsl_importKeyPairEncoded", NULL); if (tqsl_init()) return 1; if (certbuf == NULL || type == NULL) { tqslTrace("tqsl_importKeyPairEncoded", "arg error certbuf=0x%lx, type=0x%lx", certbuf, type); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (strcmp(type, "user") == 0) { if (keybuf == NULL) { tqslTrace("tqsl_importKeyPairEncoded", "arg error user cert keybuf null"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } cb = TQSL_CERT_CB_USER; } else if (strcmp(type, "root") == 0) { cb = TQSL_CERT_CB_ROOT; } else if (strcmp(type, "authorities") == 0) { cb = TQSL_CERT_CB_CA; } else { tqslTrace("tqsl_importKeyPairEncoded", "arg error type unknown"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (keybuf) { if (!tqsl_make_key_path(callsign, path, sizeof path)) { goto noprv; } in = BIO_new_mem_buf(static_cast(const_cast(keybuf)), strlen(keybuf)); if (in == NULL) { goto noprv; } b64 = BIO_new(BIO_f_base64()); in = BIO_push(b64, in); size_t bloblen; bloblen = BIO_read(in, biobuf, strlen(keybuf)); biobuf[bloblen] = '\0'; strncpy(temppath, tQSL_BaseDir, sizeof temppath); FILE *temp = NULL; #ifdef _WIN32 strncat(temppath, "\\pk.tmp", sizeof temppath - strlen(temppath) -1); wchar_t* wpath = utf8_to_wchar(temppath); if ((temp = _wfopen(wpath, TQSL_OPEN_WRITE)) == NULL) { free_wchar(wpath); #else strncat(temppath, "/pk.tmp", sizeof temppath - strlen(temppath) -1); if ((temp = fopen(temppath, TQSL_OPEN_WRITE)) == NULL) { #endif strncpy(tQSL_ErrorFile, temppath, sizeof tQSL_ErrorFile); tqslTrace("tqsl_importKeyPairEncoded", "Open file - system error %s", strerror(errno)); tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; return 1; } #ifdef _WIN32 free_wchar(wpath); #endif if (fputs(biobuf, temp) == EOF) { strncpy(tQSL_ErrorFile, temppath, sizeof tQSL_ErrorFile); tqslTrace("tqsl_importKeyPairEncoded", "Write request file - system error %s", strerror(errno)); tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; return 1; } if (fclose(temp) == EOF) { strncpy(tQSL_ErrorFile, temppath, sizeof tQSL_ErrorFile); tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_importKeyPairEncoded", "write error %d", errno); return 1; } // Now is there a private key already with this serial? char *pubkey = strstr(biobuf, "-----BEGIN PUBLIC KEY-----"); char *endpub = strstr(biobuf, "-----END PUBLIC KEY-----"); int publen = endpub - pubkey + strlen("-----END PUBLIC KEY-----"); if (pubkey) { EVP_PKEY *new_key = NULL; if ((pub = BIO_new_mem_buf(reinterpret_cast(pubkey), publen)) == NULL) { goto noprv; } if ((new_key = PEM_read_bio_PUBKEY(pub, NULL, NULL, NULL)) == NULL) { goto noprv; } BIO_free(pub); pub = 0; if (!tqsl_key_exists(callsign, new_key)) { // Populate fields from the temp file if (!tqsl_open_key_file(temppath)) { if (!tqsl_read_key(fields)) { tqsl_replace_key(callsign, path, fields, NULL, userdata); } tqsl_close_key_file(); } } BIO_free_all(in); } } // Import of private key noprv: if (strlen(certbuf) == 0) // Keyonly 'certificates' return 0; // Now process the certificate in = BIO_new_mem_buf(static_cast(const_cast(certbuf)), strlen(certbuf)); if (in == NULL) { tqslTrace("tqsl_importKeyPairEncoded", "cert new_mem_buf err %s", tqsl_openssl_error()); tQSL_Error = TQSL_OPENSSL_ERROR; return 1; } cert = PEM_read_bio_X509(in, NULL, NULL, NULL); BIO_free(in); if (cert == NULL) { tqslTrace("tqsl_importKeyPairEncoded", "read_bio_x509 err %s", tqsl_openssl_error()); tQSL_Error = TQSL_OPENSSL_ERROR; return 1; } int ret = tqsl_store_cert(certbuf, cert, type, cb, true, NULL, NULL); // it's OK if installing the cert gets a dupe if (ret != 0 && tQSL_Error == TQSL_CERT_ERROR) { ret = 0; } return ret; } DLLEXPORT int CALLCONVENTION tqsl_getCertificateCallSign(tQSL_Cert cert, char *buf, int bufsiz) { char nbuf[40]; TQSL_X509_NAME_ITEM item; tqslTrace("tqsl_getCertificateCallSign", NULL); if (tqsl_init()) return 1; if (cert == NULL || buf == NULL || !tqsl_cert_check(TQSL_API_TO_CERT(cert), false)) { tqslTrace("tqsl_getCertificateCallSign", "arg err cert=0x%lx buf=0x%lx", cert, buf); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (TQSL_API_TO_CERT(cert)->keyonly && TQSL_API_TO_CERT(cert)->crq) { // Handle the key-only case if (bufsiz <= static_cast(strlen(TQSL_API_TO_CERT(cert)->crq->callSign))) { tqslTrace("tqsl_getCertificateCallSign", "bufsiz=%d, needed=%d", bufsiz, static_cast(strlen(TQSL_API_TO_CERT(cert)->crq->callSign))); tQSL_Error = TQSL_BUFFER_ERROR; return 1; } strncpy(buf, TQSL_API_TO_CERT(cert)->crq->callSign, bufsiz); tqslTrace("tqsl_getCertificateCallSign", "KeyOnly, call=%s", buf); return 0; } item.name_buf = nbuf; item.name_buf_size = sizeof nbuf; item.value_buf = buf; item.value_buf_size = bufsiz; int ret = tqsl_cert_get_subject_name_entry(TQSL_API_TO_CERT(cert)->cert, "AROcallsign", &item); tqslTrace("tqsl_getCertificateCallSign", "Result=%d, call=%s", ret, buf); return !ret; } DLLEXPORT int CALLCONVENTION tqsl_getCertificateAROName(tQSL_Cert cert, char *buf, int bufsiz) { char nbuf[40]; TQSL_X509_NAME_ITEM item; tqslTrace("tqsl_getCertificateAROName", NULL); if (tqsl_init()) return 1; if (cert == NULL || buf == NULL || !tqsl_cert_check(TQSL_API_TO_CERT(cert))) { tqslTrace("tqsl_getCertificateAROName", "cert=0x%lx, buf=0x%lx", cert, buf); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } item.name_buf = nbuf; item.name_buf_size = sizeof nbuf; item.value_buf = buf; item.value_buf_size = bufsiz; return !tqsl_cert_get_subject_name_entry(TQSL_API_TO_CERT(cert)->cert, "commonName", &item); } DLLEXPORT int CALLCONVENTION tqsl_getCertificateEmailAddress(tQSL_Cert cert, char *buf, int bufsiz) { char nbuf[40]; TQSL_X509_NAME_ITEM item; tqslTrace("tqsl_getCertificateEmailAddress", NULL); if (tqsl_init()) return 1; if (cert == NULL || buf == NULL || !tqsl_cert_check(TQSL_API_TO_CERT(cert))) { tqslTrace("tqsl_getCertificateEmailAddress", "arg err cert=0x%lx, buf=0x%lx", cert, buf); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } item.name_buf = nbuf; item.name_buf_size = sizeof nbuf; item.value_buf = buf; item.value_buf_size = bufsiz; return !tqsl_cert_get_subject_name_entry(TQSL_API_TO_CERT(cert)->cert, "emailAddress", &item); } DLLEXPORT int CALLCONVENTION tqsl_getCertificateSerial(tQSL_Cert cert, long *serial) { tqslTrace("tqsl_getCertificateSerial", NULL); if (tqsl_init()) return 1; if (cert == NULL || serial == NULL || !tqsl_cert_check(TQSL_API_TO_CERT(cert))) { tqslTrace("tqsl_getCertificateSerial", "arg err cert=0x%lx, serial=0x%lx", cert, serial); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } *serial = ASN1_INTEGER_get(X509_get_serialNumber(TQSL_API_TO_CERT(cert)->cert)); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getCertificateSerialExt(tQSL_Cert cert, char *serial, int serialsiz) { tqslTrace("tqsl_getCertificateSerialExt", NULL); if (tqsl_init()) return 1; if (cert == NULL || serial == NULL || !tqsl_cert_check(TQSL_API_TO_CERT(cert)) || serialsiz < 1) { tqslTrace("tqsl_getCertificateSerialExt", "arg err cert=0x%lx, serial=0x%lx", cert, serial); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } BIGNUM *bn = BN_new(); ASN1_INTEGER_to_BN(X509_get_serialNumber(TQSL_API_TO_CERT(cert)->cert), bn); char *s = BN_bn2hex(bn); strncpy(serial, s, serialsiz); serial[serialsiz-1] = 0; OPENSSL_free(s); BN_free(bn); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getCertificateSerialLength(tQSL_Cert cert) { int rval; tqslTrace("tqsl_getCertificateSerialLength", NULL); if (tqsl_init()) return 1; if (cert == NULL) { tqslTrace("tqsl_getCertificateSerialLength", "arg error,cert=null"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } BIGNUM *bn = BN_new(); ASN1_INTEGER_to_BN(X509_get_serialNumber(TQSL_API_TO_CERT(cert)->cert), bn); char *s = BN_bn2hex(bn); rval = strlen(s); OPENSSL_free(s); BN_free(bn); return rval; } DLLEXPORT int CALLCONVENTION tqsl_getCertificateIssuer(tQSL_Cert cert, char *buf, int bufsiz) { char *cp; tqslTrace("tqsl_getCertificateIssuer", NULL); if (tqsl_init()) return 1; if (cert == NULL || buf == NULL || !tqsl_cert_check(TQSL_API_TO_CERT(cert))) { tqslTrace("tqsl_getCertificateIssuer", "arg err cert=0x%lx, buf=0x%lx", cert, buf); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } cp = X509_NAME_oneline(X509_get_issuer_name(TQSL_API_TO_CERT(cert)->cert), buf, bufsiz); if (cp == NULL) { tqslTrace("tqsl_getCertificateIssuer", "X509_NAME_oneline error %s", tqsl_openssl_error()); tQSL_Error = TQSL_OPENSSL_ERROR; } return (cp == NULL); } DLLEXPORT int CALLCONVENTION tqsl_getCertificateIssuerOrganization(tQSL_Cert cert, char *buf, int bufsiz) { char nbuf[40]; TQSL_X509_NAME_ITEM item; X509_NAME *iss; tqslTrace("tqsl_getCertificateIssuerOrganization", NULL); if (tqsl_init()) return 1; if (cert == NULL || buf == NULL || !tqsl_cert_check(TQSL_API_TO_CERT(cert), false)) { tqslTrace("tqsl_getCertificateIssuerOrganization", "arg error cert=0x%lx buf=0x%lx", cert, buf); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (TQSL_API_TO_CERT(cert)->keyonly && TQSL_API_TO_CERT(cert)->crq) { // Handle the key-only case if (bufsiz <= static_cast(strlen(TQSL_API_TO_CERT(cert)->crq->providerName))) { tqslTrace("tqsl_getCertificateIssuerOrganization", "bufsiz error have=%d need=%d", bufsiz, static_cast(strlen(TQSL_API_TO_CERT(cert)->crq->providerName))); tQSL_Error = TQSL_BUFFER_ERROR; return 1; } strncpy(buf, TQSL_API_TO_CERT(cert)->crq->providerName, bufsiz); return 0; } item.name_buf = nbuf; item.name_buf_size = sizeof nbuf; item.value_buf = buf; item.value_buf_size = bufsiz; if ((iss = X509_get_issuer_name(TQSL_API_TO_CERT(cert)->cert)) == NULL) { tqslTrace("tqsl_getCertificateIssuerOrganization", "get_issuer_name err %s", tqsl_openssl_error()); tQSL_Error = TQSL_OPENSSL_ERROR; return 1; } return !tqsl_get_name_entry(iss, "organizationName", &item); } DLLEXPORT int CALLCONVENTION tqsl_getCertificateIssuerOrganizationalUnit(tQSL_Cert cert, char *buf, int bufsiz) { char nbuf[40]; TQSL_X509_NAME_ITEM item; X509_NAME *iss; tqslTrace("tqsl_getCertificateIssuerOrganizationalUnit", NULL); if (tqsl_init()) return 1; if (cert == NULL || buf == NULL || !tqsl_cert_check(TQSL_API_TO_CERT(cert), false)) { tqslTrace("tqsl_getCertificateIssuerOrganizationalUnit", "arg err cert=0x%lx, buf=0x%lx", cert, buf); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (TQSL_API_TO_CERT(cert)->keyonly && TQSL_API_TO_CERT(cert)->crq) { // Handle the key-only case if (bufsiz <= static_cast(strlen(TQSL_API_TO_CERT(cert)->crq->providerUnit))) { tqslTrace("tqsl_getCertificateIssuerOrganizationalUnit", "bufsize error have=%d need=%d", bufsiz, static_cast(strlen(TQSL_API_TO_CERT(cert)->crq->providerUnit))); tQSL_Error = TQSL_BUFFER_ERROR; return 1; } strncpy(buf, TQSL_API_TO_CERT(cert)->crq->providerUnit, bufsiz); return 0; } item.name_buf = nbuf; item.name_buf_size = sizeof nbuf; item.value_buf = buf; item.value_buf_size = bufsiz; if ((iss = X509_get_issuer_name(TQSL_API_TO_CERT(cert)->cert)) == NULL) { tqslTrace("tqsl_getCertificateIssuerOrganizationalUnit", "get_issuer_name err %s", tqsl_openssl_error()); tQSL_Error = TQSL_OPENSSL_ERROR; return 1; } return !tqsl_get_name_entry(iss, "organizationalUnitName", &item); } DLLEXPORT int CALLCONVENTION tqsl_getCertificateQSONotBeforeDate(tQSL_Cert cert, tQSL_Date *date) { char datebuf[40]; int len = (sizeof datebuf) -1; tqslTrace("tqsl_getCertificateQSONotBeforeDate", NULL); if (tqsl_init()) return 1; if (cert == NULL || date == NULL || !tqsl_cert_check(TQSL_API_TO_CERT(cert), false)) { tqslTrace("tqsl_getCertificateQSONotBeforeDate", "arg err cert=0x%lx date=0x%lx", cert, date); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (TQSL_API_TO_CERT(cert)->keyonly && TQSL_API_TO_CERT(cert)->crq) { // Handle the key-only case *date = TQSL_API_TO_CERT(cert)->crq->qsoNotBefore; return 0; } if (tqsl_get_cert_ext(TQSL_API_TO_CERT(cert)->cert, "QSONotBeforeDate", (unsigned char *)datebuf, &len, NULL)) return 1; datebuf[len] = 0; return tqsl_initDate(date, const_cast(datebuf)); } DLLEXPORT int CALLCONVENTION tqsl_getCertificateQSONotAfterDate(tQSL_Cert cert, tQSL_Date *date) { char datebuf[40]; int len = (sizeof datebuf) -1; tqslTrace("tqsl_getCertificateQSONotAfterDate", NULL); if (tqsl_init()) return 1; if (cert == NULL || date == NULL || !tqsl_cert_check(TQSL_API_TO_CERT(cert), false)) { tqslTrace("tqsl_getCertificateQSONotAfterDate", "arg err cert=0x%lx date=0x%lx", cert, date); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (TQSL_API_TO_CERT(cert)->keyonly && TQSL_API_TO_CERT(cert)->crq) { // Handle the key-only case *date = TQSL_API_TO_CERT(cert)->crq->qsoNotAfter; return 0; } if (tqsl_get_cert_ext(TQSL_API_TO_CERT(cert)->cert, "QSONotAfterDate", (unsigned char *)datebuf, &len, NULL)) return 1; datebuf[len] = 0; return tqsl_initDate(date, const_cast(datebuf)); } DLLEXPORT int CALLCONVENTION tqsl_getCertificateNotBeforeDate(tQSL_Cert cert, tQSL_Date *date) { const ASN1_TIME *tm; tqslTrace("tqsl_getCertificateNotBeforeDate", NULL); if (tqsl_init()) return 1; if (cert == NULL || date == NULL || !tqsl_cert_check(TQSL_API_TO_CERT(cert))) { tqslTrace("tqsl_getCertificateNotBeforeDate", "arg err cert=0x%lx date=0x%lx", cert, date); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (TQSL_API_TO_CERT(cert)->keyonly) { tqslTrace("tqsl_getCertificateNotBeforeDate", "Err:cert is keyonly"); tQSL_Error = TQSL_CERT_KEY_ONLY; return 1; } if ((tm = X509_get_notBefore(TQSL_API_TO_CERT(cert)->cert)) == NULL) { tqslTrace("tqsl_getCertificateNotBeforeDate", "get_notBefore err %s", tqsl_openssl_error()); tQSL_Error = TQSL_OPENSSL_ERROR; return 1; } return tqsl_get_asn1_date(tm, date); } DLLEXPORT int CALLCONVENTION tqsl_getCertificateNotAfterDate(tQSL_Cert cert, tQSL_Date *date) { const ASN1_TIME *tm; if (tqsl_init()) return 1; if (cert == NULL || date == NULL || !tqsl_cert_check(TQSL_API_TO_CERT(cert))) { tqslTrace("tqsl_getCertificateNotAfterDate", "arg err cert=0x%lx date=0x%lx", cert, date); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (TQSL_API_TO_CERT(cert)->keyonly) { tqslTrace("tqsl_getCertificateNotAfterDate", "Err:cert is keyonly"); tQSL_Error = TQSL_CERT_KEY_ONLY; return 1; } if ((tm = X509_get_notAfter(TQSL_API_TO_CERT(cert)->cert)) == NULL) { tqslTrace("tqsl_getCertificateNotAfterDate", "get_notAfter err %s", tqsl_openssl_error()); tQSL_Error = TQSL_OPENSSL_ERROR; return 1; } return tqsl_get_asn1_date(tm, date); } DLLEXPORT int CALLCONVENTION tqsl_getCertificateDXCCEntity(tQSL_Cert cert, int *dxcc) { char buf[40]; int len = sizeof buf; tqslTrace("tqsl_getCertificateDXCCEntity", NULL); if (tqsl_init()) return 1; if (cert == NULL || dxcc == NULL || !tqsl_cert_check(TQSL_API_TO_CERT(cert), false)) { tqslTrace("tqsl_getCertificateDXCCEntity", "arg err cert=0x%lx dxcc=0x%lx", cert, dxcc); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (TQSL_API_TO_CERT(cert)->keyonly && TQSL_API_TO_CERT(cert)->crq) { // Handle the key-only case *dxcc = TQSL_API_TO_CERT(cert)->crq->dxccEntity; return 0; } if (tqsl_get_cert_ext(TQSL_API_TO_CERT(cert)->cert, "dxccEntity", (unsigned char *)buf, &len, NULL)) { tqslTrace("tqsl_getCertificateDXCCEntity", "Cert does not have dxcc extension"); return 1; } *dxcc = strtol(buf, NULL, 10); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getCertificatePrivateKeyType(tQSL_Cert cert) { tqslTrace("tqsl_getCertificatePrivateKeyType", NULL); if (tqsl_init()) return 1; if (!tqsl_cert_check(TQSL_API_TO_CERT(cert), false)) { tqslTrace("tqsl_getCertificatePrivateKeyType", "arg err, bad cert"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (tqsl_beginSigning(cert, const_cast (""), 0, 0)) { // Try to unlock the key using no password if (tQSL_Error == TQSL_PASSWORD_ERROR) { tqsl_getErrorString(); // Clear the error tqslTrace("tqsl_getCertificatePrivateKeyType", "password error - encrypted"); return TQSL_PK_TYPE_ENC; } tqslTrace("tqsl_getCertificatePrivateKeyType", "other error"); return TQSL_PK_TYPE_ERR; } tqslTrace("tqsl_getCertificatePrivateKeyType", "unencrypted"); return TQSL_PK_TYPE_UNENC; } DLLEXPORT void CALLCONVENTION tqsl_freeCertificate(tQSL_Cert cert) { if (cert == NULL) return; tqsl_cert_free(TQSL_API_TO_CERT(cert)); } DLLEXPORT void CALLCONVENTION tqsl_freeCertificateList(tQSL_Cert* list, int ncerts) { for (int i = 0; i < ncerts; i++) if (list[i]) tqsl_cert_free(TQSL_API_TO_CERT(list[i])); if (list) free(list); } DLLEXPORT int CALLCONVENTION tqsl_beginSigning(tQSL_Cert cert, char *password, int(*pwcb)(char *, int, void *), void *userdata) { tqslTrace("tqsl_beginSigning", NULL); if (tqsl_init()) return 1; if (cert == NULL || !tqsl_cert_check(TQSL_API_TO_CERT(cert), false)) { tqslTrace("tqsl_beginSigning", "arg err cert=0x%lx", cert); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (TQSL_API_TO_CERT(cert)->key != NULL) return 0; if (TQSL_API_TO_CERT(cert)->keyonly) { if (TQSL_API_TO_CERT(cert)->privkey == 0) { tqslTrace("tqsl_beginSigning", "can't sign, keyonly"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } return tqsl_unlock_key(TQSL_API_TO_CERT(cert)->privkey, &(TQSL_API_TO_CERT(cert)->key), password, pwcb, userdata); } return tqsl_find_matching_key(TQSL_API_TO_CERT(cert)->cert, &(TQSL_API_TO_CERT(cert)->key), &(TQSL_API_TO_CERT(cert)->crq), password, pwcb, userdata); } DLLEXPORT int CALLCONVENTION tqsl_getMaxSignatureSize(tQSL_Cert cert, int *sigsize) { tqslTrace("tqsl_getMaxSignatureSize", NULL); if (tqsl_init()) return 1; if (cert == NULL || sigsize == NULL || !tqsl_cert_check(TQSL_API_TO_CERT(cert))) { tqslTrace("tqsl_getMaxSignatureSize", "arg err cert=0x%lx, sigsize=0x%lx", cert, sigsize); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (TQSL_API_TO_CERT(cert)->key == NULL) { tqslTrace("tqsl_getMaxSignatureSize", "arg err key=null"); tQSL_Error = TQSL_SIGNINIT_ERROR; return 1; } *sigsize = EVP_PKEY_size(TQSL_API_TO_CERT(cert)->key); return 0; } DLLEXPORT int CALLCONVENTION tqsl_checkSigningStatus(tQSL_Cert cert) { tqslTrace("tqsl_checkSigningStatus", NULL); if (tqsl_init()) return 1; if (cert == NULL || !tqsl_cert_check(TQSL_API_TO_CERT(cert))) { tqslTrace("tqsl_checkSigningStatus", "arg err cert=0x%lx", cert); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (TQSL_API_TO_CERT(cert)->key == NULL) { tqslTrace("tqsl_checkSigningStatus", "arg err no key"); tQSL_Error = TQSL_SIGNINIT_ERROR; return 1; } return 0; } DLLEXPORT int CALLCONVENTION tqsl_signDataBlock(tQSL_Cert cert, const unsigned char *data, int datalen, unsigned char *sig, int *siglen) { tqslTrace("tqsl_signDataBlock", NULL); if (tqsl_init()) return 1; if (cert == NULL || data == NULL || sig == NULL || siglen == NULL || !tqsl_cert_check(TQSL_API_TO_CERT(cert))) { tqslTrace("tqsl_signDataBlock", "arg error cert=0x%lx data=0x%lx sig=0x%lx siglen=0x%lx", cert, data, sig, siglen); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } EVP_MD_CTX *ctx = EVP_MD_CTX_create(); if (ctx == NULL) return 1; unsigned int slen = *siglen; if (TQSL_API_TO_CERT(cert)->key == NULL) { tqslTrace("tqsl_signDataBlock", "can't sign, no key"); tQSL_Error = TQSL_SIGNINIT_ERROR; if (ctx) EVP_MD_CTX_destroy(ctx); return 1; } EVP_SignInit(ctx, EVP_sha1()); EVP_SignUpdate(ctx, data, datalen); if (!EVP_SignFinal(ctx, sig, &slen, TQSL_API_TO_CERT(cert)->key)) { tqslTrace("tqsl_signDataBlock", "signing failed %s", tqsl_openssl_error()); tQSL_Error = TQSL_OPENSSL_ERROR; if (ctx) EVP_MD_CTX_destroy(ctx); return 1; } *siglen = slen; if (ctx) EVP_MD_CTX_destroy(ctx); return 0; } DLLEXPORT int CALLCONVENTION tqsl_verifyDataBlock(tQSL_Cert cert, const unsigned char *data, int datalen, unsigned char *sig, int siglen) { EVP_MD_CTX *ctx = EVP_MD_CTX_create(); unsigned int slen = siglen; tqslTrace("tqsl_verifyDataBlock", NULL); if (ctx == NULL) return 1; if (tqsl_init()) return 1; if (cert == NULL || data == NULL || sig == NULL || !tqsl_cert_check(TQSL_API_TO_CERT(cert))) { tqslTrace("tqsl_verifyDataBlock", "arg error cert=0x%lx data=0x%lx sig=0x%lx", cert, data, sig); tQSL_Error = TQSL_ARGUMENT_ERROR; if (ctx) EVP_MD_CTX_destroy(ctx); return 1; } if (TQSL_API_TO_CERT(cert)->key == NULL) { tqslTrace("tqsl_verifyDataBlock", "no key"); tQSL_Error = TQSL_SIGNINIT_ERROR; if (ctx) EVP_MD_CTX_destroy(ctx); return 1; } EVP_VerifyInit(ctx, EVP_sha1()); EVP_VerifyUpdate(ctx, data, datalen); if (EVP_VerifyFinal(ctx, sig, slen, TQSL_API_TO_CERT(cert)->key) <= 0) { tqslTrace("tqsl_verifyDataBlock", "verify fail %s", tqsl_openssl_error()); tQSL_Error = TQSL_OPENSSL_ERROR; if (ctx) EVP_MD_CTX_destroy(ctx); return 1; } if (ctx) EVP_MD_CTX_destroy(ctx); return 0; } DLLEXPORT int CALLCONVENTION tqsl_endSigning(tQSL_Cert cert) { tqslTrace("tqsl_endSigning", NULL); if (tqsl_init()) return 1; if (cert == NULL || !tqsl_cert_check(TQSL_API_TO_CERT(cert))) { tqslTrace("tqsl_endSigning", "arg err cert=0x%lx", cert); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (TQSL_API_TO_CERT(cert)->key != NULL) { EVP_PKEY_free(TQSL_API_TO_CERT(cert)->key); TQSL_API_TO_CERT(cert)->key = NULL; } return 0; } DLLEXPORT int CALLCONVENTION tqsl_getCertificateRequestAddress1(tQSL_Cert cert, char *buf, int bufsiz) { tqslTrace("tqsl_getCertificateRequestAddress1", NULL); if (tqsl_check_crq_field(cert, buf, bufsiz)) { tqslTrace("tqsl_getCertificateRequestAddress1", "check fail"); return 1; } strncpy(buf, (((TQSL_API_TO_CERT(cert)->crq)->address1) == NULL ? "" : (TQSL_API_TO_CERT(cert)->crq)->address1), bufsiz); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getCertificateRequestAddress2(tQSL_Cert cert, char *buf, int bufsiz) { tqslTrace("tqsl_getCertificateRequestAddress2", NULL); if (tqsl_check_crq_field(cert, buf, bufsiz)) { tqslTrace("tqsl_getCertificateRequestAddress2", "check fail"); return 1; } strncpy(buf, (((TQSL_API_TO_CERT(cert)->crq)->address2) == NULL ? "" : (TQSL_API_TO_CERT(cert)->crq)->address2), bufsiz); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getCertificateRequestCity(tQSL_Cert cert, char *buf, int bufsiz) { tqslTrace("tqsl_getCertificateRequestCity", NULL); if (tqsl_check_crq_field(cert, buf, bufsiz)) { tqslTrace("tqsl_getCertificateRequestCity", "check fail"); return 1; } strncpy(buf, (((TQSL_API_TO_CERT(cert)->crq)->city) == NULL ? "" : (TQSL_API_TO_CERT(cert)->crq)->city), bufsiz); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getCertificateRequestState(tQSL_Cert cert, char *buf, int bufsiz) { tqslTrace("tqsl_getCertificateRequestState", NULL); if (tqsl_check_crq_field(cert, buf, bufsiz)) { tqslTrace("tqsl_getCertificateRequestState", "check fail"); return 1; } strncpy(buf, (((TQSL_API_TO_CERT(cert)->crq)->state) == NULL ? "" : (TQSL_API_TO_CERT(cert)->crq)->state), bufsiz); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getCertificateRequestPostalCode(tQSL_Cert cert, char *buf, int bufsiz) { tqslTrace("tqsl_getCertificateRequestPostalCode", NULL); if (tqsl_check_crq_field(cert, buf, bufsiz)) { tqslTrace("tqsl_getCertificateRequestPostalCode", "check fail"); return 1; } strncpy(buf, (((TQSL_API_TO_CERT(cert)->crq)->postalCode) == NULL ? "" : (TQSL_API_TO_CERT(cert)->crq)->postalCode), bufsiz); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getCertificateRequestCountry(tQSL_Cert cert, char *buf, int bufsiz) { tqslTrace("tqsl_getCertificateRequestCountry", NULL); if (tqsl_check_crq_field(cert, buf, bufsiz)) { tqslTrace("tqsl_getCertificateRequestCountry", "check fail"); return 1; } strncpy(buf, (((TQSL_API_TO_CERT(cert)->crq)->country) == NULL ? "" : (TQSL_API_TO_CERT(cert)->crq)->country), bufsiz); return 0; } static int tqsl_add_bag_attribute(PKCS12_SAFEBAG *bag, const char *oidname, const string& value) { int nid; nid = OBJ_txt2nid(const_cast(oidname)); if (nid == NID_undef) { tqslTrace("tqsl_add_bag_attribute", "OBJ_txt2nid err %s", tqsl_openssl_error()); return 1; } unsigned char *uni; int unilen; if (asc2uni(value.c_str(), value.length(), &uni, &unilen)) { ASN1_TYPE *val; X509_ATTRIBUTE *attrib; if (!uni[unilen - 1] && !uni[unilen - 2]) unilen -= 2; if ((val = ASN1_TYPE_new()) != 0) { ASN1_TYPE_set(val, V_ASN1_BMPSTRING, uni); if ((attrib = X509_ATTRIBUTE_new()) != 0) { X509_ATTRIBUTE_set1_object(attrib, OBJ_nid2obj(nid)); if ((X509_ATTRIBUTE_set1_data(attrib, V_ASN1_BMPSTRING, uni, unilen)) != 0) { #if (OPENSSL_VERSION_NUMBER & 0xfffff000) == 0x00906000 attrib->set = 1; #else #if OPENSSL_VERSION_NUMBER < 0x10100000L #if (OPENSSL_VERSION_NUMBER & 0xfffff000) >= 0x00907000 attrib->single = 0; #else #error "Unexpected OpenSSL version; check X509_ATTRIBUTE struct compatibility" #endif #endif #endif #if OPENSSL_VERSION_NUMBER >= 0x10100000L STACK_OF(X509_ATTRIBUTE) *sk; sk = (STACK_OF(X509_ATTRIBUTE)*)PKCS12_SAFEBAG_get0_attrs(bag); if (sk) { sk_X509_ATTRIBUTE_push(sk, attrib); #else if (bag->attrib) { sk_X509_ATTRIBUTE_push(bag->attrib, attrib); #endif //cerr << "Added " << oidname << endl; } else { tqslTrace("tqsl_add_bag_attribute", "no attrib"); return 1; } } else { tqslTrace("tqsl_add_bag_attribute", "no value set"); return 1; } } else { tqslTrace("tqsl_add_bag_attribute", "attrib create err %s", tqsl_openssl_error()); return 1; } } else { tqslTrace("tqsl_add_bag_attribute", "bmp->data empty"); return 1; } } else { // asc2uni ok tqslTrace("tqsl_add_bag_attribute", "asc2uni err %s", tqsl_openssl_error()); return 1; } return 0; } static int tqsl_exportPKCS12(tQSL_Cert cert, bool returnB64, const char *filename, char *base64, int b64len, const char *p12password) { STACK_OF(X509) *root_sk = 0, *ca_sk = 0, *chain = 0; const char *cp; char rootpath[256], capath[256]; char buf[256]; unsigned char keyid[EVP_MAX_MD_SIZE]; unsigned int keyidlen = 0; STACK_OF(PKCS12_SAFEBAG) *bags = 0; PKCS12_SAFEBAG *bag = 0; STACK_OF(PKCS7) *safes = 0; PKCS7 *authsafe = 0; int cert_pbe = NID_pbe_WithSHA1And40BitRC2_CBC; int key_pbe = NID_pbe_WithSHA1And3_Key_TripleDES_CBC; PKCS8_PRIV_KEY_INFO *p8 = 0; PKCS12 *p12 = 0; BIO *out = 0, *b64 = 0; string callSign, issuerOrganization, issuerOrganizationalUnit; tQSL_Date date; string QSONotBeforeDate, QSONotAfterDate, dxccEntity, Email, Address1, Address2, City, State, Postal, Country; int dxcc = 0; int rval = 1; tqslTrace("tqsl_exportPKCS12", NULL); if (cert == NULL || !tqsl_cert_check(TQSL_API_TO_CERT(cert), false)) { tqslTrace("tqsl_exportPKCS12", "arg error cert=0x%lx", cert); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if ((returnB64 && base64 == NULL) || (!returnB64 && filename == NULL)) { tqslTrace("tqsl_exportPKCS12", "arg error returnB64=%d base64=0x%lx filename=0x%lx", returnB64, base64, filename); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } /* Get parameters for key bag attributes */ if (tqsl_getCertificateCallSign(cert, buf, sizeof buf)) { tqslTrace("tqsl_exportPKCS12", "get callsign err %d", tQSL_Error); return 1; } callSign = buf; if (tqsl_getCertificateIssuerOrganization(cert, buf, sizeof buf)) { tqslTrace("tqsl_exportPKCS12", "get org err %d", tQSL_Error); return 1; } issuerOrganization = buf; if (tqsl_getCertificateIssuerOrganizationalUnit(cert, buf, sizeof buf)) { tqslTrace("tqsl_exportPKCS12", "get ou err %d", tQSL_Error); return 1; } issuerOrganizationalUnit = buf; if (!TQSL_API_TO_CERT(cert)->keyonly) { if (tqsl_getCertificateEmailAddress(cert, buf, sizeof buf)) { tqslTrace("tqsl_exportPKCS12", "get email err %d", tQSL_Error); return 1; } Email = buf; if (tqsl_getCertificateRequestAddress1(cert, buf, sizeof buf)) { tqslTrace("tqsl_exportPKCS12", "get addr1 err %d", tQSL_Error); return 1; } Address1 = buf; if (tqsl_getCertificateRequestAddress2(cert, buf, sizeof buf)) { tqslTrace("tqsl_exportPKCS12", "get addr2 err %d", tQSL_Error); return 1; } Address2 = buf; if (tqsl_getCertificateRequestCity(cert, buf, sizeof buf)) { tqslTrace("tqsl_exportPKCS12", "get city err %d", tQSL_Error); return 1; } City = buf; if (tqsl_getCertificateRequestState(cert, buf, sizeof buf)) { tqslTrace("tqsl_exportPKCS12", "get state err %d", tQSL_Error); return 1; } State = buf; if (tqsl_getCertificateRequestPostalCode(cert, buf, sizeof buf)) { tqslTrace("tqsl_exportPKCS12", "get postal err %d", tQSL_Error); return 1; } Postal = buf; if (tqsl_getCertificateRequestCountry(cert, buf, sizeof buf)) { tqslTrace("tqsl_exportPKCS12", "get country err %d", tQSL_Error); return 1; } Country = buf; } if (tqsl_getCertificateQSONotBeforeDate(cert, &date)) { tqslTrace("tqsl_exportPKCS12", "get qso not before err %d", tQSL_Error); return 1; } if (!tqsl_convertDateToText(&date, buf, sizeof buf)) { tqslTrace("tqsl_exportPKCS12", "qso not before err %d", tQSL_Error); return 1; } QSONotBeforeDate = buf; if (tqsl_getCertificateQSONotAfterDate(cert, &date)) { tqslTrace("tqsl_exportPKCS12", "get qso not after err %d", tQSL_Error); return 1; } if (!tqsl_isDateNull(&date)) { if (!tqsl_convertDateToText(&date, buf, sizeof buf)) { tqslTrace("tqsl_exportPKCS12", "qso not before err %d", tQSL_Error); return 1; } QSONotAfterDate = buf; } if (tqsl_getCertificateDXCCEntity(cert, &dxcc)) { tqslTrace("tqsl_exportPKCS12", "get entity err %d", tQSL_Error); return 1; } snprintf(buf, sizeof buf, "%d", dxcc); dxccEntity = buf; if (TQSL_API_TO_CERT(cert)->key == NULL) { tqslTrace("tqsl_exportPKCS12", "key is null"); tQSL_Error = TQSL_SIGNINIT_ERROR; return 1; } if (!TQSL_API_TO_CERT(cert)->keyonly) { tqslTrace("tqsl_exportPKCS12", "keyonly cert"); /* Generate local key ID to tie key to cert */ X509_digest(TQSL_API_TO_CERT(cert)->cert, EVP_sha1(), keyid, &keyidlen); /* Check the chain of authority back to a trusted root */ tqsl_make_cert_path("root", rootpath, sizeof rootpath); if ((root_sk = tqsl_ssl_load_certs_from_file(rootpath)) == NULL) { if (!tqsl_ssl_error_is_nofile()) { tqslTrace("tqsl_exportPKCS12", "can't find certs"); goto p12_end; } } tqsl_make_cert_path("authorities", capath, sizeof capath); if ((ca_sk = tqsl_ssl_load_certs_from_file(capath)) == NULL) { if (!tqsl_ssl_error_is_nofile()) { tqslTrace("tqsl_exportPKCS12", "can't find certs"); goto p12_end; } } /* tqsl_ssl_verify_cert will collect the certificates in the chain, back to the * root certificate, verify them and return a stack containing copies of just * those certificates (including the user certificate). */ cp = tqsl_ssl_verify_cert(TQSL_API_TO_CERT(cert)->cert, ca_sk, root_sk, 0, &tqsl_expired_is_ok, &chain); if (cp) { if (chain) sk_X509_free(chain); tQSL_Error = TQSL_CUSTOM_ERROR; strncpy(tQSL_CustomError, cp, sizeof tQSL_CustomError); tqslTrace("tqsl_exportPKCS12", "verify fail: %s", cp); return 1; } } // !keyonly tQSL_Error = TQSL_OPENSSL_ERROR; // Assume error /* Open the output file */ if (!returnB64) { out = BIO_new_file(filename, "wb"); } else { b64 = BIO_new(BIO_f_base64()); out = BIO_new(BIO_s_mem()); out = BIO_push(b64, out); } if (!out) { tqslTrace("tqsl_exportPKCS12", "BIO_new err %s", tqsl_openssl_error()); goto p12_end; } safes = sk_PKCS7_new_null(); if (!TQSL_API_TO_CERT(cert)->keyonly) { /* Create a safebag stack and fill it with the needed certs */ bags = sk_PKCS12_SAFEBAG_new_null(); for (int i = 0; i < sk_X509_num(chain); i++) { X509 *x = sk_X509_value(chain, i); #if (OPENSSL_VERSION_NUMBER & 0xfffff000) == 0x00906000 bag = PKCS12_pack_safebag(reinterpret_cast(x), (int (*)())i2d_X509, NID_x509Certificate, NID_certBag); #else bag = PKCS12_x5092certbag(x); #endif if (!bag) { tqslTrace("tqsl_exportPKCS12", "Error creating bag: %s", tqsl_openssl_error()); goto p12_end; } if (x == TQSL_API_TO_CERT(cert)->cert) { PKCS12_add_friendlyname(bag, "TrustedQSL user certificate", -1); PKCS12_add_localkeyid(bag, keyid, keyidlen); } sk_PKCS12_SAFEBAG_push(bags, bag); } /* Convert stack of safebags into an authsafe */ authsafe = PKCS12_pack_p7encdata(cert_pbe, p12password, -1, 0, 0, PKCS12_DEFAULT_ITER, bags); if (!authsafe) { tqslTrace("tqsl_exportPKCS12", "Error creating authsafe: %s", tqsl_openssl_error()); goto p12_end; } sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free); bags = 0; /* Add to stack of authsafes */ sk_PKCS7_push(safes, authsafe); } // !keyonly /* Make a shrouded key bag */ p8 = EVP_PKEY2PKCS8(TQSL_API_TO_CERT(cert)->key); if (!p8) { tqslTrace("tqsl_exportPKCS12", "Error creating p8 container: %s", tqsl_openssl_error()); goto p12_end; } bag = PKCS12_MAKE_SHKEYBAG(key_pbe, p12password, -1, 0, 0, PKCS12_DEFAULT_ITER, p8); if (!bag) { tqslTrace("tqsl_exportPKCS12", "Error creating p8 keybag: %s", tqsl_openssl_error()); goto p12_end; } PKCS8_PRIV_KEY_INFO_free(p8); p8 = NULL; PKCS12_add_friendlyname(bag, "TrustedQSL user certificate", -1); if (!TQSL_API_TO_CERT(cert)->keyonly) PKCS12_add_localkeyid(bag, keyid, keyidlen); /* Add the attributes to the private key bag */ tqsl_add_bag_attribute(bag, "AROcallsign", callSign); tqsl_add_bag_attribute(bag, "QSONotBeforeDate", QSONotBeforeDate); if (QSONotAfterDate != "") tqsl_add_bag_attribute(bag, "QSONotAfterDate", QSONotAfterDate); tqsl_add_bag_attribute(bag, "tqslCRQIssuerOrganization", issuerOrganization); tqsl_add_bag_attribute(bag, "tqslCRQIssuerOrganizationalUnit", issuerOrganizationalUnit); tqsl_add_bag_attribute(bag, "dxccEntity", dxccEntity); tqsl_add_bag_attribute(bag, "tqslCRQEmail", Email); tqsl_add_bag_attribute(bag, "tqslCRQAddress1", Address1); tqsl_add_bag_attribute(bag, "tqslCRQAddress2", Address2); tqsl_add_bag_attribute(bag, "tqslCRQCity", City); tqsl_add_bag_attribute(bag, "tqslCRQState", State); tqsl_add_bag_attribute(bag, "tqslCRQPostal", Postal); tqsl_add_bag_attribute(bag, "tqslCRQCountry", Country); bags = sk_PKCS12_SAFEBAG_new_null(); if (!bags) { tqslTrace("tqsl_exportPKCS12", "Error creating safebag: %s", tqsl_openssl_error()); goto p12_end; } sk_PKCS12_SAFEBAG_push(bags, bag); /* Turn shrouded key bag into unencrypted safe bag and add to safes stack */ authsafe = PKCS12_pack_p7data(bags); sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free); bags = NULL; sk_PKCS7_push(safes, authsafe); /* Form into PKCS12 data */ p12 = PKCS12_init(NID_pkcs7_data); M_PKCS12_pack_authsafes(p12, safes); sk_PKCS7_pop_free(safes, PKCS7_free); safes = NULL; PKCS12_set_mac(p12, p12password, -1, 0, 0, PKCS12_DEFAULT_ITER, 0); /* Write the PKCS12 data */ i2d_PKCS12_bio(out, p12); if (BIO_flush(out) != 1) { rval = 1; tqslTrace("tqsl_exportPKCS12", "Error writing pkcs12: %s", tqsl_openssl_error()); goto p12_end; } if (returnB64) { char *encoded; int len; len = BIO_get_mem_data(out, &encoded); encoded[len - 1] = '\0'; strncpy(base64, encoded, b64len); } rval = 0; tQSL_Error = TQSL_NO_ERROR; p12_end: if (out) { BIO_free(out); if (rval && !returnB64) { #ifdef _WIN32 wchar_t* wfilename = utf8_to_wchar(filename); _wunlink(wfilename); free_wchar(wfilename); #else unlink(filename); #endif } } if (chain) sk_X509_free(chain); if (root_sk) sk_X509_free(root_sk); if (ca_sk) sk_X509_free(ca_sk); if (bags) sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free); if (safes) sk_PKCS7_pop_free(safes, PKCS7_free); if (p8) PKCS8_PRIV_KEY_INFO_free(p8); return rval; } DLLEXPORT int CALLCONVENTION tqsl_exportPKCS12File(tQSL_Cert cert, const char *filename, const char *p12password) { tqslTrace("tqsl_exportPKCS12File", NULL); return tqsl_exportPKCS12(cert, false, filename, NULL, 0, p12password); } DLLEXPORT int CALLCONVENTION tqsl_exportPKCS12Base64(tQSL_Cert cert, char *base64, int b64len, const char *p12password) { tqslTrace("tqsl_exportPKCS12Base64", NULL); return tqsl_exportPKCS12(cert, true, NULL, base64, b64len, p12password); } static string tqsl_asn1_octet_string_to_hex(ASN1_OCTET_STRING *os) { string str; for (int k = 0; k < os->length; k++) { char hex[3] = " "; hex[0] = ((os->data[k] >> 4) & 0xf) + '0'; if (hex[0] > '9') hex[0] += 'A' - '9' - 1; hex[1] = (os->data[k] & 0xf) + '0'; if (hex[1] > '9') hex[1] += 'A' - '9' - 1; if (str.size()) str += " "; str += hex; } return str; } struct tqsl_imported_cert { string pem; string keyid; string callsign; }; static int tqsl_get_bag_attribute(PKCS12_SAFEBAG *bag, const char *oidname, string& str) { const ASN1_TYPE *attr; str = ""; if ((attr = PKCS12_get_attr(bag, OBJ_txt2nid(const_cast(oidname)))) != 0) { if (attr->type != V_ASN1_BMPSTRING) { tQSL_Error = TQSL_CERT_TYPE_ERROR; tqslTrace("tqsl_get_bag_attribute", "cert type error oid %s", oidname); return 1; } char *c = uni2asc(attr->value.bmpstring->data, attr->value.bmpstring->length); str = c; OPENSSL_free(c); } return 0; } static int tqsl_importPKCS12(bool importB64, const char *filename, const char *base64, const char *p12password, const char *password, int (*pwcb)(char *, int, void *), int(*cb)(int, const char *, void *), void *userdata) { PKCS12 *p12 = 0; PKCS12_SAFEBAG *bag; PKCS8_PRIV_KEY_INFO *p8 = 0; EVP_PKEY *pkey = 0; BIO *in = 0, *bio = 0 , *b64 = 0; STACK_OF(PKCS7) *safes = 0; STACK_OF(PKCS12_SAFEBAG) *bags = 0; PKCS7 *p7; X509 *x; BASIC_CONSTRAINTS *bs = 0; ASN1_OBJECT *callobj = 0, *obj = 0; const ASN1_TYPE *attr = 0; const EVP_CIPHER *cipher; unsigned char *cp; int i, j, bagnid, len; vector rootcerts; vector cacerts; vector usercerts; vector *certlist; vector::iterator it; bool is_cacert; string public_key, private_key, private_keyid, key_callsign, str; map key_attr; map newrecord; map::iterator mit; char path[256], pw[256]; int rval = 1; tqslTrace("tqsl_importPKCS12", NULL); if (tqsl_init()) return 1; if ((!importB64 && filename == NULL) || (importB64 && base64 == NULL)) { tqslTrace("tqsl_importPKCS12", "arg error importB64=%d filename=0x%lx base64=0x%lx", importB64, filename, base64); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } tQSL_ImportCall[0] = '\0'; tQSL_ImportSerial = 0; ImportCall[0] = '\0'; tQSL_Error = TQSL_OPENSSL_ERROR; /* Read in the PKCS#12 file */ if (importB64) { b64 = BIO_new(BIO_f_base64()); in = BIO_new_mem_buf(const_cast(base64), strlen(base64)); in = BIO_push(b64, in); } else { in = BIO_new_file(filename, "rb"); } if (in == 0) { tqslTrace("tqsl_importPKCS12", "Couldn't create bio: %s", tqsl_openssl_error()); goto imp_end; } if ((p12 = d2i_PKCS12_bio(in, 0)) == 0) { tqslTrace("tqsl_importPKCS12", "Couldn't read pkcs12: %s", tqsl_openssl_error()); goto imp_end; } BIO_free(in); in = 0; /* Verify MAC */ if (!PKCS12_verify_mac(p12, p12password, -1)) { tqslTrace("tqsl_importPKCS12", "Mac doesn't verify"); tQSL_Error = TQSL_PASSWORD_ERROR; goto imp_end; } /* Loop through the authsafes */ if ((safes = M_PKCS12_unpack_authsafes(p12)) == 0) { tqslTrace("tqsl_importPKCS12", "Can't unpack authsafe: %s", tqsl_openssl_error()); goto imp_end; } callobj = OBJ_txt2obj("AROcallsign", 0); for (i = 0; i < sk_PKCS7_num(safes); i++) { tqsl_imported_cert imported_cert; p7 = sk_PKCS7_value(safes, i); bagnid = OBJ_obj2nid(p7->type); if (bagnid == NID_pkcs7_data) { bags = PKCS12_unpack_p7data(p7); } else if (bagnid == NID_pkcs7_encrypted) { bags = PKCS12_unpack_p7encdata(p7, p12password, strlen(p12password)); } else { continue; // Not something we understand } if (!bags) { tQSL_Error = TQSL_PKCS12_ERROR; tqslTrace("tqsl_importPKCS12", "bags empty: %s", tqsl_openssl_error()); goto imp_end; } /* Loop through safebags */ for (j = 0; j < sk_PKCS12_SAFEBAG_num(bags); j++) { tqsl_imported_cert imported_cert; bag = sk_PKCS12_SAFEBAG_value(bags, j); switch (M_PKCS12_bag_type(bag)) { case NID_certBag: if (M_PKCS12_cert_bag_type(bag) != NID_x509Certificate) break; // Can't handle anything else if ((x = M_PKCS12_certbag2x509(bag)) == 0) { tqslTrace("tqsl_importPKCS12", "bag2x509: %s", tqsl_openssl_error()); goto imp_end; } if ((bio = BIO_new(BIO_s_mem())) == NULL) { tqslTrace("tqsl_importPKCS12", "bio_new: %s", tqsl_openssl_error()); goto imp_end; } if (!PEM_write_bio_X509(bio, x)) { tqslTrace("tqsl_importPKCS12", "write_bio: %s", tqsl_openssl_error()); goto imp_end; } len = static_cast(BIO_get_mem_data(bio, &cp)); imported_cert.pem = string((const char *)cp, len); if ((attr = PKCS12_get_attr(bag, NID_localKeyID)) != 0) { if (attr->type != V_ASN1_OCTET_STRING) { tqslTrace("tqsl_importPKCS12", "Cert type mismatch"); tQSL_Error = TQSL_CERT_TYPE_ERROR; goto imp_end; } imported_cert.keyid = tqsl_asn1_octet_string_to_hex(attr->value.octet_string); } BIO_free(bio); bio = 0; is_cacert = false; if ((bs = reinterpret_cast(X509_get_ext_d2i(x, NID_basic_constraints, 0, 0))) != 0) { if (bs->ca) is_cacert = true; BASIC_CONSTRAINTS_free(bs); bs = 0; } certlist = &usercerts; if (is_cacert) { if (X509_check_issued(x, x) == X509_V_OK) // Self signed must be trusted certlist = &rootcerts; else certlist = &cacerts; } else { /* Make sure the cert is TQSL compatible */ TQSL_X509_NAME_ITEM item; char nbuf[40]; char callbuf[256]; item.name_buf = nbuf; item.name_buf_size = sizeof nbuf; item.value_buf = callbuf; item.value_buf_size = sizeof callbuf; if (!tqsl_cert_get_subject_name_entry(x, "AROcallsign", &item)) { tqslTrace("tqsl_importPKCS12", "Cert type mismatch"); tQSL_Error = TQSL_CERT_TYPE_ERROR; goto imp_end; } imported_cert.callsign = callbuf; } (*certlist).push_back(imported_cert); break; case NID_pkcs8ShroudedKeyBag: if ((attr = PKCS12_get_attr(bag, NID_localKeyID)) != 0) { if (attr->type != V_ASN1_OCTET_STRING) { tQSL_Error = TQSL_CERT_TYPE_ERROR; tqslTrace("tqsl_importPKCS12", "Cert type mismatch"); goto imp_end; } private_keyid = tqsl_asn1_octet_string_to_hex(attr->value.octet_string); } if (tqsl_get_bag_attribute(bag, "AROcallsign", key_callsign)) { tqslTrace("tqsl_importPKCS12", "Cert type mismatch - no callsign"); goto imp_end; } if (tqsl_get_bag_attribute(bag, "dxccEntity", str)) { tqslTrace("tqsl_importPKCS12", "Cert type mismatch - no dxcc"); goto imp_end; } if (str != "") key_attr["TQSL_CRQ_DXCC_ENTITY"] = str; if (tqsl_get_bag_attribute(bag, "QSONotBeforeDate", str)) { tqslTrace("tqsl_importPKCS12", "Cert type mismatch - no qsonotbefore"); goto imp_end; } if (str != "") key_attr["TQSL_CRQ_QSO_NOT_BEFORE"] = str; if (tqsl_get_bag_attribute(bag, "QSONotAfterDate", str)) { tqslTrace("tqsl_importPKCS12", "Cert type mismatch - no qsonotafter"); goto imp_end; } if (str != "") key_attr["TQSL_CRQ_QSO_NOT_AFTER"] = str; if (tqsl_get_bag_attribute(bag, "tqslCRQIssuerOrganization", str)) { tqslTrace("tqsl_importPKCS12", "Cert type mismatch - no org"); goto imp_end; } if (str != "") key_attr["TQSL_CRQ_PROVIDER"] = str; if (tqsl_get_bag_attribute(bag, "tqslCRQIssuerOrganizationalUnit", str)) { tqslTrace("tqsl_importPKCS12", "Cert type mismatch - no ou"); goto imp_end; } if (str != "") key_attr["TQSL_CRQ_PROVIDER_UNIT"] = str; if (tqsl_get_bag_attribute(bag, "tqslCRQEmail", str)) { tqslTrace("tqsl_importPKCS12", "Cert type mismatch - no email"); goto imp_end; } if (str != "") key_attr["TQSL_CRQ_EMAIL"] = str; if (tqsl_get_bag_attribute(bag, "tqslCRQAddress1", str)) { tqslTrace("tqsl_importPKCS12", "Cert type mismatch - no addr1"); goto imp_end; } if (str != "") key_attr["TQSL_CRQ_ADDRESS1"] = str; if (tqsl_get_bag_attribute(bag, "tqslCRQAddress2", str)) { tqslTrace("tqsl_importPKCS12", "Cert type mismatch - no addr2"); goto imp_end; } if (str != "") key_attr["TQSL_CRQ_ADDRESS2"] = str; if (tqsl_get_bag_attribute(bag, "tqslCRQCity", str)) { tqslTrace("tqsl_importPKCS12", "Cert type mismatch - no city"); goto imp_end; } if (str != "") key_attr["TQSL_CRQ_CITY"] = str; if (tqsl_get_bag_attribute(bag, "tqslCRQState", str)) { tqslTrace("tqsl_importPKCS12", "Cert type mismatch - no state"); goto imp_end; } if (str != "") key_attr["TQSL_CRQ_STATE"] = str; if (tqsl_get_bag_attribute(bag, "tqslCRQPostal", str)) { tqslTrace("tqsl_importPKCS12", "Cert type mismatch - no postal"); goto imp_end; } if (str != "") key_attr["TQSL_CRQ_POSTAL"] = str; if (tqsl_get_bag_attribute(bag, "tqslCRQCountry", str)) { tqslTrace("tqsl_importPKCS12", "Cert type mismatch - no country"); goto imp_end; } if (str != "") key_attr["TQSL_CRQ_COUNTRY"] = str; if ((p8 = M_PKCS12_decrypt_skey(bag, p12password, strlen(p12password))) == 0) { tqslTrace("tqsl_importPKCS12", "password error"); goto imp_end; } if ((pkey = EVP_PKCS82PKEY(p8)) == 0) { tqslTrace("tqsl_importPKCS12", "pkey error"); goto imp_end; } if ((bio = BIO_new(BIO_s_mem())) == NULL) { tqslTrace("tqsl_importPKCS12", "bio_new error %s", tqsl_openssl_error()); goto imp_end; } if (password == 0) { if (pwcb) { if ((*pwcb)(pw, sizeof pw -1, userdata)) { tqslTrace("tqsl_importPKCS12", "operator aborted at password prompt"); tQSL_Error = TQSL_OPERATOR_ABORT; goto imp_end; } password = pw; } else { password = NULL; } } if (password && *password != '\0') { cipher = EVP_des_ede3_cbc(); len = strlen(password); } else { cipher = 0; len = 0; } if (!PEM_write_bio_PrivateKey(bio, pkey, cipher, (unsigned char *)password, len, 0, 0)) { tqslTrace("tqsl_importPKCS12", "writing bio err: %s", tqsl_openssl_error()); goto imp_end; } len = static_cast(BIO_get_mem_data(bio, &cp)); private_key = string((const char *)cp, len); BIO_free(bio); if ((bio = BIO_new(BIO_s_mem())) == NULL) { tqslTrace("tqsl_importPKCS12", "new bio err: %s", tqsl_openssl_error()); goto imp_end; } if (!PEM_write_bio_PUBKEY(bio, pkey)) { tqslTrace("tqsl_importPKCS12", "write pubkey bio err: %s", tqsl_openssl_error()); goto imp_end; } len = static_cast(BIO_get_mem_data(bio, &cp)); public_key = string((const char *)cp, len); BIO_free(bio); bio = 0; EVP_PKEY_free(pkey); pkey = 0; PKCS8_PRIV_KEY_INFO_free(p8); p8 = 0; break; case NID_keyBag: tqslTrace("tqsl_importPKCS12", "cert type err: NID_keyBag"); tQSL_Error = TQSL_CERT_TYPE_ERROR; goto imp_end; } // bag type switch } // safebags loop sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free); bags = 0; } sk_PKCS7_pop_free(safes, PKCS7_free); safes = 0; /* Now we have the certificates and key pair, so add them to the local store */ tqslTrace("tqsl_importPKCS12", "have keys"); if (key_callsign == "") { /* Need to get call sign from cert. The tQSL_exportKeys function puts the * call sign in a safebag attribute that should have been read already, * but if some other software created the PKCS#12 file that may not have * happened. There should, however, be a localKeyID attribute that matches * the key to the certificate. If not, it's an error. */ if (private_keyid == "") { // No key ID, can't find cert tqslTrace("tqsl_importPKCS12", "no callsign attribute"); tQSL_Error = TQSL_CERT_TYPE_ERROR; goto imp_end; } for (it = usercerts.begin(); it != usercerts.end(); it++) { if ((*it).keyid == private_keyid) { key_callsign = (*it).callsign; break; } } if (key_callsign == "") { // Can't find cert or callsign tqslTrace("tqsl_importPKCS12", "can't find cert or callsign"); tQSL_Error = TQSL_CERT_TYPE_ERROR; goto imp_end; } } if (!tqsl_make_key_path(key_callsign.c_str(), path, sizeof path)) { tqslTrace("tqsl_importPKCS12", "keypath error %d", tQSL_Error); goto imp_end; } newrecord["PUBLIC_KEY"] = public_key; newrecord["PRIVATE_KEY"] = private_key; newrecord["CALLSIGN"] = key_callsign; for (mit = key_attr.begin(); mit != key_attr.end(); mit++) newrecord[mit->first] = mit->second; if (tqsl_replace_key(key_callsign.c_str(), path, newrecord, cb, userdata)) { tqslTrace("tqsl_importPKCS12", "replace key error %d", tQSL_Error); goto imp_end; } for (it = rootcerts.begin(); it != rootcerts.end(); it++) { if (tqsl_import_cert(it->pem.c_str(), tqsllib::ROOTCERT, cb, userdata) && tQSL_Error != TQSL_CERT_ERROR) { tqslTrace("tqsl_importPKCS12", "import root cert error %d", tQSL_Error); goto imp_end; } } for (it = cacerts.begin(); it != cacerts.end(); it++) { if (tqsl_import_cert(it->pem.c_str(), tqsllib::CACERT, cb, userdata) && tQSL_Error != TQSL_CERT_ERROR) { tqslTrace("tqsl_importPKCS12", "import ca cert error %d", tQSL_Error); goto imp_end; } } rval = 0; // Assume no errors for (it = usercerts.begin(); it != usercerts.end(); it++) { if (tqsl_import_cert(it->pem.c_str(), tqsllib::USERCERT, cb, userdata)) { if (tQSL_Error == TQSL_CERT_ERROR) { rval = 1; // Remember failure to import continue; } char savepath[1024], badpath[1024]; strncpy(badpath, path, sizeof(badpath)); strncat(badpath, ".bad", sizeof(badpath)-strlen(badpath)-1); badpath[sizeof(badpath)-1] = '\0'; #ifdef _WIN32 wchar_t* wpath = utf8_to_wchar(path); wchar_t* wbadpath = utf8_to_wchar(badpath); wchar_t* wsavepath = NULL; if (!_wrename(wpath, wbadpath)) { #else if (!rename(path, badpath)) { #endif strncpy(savepath, path, sizeof(savepath)); strncat(savepath, ".save", sizeof(savepath)-strlen(savepath)-1); savepath[sizeof(savepath)-1] = '\0'; #ifdef _WIN32 wsavepath = utf8_to_wchar(savepath); if (_wrename(wsavepath, wpath)) // didn't work _wrename(wbadpath, wpath); #else if (rename(savepath, path)) // didn't work rename(badpath, path); #endif else #ifdef _WIN32 { _wunlink(wbadpath); free_wchar(wpath); free_wchar(wbadpath); if (wsavepath) free_wchar(wsavepath); } #else unlink(badpath); #endif } goto imp_end; } } if (rval == 0) { tQSL_Error = TQSL_NO_ERROR; strncpy(tQSL_ImportCall, ImportCall, sizeof tQSL_ImportCall); } else { if (tQSL_Error == 0) { tQSL_Error = TQSL_CERT_ERROR; } } imp_end: if (p12) PKCS12_free(p12); if (in) BIO_free(in); if (bags) sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free); if (safes) sk_PKCS7_pop_free(safes, PKCS7_free); if (obj) ASN1_OBJECT_free(obj); if (callobj) ASN1_OBJECT_free(callobj); if (bs) BASIC_CONSTRAINTS_free(bs); if (p8) PKCS8_PRIV_KEY_INFO_free(p8); if (pkey) EVP_PKEY_free(pkey); return rval; } DLLEXPORT int CALLCONVENTION tqsl_importPKCS12File(const char *filename, const char *p12password, const char *password, int (*pwcb)(char *, int, void *), int(*cb)(int, const char *, void *), void *userdata) { tqslTrace("tqsl_importPKCS12File", NULL); return tqsl_importPKCS12(false, filename, NULL, p12password, password, pwcb, cb, userdata); } DLLEXPORT int CALLCONVENTION tqsl_importPKCS12Base64(const char *base64, const char *p12password, const char *password, int (*pwcb)(char *, int, void *), int(*cb)(int, const char *, void *), void *userdata) { tqslTrace("tqsl_importPKCS12Base64", NULL); return tqsl_importPKCS12(true, NULL, base64, p12password, password, pwcb, cb, userdata); } static int tqsl_backup_cert(tQSL_Cert cert) { char callsign[64]; long serial = 0; int dxcc = 0; int keyonly; tqsl_getCertificateKeyOnly(cert, &keyonly); tqsl_getCertificateCallSign(cert, callsign, sizeof callsign); if (!keyonly) tqsl_getCertificateSerial(cert, &serial); tqsl_getCertificateDXCCEntity(cert, &dxcc); #ifndef PATH_MAX #define PATH_MAX 1024 #endif char backupPath[PATH_MAX]; tqsl_make_backup_path(callsign, backupPath, sizeof backupPath); FILE* out = NULL; #ifdef _WIN31 wchar_t* wpath = utf8_to_wchar(backupPath); _wunlink(wpath); fd = _wfopen(lfn, L"wb"); free_wchar(wpath); #else unlink(backupPath); out = fopen(backupPath, "wb"); #endif if (!out) { tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; strncpy(tQSL_ErrorFile, backupPath, sizeof tQSL_ErrorFile); tQSL_ErrorFile[sizeof tQSL_ErrorFile-1] = 0; tqslTrace("tqsl_backup_cert", "Error %d errno %d file %s", tQSL_Error, tQSL_Errno, backupPath); return 1; } char buf[8192]; fprintf(out, "\n", callsign, dxcc, serial); if (!keyonly) { fprintf(out, "\n"); tqsl_getCertificateEncoded(cert, buf, sizeof buf); fprintf(out, "%s", buf); fprintf(out, "\n"); } fprintf(out, "\n"); tqsl_getKeyEncoded(cert, buf, sizeof buf); fprintf(out, "%s", buf); fprintf(out, "\n\n"); fclose(out); return 0; } static int tqsl_make_backup_list(const char* filter, vector& keys) { keys.clear(); string path = tQSL_BaseDir; #ifdef _WIN32 path += "\\certtrash"; wchar_t* wpath = utf8_to_wchar(path.c_str()); MKDIR(wpath, 0700); #else path += "/certtrash"; MKDIR(path.c_str(), 0700); #endif #ifdef _WIN32 WDIR *dir = wopendir(wpath); free_wchar(wpath); #else DIR *dir = opendir(path.c_str()); #endif if (dir == NULL) { tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_make_backup_list", "Opendir %s error %s", path.c_str(), strerror(errno)); return 1; } #ifdef _WIN32 struct wdirent *ent; #else struct dirent *ent; #endif int rval = 0; int savedError = 0; int savedErrno = 0; char *savedFile = NULL; #ifdef _WIN32 while ((ent = wreaddir(dir)) != NULL) { if (ent->d_name[0] == '.') continue; char dname[TQSL_MAX_PATH_LEN]; wcstombs(dname, ent->d_name, TQSL_MAX_PATH_LEN); string filename = path + "\\" + dname; struct _stat32 s; wchar_t* wfilename = utf8_to_wchar(filename.c_str()); if (_wstat32(wfilename, &s) == 0) { if (S_ISDIR(s.st_mode)) { free_wchar(wfilename); continue; // If it's a directory, skip it. } } #else while ((ent = readdir(dir)) != NULL) { if (ent->d_name[0] == '.') continue; string filename = path + "/" + ent->d_name; struct stat s; if (stat(filename.c_str(), &s) == 0) { if (S_ISDIR(s.st_mode)) continue; // If it's a directory, skip it. } #endif XMLElement xel; int status = xel.parseFile(filename.c_str()); if (status) continue; // Can't be parsed XMLElement cert; xel.getFirstElement(cert); pair atrval = cert.getAttribute("CallSign"); if (atrval.second) { // If the callsign matches, or if the filter is empty, add it. if (filter == NULL || atrval.first == filter) { keys.push_back(atrval.first); } } } #ifdef _WIN32 _wclosedir(dir); #else closedir(dir); #endif if (rval) { tQSL_Error = savedError; tQSL_Errno = savedErrno; if (savedFile) { strncpy(tQSL_ErrorFile, savedFile, sizeof tQSL_ErrorFile); free(savedFile); } tqslTrace("tqsl_make_backup_list", "error %s %s", tQSL_ErrorFile, strerror(tQSL_Errno)); } return rval; } DLLEXPORT int CALLCONVENTION tqsl_deleteCertificate(tQSL_Cert cert) { tqslTrace("tqsl_deleteCertificate", NULL); if (tqsl_init()) return 1; if (cert == NULL || !tqsl_cert_check(TQSL_API_TO_CERT(cert), false)) { tqslTrace("tqsl_deleteCertificate", "arg err cert=0x%lx", cert); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } tqsl_backup_cert(cert); char callsign[256], path[256], newpath[256]; if (tqsl_getCertificateCallSign(cert, callsign, sizeof callsign)) { tqslTrace("tqsl_deleteCertificate", "no callsign %d", tQSL_Error); return 1; } int rval = 1; EVP_PKEY *key = 0; BIO *bio = 0; tQSL_Error = TQSL_OPENSSL_ERROR; // Delete private key map rec; if (TQSL_API_TO_CERT(cert)->pubkey) { rec["PUBLIC_KEY"] = TQSL_API_TO_CERT(cert)->pubkey; } else { // Get public key from cert if ((key = X509_get_pubkey(TQSL_API_TO_CERT(cert)->cert)) == 0) { tqslTrace("tqsl_deleteCertificate", "no public key %s", tqsl_openssl_error()); goto dc_end; } if ((bio = BIO_new(BIO_s_mem())) == NULL) { tqslTrace("tqsl_deleteCertificate", "bio err %s", tqsl_openssl_error()); goto dc_end; } if (!PEM_write_bio_PUBKEY(bio, key)) { tqslTrace("tqsl_deleteCertificate", "bio write err %s", tqsl_openssl_error()); goto dc_end; } char *cp; int len = static_cast(BIO_get_mem_data(bio, &cp)); rec["PUBLIC_KEY"] = string(cp, len); BIO_free(bio); bio = 0; EVP_PKEY_free(key); key = 0; } rec["CALLSIGN"] = callsign; if (!tqsl_make_key_path(callsign, path, sizeof path)) { tqslTrace("tqsl_deleteCertificate", "key path err %s", tQSL_Error); goto dc_end; } // Since there is no private key in "rec," tqsl_replace_key will just remove the // existing private key. tqsl_replace_key(callsign, path, rec, 0, 0); if (TQSL_API_TO_CERT(cert)->keyonly) { tqslTrace("tqsl_deleteCertificate", "key only"); goto dc_ok; } // Now the certificate tqsl_make_cert_path("user", path, sizeof path); tqsl_make_cert_path("user.new", newpath, sizeof newpath); if (xcerts == NULL) { if ((xcerts = tqsl_ssl_load_certs_from_file(path)) == 0) { tqslTrace("tqsl_deleteCertificate", "error reading certs %d", tQSL_Error); goto dc_end; } } if ((bio = BIO_new_file(newpath, "wb")) == 0) { tqslTrace("tqsl_deleteCertificate", "bio_new_file %s", tqsl_openssl_error()); goto dc_end; } X509 *x; while ((x = sk_X509_shift(xcerts)) != 0) { if (X509_issuer_and_serial_cmp(x, TQSL_API_TO_CERT(cert)->cert)) { if (!PEM_write_bio_X509(bio, x)) { // No match -- keep this one tqslTrace("tqsl_deleteCertificate", "pem_write_bio %s", tqsl_openssl_error()); goto dc_end; } } } BIO_free(bio); bio = 0; // Looks like the new file is okay, commit it #ifdef _WIN32 wchar_t* wpath = utf8_to_wchar(path); if (_wunlink(wpath) && errno != ENOENT) { free_wchar(wpath); #else if (unlink(path) && errno != ENOENT) { #endif tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_deleteCertificate", "unlink err %d", errno); goto dc_end; } #ifdef _WIN32 wchar_t* wnewpath = utf8_to_wchar(newpath); if (_wrename(wnewpath, wpath)) { free_wchar(wpath); free_wchar(wnewpath); #else if (rename(newpath, path)) { #endif tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_deleteCertificate", "rename err %d", errno); goto dc_end; } #ifdef _WIN32 free_wchar(wpath); free_wchar(wnewpath); #endif dc_ok: rval = 0; tQSL_Error = TQSL_NO_ERROR; dc_end: if (xcerts) { sk_X509_free(xcerts); xcerts = NULL; } if (key) EVP_PKEY_free(key); if (bio) BIO_free(bio); return rval; } /** Get the list of restorable callsign certificates. */ DLLEXPORT int CALLCONVENTION tqsl_getDeletedCallsignCertificates(char ***calls, int *ncall, const char *filter) { vector callsigns; if (tqsl_make_backup_list(filter, callsigns)) { return 1; } *ncall = callsigns.size(); if (*ncall == 0) { if (calls) { *calls = NULL; } return 0; } if (calls == NULL) { return 0; } *calls = reinterpret_cast(calloc(*ncall, sizeof(**calls))); vector::iterator it; char **p = *calls; for (it = callsigns.begin(); it != callsigns.end(); it++) { *p++ = strdup((*it).c_str()); } return 0; } DLLEXPORT void CALLCONVENTION tqsl_freeDeletedCertificateList(char **list, int nloc) { if (!list) return; for (int i = 0; i < nloc; i++) if (list[i]) free(list[i]); if (list) free(list); } /** Restore a deleted callsign certificate by callsign. */ DLLEXPORT int CALLCONVENTION tqsl_restoreCallsignCertificate(const char *callsign) { tqslTrace("tqsl_restoreCallsignCertificate", "callsign = %s", callsign); char backupPath[PATH_MAX]; tqsl_make_backup_path(callsign, backupPath, sizeof backupPath); XMLElement xel; int status = xel.parseFile(backupPath); if (status) { if (errno == ENOENT) { // No file is OK tqslTrace("tqsl_restoreCallsignCertificate", "FNF"); return 0; } strncpy(tQSL_ErrorFile, backupPath, sizeof tQSL_ErrorFile); if (status == XML_PARSE_SYSTEM_ERROR) { tQSL_Error = TQSL_FILE_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_restoreCallsignCertificate", "open error %s: %s", backupPath, strerror(tQSL_Errno)); } else { tqslTrace("tqsl_restoreCallsignCertificate", "syntax error %s", backupPath); tQSL_Error = TQSL_FILE_SYNTAX_ERROR; } return 1; } XMLElement cert; string call; int dxcc = 0; long serial = 0; string privateKey; string publicKey; XMLElementList& ellist = xel.getElementList(); XMLElementList::iterator ep; for (ep = ellist.find("UserCert"); ep != ellist.end(); ep++) { if (ep->first != "UserCert") break; pair rval = ep->second->getAttribute("CallSign"); if (rval.second) call = rval.first; rval = ep->second->getAttribute("serial"); if (rval.second) serial = strtol(rval.first.c_str(), NULL, 10); rval = ep->second->getAttribute("dxcc"); if (rval.second) dxcc = strtol(rval.first.c_str(), NULL, 10); XMLElement el; if (ep->second->getFirstElement("SignedCert", el)) { publicKey = el.getText(); } if (ep->second->getFirstElement("PrivateKey", el)) { privateKey = el.getText(); } } // See if this certificate exists tQSL_Cert *certlist; int ncerts; tqsl_selectCertificates(&certlist, &ncerts, call.c_str(), dxcc, 0, 0, TQSL_SELECT_CERT_EXPIRED|TQSL_SELECT_CERT_SUPERCEDED|TQSL_SELECT_CERT_WITHKEYS); for (int i = 0; i < ncerts; i++) { long s = 0; int keyonly = false; tqsl_getCertificateKeyOnly(certlist[i], &keyonly); if (keyonly) { if (serial != 0) { // A full cert for this was imported continue; } } if (tqsl_getCertificateSerial(certlist[i], &s)) { continue; } if (s == serial) { // Already imported tqsl_freeCertificateList(certlist, ncerts); tQSL_Error = TQSL_CUSTOM_ERROR; strncpy(tQSL_CustomError, "This callsign certificate is already active and cannot be restored.", sizeof tQSL_CustomError); tqslTrace("tqsl_restoreCallsignCertificate", "certificate already exists"); return 1; } } tqsl_freeCertificateList(certlist, ncerts); int stat = tqsl_importKeyPairEncoded(call.c_str(), "user", privateKey.c_str(), publicKey.c_str()); if (!stat) { // Remove the backup file #ifdef _WIN32 wchar_t* wbpath = utf8_to_wchar(backupPath); _wunlink(wbpath); free_wchar(wbpath); #else unlink(backupPath); #endif } return stat; } /********** END OF PUBLIC API FUNCTIONS **********/ /* Utility functions to manage private data structures */ static int tqsl_check_crq_field(tQSL_Cert cert, char *buf, int bufsiz) { if (tqsl_init()) return 1; if (cert == NULL || buf == NULL || bufsiz < 0 || !tqsl_cert_check(TQSL_API_TO_CERT(cert))) { tqslTrace("tqsl_check_crq_field", "arg err cert=0x%lx buf=0x%lx bufsiz=%d", cert, buf, bufsiz); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (TQSL_API_TO_CERT(cert)->crq == NULL) { if (tqsl_find_matching_key(TQSL_API_TO_CERT(cert)->cert, NULL, &(TQSL_API_TO_CERT(cert)->crq), "", NULL, NULL) && tQSL_Error != TQSL_PASSWORD_ERROR) { tqslTrace("tqsl_check_crq_field", "can't find matching key err %d", tQSL_Error); return 1; } } return 0; } static int tqsl_cert_check(tqsl_cert *p, bool needcert) { if (p != NULL && p->id == 0xCE && (!needcert || p->cert != NULL)) return 1; tQSL_Error = TQSL_ARGUMENT_ERROR; return 0; } static tqsl_cert * tqsl_cert_new() { tqsl_cert *p; p = reinterpret_cast(tqsl_calloc(1, sizeof(tqsl_cert))); if (p != NULL) p->id = 0xCE; return p; } static void tqsl_cert_free(tqsl_cert *p) { if (p == NULL || p->id != 0xCE) return; p->id = 0; if (p->cert != NULL) X509_free(p->cert); if (p->key != NULL) EVP_PKEY_free(p->key); if (p->crq != NULL) tqsl_free_cert_req(p->crq, 0); if (p->pubkey != NULL) delete[] p->pubkey; if (p->privkey != NULL) delete[] p->privkey; tqsl_free(p); } static TQSL_CERT_REQ * tqsl_free_cert_req(TQSL_CERT_REQ *req, int seterr) { if (req == NULL) return NULL; tqsl_free(req); if (seterr) tQSL_Error = seterr; return NULL; } static TQSL_CERT_REQ * tqsl_copy_cert_req(TQSL_CERT_REQ *userreq) { TQSL_CERT_REQ *req; if ((req = reinterpret_cast(tqsl_calloc(1, sizeof(TQSL_CERT_REQ)))) == NULL) { tqslTrace("tqsl_copy_cert_req", "ENOMEM"); errno = ENOMEM; return NULL; } *req = *userreq; return req; } static int tqsl_check_parm(const char *p, const char *parmName) { if (strlen(p) == 0) { tQSL_Error = TQSL_CUSTOM_ERROR; snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "Missing parameter: %s", parmName); tqslTrace("tqsl_check_parm", "error %s", tQSL_CustomError); return 1; } return 0; } static char * tqsl_trim(char *buf) { char lastc; char *cp, *op; /* Trim white space off end of string */ cp = buf + strlen(buf); while (cp != buf) { cp--; if (!isspace(*cp)) break; *cp = 0; } /* Skip past leading whitespace */ for (cp = buf; isspace(*cp); cp++) {} /* Fold runs of white space into single space */ lastc = 0; op = buf; for (; *cp != '\0'; cp++) { if (isspace(*cp)) *cp = ' '; if (*cp != ' ' || lastc != ' ') *op++ = *cp; lastc = *cp; } *op = '\0'; return cp; } /* Filter a list (stack) of X509 certs based on call sign, QSO date and/or * issuer. * * Returns a new stack of matching certs without altering the original stack. * * Note that you don't have to supply any of the criteria. If you supply * none, you;ll just get back an exact copy of the stack. */ CLIENT_STATIC STACK_OF(X509) * tqsl_filter_cert_list(STACK_OF(X509) *sk, const char *callsign, int dxcc, const tQSL_Date *date, const TQSL_PROVIDER *issuer, int flags) { set superceded_certs; char buf[256], name_buf[256]; TQSL_X509_NAME_ITEM item; X509 *x; STACK_OF(X509) *newsk; int i, ok, len; tQSL_Date qso_date; tqslTrace("tqsl_filter_cert_list", NULL); if (tqsl_init()) return NULL; if ((newsk = sk_X509_new_null()) == NULL) { tqslTrace("tqsl_filter_cert_list", "sk_X509_new_null err %s", tqsl_openssl_error()); return NULL; } tqsl_cert* cp = tqsl_cert_new(); /* Loop through the list of certs */ for (i = 0; i < sk_X509_num(sk); i++) { ok = 1; /* Certificate is selected unless some check says otherwise */ x = sk_X509_value(sk, i); cp->cert = x; /* Check for expired unless asked not to */ if (ok && !(flags & TQSL_SELECT_CERT_EXPIRED)) { int exp = false; if (!tqsl_isCertificateExpired(TQSL_OBJ_TO_API(cp), &exp)) { if (exp) { ok = 0; } } } /* Check for superceded unless asked not to */ if (ok && !(flags & TQSL_SELECT_CERT_SUPERCEDED)) { int sup = false; if (!tqsl_isCertificateSuperceded(TQSL_OBJ_TO_API(cp), &sup)) { if (sup) { ok = 0; } } } /* Compare issuer if asked to */ if (ok && issuer != NULL) { X509_NAME *iss; if ((iss = X509_get_issuer_name(x)) == NULL) ok = 0; if (ok) { item.name_buf = name_buf; item.name_buf_size = sizeof name_buf; item.value_buf = buf; item.value_buf_size = sizeof buf; tqsl_get_name_entry(iss, "organizationName", &item); ok = !strcmp(issuer->organizationName, item.value_buf); } if (ok) { item.name_buf = name_buf; item.name_buf_size = sizeof name_buf; item.value_buf = buf; item.value_buf_size = sizeof buf; tqsl_get_name_entry(iss, "organizationalUnitName", &item); ok = !strcmp(issuer->organizationalUnitName, item.value_buf); } } /* Check call sign if asked */ if (ok && callsign != NULL) { item.name_buf = name_buf; item.name_buf_size = sizeof name_buf; item.value_buf = buf; item.value_buf_size = sizeof buf; if (!tqsl_cert_get_subject_name_entry(x, "AROcallsign", &item)) ok = 0; else ok = !strcmp(callsign, item.value_buf); } /* Check DXCC entity if asked */ if (ok && dxcc > 0) { len = sizeof buf-1; if (tqsl_get_cert_ext(x, "dxccEntity", (unsigned char *)buf, &len, NULL)) { ok = 0; } else { buf[len] = 0; if (dxcc != strtol(buf, NULL, 10)) ok = 0; } } /* Check QSO date if asked */ if (ok && date != NULL && !tqsl_isDateNull(date)) { len = sizeof buf-1; if (tqsl_get_cert_ext(x, "QSONotBeforeDate", (unsigned char *)buf, &len, NULL)) ok = 0; else if (tqsl_initDate(&qso_date, buf)) ok = 0; else if (tqsl_compareDates(date, &qso_date) < 0) ok = 0; } if (ok && date != NULL && !tqsl_isDateNull(date)) { len = sizeof buf-1; if (tqsl_get_cert_ext(x, "QSONotAfterDate", (unsigned char *)buf, &len, NULL)) ok = 0; else if (tqsl_initDate(&qso_date, buf)) ok = 0; else if (tqsl_compareDates(date, &qso_date) > 0) ok = 0; } /* If no check failed, copy this cert onto the new stack */ if (ok) sk_X509_push(newsk, X509_dup(x)); } tqsl_free(cp); return newsk; } /* Set up a read-only BIO from the given file and pass to * tqsl_ssl_load_certs_from_BIO. */ CLIENT_STATIC STACK_OF(X509) * tqsl_ssl_load_certs_from_file(const char *filename) { BIO *in; STACK_OF(X509) *sk; FILE *cfile; tqslTrace("tqsl_ssl_load_certs_from_file", "filename=%s", filename); #ifdef _WIN32 wchar_t* wfilename = utf8_to_wchar(filename); if ((cfile = _wfopen(wfilename, L"r")) == NULL) { free_wchar(wfilename); #else if ((cfile = fopen(filename, "r")) == NULL) { #endif strncpy(tQSL_ErrorFile, filename, sizeof tQSL_ErrorFile); tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_ssl_load_certs_from_file", "File open error %s", strerror(errno)); return NULL; } #ifdef _WIN32 free(wfilename); #endif if ((in = BIO_new_fp(cfile, 0)) == NULL) { tQSL_Error = TQSL_OPENSSL_ERROR; tqslTrace("tqsl_ssl_load_certs_from_file", "bio_new_fp err %s", tqsl_openssl_error()); return NULL; } sk = tqsl_ssl_load_certs_from_BIO(in); BIO_free(in); fclose(cfile); return sk; } /* Load a set of certs from a file into a stack. The file may contain * other X509 objects (e.g., CRLs), but we'll ignore those. * * Return NULL if there are no certs in the file or on error. */ CLIENT_STATIC STACK_OF(X509) * tqsl_ssl_load_certs_from_BIO(BIO *in) { STACK_OF(X509_INFO) *sk = NULL; STACK_OF(X509) *stack = NULL; X509_INFO *xi; if (tqsl_init()) return NULL; if (!(stack = sk_X509_new_null())) { tqslTrace("tqsl_ssl_load_certs_from_BIO", "bio_new_fp err %s", tqsl_openssl_error()); tQSL_Error = TQSL_OPENSSL_ERROR; return NULL; } if (!(sk = PEM_X509_INFO_read_bio(in, NULL, NULL, NULL))) { sk_X509_free(stack); tqslTrace("tqsl_ssl_load_certs_from_BIO", "PEM_X509_INFO_read_bio err %s", tqsl_openssl_error()); tQSL_Error = TQSL_OPENSSL_ERROR; return NULL; } /* Extract the certs from the X509_INFO objects and put them on a stack */ while (sk_X509_INFO_num(sk)) { xi = sk_X509_INFO_shift(sk); if (xi->x509 != NULL) { sk_X509_push(stack, xi->x509); xi->x509 = NULL; } X509_INFO_free(xi); } /* Empty file isn't really an error, is it? if(!sk_X509_num(stack)) { sk_X509_free(stack); stack = NULL; strcpy(tQSL_CustomError, "No certificates found"); tQSL_Error = TQSL_CUSTOM_ERROR; } */ sk_X509_INFO_free(sk); return stack; } /* Chain-verify a cert against a set of CA and a set of trusted root certs. * * Returns NULL if cert verifies, an error message if it does not. */ CLIENT_STATIC const char * tqsl_ssl_verify_cert(X509 *cert, STACK_OF(X509) *cacerts, STACK_OF(X509) *rootcerts, int purpose, int (*cb)(int ok, X509_STORE_CTX *ctx), STACK_OF(X509) **chain) { X509_STORE *store; X509_STORE_CTX *ctx; int rval; const char *errm; if (cert == NULL) { tqslTrace("tqsl_ssl_verify_cert", "No certificate to verify"); return "No certificate to verify"; } if (tqsl_init()) return NULL; store = X509_STORE_new(); if (store == NULL) { tqslTrace("tqsl_ssl_verify_cert", "out of memory"); return "Out of memory"; } if (cb != NULL) X509_STORE_set_verify_cb(store, cb); ctx = X509_STORE_CTX_new(); if (ctx == NULL) { X509_STORE_free(store); tqslTrace("tqsl_ssl_verify_cert", "store_ctx_new out of memory"); return "Out of memory"; } X509_STORE_CTX_init(ctx, store, cert, cacerts); if (cb != NULL) X509_STORE_CTX_set_verify_cb(ctx, cb); if (rootcerts) X509_STORE_CTX_trusted_stack(ctx, rootcerts); if (purpose >= 0) X509_STORE_CTX_set_purpose(ctx, purpose); X509_STORE_CTX_set_flags(ctx, X509_V_FLAG_CB_ISSUER_CHECK); rval = X509_verify_cert(ctx); errm = X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx)); #if OPENSSL_VERSION_NUMBER < 0x10100000L #define X509_STORE_CTX_get0_chain(o) ((o)->chain) #endif if (chain) { if (rval && X509_STORE_CTX_get0_chain(ctx)) *chain = sk_X509_dup(X509_STORE_CTX_get0_chain(ctx)); else *chain = 0; } X509_STORE_CTX_free(ctx); if (rval) return NULL; if (errm != NULL) { tqslTrace("tqsl_ssl_verify_cert", "err %s", errm); return errm; } return "Verification failed"; } /* [static] - Grab the data from an X509_NAME_ENTRY and put it into * a TQSL_X509_NAME_ITEM object, checking buffer sizes. * * Returns 0 on error, 1 if okay. * * It's okay for the name_buf or value_buf item of the object to * be NULL; it'll just be skipped. */ static int tqsl_get_name_stuff(X509_NAME_ENTRY *entry, TQSL_X509_NAME_ITEM *name_item) { ASN1_OBJECT *obj; ASN1_STRING *value; const char *val; unsigned int len; if (entry == NULL) { tqslTrace("tqsl_get_name_stuff", "entry=null"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 0; } obj = X509_NAME_ENTRY_get_object(entry); if (obj == NULL) { tqslTrace("tqsl_get_name_stuff", "get_object err %s", tqsl_openssl_error()); tQSL_Error = TQSL_OPENSSL_ERROR; return 0; } if (name_item->name_buf != NULL) { len = i2t_ASN1_OBJECT(name_item->name_buf, name_item->name_buf_size, obj); if (len <= 0 || len > strlen(name_item->name_buf)) { tqslTrace("tqsl_get_name_stuff", "len error len= %d need=%d", len, strlen(name_item->name_buf)); tQSL_Error = TQSL_OPENSSL_ERROR; return 0; } } if (name_item->value_buf != NULL) { value = X509_NAME_ENTRY_get_data(entry); val = (const char *)ASN1_STRING_get0_data(value); strncpy(name_item->value_buf, val, name_item->value_buf_size); name_item->value_buf[name_item->value_buf_size-1] = '\0'; if (strlen(val) > strlen(name_item->value_buf)) { tqslTrace("tqsl_get_name_stuff", "len error len= %d need=%d", strlen(val), strlen(name_item->value_buf)); tQSL_Error = TQSL_OPENSSL_ERROR; return 0; } } return 1; } /* Get a name entry from an X509_NAME by its name. */ CLIENT_STATIC int tqsl_get_name_entry(X509_NAME *name, const char *obj_name, TQSL_X509_NAME_ITEM *name_item) { X509_NAME_ENTRY *entry; int num_entries, i; if (tqsl_init()) return 0; num_entries = X509_NAME_entry_count(name); if (num_entries <= 0) return 0; /* Loop through the name entries */ for (i = 0; i < num_entries; i++) { entry = X509_NAME_get_entry(name, i); if (!tqsl_get_name_stuff(entry, name_item)) continue; if (name_item->name_buf != NULL && !strcmp(name_item->name_buf, obj_name)) { /* Found the wanted entry */ return 1; } } return 0; } /* Get a name entry from a cert's subject name by its name. */ CLIENT_STATIC int tqsl_cert_get_subject_name_entry(X509 *cert, const char *obj_name, TQSL_X509_NAME_ITEM *name_item) { X509_NAME *name; if (cert == NULL) return 0; if (tqsl_init()) return 0; if ((name = X509_get_subject_name(cert)) == NULL) return 0; return tqsl_get_name_entry(name, obj_name, name_item); } /* Initialize the tQSL (really OpenSSL) random number generator * Return 0 on error. */ CLIENT_STATIC int tqsl_init_random() { char fname[256]; static int initialized = 0; if (initialized) return 1; if (RAND_file_name(fname, sizeof fname) != NULL) RAND_load_file(fname, -1); initialized = RAND_status(); if (!initialized) { tqslTrace("tqsl_init_random", "init error %s", tqsl_openssl_error()); tQSL_Error = TQSL_RANDOM_ERROR; } return initialized; } /* Generate an RSA key of at least 1024 bits length */ CLIENT_STATIC EVP_PKEY * tqsl_new_rsa_key(int nbits) { EVP_PKEY *pkey; if (nbits < 1024) { tqslTrace("tqsl_new_rsa_key", "nbits too small %d", nbits); tQSL_Error = TQSL_ARGUMENT_ERROR; return NULL; } if ((pkey = EVP_PKEY_new()) == NULL) { tqslTrace("tqsl_new_rsa_key", "EVP_PKEY_new err %s", tqsl_openssl_error()); tQSL_Error = TQSL_OPENSSL_ERROR; return NULL; } if (!tqsl_init_random()) /* Unable to init RN generator */ return NULL; RSA *rsa = RSA_new(); if (rsa == NULL) { EVP_PKEY_free(pkey); tqslTrace("tqsl_new_rsa_key", "RSA_new err %s", tqsl_openssl_error()); tQSL_Error = TQSL_OPENSSL_ERROR; return NULL; } BIGNUM *pubexp = BN_new(); if (pubexp == NULL) { EVP_PKEY_free(pkey); RSA_free(rsa); tqslTrace("tqsl_new_rsa_key", "BN_new err %s", tqsl_openssl_error()); tQSL_Error = TQSL_OPENSSL_ERROR; return NULL; } if (BN_set_word(pubexp, 0x10001) != 1) { EVP_PKEY_free(pkey); RSA_free(rsa); BN_free(pubexp); tqslTrace("tqsl_new_rsa_key", "BN_set_word err %s", tqsl_openssl_error()); tQSL_Error = TQSL_OPENSSL_ERROR; return NULL; } if (RSA_generate_key_ex(rsa, nbits, pubexp, NULL) != 1) { EVP_PKEY_free(pkey); RSA_free(rsa); BN_free(pubexp); tqslTrace("tqsl_new_rsa_key", "RSA_generate_key err %s", tqsl_openssl_error()); tQSL_Error = TQSL_OPENSSL_ERROR; return NULL; } if (!EVP_PKEY_assign_RSA(pkey, rsa)) { EVP_PKEY_free(pkey); RSA_free(rsa); BN_free(pubexp); tqslTrace("tqsl_new_rsa_key", "EVP_PKEY_assign_RSA err %s", tqsl_openssl_error()); tQSL_Error = TQSL_OPENSSL_ERROR; return NULL; } BN_free(pubexp); return pkey; } /* Output an ADIF field to a file descriptor. */ CLIENT_STATIC int tqsl_write_adif_field(FILE *fp, const char *fieldname, char type, const unsigned char *value, int len) { if (fieldname == NULL) /* Silly caller */ return 0; if (fputc('<', fp) == EOF) return 1; if (fputs(fieldname, fp) == EOF) return 1; if (type && type != ' ' && type != '\0') { if (fputc(':', fp) == EOF) return 1; if (fputc(type, fp) == EOF) return 1; } if (value != NULL && len != 0) { if (len < 0) len = strlen((const char *)value); if (fputc(':', fp) == EOF) return 1; fprintf(fp, "%d>", len); if (fwrite(value, 1, len, fp) != (unsigned int) len) return 1; } else if (fputc('>', fp) == EOF) { return 1; } if (fputs("\n\n", fp) == EOF) return 1; return 0; } /* Output an ADIF field to a BIO */ CLIENT_STATIC int tqsl_bio_write_adif_field(BIO *bio, const char *fieldname, char type, const unsigned char *value, int len) { int bret; if (fieldname == NULL) /* Silly caller */ return 0; if ((bret = BIO_write(bio, "<", 1)) <= 0) return 1; if ((bret = BIO_write(bio, fieldname, strlen(fieldname))) <= 0) return 1; if (type && type != ' ' && type != '\0') { if ((bret = BIO_write(bio, ":", 1)) <= 0) return 1; if ((bret = BIO_write(bio, &type, 1)) <= 0) return 1; } if (value != NULL && len != 0) { if (len < 0) len = strlen((const char *)value); if ((bret = BIO_write(bio, ":", 1)) <= 0) return 1; char numbuf[20]; snprintf(numbuf, sizeof numbuf, "%d>", len); if ((bret = BIO_write(bio, numbuf, strlen(numbuf))) <= 0) return 1; if ((bret = BIO_write(bio, value, len)) != len) return 1; } else if ((bret = BIO_write(bio, ">", 1)) <= 0) { return 1; } if ((bret = BIO_write(bio, "\n\n", 2)) <= 0) return 1; return 0; } static int tqsl_self_signed_is_ok(int ok, X509_STORE_CTX *ctx) { if (X509_STORE_CTX_get_error(ctx) == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) return 1; if (X509_STORE_CTX_get_error(ctx) == X509_V_ERR_CERT_UNTRUSTED) return 1; return ok; } static int tqsl_expired_is_ok(int ok, X509_STORE_CTX *ctx) { if (X509_STORE_CTX_get_error(ctx) == X509_V_ERR_CERT_HAS_EXPIRED || X509_STORE_CTX_get_error(ctx) == X509_V_ERR_CERT_UNTRUSTED) return 1; return ok; } static char * tqsl_make_cert_path(const char *filename, char *path, int size) { strncpy(path, tQSL_BaseDir, size); #ifdef _WIN32 strncat(path, "\\certs", size - strlen(path)); wchar_t* wpath = utf8_to_wchar(path); if (MKDIR(wpath, 0700) && errno != EEXIST) { free_wchar(wpath); #else strncat(path, "/certs", size - strlen(path)); if (MKDIR(path, 0700) && errno != EEXIST) { #endif tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_make_cert_path", "Making path %s failed with %s", path, strerror(errno)); return NULL; } #ifdef _WIN32 free_wchar(wpath); strncat(path, "\\", size - strlen(path)); #else strncat(path, "/", size - strlen(path)); #endif strncat(path, filename, size - strlen(path)); return path; } static int tqsl_clean_call(const char *callsign, char *buf, int size) { if ((static_cast(strlen(callsign))) > size-1) { tQSL_Error = TQSL_BUFFER_ERROR; return 1; } const char *cp; for (cp = callsign; *cp; cp++) { if (!isdigit(*cp) && !isalpha(*cp)) *buf = '_'; else *buf = *cp; ++buf; } *buf = 0; return 0; } static char * tqsl_make_key_path(const char *callsign, char *path, int size) { char fixcall[256]; tqsl_clean_call(callsign, fixcall, sizeof fixcall); strncpy(path, tQSL_BaseDir, size); #ifdef _WIN32 strncat(path, "\\keys", size - strlen(path)); wchar_t* wpath = utf8_to_wchar(path); if (MKDIR(wpath, 0700) && errno != EEXIST) { free_wchar(wpath); #else strncat(path, "/keys", size - strlen(path)); if (MKDIR(path, 0700) && errno != EEXIST) { #endif strncpy(tQSL_ErrorFile, path, sizeof tQSL_ErrorFile); tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_make_key_path", "Making path %s failed with %s", path, strerror(errno)); return 0; } #ifdef _WIN32 free_wchar(wpath); strncat(path, "\\", size - strlen(path)); #else strncat(path, "/", size - strlen(path)); #endif strncat(path, fixcall, size - strlen(path)); return path; } static char * tqsl_make_backup_path(const char *callsign, char *path, int size) { char fixcall[256]; tqsl_clean_call(callsign, fixcall, sizeof fixcall); strncpy(path, tQSL_BaseDir, size); #ifdef _WIN32 strncat(path, "\\certtrash", size - strlen(path)); wchar_t* wpath = utf8_to_wchar(path); if (MKDIR(wpath, 0700) && errno != EEXIST) { free_wchar(wpath); #else strncat(path, "/certtrash", size - strlen(path)); if (MKDIR(path, 0700) && errno != EEXIST) { #endif strncpy(tQSL_ErrorFile, path, sizeof tQSL_ErrorFile); tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_make_backup_path", "Making path %s failed with %s", path, strerror(errno)); return 0; } #ifdef _WIN32 free_wchar(wpath); strncat(path, "\\", size - strlen(path)); #else strncat(path, "/", size - strlen(path)); #endif strncat(path, fixcall, size - strlen(path)); return path; } static int tqsl_handle_root_cert(const char *pem, X509 *x, int (*cb)(int, const char *, void *), void *userdata) { const char *cp; /* Verify self-signature on the root certificate */ if ((cp = tqsl_ssl_verify_cert(x, NULL, NULL, 0, &tqsl_self_signed_is_ok)) != NULL) { strncpy(tQSL_CustomError, cp, sizeof tQSL_CustomError); tQSL_Error = TQSL_CUSTOM_ERROR; tqslTrace("tqsl_handle_root_cert", "sig verify err %s", tQSL_CustomError); return 1; } return tqsl_store_cert(pem, x, "root", TQSL_CERT_CB_ROOT, false, cb, userdata); } static int tqsl_ssl_error_is_nofile() { unsigned long l = ERR_peek_error(); if (tQSL_Error == TQSL_OPENSSL_ERROR && ERR_GET_LIB(l) == ERR_LIB_SYS && ERR_GET_FUNC(l) == SYS_F_FOPEN) return 1; if (tQSL_Error == TQSL_SYSTEM_ERROR && tQSL_Errno == ENOENT) return 1; return 0; } static int tqsl_handle_ca_cert(const char *pem, X509 *x, int (*cb)(int, const char *, void *), void *userdata) { STACK_OF(X509) *root_sk; char rootpath[256]; const char *cp; tqsl_make_cert_path("root", rootpath, sizeof rootpath); if ((root_sk = tqsl_ssl_load_certs_from_file(rootpath)) == NULL) { if (!tqsl_ssl_error_is_nofile()) { tqslTrace("tqsl_handle_ca_cert", "error not nofile - %d", errno); return 1; } } cp = tqsl_ssl_verify_cert(x, NULL, root_sk, 0, &tqsl_expired_is_ok); sk_X509_free(root_sk); if (cp) { strncpy(tQSL_CustomError, cp, sizeof tQSL_CustomError); tQSL_Error = TQSL_CUSTOM_ERROR; tqslTrace("tqsl_handle_ca_cert", "verify error %s", tQSL_CustomError); return 1; } return tqsl_store_cert(pem, x, "authorities", TQSL_CERT_CB_CA, false, cb, userdata); } static int tqsl_handle_user_cert(const char *cpem, X509 *x, int (*cb)(int, const char *, void *), void *userdata) { STACK_OF(X509) *root_sk, *ca_sk; char rootpath[256], capath[256]; char pem[sizeof tqsl_static_buf]; const char *cp; strncpy(pem, cpem, sizeof pem); /* Match the public key in the supplied certificate with a * private key in the key store. */ if (tqsl_find_matching_key(x, NULL, NULL, "", NULL, NULL)) { if (tQSL_Error != TQSL_PASSWORD_ERROR) { tqslTrace("tqsl_handle_user_cert", "match error %s", tqsl_openssl_error()); return 1; } tQSL_Error = TQSL_NO_ERROR; /* clear error */ } /* Check the chain of authority back to a trusted root */ tqsl_make_cert_path("root", rootpath, sizeof rootpath); if ((root_sk = tqsl_ssl_load_certs_from_file(rootpath)) == NULL) { if (!tqsl_ssl_error_is_nofile()) { tqslTrace("tqsl_handle_user_cert", "Error loading certs %s", tqsl_openssl_error()); return 1; } } tqsl_make_cert_path("authorities", capath, sizeof capath); if ((ca_sk = tqsl_ssl_load_certs_from_file(capath)) == NULL) { if (!tqsl_ssl_error_is_nofile()) { sk_X509_free(root_sk); tqslTrace("tqsl_handle_user_cert", "Error loading authorities %s", tqsl_openssl_error()); return 1; } } cp = tqsl_ssl_verify_cert(x, ca_sk, root_sk, 0, &tqsl_expired_is_ok); sk_X509_free(ca_sk); sk_X509_free(root_sk); if (cp) { strncpy(tQSL_CustomError, cp, sizeof tQSL_CustomError); tQSL_Error = TQSL_CUSTOM_ERROR; tqslTrace("tqsl_handle_user_cert", "verify error %s", cp); return 1; } return tqsl_store_cert(pem, x, "user", TQSL_CERT_CB_USER, false, cb, userdata); } CLIENT_STATIC int tqsl_store_cert(const char *pem, X509 *cert, const char *certfile, int type, bool force, int (*cb)(int, const char *, void *), void *userdata) { STACK_OF(X509) *sk; char path[256]; char issuer[256]; char name[256]; char value[256]; FILE *out; BIGNUM *bserial, *oldserial; string subjid, msg, callsign; TQSL_X509_NAME_ITEM item; int len, rval; tQSL_Date newExpires; string stype = "Unknown"; const ASN1_TIME *tm; if (type == TQSL_CERT_CB_ROOT) { stype = "Trusted Root Authority"; } else if (type == TQSL_CERT_CB_CA) { stype = "Certificate Authority"; } else if (type == TQSL_CERT_CB_USER) { stype = "Callsign"; // Invalidate the cached user certs if (xcerts != NULL) { sk_X509_free(xcerts); xcerts = NULL; } } tqsl_make_cert_path(certfile, path, sizeof path); item.name_buf = name; item.name_buf_size = sizeof name; item.value_buf = value; item.value_buf_size = sizeof value; if (tqsl_cert_get_subject_name_entry(cert, "AROcallsign", &item)) { // Subject contains a call sign (probably a user cert) callsign = value; strncpy(ImportCall, callsign.c_str(), sizeof(tQSL_ImportCall)); tQSL_ImportSerial = ASN1_INTEGER_get(X509_get_serialNumber(cert)); subjid = string(" ") + value; tm = X509_get_notAfter(cert); if (tm) { tqsl_get_asn1_date(tm, &newExpires); } else { newExpires.year = 9999; newExpires.month = 1; newExpires.day = 1; } if (tqsl_cert_get_subject_name_entry(cert, "commonName", &item)) subjid += string(" (") + value + ")"; len = sizeof value-1; if (!tqsl_get_cert_ext(cert, "dxccEntity", (unsigned char *)value, &len, NULL)) { value[len] = 0; subjid += string(" DXCC = ") + value; } } else if (tqsl_cert_get_subject_name_entry(cert, "organizationName", &item)) { // Subject contains an organization (probably a CA or root CA cert) subjid = string(" ") + value; if (tqsl_cert_get_subject_name_entry(cert, "organizationalUnitName", &item)) subjid += string(" ") + value; } if (subjid == "") { // If haven't found a displayable subject name we undertand, use the raw DN X509_NAME_oneline(X509_get_subject_name(cert), issuer, sizeof issuer); subjid = string(" ") + issuer; } X509_NAME_oneline(X509_get_issuer_name(cert), issuer, sizeof issuer); /* Check for dupes */ if ((sk = tqsl_ssl_load_certs_from_file(path)) == NULL) { if (!tqsl_ssl_error_is_nofile()) { tqslTrace("tqsl_store_cert", "unexpected openssl err %s", tqsl_openssl_error()); return 1; /* Unexpected OpenSSL error */ } } /* Check each certificate */ if (sk != NULL) { int i, n; tQSL_Date expires; bserial = BN_new(); ASN1_INTEGER_to_BN(X509_get_serialNumber(cert), bserial); n = sk_X509_num(sk); for (i = 0; i < n; i++) { char buf[256]; X509 *x; const char *cp; x = sk_X509_value(sk, i); cp = X509_NAME_oneline(X509_get_issuer_name(x), buf, sizeof buf); if (cp != NULL && !strcmp(cp, issuer)) { oldserial = BN_new(); ASN1_INTEGER_to_BN(X509_get_serialNumber(x), oldserial); int result = BN_ucmp(bserial, oldserial); BN_free(oldserial); if (result == 0) break; /* We have a match */ } if (!force && type == TQSL_CERT_CB_USER) { // Don't check for newer certs on restore item.name_buf = name; item.name_buf_size = sizeof name; item.value_buf = value; item.value_buf_size = sizeof value; if (tqsl_cert_get_subject_name_entry(x, "AROcallsign", &item)) { if (value == callsign) { /* * If it's another cert for * this call, is it older? */ tm = X509_get_notAfter(x); if (tm) { tqsl_get_asn1_date(tm, &expires); } else { expires.year = 0; expires.month = 0; expires.day = 0; } if (tqsl_compareDates(&newExpires, &expires) < 0) { tQSL_Error = TQSL_CUSTOM_ERROR; strncpy(tQSL_CustomError, "A newer certificate for this callsign is already installed", sizeof tQSL_CustomError); tqslTrace("tqsl_load_cert", tQSL_CustomError); BN_free(bserial); sk_X509_free(sk); return 1; } } } } } BN_free(bserial); sk_X509_free(sk); if (i < n) { /* Have a match -- cert is already in the file */ if (cb != NULL) { int rval; string msg = "Duplicate " + stype + " certificate: " + subjid; rval = (*cb)(TQSL_CERT_CB_RESULT | type | TQSL_CERT_CB_DUPLICATE, msg.c_str(), userdata); if (rval) { tQSL_Error = TQSL_CUSTOM_ERROR; strncpy(tQSL_CustomError, "Duplicate Callsign certificate", sizeof tQSL_CustomError); tqslTrace("tqsl_load_cert", tQSL_CustomError); return 1; } } if (tQSL_Error == 0) { tQSL_Error = TQSL_CERT_ERROR; } return 1; } } /* Cert is not a duplicate. Append it to the certificate file */ if (cb != NULL) { msg = "Adding " + stype + " Certificate for: " + subjid; tqslTrace("tqsl_load_cert", msg.c_str()); rval = (*cb)(TQSL_CERT_CB_MILESTONE | type | TQSL_CERT_CB_PROMPT, msg.c_str(), userdata); if (rval) { tqslTrace("tqsl_load_cert", "operator aborted"); tQSL_Error = TQSL_OPERATOR_ABORT; return 1; } } #ifdef _WIN32 wchar_t* wpath = utf8_to_wchar(path); if ((out = _wfopen(wpath, L"a")) == NULL) { free_wchar(wpath); #else if ((out = fopen(path, "a")) == NULL) { #endif strncpy(tQSL_ErrorFile, path, sizeof tQSL_ErrorFile); tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_load_cert", "opening file err %s", strerror(errno)); return 1; } #ifdef _WIN32 free_wchar(wpath); #endif // Make sure there's always a newline between certs size_t pemlen = strlen(pem); if (fwrite("\n", 1, 1, out) != 1 || fwrite(pem, 1, pemlen, out) != pemlen) { strncpy(tQSL_ErrorFile, certfile, sizeof tQSL_ErrorFile); tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_load_cert", "writing file err %s", strerror(errno)); return 1; } if (fclose(out) == EOF) { strncpy(tQSL_ErrorFile, certfile, sizeof tQSL_ErrorFile); tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_load_cert", "writing file err %s", strerror(errno)); return 1; } msg = "Loaded: " + subjid; if (cb) rval = (*cb)(TQSL_CERT_CB_RESULT | type | TQSL_CERT_CB_LOADED, msg.c_str(), userdata); else rval = 0; if (rval) { tQSL_Error = TQSL_OPERATOR_ABORT; return 1; } strncpy(tQSL_ImportCall, ImportCall, sizeof tQSL_ImportCall); return 0; } static int pw_aborted; static int fixed_password_callback(char *buf, int bufsiz, int verify, void *userdata) { pw_aborted = 0; if (userdata != NULL) strncpy(buf, reinterpret_cast(userdata), bufsiz); else buf[0] = 0; return strlen(buf); } static void *prompt_userdata; static int prompted_password_callback(char *buf, int bufsiz, int verify, void *userfunc) { pw_aborted = 0; if (userfunc != NULL) { int (*cb)(char *, int, void *) = (int (*)(char *, int, void *))userfunc; if ((*cb)(buf, bufsiz, prompt_userdata)) { pw_aborted = 1; return 0; } } else { buf[0] = 0; } return strlen(buf); } static tQSL_ADIF keyf_adif = 0; static int tqsl_open_key_file(const char *path) { if (keyf_adif) tqsl_endADIF(&keyf_adif); return tqsl_beginADIF(&keyf_adif, path); } static int tqsl_read_key(map& fields) { TQSL_ADIF_GET_FIELD_ERROR adif_status; tqsl_adifFieldResults field; static tqsl_adifFieldDefinitions adif_fields[] = { { "PUBLIC_KEY", "", TQSL_ADIF_RANGE_TYPE_NONE, 2000, 0, 0, NULL, NULL }, { "PRIVATE_KEY", "", TQSL_ADIF_RANGE_TYPE_NONE, 2000, 0, 0, NULL, NULL }, { "CALLSIGN", "", TQSL_ADIF_RANGE_TYPE_NONE, 2000, 0, 0, NULL, NULL }, { "TQSL_CRQ_PROVIDER", "", TQSL_ADIF_RANGE_TYPE_NONE, 2000, 0, 0, NULL, NULL }, { "TQSL_CRQ_PROVIDER_UNIT", "", TQSL_ADIF_RANGE_TYPE_NONE, 2000, 0, 0, NULL, NULL }, { "TQSL_CRQ_ADDRESS1", "", TQSL_ADIF_RANGE_TYPE_NONE, 2000, 0, 0, NULL, NULL }, { "TQSL_CRQ_ADDRESS2", "", TQSL_ADIF_RANGE_TYPE_NONE, 2000, 0, 0, NULL, NULL }, { "TQSL_CRQ_CITY", "", TQSL_ADIF_RANGE_TYPE_NONE, 2000, 0, 0, NULL, NULL }, { "TQSL_CRQ_STATE", "", TQSL_ADIF_RANGE_TYPE_NONE, 2000, 0, 0, NULL, NULL }, { "TQSL_CRQ_POSTAL", "", TQSL_ADIF_RANGE_TYPE_NONE, 2000, 0, 0, NULL, NULL }, { "TQSL_CRQ_COUNTRY", "", TQSL_ADIF_RANGE_TYPE_NONE, 2000, 0, 0, NULL, NULL }, { "TQSL_CRQ_DXCC_ENTITY", "", TQSL_ADIF_RANGE_TYPE_NONE, 2000, 0, 0, NULL, NULL }, { "TQSL_CRQ_QSO_NOT_BEFORE", "", TQSL_ADIF_RANGE_TYPE_NONE, 2000, 0, 0, NULL, NULL }, { "TQSL_CRQ_QSO_NOT_AFTER", "", TQSL_ADIF_RANGE_TYPE_NONE, 2000, 0, 0, NULL, NULL }, { "DELETED", "", TQSL_ADIF_RANGE_TYPE_NONE, 200, 0, 0, NULL, NULL }, { "eor", "", TQSL_ADIF_RANGE_TYPE_NONE, 0, 0, 0, NULL, NULL }, { "", "", TQSL_ADIF_RANGE_TYPE_NONE, 0, 0, 0, NULL, NULL }, }; fields.clear(); do { if (tqsl_getADIFField(keyf_adif, &field, &adif_status, adif_fields, notypes, &tqsl_static_alloc)) return 1; if (adif_status == TQSL_ADIF_GET_FIELD_EOF) return 1; if (!strcasecmp(field.name, "eor")) return 0; if (adif_status == TQSL_ADIF_GET_FIELD_SUCCESS) { char *cp; for (cp = field.name; *cp; cp++) *cp = toupper(*cp); fields[field.name] = reinterpret_cast(field.data); } } while (adif_status == TQSL_ADIF_GET_FIELD_SUCCESS || adif_status == TQSL_ADIF_GET_FIELD_NO_NAME_MATCH); tQSL_Error = TQSL_ADIF_ERROR; return 1; } static void tqsl_close_key_file(void) { tqsl_endADIF(&keyf_adif); } static int tqsl_replace_key(const char *callsign, const char *path, map& newfields, int (*cb)(int, const char *, void *), void *userdata) { char newpath[300]; char savepath[300]; #ifdef _WIN32 wchar_t* wnewpath = NULL; #endif map fields; vector< map > records; vector< map >::iterator it; EVP_PKEY *new_key = NULL, *key = NULL; BIO *bio = 0; FILE *out = 0; int rval = 1; if ((bio = BIO_new_mem_buf(reinterpret_cast(const_cast(newfields["PUBLIC_KEY"].c_str())), newfields["PUBLIC_KEY"].length())) == NULL) { tqslTrace("tqsl_replace_key", "BIO_new_mem_buf err %s", tqsl_openssl_error()); goto trk_end; } if ((new_key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL)) == NULL) { tqslTrace("tqsl_replace_key", "PEM_read_bio_PUBKEY err %s", tqsl_openssl_error()); goto trk_end; } BIO_free(bio); bio = 0; if (tqsl_open_key_file(path)) { if (tQSL_Error != TQSL_SYSTEM_ERROR || tQSL_Errno != ENOENT) { tqslTrace("tqsl_replace_key", "error opening key file %s: %s", path, strerror(tQSL_Errno)); return 1; } tQSL_Error = TQSL_NO_ERROR; } while (tqsl_read_key(fields) == 0) { vectorseen; bool match = false; for (size_t i = 0; i < seen.size(); i++) { if (seen[i] == fields["PUBLIC_KEY"]) { match = true; break; } } if (match) continue; // Drop dupes seen.push_back(fields["PUBLIC_KEY"]); if ((bio = BIO_new_mem_buf(reinterpret_cast(const_cast(fields["PUBLIC_KEY"].c_str())), fields["PUBLIC_KEY"].length())) == NULL) { tqslTrace("tqsl_replace_key", "BIO_new_mem_buf error %s", tqsl_openssl_error()); goto trk_end; } if ((key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL)) == NULL) { tqslTrace("tqsl_replace_key", "Pem_read_bio_rsa_pubkey error %s", tqsl_openssl_error()); goto trk_end; } BIO_free(bio); bio = NULL; if (EVP_PKEY_cmp(key, new_key) == 1) { fields["DELETED"] = "True"; // Mark as deleted } records.push_back(fields); } tqsl_close_key_file(); if (newfields["PRIVATE_KEY"] != "") records.push_back(newfields); strncpy(newpath, path, sizeof newpath); strncat(newpath, ".new", sizeof newpath - strlen(newpath)-1); strncpy(savepath, path, sizeof savepath); strncat(savepath, ".save", sizeof savepath - strlen(savepath)-1); #ifdef _WIN32 wnewpath = utf8_to_wchar(newpath); if ((out = _wfopen(wnewpath, TQSL_OPEN_WRITE)) == NULL) { free_wchar(wnewpath); #else if ((out = fopen(newpath, TQSL_OPEN_WRITE)) == NULL) { #endif tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_replace_key", "open file %s: %s", newpath, strerror(tQSL_Errno)); goto trk_end; } for (it = records.begin(); it != records.end(); it++) { map::iterator mit; for (mit = it->begin(); mit != it->end(); mit++) { if (tqsl_write_adif_field(out, mit->first.c_str(), 0, (const unsigned char *)mit->second.c_str(), -1)) { tqslTrace("tqsl_replace_key", "error writing %s", strerror(tQSL_Errno)); #ifdef _WIN32 free_wchar(wnewpath); #endif goto trk_end; } } tqsl_write_adif_field(out, "eor", 0, NULL, 0); } if (fclose(out) == EOF) { tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_replace_key", "error closing %s", strerror(tQSL_Errno)); #ifdef _WIN32 free_wchar(wnewpath); #endif goto trk_end; } out = 0; /* Output file looks okay. Replace the old file with the new one. */ #ifdef _WIN32 wchar_t* wsavepath = utf8_to_wchar(savepath); if (_wunlink(wsavepath) && errno != ENOENT) { free_wchar(wsavepath); free_wchar(wnewpath); #else if (unlink(savepath) && errno != ENOENT) { #endif tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_replace_key", "unlink file %s: %s", savepath, strerror(tQSL_Errno)); goto trk_end; } #ifdef _WIN32 wchar_t* wpath = utf8_to_wchar(path); if (_wrename(wpath, wsavepath) && errno != ENOENT) { free_wchar(wpath); free_wchar(wsavepath); free_wchar(wnewpath); #else if (rename(path, savepath) && errno != ENOENT) { #endif tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_replace_key", "rename file %s->%s: %s", path, savepath, strerror(tQSL_Errno)); goto trk_end; } #ifdef _WIN32 if (_wrename(wnewpath, wpath)) { free_wchar(wnewpath); free_wchar(wpath); free_wchar(wsavepath); #else if (rename(newpath, path)) { #endif tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_replace_key", "rename file %s->%s: %s", newpath, path, strerror(tQSL_Errno)); goto trk_end; } #ifdef _WIN32 free_wchar(wnewpath); free_wchar(wpath); free_wchar(wsavepath); #endif tqslTrace("tqsl_replace_key", "loaded private key for: %s", callsign); if (cb) { string msg = string("Loaded private key for: ") + callsign; (*cb)(TQSL_CERT_CB_RESULT + TQSL_CERT_CB_PKEY + TQSL_CERT_CB_LOADED, msg.c_str(), userdata); } rval = 0; trk_end: tqsl_close_key_file(); if (out) fclose(out); if (new_key) EVP_PKEY_free(new_key); if (key) EVP_PKEY_free(key); if (bio) BIO_free(bio); return rval; } static int tqsl_unlock_key(const char *pem, EVP_PKEY **keyp, const char *password, int (*cb)(char *, int, void *), void *userdata) { RSA *prsa = NULL; BIO *bio; int (*ssl_cb)(char *, int, int, void *) = NULL; void *cb_user = NULL; long e; int rval = 1; if ((bio = BIO_new_mem_buf(reinterpret_cast(const_cast(pem)), strlen(pem))) == NULL) { tqslTrace("tqsl_unlock_key", "BIO_new_mem_buf err %s", tqsl_openssl_error()); goto err; } if (password != NULL) { ssl_cb = &fixed_password_callback; cb_user = reinterpret_cast(const_cast(password)); } else if (cb != NULL) { prompt_userdata = userdata; ssl_cb = &prompted_password_callback; cb_user = reinterpret_cast(cb); } if ((prsa = PEM_read_bio_RSAPrivateKey(bio, NULL, ssl_cb, cb_user)) == NULL) { tqslTrace("tqsl_unlock_key", "PEM_read_bio_RSAPrivateKey err %s", tqsl_openssl_error()); goto err; } if (keyp != NULL) { if ((*keyp = EVP_PKEY_new()) == NULL) goto err; EVP_PKEY_assign_RSA(*keyp, prsa); prsa = NULL; } rval = 0; goto end; err: e = ERR_peek_error(); if ((ERR_GET_LIB(e) == ERR_LIB_EVP && ERR_GET_REASON(e) == EVP_R_BAD_DECRYPT) || (ERR_GET_LIB(e) == ERR_LIB_PEM && ERR_GET_REASON(e) == PEM_R_BAD_PASSWORD_READ) || (ERR_GET_LIB(e) == ERR_LIB_PKCS12 && ERR_GET_REASON(e) == PKCS12_R_PKCS12_CIPHERFINAL_ERROR)) { tqsl_getErrorString(); /* clear error */ tQSL_Error = pw_aborted ? TQSL_OPERATOR_ABORT : TQSL_PASSWORD_ERROR; ERR_clear_error(); } else { tQSL_Error = TQSL_OPENSSL_ERROR; } tqslTrace("tqsl_unlock_key", "Key read error %d", tQSL_Error); end: if (prsa != NULL) RSA_free(prsa); if (bio != NULL) BIO_free(bio); return rval; } static int tqsl_find_matching_key(X509 *cert, EVP_PKEY **keyp, TQSL_CERT_REQ **crq, const char *password, int (*cb)(char *, int, void *), void *userdata) { char path[256]; char aro[256]; TQSL_X509_NAME_ITEM item = { path, sizeof path, aro, sizeof aro }; EVP_PKEY *cert_key = NULL; EVP_PKEY *curkey = NULL; int rval = 0; int match = 0; int deleted = 0; BIO *bio = NULL; map fields; bool finddeleted = false; if (keyp != NULL) *keyp = NULL; if (!tqsl_cert_get_subject_name_entry(cert, "AROcallsign", &item)) { tqslTrace("tqsl_find_matching_key", "get subj name err %d", tQSL_Error); return 1; } tQSL_ImportSerial = ASN1_INTEGER_get(X509_get_serialNumber(cert)); if (!tqsl_make_key_path(aro, path, sizeof path)) { tqslTrace("tqsl_find_matching_key", "key path err %d", tQSL_Error); rval = 1; goto end_nokey; } strncpy(ImportCall, aro, sizeof ImportCall); again: if (tqsl_open_key_file(path)) { /* Friendly error for file not found */ if (tQSL_Error == TQSL_SYSTEM_ERROR) { if (tQSL_Errno == ENOENT) { snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "You can only open this callsign certificate by running TQSL on the computer where you created the certificate request for %s.", aro); } else { snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "Can't open %s: %s\nThis file is needed to open this callsign certificate.", aro, strerror(tQSL_Errno)); } tQSL_Error = TQSL_CUSTOM_ERROR; tqslTrace("tqsl_find_matching_key", "opening file path err %s", tQSL_CustomError); } return 1; } if ((cert_key = X509_get_pubkey(cert)) == NULL) { tqslTrace("tqsl_find_matching_key", "error getting public key %s", tqsl_openssl_error()); goto err; } if (crq != NULL) { if (*crq != NULL) tqsl_free_cert_req(*crq, 0); *crq = reinterpret_cast(tqsl_calloc(1, sizeof(TQSL_CERT_REQ))); } while (!tqsl_read_key(fields)) { /* Compare the keys */ if ((bio = BIO_new_mem_buf(reinterpret_cast(const_cast(fields["PUBLIC_KEY"].c_str())), fields["PUBLIC_KEY"].length())) == NULL) { tqslTrace("tqsl_find_matching_key", "BIO_new_mem_buf err %s", tqsl_openssl_error()); goto err; } if ((curkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL)) == NULL) { tqslTrace("tqsl_find_matching_key", "PEM_read_bio_RSA_PUBKEY err %s", tqsl_openssl_error()); goto err; } BIO_free(bio); bio = NULL; if (EVP_PKEY_cmp(curkey, cert_key) == 1) { if (fields["DELETED"] == "True") { if (finddeleted) { match = 1; deleted = 1; } } else { match = 1; } } if (match) { /* We have a winner */ if (tqsl_unlock_key(fields["PRIVATE_KEY"].c_str(), keyp, password, cb, userdata)) { tqslTrace("tqsl_find_matching_key", "tqsl_unlock_key err %d", tQSL_Error); rval = 1; goto end; } if (crq != NULL) { tQSL_Error = TQSL_BUFFER_ERROR; if (!safe_strncpy((*crq)->providerName, fields["TQSL_CRQ_PROVIDER"].c_str(), sizeof (*crq)->providerName)) goto end; if (!safe_strncpy((*crq)->providerUnit, fields["TQSL_CRQ_PROVIDER_UNIT"].c_str(), sizeof (*crq)->providerUnit)) goto end; if (!safe_strncpy((*crq)->address1, fields["TQSL_CRQ_ADDRESS1"].c_str(), sizeof (*crq)->address1)) goto end; if (!safe_strncpy((*crq)->address2, fields["TQSL_CRQ_ADDRESS2"].c_str(), sizeof (*crq)->address2)) goto end; if (!safe_strncpy((*crq)->city, fields["TQSL_CRQ_CITY"].c_str(), sizeof (*crq)->city)) goto end; if (!safe_strncpy((*crq)->state, fields["TQSL_CRQ_STATE"].c_str(), sizeof (*crq)->state)) goto end; if (!safe_strncpy((*crq)->postalCode, fields["TQSL_CRQ_POSTAL"].c_str(), sizeof (*crq)->postalCode)) goto end; if (!safe_strncpy((*crq)->country, fields["TQSL_CRQ_COUNTRY"].c_str(), sizeof (*crq)->country)) goto end; tQSL_Error = 0; } rval = 0; break; } } if (match) goto end; if (!finddeleted) { finddeleted = true; tqsl_close_key_file(); goto again; } tqslTrace("tqsl_find_matching_key", "No matching private key found"); rval = 1; tQSL_Error = TQSL_CERT_NOT_FOUND; strncpy(tQSL_ImportCall, ImportCall, sizeof tQSL_ImportCall); goto end; err: rval = 1; tQSL_Error = TQSL_OPENSSL_ERROR; end: tqsl_close_key_file(); if (deleted) { int savedErr = tQSL_Error; tqsl_clear_deleted(aro, path, cert_key); tQSL_Error = savedErr; } end_nokey: if (curkey != NULL) EVP_PKEY_free(curkey); if (bio != NULL) BIO_free(bio); if (cert_key != NULL) EVP_PKEY_free(cert_key); // if (in != NULL) // fclose(in); if (rval == 0) { strncpy(tQSL_ImportCall, ImportCall, sizeof tQSL_ImportCall); } return rval; } static int tqsl_make_key_list(vector< map > & keys) { keys.clear(); string path = tQSL_BaseDir; #ifdef _WIN32 path += "\\keys"; wchar_t* wpath = utf8_to_wchar(path.c_str()); MKDIR(wpath, 0700); #else path += "/keys"; MKDIR(path.c_str(), 0700); #endif #ifdef _WIN32 WDIR *dir = wopendir(wpath); free_wchar(wpath); #else DIR *dir = opendir(path.c_str()); #endif if (dir == NULL) { tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_make_key_list", "Opendir %s error %s", path.c_str(), strerror(errno)); return 1; } #ifdef _WIN32 struct wdirent *ent; #else struct dirent *ent; #endif int rval = 0; int savedError = 0; int savedErrno = 0; char *savedFile = NULL; #ifdef _WIN32 while ((ent = wreaddir(dir)) != NULL) { if (ent->d_name[0] == '.') continue; if (wcsstr(ent->d_name, L".save") || wcsstr(ent->d_name, L".new")) continue; char dname[TQSL_MAX_PATH_LEN]; wcstombs(dname, ent->d_name, TQSL_MAX_PATH_LEN); string filename = path + "\\" + dname; #else while ((ent = readdir(dir)) != NULL) { if (ent->d_name[0] == '.') continue; if (strstr(ent->d_name, ".save") || strstr(ent->d_name, ".new")) continue; string filename = path + "/" + ent->d_name; #endif char fixcall[256]; #ifdef _WIN32 struct _stat32 s; wchar_t* wfilename = utf8_to_wchar(filename.c_str()); if (_wstat32(wfilename, &s) == 0) { if (S_ISDIR(s.st_mode)) { free_wchar(wfilename); continue; // If it's a directory, skip it. } } #else struct stat s; if (stat(filename.c_str(), &s) == 0) { if (S_ISDIR(s.st_mode)) continue; // If it's a directory, skip it. } #endif if (!tqsl_open_key_file(filename.c_str())) { vectorseen; map fields; while (!tqsl_read_key(fields)) { if (fields["DELETED"] == "True") continue; // Skip this one bool match = false; for (size_t i = 0; i < seen.size(); i++) { if (seen[i] == fields["PUBLIC_KEY"]) { match = true; break; } } if (match) continue; seen.push_back(fields["PUBLIC_KEY"]); if (tqsl_clean_call(fields["CALLSIGN"].c_str(), fixcall, sizeof fixcall)) { rval = 1; savedError = tQSL_Error; savedErrno = tQSL_Errno; if (savedFile) free(savedFile); savedFile = strdup(tQSL_ErrorFile); continue; // Keep looking for keys } #ifdef _WIN32 wchar_t* wfixcall = utf8_to_wchar(fixcall); if (wcscmp(wfixcall, ent->d_name)) { free_wchar(wfixcall); #else if (strcasecmp(fixcall, ent->d_name)) { #endif continue; } keys.push_back(fields); } tqsl_close_key_file(); } else { rval = 1; // Unable to open - remember that savedErrno = tQSL_Errno; savedError = tQSL_Error; if (savedFile) free(savedFile); savedFile = strdup(tQSL_ErrorFile); } } #ifdef _WIN32 _wclosedir(dir); #else closedir(dir); #endif if (rval) { tQSL_Error = savedError; tQSL_Errno = savedErrno; if (savedFile) { strncpy(tQSL_ErrorFile, savedFile, sizeof tQSL_ErrorFile); free(savedFile); } tqslTrace("tqsl_make_key_list", "error %s %s", tQSL_ErrorFile, strerror(tQSL_Errno)); } return rval; } static int tqsl_get_cert_ext(X509 *cert, const char *ext, unsigned char *userbuf, int *buflen, int *crit) { int i, n, datasiz; X509_EXTENSION *xe; char buf[256]; ASN1_OBJECT *obj; ASN1_OCTET_STRING *data; if (tqsl_init()) return 1; if (cert == NULL || ext == NULL || userbuf == NULL || buflen == NULL) { tqslTrace("tqsl_get_cert_ext", "arg error cert=0x%lx, ext=0x%lx userbuf=0x%lx, buflen=0x%lx crit=0x%lx", cert, ext, userbuf, buflen, crit); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } n = X509_get_ext_count(cert); for (i = 0; i < n; i++) { xe = X509_get_ext(cert, i); if (xe == NULL) { tqslTrace("tqsl_get_cert_ext", "X509_get_ext error %s", tqsl_openssl_error()); tQSL_Error = TQSL_OPENSSL_ERROR; return 1; } buf[0] = '\0'; obj = X509_EXTENSION_get_object(xe); if (obj) OBJ_obj2txt(buf, sizeof buf, obj, 0); if (strcmp(buf, ext)) continue; /* This is the desired extension */ data = X509_EXTENSION_get_data(xe); if (data != NULL) { datasiz = ASN1_STRING_length(data); if (datasiz > *buflen-1) { tqslTrace("tqsl_get_cert_ext", "buffer len %d needed %d", *buflen, datasiz); tQSL_Error = TQSL_BUFFER_ERROR; return 1; } *buflen = datasiz; if (datasiz) memcpy(userbuf, ASN1_STRING_get0_data(data), datasiz); userbuf[datasiz] = '\0'; } if (crit != NULL) *crit = X509_EXTENSION_get_critical(xe); return 0; } snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "Certificate Extension not found: %s", ext); tQSL_Error = TQSL_CUSTOM_ERROR; if (strcmp(ext, "supercededCertificate")) tqslTrace("tqsl_get_cert_ext", "Err %s", tQSL_CustomError); return 1; } CLIENT_STATIC int tqsl_get_asn1_date(const ASN1_TIME *tm, tQSL_Date *date) { char *v; int i; i = tm->length; v = reinterpret_cast(tm->data); if (i >= 14) { for (i = 0; i < 12; i++) if ((v[i] > '9') || (v[i] < '0')) goto err; date->year = (v[0]-'0')*1000+(v[1]-'0')*100 + (v[2]-'0')*10+(v[3]-'0'); date->month = (v[4]-'0')*10+(v[5]-'0'); if ((date->month > 12) || (date->month < 1)) goto err; date->day = (v[6]-'0')*10+(v[7]-'0'); } else if (i < 12) { goto err; } else { for (i = 0; i < 10; i++) if ((v[i] > '9') || (v[i] < '0')) goto err; date->year = (v[0]-'0')*10+(v[1]-'0'); if (date->year < 50) date->year+=100; date->year += 1900; date->month = (v[2]-'0')*10+(v[3]-'0'); if ((date->month > 12) || (date->month < 1)) goto err; date->day = (v[4]-'0')*10+(v[5]-'0'); } return 0; err: tqslTrace("tqsl_get_asn1_date", "invalid date"); tQSL_Error = TQSL_INVALID_DATE; return 1; } static char * tqsl_sign_base64_data(tQSL_Cert cert, char *b64data) { int len; static unsigned char sig[256]; int siglen = sizeof sig; if (b64data && !strncmp(b64data, "-----", 5)) { b64data = strchr(b64data, '\n'); if (b64data == NULL) return NULL; b64data++; } len = sizeof tqsl_static_buf; if (tqsl_decodeBase64(b64data, tqsl_static_buf, &len)) return NULL; if (tqsl_signDataBlock(cert, tqsl_static_buf, len, sig, &siglen)) return NULL; if (tqsl_encodeBase64(sig, siglen, reinterpret_cast(tqsl_static_buf), sizeof tqsl_static_buf)) return NULL; return reinterpret_cast(tqsl_static_buf); } static bool safe_strncpy(char *dest, const char *src, int size) { strncpy(dest, src, size); dest[size-1] = 0; return ((static_cast((strlen(src))) < size)); } static string tqsl_cert_status_filename(const char *f = "cert_status.xml") { string s = tQSL_BaseDir; #ifdef _WIN32 s += "\\"; #else s += "/"; #endif s += f; return s; } static int tqsl_load_cert_status_data(XMLElement &xel) { int status = xel.parseFile(tqsl_cert_status_filename().c_str()); if (status) { if (errno == ENOENT) { // No file is OK tqslTrace("tqsl_load_cert_status_data", "FNF"); return 0; } strncpy(tQSL_ErrorFile, tqsl_cert_status_filename().c_str(), sizeof tQSL_ErrorFile); if (status == XML_PARSE_SYSTEM_ERROR) { tQSL_Error = TQSL_FILE_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_load_cert_status_data", "open error %s: %s", tqsl_cert_status_filename().c_str(), strerror(tQSL_Errno)); } else { tqslTrace("tqsl_load_cert_status_data", "syntax error %s", tqsl_cert_status_filename().c_str()); tQSL_Error = TQSL_FILE_SYNTAX_ERROR; } return 1; } return status; } static int tqsl_dump_cert_status_data(XMLElement &xel) { ofstream out; string fn = tqsl_cert_status_filename(); out.exceptions(std::ios::failbit | std::ios::eofbit | std::ios::badbit); try { #ifdef _WIN32 wchar_t* wfn = utf8_to_wchar(fn.c_str()); out.open(wfn); free_wchar(wfn); #else out.open(fn.c_str()); #endif out << xel << endl; out.close(); } catch(exception& x) { tQSL_Error = TQSL_CUSTOM_ERROR; snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "Error writing certificate status file (%s): %s/%s", fn.c_str(), x.what(), strerror(errno)); tqslTrace("tqsl_dump_cert_status_data", "write error %s", tQSL_CustomError); return 1; } return 0; } DLLEXPORT int CALLCONVENTION tqsl_getCertificateStatus(long serial) { XMLElement top_el; if (tqsl_load_cert_status_data(top_el)) return TQSL_CERT_STATUS_UNK; XMLElement sfile; if (top_el.getFirstElement(sfile)) { XMLElement cd; bool ok = sfile.getFirstElement("Cert", cd); while (ok && cd.getElementName() == "Cert") { pair s = cd.getAttribute("serial"); if (s.second && strtol(s.first.c_str(), NULL, 10) == serial) { XMLElement xs; if (cd.getFirstElement("status", xs)) { if (!strcasecmp(xs.getText().c_str(), "Bad serial")) return TQSL_CERT_STATUS_INV; else if (!strcasecmp(xs.getText().c_str(), "Superceded")) return TQSL_CERT_STATUS_SUP; else if (!strcasecmp(xs.getText().c_str(), "Expired")) return TQSL_CERT_STATUS_EXP; else if (!strcasecmp(xs.getText().c_str(), "Unrevoked")) return TQSL_CERT_STATUS_OK; else return TQSL_CERT_STATUS_UNK; } } ok = sfile.getNextElement(cd); } } return TQSL_CERT_STATUS_UNK; } DLLEXPORT int CALLCONVENTION tqsl_setCertificateStatus(long serial, const char *status) { if (status == NULL) { tqslTrace("tqsl_setCertificateStatus", "status=null"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } char sstr[32]; snprintf(sstr, sizeof sstr, "%ld", serial); XMLElement top_el; int stat = tqsl_load_cert_status_data(top_el); if (stat == TQSL_FILE_SYSTEM_ERROR) { tqslTrace("tqsl_setCertificateStatus", "error %d", tQSL_Error); return 1; } XMLElement sfile; if (!top_el.getFirstElement(sfile)) sfile.setElementName("CertStatus"); XMLElementList& ellist = sfile.getElementList(); bool exists = false; XMLElementList::iterator ep; for (ep = ellist.find("Cert"); ep != ellist.end(); ep++) { if (ep->first != "Cert") break; pair rval = ep->second->getAttribute("serial"); if (rval.second && strtol(rval.first.c_str(), NULL, 10) == serial) { exists = true; break; } } XMLElement *cs = new XMLElement("Cert"); cs->setPretext("\n "); XMLElement *se = new XMLElement; se->setPretext(cs->getPretext() + " "); se->setElementName("status"); se->setText(status); cs->addElement(se); cs->setAttribute("serial", sstr); cs->setText("\n "); if (exists) ellist.erase(ep); sfile.addElement(cs); sfile.setText("\n"); return tqsl_dump_cert_status_data(sfile); } static int tqsl_clear_deleted(const char *callsign, const char *path, EVP_PKEY *cert_key) { char newpath[300]; char savepath[300]; #ifdef _WIN32 wchar_t* wnewpath = NULL; #endif map fields; vector< map > records; vector< map >::iterator it; EVP_PKEY *new_key = NULL, *key = NULL; BIO *bio = 0; FILE *out = 0; int rval = 1; if (tqsl_open_key_file(path)) { if (tQSL_Error != TQSL_SYSTEM_ERROR || tQSL_Errno != ENOENT) { tqslTrace("tqsl_clear_deleted", "error opening key file %s: %s", path, strerror(tQSL_Errno)); return 1; } tQSL_Error = TQSL_NO_ERROR; } while (tqsl_read_key(fields) == 0) { if ((bio = BIO_new_mem_buf(reinterpret_cast(const_cast(fields["PUBLIC_KEY"].c_str())), fields["PUBLIC_KEY"].length())) == NULL) { tqslTrace("tqsl_clear_deleted", "BIO_new_mem_buf error %s", tqsl_openssl_error()); goto trk_end; } if ((key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL)) == NULL) { tqslTrace("tqsl_clear_deleted", "Pem_read_bio_rsa_pubkey error %s", tqsl_openssl_error()); goto trk_end; } BIO_free(bio); bio = NULL; if (EVP_PKEY_cmp(key, cert_key) == 1) { fields["DELETED"] = "False"; } records.push_back(fields); } tqsl_close_key_file(); strncpy(newpath, path, sizeof newpath); strncat(newpath, ".new", sizeof newpath - strlen(newpath)-1); strncpy(savepath, path, sizeof savepath); strncat(savepath, ".save", sizeof savepath - strlen(savepath)-1); #ifdef _WIN32 wnewpath = utf8_to_wchar(newpath); if ((out = _wfopen(wnewpath, TQSL_OPEN_WRITE)) == NULL) { free_wchar(wnewpath); #else if ((out = fopen(newpath, TQSL_OPEN_WRITE)) == NULL) { #endif tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_clear_deleted", "open file %s: %s", newpath, strerror(tQSL_Errno)); goto trk_end; } for (it = records.begin(); it != records.end(); it++) { map::iterator mit; for (mit = it->begin(); mit != it->end(); mit++) { if (tqsl_write_adif_field(out, mit->first.c_str(), 0, (const unsigned char *)mit->second.c_str(), -1)) { tqslTrace("tqsl_clear_deleted", "error writing %s", strerror(tQSL_Errno)); #ifdef _WIN32 free_wchar(wnewpath); #endif goto trk_end; } } tqsl_write_adif_field(out, "eor", 0, NULL, 0); } if (fclose(out) == EOF) { tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_clear_deleted", "error closing %s", strerror(tQSL_Errno)); #ifdef _WIN32 free_wchar(wnewpath); #endif goto trk_end; } out = 0; /* Output file looks okay. Replace the old file with the new one. */ #ifdef _WIN32 wchar_t* wsavepath = utf8_to_wchar(savepath); if (_wunlink(wsavepath) && errno != ENOENT) { free_wchar(wsavepath); free_wchar(wnewpath); #else if (unlink(savepath) && errno != ENOENT) { #endif tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_clear_deleted", "unlink file %s: %s", savepath, strerror(tQSL_Errno)); goto trk_end; } #ifdef _WIN32 wchar_t* wpath = utf8_to_wchar(path); if (_wrename(wpath, wsavepath) && errno != ENOENT) { free_wchar(wpath); free_wchar(wsavepath); free_wchar(wnewpath); #else if (rename(path, savepath) && errno != ENOENT) { #endif tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_clear_deleted", "rename file %s->%s: %s", path, savepath, strerror(tQSL_Errno)); goto trk_end; } #ifdef _WIN32 if (_wrename(wnewpath, wpath)) { free_wchar(wnewpath); free_wchar(wpath); free_wchar(wsavepath); #else if (rename(newpath, path)) { #endif tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_clear_deleted", "rename file %s->%s: %s", newpath, path, strerror(tQSL_Errno)); goto trk_end; } #ifdef _WIN32 free_wchar(wnewpath); free_wchar(wpath); free_wchar(wsavepath); #endif rval = 0; trk_end: tqsl_close_key_file(); if (out) fclose(out); if (new_key) EVP_PKEY_free(new_key); if (key) EVP_PKEY_free(key); if (bio) BIO_free(bio); return rval; } static int tqsl_key_exists(const char *callsign, EVP_PKEY *cert_key) { map fields; vector< map >::iterator it; EVP_PKEY *key = NULL; BIO *bio = 0; int rval = 0; char path[256]; if (!tqsl_make_key_path(callsign, path, sizeof path)) { tqslTrace("tqsl_createCertRequest", "make_key_path error %d", errno); return 0; } if (tqsl_open_key_file(path)) { return 0; } while (tqsl_read_key(fields) == 0) { if ((bio = BIO_new_mem_buf(reinterpret_cast(const_cast(fields["PUBLIC_KEY"].c_str())), fields["PUBLIC_KEY"].length())) == NULL) { tqslTrace("tqsl_clear_deleted", "BIO_new_mem_buf error %s", tqsl_openssl_error()); goto trk_end; } if ((key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL)) == NULL) { tqslTrace("tqsl_clear_deleted", "Pem_read_bio_rsa_pubkey error %s", tqsl_openssl_error()); goto trk_end; } BIO_free(bio); bio = NULL; if (EVP_PKEY_cmp(key, cert_key) == 1) { rval = 1; } } trk_end: tqsl_close_key_file(); if (key) EVP_PKEY_free(key); if (bio) BIO_free(bio); return rval; } /** Save the json results for a given callsign location Detail. */ DLLEXPORT int CALLCONVENTION tqsl_saveCallsignLocationInfo(const char *callsign, const char *json) { FILE *out; if (callsign == NULL || json == NULL) { tqslTrace("tqsl_saveCallsinLocationInfo", "arg error callsign=0x%lx, json=0x%lx", callsign, json); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } char fixcall[256]; char path[PATH_MAX]; size_t size = sizeof path; tqsl_clean_call(callsign, fixcall, sizeof fixcall); strncpy(path, tQSL_BaseDir, size); #ifdef _WIN32 strncat(path, "\\", size - strlen(path)); #else strncat(path, "/", size - strlen(path)); #endif strncat(path, fixcall, size - strlen(path)); strncat(path, ".json", size - strlen(path)); /* Try opening the output stream */ #ifdef _WIN32 wchar_t* wfilename = utf8_to_wchar(path); if ((out = _wfopen(wfilename, TQSL_OPEN_WRITE)) == NULL) { free_wchar(wfilename); #else if ((out = fopen(path, TQSL_OPEN_WRITE)) == NULL) { #endif strncpy(tQSL_ErrorFile, path, sizeof tQSL_ErrorFile); tqslTrace("tqsl_saveCallsignLocationInfo", "Open file - system error %s", strerror(errno)); tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; return 1; } #ifdef _WIN32 free_wchar(wfilename); #endif if (fputs(json, out) == EOF) { strncpy(tQSL_ErrorFile, path, sizeof tQSL_ErrorFile); tqslTrace("tqsl_createCertRequest", "Write request file - system error %s", strerror(errno)); tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; return 1; } if (fclose(out) == EOF) { strncpy(tQSL_ErrorFile, path, sizeof tQSL_ErrorFile); tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_saveCallsignLocationInfo", "write error %d", errno); return 1; } return 0; } /** Retrieve the json results for a given callsign location Detail. */ DLLEXPORT int CALLCONVENTION tqsl_getCallsignLocationInfo(const char *callsign, char **buf) { FILE *in; static void* mybuf = NULL; static size_t bufsize = 0; if (bufsize == 0) { bufsize = 4096; mybuf = tqsl_malloc(bufsize); } if (callsign == NULL || buf == NULL) { tqslTrace("tqsl_getCallsinLocationInfo", "arg error callsign=0x%lx, buf=0x%lx", callsign, buf); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } char fixcall[256]; char path[PATH_MAX]; size_t size = sizeof path; tqsl_clean_call(callsign, fixcall, sizeof fixcall); strncpy(path, tQSL_BaseDir, size); #ifdef _WIN32 strncat(path, "\\", size - strlen(path)); #else strncat(path, "/", size - strlen(path)); #endif strncat(path, fixcall, size - strlen(path)); strncat(path, ".json", size - strlen(path)); size_t buflen = bufsize; #ifdef _WIN32 struct _stat32 s; wchar_t* wfilename = utf8_to_wchar(path); if (_wstat32(wfilename, &s) == 0) { buflen = s.st_size + 512; } if ((in = _wfopen(wfilename, TQSL_OPEN_READ)) == NULL) { free_wchar(wfilename); #else struct stat s; if (stat(path, &s) == 0) { buflen = s.st_size + 512; } if ((in = fopen(path, TQSL_OPEN_READ)) == NULL) { #endif strncpy(tQSL_ErrorFile, path, sizeof tQSL_ErrorFile); tqslTrace("tqsl_getCallsignLocationInfo", "Open file - system error %s", strerror(errno)); tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; return 1; } #ifdef _WIN32 free_wchar(wfilename); #endif if (buflen > bufsize) { bufsize = buflen + 512; mybuf = tqsl_realloc(mybuf, bufsize); } *buf = reinterpret_cast(mybuf); size_t len; if ((len = fread(mybuf, 1, buflen, in)) == 0) { strncpy(tQSL_ErrorFile, path, sizeof tQSL_ErrorFile); tqslTrace("tqsl_getCallsignLocationInformation", "Read file - system error %s", strerror(errno)); tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; return 1; } if (fclose(in) == EOF) { strncpy(tQSL_ErrorFile, path, sizeof tQSL_ErrorFile); tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_getCallsignLocationInformation", "read error %d", errno); return 1; } if (len < (size_t)buflen) { char *t = reinterpret_cast(mybuf); t[len] = '\0'; } return 0; } tqsl-2.5.1/src/location.h0000664000076400007640000000123413563134442015267 0ustar rmurphyrmurphy/*************************************************************************** location.h - description ------------------- begin : Fri Nov 15 2002 copyright : (C) 2002 by ARRL author : Jon Bloom email : jbloom@arrl.org revision : $Id$ ***************************************************************************/ #ifndef __location_h #define __location_h // TQSL_LOCATION_FIELD flag bits #define TQSL_LOCATION_FIELD_UPPER 1 #define TQSL_LOCATION_FIELD_MUSTSEL 2 #define TQSL_LOCATION_FIELD_SELNXT 4 #endif // __location_h tqsl-2.5.1/src/location.cpp0000775000076400007640000034353613563134442015643 0ustar rmurphyrmurphy/*************************************************************************** location.cpp - description ------------------- begin : Wed Nov 6 2002 copyright : (C) 2002 by ARRL author : Jon Bloom email : jbloom@arrl.org revision : $Id: location.cpp,v 1.14 2013/03/01 13:20:30 k1mu Exp $ ***************************************************************************/ #define DXCC_TEST #define TQSLLIB_DEF #include "location.h" #include #include #include #ifdef __APPLE__ #include #endif #include #include #include #include #include #include #include #include #include #include #include "tqsllib.h" #include "tqslerrno.h" #include "xml.h" #include "openssl_cert.h" #ifdef _WIN32 #include "windows.h" #include #endif #include "winstrdefs.h" using std::string; using std::vector; using std::map; using std::pair; using std::make_pair; using std::ofstream; using std::ios; using std::endl; using std::exception; namespace tqsllib { class TQSL_LOCATION_ITEM { public: TQSL_LOCATION_ITEM() : ivalue(0) {} string text; string label; string zonemap; int ivalue; }; class TQSL_LOCATION_FIELD { public: TQSL_LOCATION_FIELD() {} 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 = 0); string label; string gabbi_name; int data_type; int data_len; string cdata; vector items; int idx; int idata; int input_type; int flags; bool changed; string dependency; }; TQSL_LOCATION_FIELD::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) : data_type(i_data_type), data_len(i_data_len), cdata(""), input_type(i_input_type), flags(i_flags) { if (!i_gabbi_name.empty()) gabbi_name = i_gabbi_name; if (i_label) label = i_label; idx = idata = 0; } typedef vector TQSL_LOCATION_FIELDLIST; class TQSL_LOCATION_PAGE { public: TQSL_LOCATION_PAGE() : complete(false), prev(0), next(0) {} bool complete; int prev, next; string dependentOn, dependency; map > hash; TQSL_LOCATION_FIELDLIST fieldlist; }; typedef vector TQSL_LOCATION_PAGELIST; class TQSL_NAME { public: explicit TQSL_NAME(string n = "", string c = "") : name(n), call(c) {} string name; string call; }; class TQSL_LOCATION { public: TQSL_LOCATION() : sentinel(0x5445), page(0), cansave(false), sign_clean(false), cert_flags(TQSL_SELECT_CERT_WITHKEYS | TQSL_SELECT_CERT_EXPIRED), newflags(false) {} ~TQSL_LOCATION() { sentinel = 0; } int sentinel; int page; bool cansave; string name; TQSL_LOCATION_PAGELIST pagelist; vector names; string signdata; string loc_details; string qso_details; bool sign_clean; string tSTATION; string tCONTACT; string sigspec; char data_errors[512]; int cert_flags; bool newflags; }; class Band { public: string name, spectrum; int low, high; }; class Mode { public: string mode, group; }; class PropMode { public: string descrip, name; }; class Satellite { public: Satellite() { start.year = start.month = start.day = 0; end.year = end.month = end.day = 0; } string descrip, name; tQSL_Date start, end; }; bool operator< (const Band& o1, const Band& o2) { static const char *suffixes[] = { "M", "CM", "MM"}; static const char *prefix_chars = "0123456789."; // get suffixes string b1_suf = o1.name.substr(o1.name.find_first_not_of(prefix_chars)); string b2_suf = o2.name.substr(o2.name.find_first_not_of(prefix_chars)); if (b1_suf != b2_suf) { // Suffixes differ -- compare suffixes int b1_idx = (sizeof suffixes / sizeof suffixes[0]); int b2_idx = b1_idx; for (int i = 0; i < static_cast(sizeof suffixes / sizeof suffixes[0]); i++) { if (b1_suf == suffixes[i]) b1_idx = i; if (b2_suf == suffixes[i]) b2_idx = i; } return b1_idx < b2_idx; } return atof(o1.name.c_str()) > atof(o2.name.c_str()); } bool operator< (const PropMode& o1, const PropMode& o2) { if (o1.descrip < o2.descrip) return true; if (o1.descrip == o2.descrip) return (o1.name < o2.name); return false; } bool operator< (const Satellite& o1, const Satellite& o2) { if (o1.descrip < o2.descrip) return true; if (o1.descrip == o2.descrip) return (o1.name < o2.name); return false; } bool operator< (const Mode& o1, const Mode& o2) { static const char *groups[] = { "CW", "PHONE", "IMAGE", "DATA" }; // m1 < m2 if m1 is a modegroup and m2 is not if (o1.mode == o1.group) { if (o2.mode != o2.group) return true; } else if (o2.mode == o2.group) { return false; } // If groups are same, compare modes if (o1.group == o2.group) return o1.mode < o2.mode; int m1_g = (sizeof groups / sizeof groups[0]); int m2_g = m1_g; for (int i = 0; i < static_cast(sizeof groups / sizeof groups[0]); i++) { if (o1.group == groups[i]) m1_g = i; if (o2.group == groups[i]) m2_g = i; } return m1_g < m2_g; } } // namespace tqsllib using tqsllib::XMLElement; using tqsllib::XMLElementList; using tqsllib::Band; using tqsllib::Mode; using tqsllib::PropMode; using tqsllib::Satellite; using tqsllib::TQSL_LOCATION; using tqsllib::TQSL_LOCATION_PAGE; using tqsllib::TQSL_LOCATION_PAGELIST; using tqsllib::TQSL_LOCATION_FIELD; using tqsllib::TQSL_LOCATION_FIELDLIST; using tqsllib::TQSL_LOCATION_ITEM; using tqsllib::TQSL_NAME; using tqsllib::ROOTCERT; using tqsllib::CACERT; using tqsllib::USERCERT; using tqsllib::tqsl_get_pem_serial; #define CAST_TQSL_LOCATION(x) (reinterpret_cast((x))) typedef map IntMap; typedef map BoolMap; typedef map DateMap; static int num_entities = 0; static bool _ent_init = false; static struct _dxcc_entity { int number; const char* name; const char *zonemap; tQSL_Date start, end; } *entity_list = 0; // config data static XMLElement tqsl_xml_config; static int tqsl_xml_config_major = -1; static int tqsl_xml_config_minor = 0; static IntMap DXCCMap; static BoolMap DeletedMap; static IntMap DXCCZoneMap; static DateMap DXCCStartMap; static DateMap DXCCEndMap; static vector< pair > DXCCList; static vector BandList; static vector ModeList; static vector PropModeList; static vector SatelliteList; static map tqsl_page_map; static map tqsl_field_map; static map tqsl_adif_map; static map tqsl_adif_submode_map; static map > tqsl_cabrillo_map; static map > tqsl_cabrillo_user_map; static char char_toupper(char c) { return toupper(c); } static string string_toupper(const string& in) { string out = in; transform(out.begin(), out.end(), out.begin(), char_toupper); return out; } // isspace() called on extended chars in UTF-8 raises asserts in // the windows C++ libs. Don't call isspace() if out of range. // static inline int isspc(int c) { if (c < 0 || c > 255) return 0; return isspace(c); } // trim from start static inline std::string <rim(std::string &s) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(isspc)))); return s; } // trim from end static inline std::string &rtrim(std::string &s) { s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(isspc))).base(), s.end()); return s; } // trim from both ends static inline std::string &trim(std::string &s) { return ltrim(rtrim(s)); } #define TQSL_NPAGES 4 static TQSL_LOCATION * check_loc(tQSL_Location loc, bool unclean = true) { if (tqsl_init()) return 0; if (loc == 0) return 0; if (unclean) CAST_TQSL_LOCATION(loc)->sign_clean = false; return CAST_TQSL_LOCATION(loc); } static int tqsl_load_xml_config() { if (tqsl_xml_config.getElementList().size() > 0) // Already init'd return 0; XMLElement default_config; XMLElement user_config; tqslTrace("tqsl_load_xml_config", NULL); #ifdef _WIN32 string default_path = string(tQSL_RsrcDir) + "\\config.xml"; string user_path = string(tQSL_BaseDir) + "\\config.xml"; #else string default_path = string(tQSL_RsrcDir) + "/config.xml"; string user_path = string(tQSL_BaseDir) + "/config.xml"; #endif tqslTrace("tqsl_load_xml_config", "user_path=%s", user_path.c_str()); int default_status = default_config.parseFile(default_path.c_str()); int user_status = user_config.parseFile(user_path.c_str()); tqslTrace("tqsl_load_xml_config", "default_status=%d, user_status=%d", default_status, user_status); if (default_status != XML_PARSE_NO_ERROR && user_status != XML_PARSE_NO_ERROR) { if (user_status == XML_PARSE_SYSTEM_ERROR) tQSL_Error = TQSL_CONFIG_ERROR; else tQSL_Error = TQSL_CONFIG_SYNTAX_ERROR; return 1; } int default_major = -1; int default_minor = 0; int user_major = -1; int user_minor = 0; XMLElement top; if (default_config.getFirstElement("tqslconfig", top)) { default_major = strtol(top.getAttribute("majorversion").first.c_str(), NULL, 10); default_minor = strtol(top.getAttribute("minorversion").first.c_str(), NULL, 10); } if (user_config.getFirstElement("tqslconfig", top)) { user_major = strtol(top.getAttribute("majorversion").first.c_str(), NULL, 10); user_minor = strtol(top.getAttribute("minorversion").first.c_str(), NULL, 10); } if (default_major > user_major || (default_major == user_major && default_minor > user_minor)) { tqsl_xml_config = default_config; tqsl_xml_config_major = default_major; tqsl_xml_config_minor = default_minor; return 0; } if (user_major < 0) { tQSL_Error = TQSL_CONFIG_SYNTAX_ERROR; tqslTrace("tqsl_load_xml_config", "Syntax error"); return 1; } tqsl_xml_config = user_config; tqsl_xml_config_major = user_major; tqsl_xml_config_minor = user_minor; return 0; } static int tqsl_get_xml_config_section(const string& section, XMLElement& el) { if (tqsl_load_xml_config()) return 1; XMLElement top; if (!tqsl_xml_config.getFirstElement("tqslconfig", top)) { tqsl_xml_config.clear(); tQSL_Error = TQSL_CONFIG_SYNTAX_ERROR; return 1; } if (!top.getFirstElement(section, el)) { tQSL_Error = TQSL_CONFIG_SYNTAX_ERROR; return 1; } return 0; } static int tqsl_load_provider_list(vector &plist) { plist.clear(); XMLElement providers; if (tqsl_get_xml_config_section("providers", providers)) return 1; tqslTrace("tqsl_load_provider_list", NULL); XMLElement provider; bool gotit = providers.getFirstElement("provider", provider); while (gotit) { TQSL_PROVIDER pdata; memset(&pdata, 0, sizeof pdata); pair rval = provider.getAttribute("organizationName"); if (!rval.second) { tQSL_Error = TQSL_PROVIDER_NOT_FOUND; tqslTrace("tqsl_load_provider_list", "Providers not found"); return 1; } strncpy(pdata.organizationName, rval.first.c_str(), sizeof pdata.organizationName); XMLElement item; if (provider.getFirstElement("organizationalUnitName", item)) strncpy(pdata.organizationalUnitName, item.getText().c_str(), sizeof pdata.organizationalUnitName); if (provider.getFirstElement("emailAddress", item)) strncpy(pdata.emailAddress, item.getText().c_str(), sizeof pdata.emailAddress); if (provider.getFirstElement("url", item)) strncpy(pdata.url, item.getText().c_str(), sizeof pdata.url); plist.push_back(pdata); gotit = providers.getNextElement(provider); if (gotit && provider.getElementName() != "provider") break; } return 0; } static XMLElement tCONTACT_sign; static int make_sign_data(TQSL_LOCATION *loc) { map field_data; // Loop through the location pages, getting field data // int old_page = loc->page; tqsl_setStationLocationCapturePage(loc, 1); do { TQSL_LOCATION_PAGE& p = loc->pagelist[loc->page-1]; for (int i = 0; i < static_cast(p.fieldlist.size()); i++) { TQSL_LOCATION_FIELD& f = p.fieldlist[i]; string s; if (f.input_type == TQSL_LOCATION_FIELD_DDLIST || f.input_type == TQSL_LOCATION_FIELD_LIST) { if (f.idx < 0 || f.idx >= static_cast(f.items.size())) s = ""; else s = f.items[f.idx].text; } else if (f.data_type == TQSL_LOCATION_FIELD_INT) { char buf[20]; snprintf(buf, sizeof buf, "%d", f.idata); s = buf; } else { s = f.cdata; } field_data[f.gabbi_name] = s; } int rval; if (tqsl_hasNextStationLocationCapture(loc, &rval) || !rval) break; tqsl_nextStationLocationCapture(loc); } while (1); tqsl_setStationLocationCapturePage(loc, old_page); loc->signdata = ""; loc->loc_details = ""; loc->sign_clean = false; XMLElement sigspecs; if (tqsl_get_xml_config_section("sigspecs", sigspecs)) { tQSL_Error = TQSL_CUSTOM_ERROR; strncpy(tQSL_CustomError, "TQSL Configuration file invalid - it does not have a sigspecs section", sizeof tQSL_CustomError); tqslTrace("make_sign_data", "Error %s", tQSL_CustomError); return 1; } XMLElement sigspec; XMLElement ss; if (!sigspecs.getFirstElement("sigspec", sigspec)) { tQSL_Error = TQSL_CUSTOM_ERROR; strncpy(tQSL_CustomError, "TQSL Configuration file invalid - it does not have a sigspec section", sizeof tQSL_CustomError); tqslTrace("make_sign_data", "Error %s", tQSL_CustomError); return 1; } ss = sigspec; bool ok; do { if (sigspec.getAttribute("status").first == "deprecated") { ok = sigspecs.getNextElement(sigspec); continue; } double ssver = atof(ss.getAttribute("version").first.c_str()); double newver = atof(sigspec.getAttribute("version").first.c_str()); if (newver > ssver) ss = sigspec; ok = sigspecs.getNextElement(sigspec); } while (ok); sigspec = ss; loc->sigspec = "SIGN_"; loc->sigspec += sigspec.getAttribute("name").first; loc->sigspec += "_V"; loc->sigspec += sigspec.getAttribute("version").first; tCONTACT_sign.clear(); if (!sigspec.getFirstElement("tCONTACT", tCONTACT_sign)) { tQSL_Error = TQSL_CUSTOM_ERROR; strncpy(tQSL_CustomError, "TQSL Configuration file invalid - missing sigspec.tCONTACT", sizeof tQSL_CustomError); tqslTrace("make_sign_data", "Error %s", tQSL_CustomError); return 1; } if (tCONTACT_sign.getElementList().size() == 0) { tQSL_Error = TQSL_CUSTOM_ERROR; strncpy(tQSL_CustomError, "TQSL Configuration file invalid - empty sigspec.tCONTACT", sizeof tQSL_CustomError); tqslTrace("make_sign_data", "Error %s", tQSL_CustomError); return 1; } XMLElement tSTATION; if (!sigspec.getFirstElement("tSTATION", tSTATION)) { tQSL_Error = TQSL_CUSTOM_ERROR; strncpy(tQSL_CustomError, "TQSL Configuration file invalid - missing sigspec.tSTATION", sizeof tQSL_CustomError); tqslTrace("make_sign_data", "Error %s", tQSL_CustomError); return 1; } XMLElement specfield; if (!(ok = tSTATION.getFirstElement(specfield))) { tQSL_Error = TQSL_CUSTOM_ERROR; strncpy(tQSL_CustomError, "TQSL Configuration file invalid - missing tSTATION.specfield", sizeof tQSL_CustomError); tqslTrace("make_sign_data", "Error %s", tQSL_CustomError); return 1; } do { string value = field_data[specfield.getElementName()]; value = trim(value); if (value == "") { pair attr = specfield.getAttribute("required"); if (attr.second && strtol(attr.first.c_str(), NULL, 10)) { string err = specfield.getElementName() + " field required by "; attr = sigspec.getAttribute("name"); if (attr.second) err += attr.first + " "; attr = sigspec.getAttribute("version"); if (attr.second) err += "V" + attr.first + " "; err += "signature specification not found"; tQSL_Error = TQSL_CUSTOM_ERROR; strncpy(tQSL_CustomError, err.c_str(), sizeof tQSL_CustomError); tqslTrace("make_sign_data", "Error %s", tQSL_CustomError); return 1; } } else { loc->signdata += value; if (loc->loc_details != "") { loc->loc_details += ", "; } loc->loc_details += specfield.getElementName() + ": " + value; } ok = tSTATION.getNextElement(specfield); } while (ok); loc->sign_clean = true; return 0; } static int init_dxcc() { if (DXCCMap.size() > 0) return 0; tqslTrace("init_dxcc", NULL); XMLElement dxcc; if (tqsl_get_xml_config_section("dxcc", dxcc)) { tqslTrace("init_dxcc", "Error %d getting dxcc config section", tQSL_Error); return 1; } XMLElement dxcc_entity; bool ok = dxcc.getFirstElement("entity", dxcc_entity); while (ok) { pair rval = dxcc_entity.getAttribute("arrlId"); pair zval = dxcc_entity.getAttribute("zonemap"); pair strdate = dxcc_entity.getAttribute("valid"); pair enddate = dxcc_entity.getAttribute("invalid"); pair deleted = dxcc_entity.getAttribute("deleted"); if (rval.second) { int num = strtol(rval.first.c_str(), NULL, 10); DXCCMap[num] = dxcc_entity.getText(); DeletedMap[num] = false; if (deleted.second) { DeletedMap[num] = (deleted.first == "1"); } if (zval.second) DXCCZoneMap[num] = zval.first; tQSL_Date d; d.year = 1945; d.month = 11; d.day = 15; DXCCStartMap[num] = d; if (strdate.second) { if (!tqsl_initDate(&d, strdate.first.c_str())) DXCCStartMap[num] = d; } d.year = 0; d.month = 0; d.day = 0; DXCCEndMap[num] = d; if (enddate.second) { if (!tqsl_initDate(&d, enddate.first.c_str())) DXCCEndMap[num] = d; } DXCCList.push_back(make_pair(num, dxcc_entity.getText())); } ok = dxcc.getNextElement(dxcc_entity); } return 0; } static int init_band() { if (BandList.size() > 0) return 0; tqslTrace("init_band", NULL); XMLElement bands; if (tqsl_get_xml_config_section("bands", bands)) { tqslTrace("init_band", "Error %d getting bands", tQSL_Error); return 1; } XMLElement config_band; bool ok = bands.getFirstElement("band", config_band); while (ok) { Band b; b.name = config_band.getText(); b.spectrum = config_band.getAttribute("spectrum").first; b.low = strtol(config_band.getAttribute("low").first.c_str(), NULL, 10); b.high = strtol(config_band.getAttribute("high").first.c_str(), NULL, 10); BandList.push_back(b); ok = bands.getNextElement(config_band); } sort(BandList.begin(), BandList.end()); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getConfigVersion(int *major, int *minor) { if (tqsl_init()) return 1; if (tqsl_load_xml_config()) { tqslTrace("tqsl_getConfigVersion", "Error %d from tqsl_load_xml_config", tQSL_Error); return 1; } tqslTrace("tqsl_getConfigVersion", "major=%d, minor=%d", tqsl_xml_config_major, tqsl_xml_config_minor); if (major) *major = tqsl_xml_config_major; if (minor) *minor = tqsl_xml_config_minor; return 0; } DLLEXPORT int CALLCONVENTION tqsl_getNumBand(int *number) { if (number == 0) { tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } tqslTrace("tqsl_getNumBand", NULL); if (init_band()) { tqslTrace("tqsl_getNumBand", "init_band error=%d", tQSL_Error); return 1; } *number = BandList.size(); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getBand(int index, const char **name, const char **spectrum, int *low, int *high) { if (index < 0 || name == 0) { tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (init_band()) { tqslTrace("tqsl_getBand", "init_band error=%d", tQSL_Error); return 1; } if (index >= static_cast(BandList.size())) { tQSL_Error = TQSL_ARGUMENT_ERROR; tqslTrace("tqsl_getBand", "init_band arg error - index %d", index); return 1; } *name = BandList[index].name.c_str(); if (spectrum) *spectrum = BandList[index].spectrum.c_str(); if (low) *low = BandList[index].low; if (high) *high = BandList[index].high; return 0; } static int init_mode() { if (ModeList.size() > 0) return 0; XMLElement modes; if (tqsl_get_xml_config_section("modes", modes)) { tqslTrace("init_mode", "Error from tqsl_get_xml_config_section %d", tQSL_Error); return 1; } XMLElement config_mode; bool ok = modes.getFirstElement("mode", config_mode); while (ok) { Mode m; m.mode = config_mode.getText(); m.group = config_mode.getAttribute("group").first; ModeList.push_back(m); ok = modes.getNextElement(config_mode); } sort(ModeList.begin(), ModeList.end()); return 0; } static int init_propmode() { if (PropModeList.size() > 0) return 0; XMLElement propmodes; if (tqsl_get_xml_config_section("propmodes", propmodes)) { tqslTrace("init_propmode", "Error getting config section %d", tQSL_Error); return 1; } XMLElement config_mode; bool ok = propmodes.getFirstElement("propmode", config_mode); while (ok) { PropMode p; p.descrip = config_mode.getText(); p.name = config_mode.getAttribute("name").first; PropModeList.push_back(p); ok = propmodes.getNextElement(config_mode); } sort(PropModeList.begin(), PropModeList.end()); return 0; } static int init_satellite() { if (SatelliteList.size() > 0) return 0; XMLElement satellites; if (tqsl_get_xml_config_section("satellites", satellites)) { tqslTrace("init_satellite", "Error getting config section %d", tQSL_Error); return 1; } XMLElement config_sat; bool ok = satellites.getFirstElement("satellite", config_sat); while (ok) { Satellite s; s.descrip = config_sat.getText(); s.name = config_sat.getAttribute("name").first; tQSL_Date d; if (!tqsl_initDate(&d, config_sat.getAttribute("startDate").first.c_str())) s.start = d; if (!tqsl_initDate(&d, config_sat.getAttribute("endDate").first.c_str())) s.end = d; SatelliteList.push_back(s); ok = satellites.getNextElement(config_sat); } sort(SatelliteList.begin(), SatelliteList.end()); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getNumMode(int *number) { if (tqsl_init()) return 1; if (number == NULL) { tqslTrace("tqsl_getNumMode", "Argument error, number = 0x%lx", number); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (init_mode()) { tqslTrace("tqsl_getNumMode", "init_mode error %d", tQSL_Error); return 1; } *number = ModeList.size(); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getMode(int index, const char **mode, const char **group) { if (index < 0 || mode == NULL) { tqslTrace("tqsl_getMode", "Arg error index=%d, mode=0x%lx, group=0x%lx", index, mode, group); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (init_mode()) { tqslTrace("tqsl_getMode", "init_mode error %d", tQSL_Error); return 1; } if (index >= static_cast(ModeList.size())) { tqslTrace("tqsl_getMode", "Argument error: %d", index); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } *mode = ModeList[index].mode.c_str(); if (group) *group = ModeList[index].group.c_str(); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getNumDXCCEntity(int *number) { if (number == NULL) { tqslTrace("tqsl_getNumDXCCEntity", "Arg error - number=null"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (init_dxcc()) { tqslTrace("tqsl_getNumDXCCEntity", "init_dxcc error %d", tQSL_Error); return 1; } *number = DXCCList.size(); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getDXCCEntity(int index, int *number, const char **name) { if (index < 0 || name == NULL || number == NULL) { tqslTrace("tqsl_getDXCCEntity", "arg error index=%d, number = 0x%lx, name=0x%lx", index, number, name); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (init_dxcc()) { tqslTrace("tqsl_getDXCCEntity", "init_dxcc error %d", tQSL_Error); return 1; } if (index >= static_cast(DXCCList.size())) { tQSL_Error = TQSL_ARGUMENT_ERROR; tqslTrace("tqsl_getDXCCEntity", "index range %d", index); return 1; } *number = DXCCList[index].first; *name = DXCCList[index].second.c_str(); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getDXCCEntityName(int number, const char **name) { if (name == NULL) { tqslTrace("tqsl_getDXCCEntityName", "Name=null"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (init_dxcc()) { tqslTrace("tqsl_getDXCCEntityName", "init_dxcc error %d", tQSL_Error); return 1; } IntMap::const_iterator it; it = DXCCMap.find(number); if (it == DXCCMap.end()) { tQSL_Error = TQSL_NAME_NOT_FOUND; return 1; } *name = it->second.c_str(); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getDXCCZoneMap(int number, const char **zonemap) { if (zonemap == NULL) { tqslTrace("tqsl_getDXCCZoneMap", "zonemap ptr null"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (init_dxcc()) { tqslTrace("tqsl_getDXCCZoneMap", "init_dxcc error %d", tQSL_Error); return 1; } IntMap::const_iterator it; it = DXCCZoneMap.find(number); if (it == DXCCZoneMap.end()) { tQSL_Error = TQSL_NAME_NOT_FOUND; return 1; } const char *map = it->second.c_str(); if (!map || map[0] == '\0') *zonemap = NULL; else *zonemap = map; return 0; } DLLEXPORT int CALLCONVENTION tqsl_getDXCCStartDate(int number, tQSL_Date *d) { if (d == NULL) { tqslTrace("tqsl_getDXCCStartDate", "date ptr null"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (init_dxcc()) { tqslTrace("tqsl_getDXCCStartDate", "init_dxcc error %d", tQSL_Error); return 1; } DateMap::const_iterator it; it = DXCCStartMap.find(number); if (it == DXCCStartMap.end()) { tQSL_Error = TQSL_NAME_NOT_FOUND; return 1; } tQSL_Date newdate = it->second; *d = newdate; return 0; } DLLEXPORT int CALLCONVENTION tqsl_getDXCCEndDate(int number, tQSL_Date *d) { if (d == NULL) { tqslTrace("tqsl_getDXCCEndDate", "date ptr null"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (init_dxcc()) { tqslTrace("tqsl_getDXCCEndDate", "init_dxcc error %d", tQSL_Error); return 1; } DateMap::const_iterator it; it = DXCCEndMap.find(number); if (it == DXCCEndMap.end()) { tQSL_Error = TQSL_NAME_NOT_FOUND; return 1; } tQSL_Date newdate = it->second; *d = newdate; return 0; } DLLEXPORT int CALLCONVENTION tqsl_getDXCCDeleted(int number, int *deleted) { if (deleted == NULL) { tqslTrace("tqsl_getDXCCDeleted", "Name=null"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (init_dxcc()) { tqslTrace("tqsl_getDXCCDeleted", "init_dxcc error %d", tQSL_Error); return 1; } *deleted = 0; BoolMap::const_iterator it; it = DeletedMap.find(number); if (it == DeletedMap.end()) { tQSL_Error = TQSL_NAME_NOT_FOUND; return 1; } *deleted = it->second; return 0; } DLLEXPORT int CALLCONVENTION tqsl_getNumPropagationMode(int *number) { if (tqsl_init()) return 1; if (number == NULL) { tqslTrace("tqsl_getNumPropagationMode", "number=null"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (init_propmode()) { tqslTrace("tqsl_getNumPropagationMode", "init_propmode error %d", tQSL_Error); return 1; } *number = PropModeList.size(); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getPropagationMode(int index, const char **name, const char **descrip) { if (index < 0 || name == NULL) { tqslTrace("tqsl_getPropagationMode", "arg error index=%d name=0x%lx descrip=0x%lx", index, name, descrip); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (init_propmode()) { tqslTrace("tqsl_getPropagationMode", "init_propmode error %d", tQSL_Error); return 1; } if (index >= static_cast(PropModeList.size())) { tqslTrace("tqsl_getPropagationMode", "index out of range: %d", index); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } *name = PropModeList[index].name.c_str(); if (descrip) *descrip = PropModeList[index].descrip.c_str(); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getNumSatellite(int *number) { if (tqsl_init()) return 1; if (number == NULL) { tqslTrace("tqsl_getNumSatellite", "arg error number = null"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (init_satellite()) { tqslTrace("tqsl_getNumSatellite", "init_satellite error %d", tQSL_Error); return 1; } *number = SatelliteList.size(); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getSatellite(int index, const char **name, const char **descrip, tQSL_Date *start, tQSL_Date *end) { if (index < 0 || name == NULL) { tqslTrace("tqsl_getSatellite", "arg error index=%d name=0x%lx", index, name); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (init_satellite()) { tqslTrace("tqsl_getSatellite", "init_satellite error %d", tQSL_Error); return 1; } if (index >= static_cast(SatelliteList.size())) { tqslTrace("tqsl_getSatellite", "index error %d", index); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } *name = SatelliteList[index].name.c_str(); if (descrip) *descrip = SatelliteList[index].descrip.c_str(); if (start) *start = SatelliteList[index].start; if (end) *end = SatelliteList[index].end; return 0; } static int init_cabrillo_map() { if (tqsl_cabrillo_map.size() > 0) return 0; XMLElement cabrillo_map; if (tqsl_get_xml_config_section("cabrillomap", cabrillo_map)) { tqslTrace("init_cabrillo_map", "get_xml_config_section error %d", tQSL_Error); return 1; } XMLElement cabrillo_item; bool ok = cabrillo_map.getFirstElement("cabrillocontest", cabrillo_item); while (ok) { if (cabrillo_item.getText() != "" && strtol(cabrillo_item.getAttribute("field").first.c_str(), NULL, 10) > TQSL_MIN_CABRILLO_MAP_FIELD) tqsl_cabrillo_map[cabrillo_item.getText()] = make_pair(strtol(cabrillo_item.getAttribute("field").first.c_str(), NULL, 10)-1, (cabrillo_item.getAttribute("type").first == "VHF") ? TQSL_CABRILLO_VHF : TQSL_CABRILLO_HF); ok = cabrillo_map.getNextElement(cabrillo_item); } return 0; } DLLEXPORT int CALLCONVENTION tqsl_clearCabrilloMap() { tqslTrace("tqsl_clearCabrilloMap", NULL); tqsl_cabrillo_user_map.clear(); return 0; } DLLEXPORT int CALLCONVENTION tqsl_setCabrilloMapEntry(const char *contest, int field, int contest_type) { if (contest == NULL || field <= TQSL_MIN_CABRILLO_MAP_FIELD || (contest_type != TQSL_CABRILLO_HF && contest_type != TQSL_CABRILLO_VHF)) { tqslTrace("tqsl_setCabrilloMapEntry", "arg error contest=0x%lx field = %d", contest, field); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } tqsl_cabrillo_user_map[string_toupper(contest)] = make_pair(field-1, contest_type); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getCabrilloMapEntry(const char *contest, int *fieldnum, int *contest_type) { if (contest == NULL || fieldnum == NULL) { tqslTrace("tqsl_getCabrilloMapEntry", "arg error contest=0x%lx fieldnum = 0x%lx", contest, fieldnum); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (init_cabrillo_map()) { tqslTrace("tqsl_getCabrilloMapEntry", "init_cabrillo_map errror %d", tQSL_Error); return 1; } map >::iterator it; if ((it = tqsl_cabrillo_user_map.find(string_toupper(contest))) == tqsl_cabrillo_user_map.end()) { if ((it = tqsl_cabrillo_map.find(string_toupper(contest))) == tqsl_cabrillo_map.end()) { *fieldnum = 0; return 0; } } *fieldnum = it->second.first + 1; if (contest_type) *contest_type = it->second.second; return 0; } static int init_adif_map() { if (tqsl_adif_map.size() > 0) return 0; XMLElement adif_map; if (tqsl_get_xml_config_section("adifmap", adif_map)) { tqslTrace("init_adif_map", "tqsl_get_xml_config_section error %d", tQSL_Error); return 1; } XMLElement adif_item; bool ok = adif_map.getFirstElement("adifmode", adif_item); while (ok) { string adifmode = adif_item.getAttribute("adif-mode").first; string submode = adif_item.getAttribute("adif-submode").first; // Prefer the "mode=" attribute of the mode definition, else get the item value. string gabbi = adif_item.getAttribute("mode").first; string melem = adif_item.getText(); if (adifmode != "" && submode != "") { tqsl_adif_submode_map[melem] = adifmode + "%" + submode; } if (adifmode == "") { // Handle entries with just a mode element adifmode = melem; } if (gabbi != "") { // There should always be one if (adifmode != "") { tqsl_adif_map[adifmode] = gabbi; } // Map this gabbi mode from submode if (submode != "" && submode != adifmode) { tqsl_adif_map[submode] = gabbi; } if (melem != "" && melem != adifmode) { tqsl_adif_map[melem] = gabbi; } // Add a mode%submode lookup too if (adifmode != "" && submode != "") { tqsl_adif_map[adifmode + "%" + submode] = gabbi; } } ok = adif_map.getNextElement(adif_item); } return 0; } DLLEXPORT int CALLCONVENTION tqsl_clearADIFModes() { tqsl_adif_map.clear(); return 0; } DLLEXPORT int CALLCONVENTION tqsl_setADIFMode(const char *adif_item, const char *mode) { if (adif_item == NULL || mode == NULL) { tqslTrace("tqsl_setADIFMode", "arg error adif_item=0x%lx mode=0x%lx", adif_item, mode); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (init_adif_map()) { tQSL_Error = TQSL_CUSTOM_ERROR; strncpy(tQSL_CustomError, "TQSL Configuration file invalid - ADIF map invalid", sizeof tQSL_CustomError); tqslTrace("tqslSetADIFMode", "Error %s", tQSL_CustomError); return 1; } string umode = string_toupper(mode); tqsl_adif_map[string_toupper(adif_item)] = umode; return 0; } DLLEXPORT int CALLCONVENTION tqsl_getADIFMode(const char *adif_item, char *mode, int nmode) { if (adif_item == NULL || mode == NULL) { tqslTrace("tqsl_getADIFMode", "arg error adif_item=0x%lx, mode=0x%lx", adif_item, mode); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (init_adif_map()) { tQSL_Error = TQSL_CUSTOM_ERROR; strncpy(tQSL_CustomError, "TQSL Configuration file invalid - ADIF map invalid", sizeof tQSL_CustomError); tqslTrace("tqsl_getADIFMode", "init_adif error %s", tQSL_CustomError); return 1; } string orig = adif_item; orig = string_toupper(orig); string amode; if (tqsl_adif_map.find(orig) != tqsl_adif_map.end()) { amode = tqsl_adif_map[orig]; } else { tQSL_Error = TQSL_NAME_NOT_FOUND; return 1; } if (nmode < static_cast(amode.length())+1) { tqslTrace("tqsl_getAdifMode", "buffer error %s %s", nmode, amode.length()); tQSL_Error = TQSL_BUFFER_ERROR; return 1; } strncpy(mode, amode.c_str(), nmode); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getADIFSubMode(const char *adif_item, char *mode, char *submode, int nmode) { if (adif_item == NULL || mode == NULL) { tqslTrace("tqsl_getADIFSubMode", "arg error adif_item=0x%lx, mode=0x%lx", adif_item, mode); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (init_adif_map()) { tQSL_Error = TQSL_CUSTOM_ERROR; strncpy(tQSL_CustomError, "TQSL Configuration file invalid - ADIF map invalid", sizeof tQSL_CustomError); tqslTrace("tqsl_getADIFSubMode", "init_adif error %s", tQSL_CustomError); return 1; } string orig = adif_item; orig = string_toupper(orig); string amode; if (tqsl_adif_submode_map.find(orig) != tqsl_adif_submode_map.end()) { amode = tqsl_adif_submode_map[orig]; } else { tQSL_Error = TQSL_NAME_NOT_FOUND; return 1; } string adifmode = amode.substr(0, amode.find("%")); string adifsubmode = amode.substr(amode.find("%")+1); if (nmode < static_cast(amode.length())+1) { tqslTrace("tqsl_getAdifSubMode", "buffer error %s %s", nmode, amode.length()); tQSL_Error = TQSL_BUFFER_ERROR; return 1; } strncpy(mode, adifmode.c_str(), nmode); strncpy(submode, adifsubmode.c_str(), nmode); return 0; } static int init_loc_maps() { if (tqsl_field_map.size() > 0) return 0; XMLElement config_pages; if (tqsl_get_xml_config_section("locpages", config_pages)) { tqslTrace("init_loc_maps", "get_xml_config_section error %d", tQSL_Error); return 1; } XMLElement config_page; tqsl_page_map.clear(); bool ok; for (ok = config_pages.getFirstElement("page", config_page); ok; ok = config_pages.getNextElement(config_page)) { pair Id = config_page.getAttribute("Id"); int page_num = strtol(Id.first.c_str(), NULL, 10); if (!Id.second || page_num < 1) { // Must have the Id! tQSL_Error = TQSL_CUSTOM_ERROR; strncpy(tQSL_CustomError, "TQSL Configuration file invalid - page missing ID", sizeof tQSL_CustomError); tqslTrace("init_loc_maps", "error %s", tQSL_CustomError); return 1; } tqsl_page_map[page_num] = config_page; } XMLElement config_fields; if (tqsl_get_xml_config_section("locfields", config_fields)) { tqslTrace("init_loc_maps", "get_xml_config_section locfields error %d", tQSL_Error); return 1; } XMLElement config_field; for (ok = config_fields.getFirstElement("field", config_field); ok; ok = config_fields.getNextElement(config_field)) { pair Id = config_field.getAttribute("Id"); if (!Id.second) { // Must have the Id! tQSL_Error = TQSL_CUSTOM_ERROR; strncpy(tQSL_CustomError, "TQSL Configuration file invalid - field missing ID", sizeof tQSL_CustomError); tqslTrace("init_loc_maps", "config field error %s", tQSL_CustomError); return 1; } tqsl_field_map[Id.first] = config_field; } return 0; } static bool inMap(int cqvalue, int ituvalue, bool cqz, bool ituz, const char *map) { /* * Parse the zone map and return true if the value is a valid zone number * The maps are colon-separated number pairs, with a list of pairs comma separated. */ int cq, itu; bool result = false; // No map or empty string -> all match if (!map || map[0] == '\0') { return true; } char *mapcopy = strdup(map); char *mapPart = strtok(mapcopy, ","); while (mapPart) { sscanf(mapPart, "%d:%d", &itu, &cq); if (cqz && ituz) { if ((cq == cqvalue || cqvalue == 0) && (itu == ituvalue || ituvalue == 0)) { result = true; break; } } else if (cqz && (cq == cqvalue || cqvalue == 0)) { result = true; break; } else if (ituz && (itu == ituvalue || ituvalue == 0)) { result = true; break; } mapPart = strtok(NULL, ","); } free(mapcopy); return result; } static int _ent_cmp(const void *a, const void *b) { return strcasecmp(((struct _dxcc_entity *)a)->name, ((struct _dxcc_entity *)b)->name); } static TQSL_LOCATION_FIELD * get_location_field(int page, const string& gabbi, TQSL_LOCATION *loc) { if (page == 0) page = loc->page; for (; page > 0; page = loc->pagelist[page-1].prev) { TQSL_LOCATION_FIELDLIST& fl = loc->pagelist[page-1].fieldlist; for (int j = 0; j < static_cast(fl.size()); j++) { if (fl[j].gabbi_name == gabbi) return &(fl[j]); } } return 0; } static int update_page(int page, TQSL_LOCATION *loc) { TQSL_LOCATION_PAGE& p = loc->pagelist[page-1]; int dxcc; int current_entity = -1; int loaded_cqz = -1; int loaded_ituz = -1; tqslTrace("update_page", "page=%d, loc=0x%lx", page, loc); for (int i = 0; i < static_cast(p.fieldlist.size()); i++) { TQSL_LOCATION_FIELD& field = p.fieldlist[i]; field.changed = false; if (field.gabbi_name == "CALL") { if (field.items.size() == 0 || loc->newflags) { // Build list of call signs from available certs field.changed = true; field.items.clear(); loc->newflags = false; field.flags = TQSL_LOCATION_FIELD_SELNXT; // Must be selected p.hash.clear(); tQSL_Cert *certlist; int ncerts; tqsl_selectCertificates(&certlist, &ncerts, 0, 0, 0, 0, loc->cert_flags); for (int i = 0; i < ncerts; i++) { char callsign[40]; tqsl_getCertificateCallSign(certlist[i], callsign, sizeof callsign); tqsl_getCertificateDXCCEntity(certlist[i], &dxcc); char ibuf[10]; snprintf(ibuf, sizeof ibuf, "%d", dxcc); bool found = false; // Only add a given DXCC entity to a call once. map >::iterator call_p; for (call_p = p.hash.begin(); call_p != p.hash.end(); call_p++) { if (call_p->first == callsign && call_p->second[0] == ibuf) { found = true; break; } } if (!found) p.hash[callsign].push_back(ibuf); tqsl_freeCertificate(certlist[i]); } free(certlist); // Fill the call sign list map >::iterator call_p; field.idx = 0; TQSL_LOCATION_ITEM none; none.text = "[None]"; field.items.push_back(none); for (call_p = p.hash.begin(); call_p != p.hash.end(); call_p++) { TQSL_LOCATION_ITEM item; item.text = call_p->first; if (item.text == field.cdata) field.idx = static_cast(field.items.size()); field.items.push_back(item); } if (field.idx == 0 && field.items.size() == 2) { field.idx = 1; } if (field.idx >= 0) { field.cdata = field.items[field.idx].text; } } } else if (field.gabbi_name == "DXCC") { // Note: Expects CALL to be field 0 of this page. string call = p.fieldlist[0].cdata; if (field.items.size() == 0 || call != field.dependency) { // rebuild list field.changed = true; init_dxcc(); int olddxcc = strtol(field.cdata.c_str(), NULL, 10); field.items.clear(); field.idx = 0; #ifdef DXCC_TEST const char *dxcc_test = getenv("TQSL_DXCC"); if (dxcc_test) { vector &entlist = p.hash[call]; char *parse_dxcc = strdup(dxcc_test); char *cp = strtok(parse_dxcc, ","); while (cp) { if (find(entlist.begin(), entlist.end(), string(cp)) == entlist.end()) entlist.push_back(cp); cp = strtok(0, ","); } free(parse_dxcc); } #endif if (call == "[None]") { int i; if (!_ent_init) { num_entities = DXCCMap.size(); entity_list = new struct _dxcc_entity[num_entities]; IntMap::const_iterator it; for (it = DXCCMap.begin(), i = 0; it != DXCCMap.end(); it++, i++) { entity_list[i].number = it->first; entity_list[i].name = it->second.c_str(); entity_list[i].zonemap = DXCCZoneMap[it->first].c_str(); entity_list[i].start = DXCCStartMap[it->first]; entity_list[i].end = DXCCEndMap[it->first]; } qsort(entity_list, num_entities, sizeof(struct _dxcc_entity), &_ent_cmp); _ent_init = true; } for (i = 0; i < num_entities; i++) { TQSL_LOCATION_ITEM item; item.ivalue = entity_list[i].number; char buf[10]; snprintf(buf, sizeof buf, "%d", item.ivalue); item.text = buf; item.label = entity_list[i].name; item.zonemap = entity_list[i].zonemap; if (item.ivalue == olddxcc) field.idx = field.items.size(); field.items.push_back(item); } field.idx = 0; } else { vector::iterator ip; // This iterator walks the list of DXCC entities associated // with this callsign for (ip = p.hash[call].begin(); ip != p.hash[call].end(); ip++) { TQSL_LOCATION_ITEM item; item.text = *ip; item.ivalue = strtol(ip->c_str(), NULL, 10); IntMap::iterator dxcc_it = DXCCMap.find(item.ivalue); if (dxcc_it != DXCCMap.end()) { item.label = dxcc_it->second; item.zonemap = DXCCZoneMap[item.ivalue]; } if (item.ivalue == olddxcc) field.idx = field.items.size(); field.items.push_back(item); } } if (field.items.size() > 0) field.cdata = field.items[field.idx].text; field.dependency = call; } // rebuild list } else { if (tqsl_field_map.find(field.gabbi_name) == tqsl_field_map.end()) { // Shouldn't happen! tQSL_Error = TQSL_CUSTOM_ERROR; strncpy(tQSL_CustomError, "TQSL Configuration file invalid - field map mismatch.", sizeof tQSL_CustomError); tqslTrace("update_page", "field map error %s", field.gabbi_name.c_str()); return 1; } XMLElement config_field = tqsl_field_map.find(field.gabbi_name)->second; pair attr = config_field.getAttribute("dependsOn"); if (attr.first != "") { // Items list depends on other field TQSL_LOCATION_FIELD *fp = get_location_field(page, attr.first, loc); if (fp) { // Found the dependency field. Now find the enums to use string val = fp->cdata; if (fp->items.size() > 0) val = fp->items[fp->idx].text; if (val == field.dependency) continue; field.dependency = val; field.changed = true; field.items.clear(); XMLElement enumlist; bool ok = config_field.getFirstElement("enums", enumlist); while (ok) { pair dependency = enumlist.getAttribute("dependency"); if (dependency.second && dependency.first == val) { if (!(field.flags & TQSL_LOCATION_FIELD_MUSTSEL)) { TQSL_LOCATION_ITEM item; item.label = "[None]"; field.items.push_back(item); } XMLElement enumitem; bool iok = enumlist.getFirstElement("enum", enumitem); while (iok) { TQSL_LOCATION_ITEM item; item.text = enumitem.getAttribute("value").first; item.label = enumitem.getText(); item.zonemap = enumitem.getAttribute("zonemap").first; field.items.push_back(item); iok = enumlist.getNextElement(enumitem); } } ok = config_field.getNextElement(enumlist); } // enum loop } else { tQSL_Error = TQSL_CUSTOM_ERROR; strncpy(tQSL_CustomError, "TQSL Configuration file invalid - dependent field not found.", sizeof tQSL_CustomError); tqslTrace("update_page", "error %s", tQSL_CustomError); return 1; } } else { // No dependencies TQSL_LOCATION_FIELD *ent = get_location_field(page, "DXCC", loc); current_entity = strtol(ent->cdata.c_str(), NULL, 10); bool cqz = field.gabbi_name == "CQZ"; bool ituz = field.gabbi_name == "ITUZ"; if (field.items.size() == 0 || (cqz && current_entity != loaded_cqz) || (ituz && current_entity != loaded_ituz)) { XMLElement enumlist; if (config_field.getFirstElement("enums", enumlist)) { field.items.clear(); field.changed = true; if (!(field.flags & TQSL_LOCATION_FIELD_MUSTSEL)) { TQSL_LOCATION_ITEM item; item.label = "[None]"; field.items.push_back(item); } XMLElement enumitem; bool iok = enumlist.getFirstElement("enum", enumitem); while (iok) { TQSL_LOCATION_ITEM item; item.text = enumitem.getAttribute("value").first; item.label = enumitem.getText(); item.zonemap = enumitem.getAttribute("zonemap").first; field.items.push_back(item); iok = enumlist.getNextElement(enumitem); } } else { // No enums supplied int ftype = strtol(config_field.getAttribute("intype").first.c_str(), NULL, 10); if (ftype == TQSL_LOCATION_FIELD_LIST || ftype == TQSL_LOCATION_FIELD_DDLIST) { // This a list field int lower = strtol(config_field.getAttribute("lower").first.c_str(), NULL, 10); int upper = strtol(config_field.getAttribute("upper").first.c_str(), NULL, 10); const char *zoneMap; /* Get the map */ if (tqsl_getDXCCZoneMap(current_entity, &zoneMap)) { zoneMap = NULL; } if (upper < lower) { tQSL_Error = TQSL_CUSTOM_ERROR; strncpy(tQSL_CustomError, "TQSL Configuration file invalid - field range order incorrect.", sizeof tQSL_CustomError); tqslTrace("update_page", "error %s", tQSL_CustomError); return 1; } field.items.clear(); field.changed = true; if (cqz) loaded_cqz = current_entity; if (ituz) loaded_ituz = current_entity; if (!(field.flags & TQSL_LOCATION_FIELD_MUSTSEL)) { TQSL_LOCATION_ITEM item; item.label = "[None]"; field.items.push_back(item); } char buf[40]; for (int j = lower; j <= upper; j++) { if (!zoneMap || inMap(j, j, cqz, ituz, zoneMap)) { snprintf(buf, sizeof buf, "%d", j); TQSL_LOCATION_ITEM item; item.text = buf; item.ivalue = j; field.items.push_back(item); } } } // intype != TEXT } // enums supplied } // itemlist not empty and current entity } // no dependencies } // field name not CALL|DXCC } // field loop /* Sanity check zones */ bool zonesok = true; // Try for subdivision info first string zone_error = ""; TQSL_LOCATION_FIELD *state = get_location_field(page, "US_STATE", loc); if (state) { zone_error = "Invalid zone selections for state"; } else { state = get_location_field(page, "CA_PROVINCE", loc); if (state) { zone_error = "Invalid zone selections for province"; } else { state = get_location_field(page, "RU_OBLAST", loc); if (state) { zone_error = "Invalid zone selections for oblast"; } else { // If no subdivision, use entity. state = get_location_field(page, "DXCC", loc); zone_error = "Invalid zone selections for DXCC entity"; } } } if (state && state->idx >=0 && state->items.size() > 0) { TQSL_LOCATION_FIELD *cqz = get_location_field(page, "CQZ", loc); TQSL_LOCATION_FIELD *ituz = get_location_field(page, "ITUZ", loc); string szm = state->items[state->idx].zonemap; const char* stateZoneMap = szm.c_str(); int currentCQ = cqz->idata; int currentITU = ituz->idata; if (!inMap(currentCQ, currentITU, true, true, stateZoneMap)) { zonesok = false; } TQSL_LOCATION_FIELD *zerr = get_location_field(page, "ZERR", loc); if (zerr) { if(!zonesok) { zerr->cdata = zone_error; } else { zerr->cdata = ""; } } } p.complete = true; return 0; } static int make_page(TQSL_LOCATION_PAGELIST& pagelist, int page_num) { if (init_loc_maps()) { tqslTrace("make_page", "init_loc_maps error %d", tQSL_Error); return 1; } if (tqsl_page_map.find(page_num) == tqsl_page_map.end()) { tQSL_Error = TQSL_CUSTOM_ERROR; strncpy(tQSL_CustomError, "TQSL Configuration file invalid - page reference could not be found.", sizeof tQSL_CustomError); tqslTrace("make_page", "Error %d %s", page_num, tQSL_CustomError); return 1; } TQSL_LOCATION_PAGE p; pagelist.push_back(p); XMLElement& config_page = tqsl_page_map[page_num]; pagelist.back().prev = strtol(config_page.getAttribute("follows").first.c_str(), NULL, 10); XMLElement config_pageField; bool field_ok = config_page.getFirstElement("pageField", config_pageField); while (field_ok) { string field_name = config_pageField.getText(); if (field_name == "" || tqsl_field_map.find(field_name) == tqsl_field_map.end()) { tQSL_Error = TQSL_CUSTOM_ERROR; strncpy(tQSL_CustomError, "TQSL Configuration file invalid - page references undefined field.", sizeof tQSL_CustomError); tqslTrace("make_page", "Error %s", tQSL_CustomError); return 1; } XMLElement& config_field = tqsl_field_map[field_name]; TQSL_LOCATION_FIELD loc_field( field_name, config_field.getAttribute("label").first.c_str(), (config_field.getAttribute("type").first == "C") ? TQSL_LOCATION_FIELD_CHAR : TQSL_LOCATION_FIELD_INT, strtol(config_field.getAttribute("len").first.c_str(), NULL, 10), strtol(config_field.getAttribute("intype").first.c_str(), NULL, 10), strtol(config_field.getAttribute("flags").first.c_str(), NULL, 10) ); // NOLINT(whitespace/parens) pagelist.back().fieldlist.push_back(loc_field); field_ok = config_page.getNextElement(config_pageField); } return 0; } DLLEXPORT int CALLCONVENTION tqsl_initStationLocationCapture(tQSL_Location *locp) { if (tqsl_init()) return 1; if (locp == NULL) { tqslTrace("tqsl_initStationLocationCapture", "Arg error locp=null"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } TQSL_LOCATION *loc = new TQSL_LOCATION; *locp = loc; if (init_loc_maps()) { tqslTrace("tqsl_initStationLocationCapture", "init_loc_maps error %d", tQSL_Error); return 1; } map::iterator pit; for (pit = tqsl_page_map.begin(); pit != tqsl_page_map.end(); pit++) { if (make_page(loc->pagelist, pit->first)) { tqslTrace("tqsl_initStationLocationCapture", "make_page error %d", tQSL_Error); return 1; } } loc->page = 1; if (update_page(1, loc)) { tqslTrace("tqsl_initStationLocationCapture", "updatePage error %d", tQSL_Error); return 1; } return 0; } DLLEXPORT int CALLCONVENTION tqsl_endStationLocationCapture(tQSL_Location *locp) { if (tqsl_init()) return 1; if (locp == NULL) { tQSL_Error = TQSL_ARGUMENT_ERROR; tqslTrace("tqsl_endStationLocationCapture", "arg error locp=NULL"); return 1; } if (*locp == 0) return 0; if (CAST_TQSL_LOCATION(*locp)->sentinel == 0x5445) delete CAST_TQSL_LOCATION(*locp); *locp = 0; return 0; } DLLEXPORT int CALLCONVENTION tqsl_updateStationLocationCapture(tQSL_Location locp) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_updateStationLocationCapture", "check_loc error %d", tQSL_Error); return 1; } // TQSL_LOCATION_PAGE &p = loc->pagelist[loc->page-1]; return update_page(loc->page, loc); } DLLEXPORT int CALLCONVENTION tqsl_getNumStationLocationCapturePages(tQSL_Location locp, int *npages) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_getNumStationLocationCapturePages", "check_loc error %d", tQSL_Error); return 1; } if (npages == NULL) { tQSL_Error = TQSL_ARGUMENT_ERROR; tqslTrace("tqsl_getNumStationLocationCapturePages", "arg error npages=NULL"); return 1; } *npages = loc->pagelist.size(); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getStationLocationCapturePage(tQSL_Location locp, int *page) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_getStationLocationCapturePage", "check_loc error %d", tQSL_Error); return 1; } if (page == NULL) { tqslTrace("tqsl_getStationLocationCapturePage", "arg error page=NULL"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } *page = loc->page; return 0; } DLLEXPORT int CALLCONVENTION tqsl_setStationLocationCapturePage(tQSL_Location locp, int page) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_setStationLocationCapturePage", "check_loc error %d", tQSL_Error); return 1; } if (page < 1 || page > static_cast(loc->pagelist.size())) { tqslTrace("tqsl_setStationLocationCapturePage", "Page %d out of range", page); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } loc->page = page; return 0; } DLLEXPORT int CALLCONVENTION tqsl_setStationLocationCertFlags(tQSL_Location locp, int flags) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_setStationLocationCertFlags", "check_loc error %d", tQSL_Error); return 1; } if (loc->cert_flags != flags) { loc->cert_flags = flags; loc->newflags = true; loc->page = 1; if (update_page(1, loc)) { tqslTrace("tqsl_setStationLocationCertFlags", "update_page error %d", tQSL_Error); return 1; } } return 0; } static int find_next_page(TQSL_LOCATION *loc) { // Set next page based on page dependencies TQSL_LOCATION_PAGE& p = loc->pagelist[loc->page-1]; map::iterator pit; p.next = 0; for (pit = tqsl_page_map.begin(); pit != tqsl_page_map.end(); pit++) { if (strtol(pit->second.getAttribute("follows").first.c_str(), NULL, 10) == loc->page) { string dependsOn = pit->second.getAttribute("dependsOn").first; string dependency = pit->second.getAttribute("dependency").first; if (dependsOn == "") { p.next = pit->first; break; } TQSL_LOCATION_FIELD *fp = get_location_field(0, dependsOn, loc); //if (fp->idx>=fp->items.size()) { cerr<<"!! " __FILE__ "(" << __LINE__ << "): Was going to index out of fp->items"<(fp->items.size()) > fp->idx && fp->idx >= 0 && fp->items[fp->idx].text == dependency) { p.next = pit->first; break; // Found next page //} } } } return 0; } DLLEXPORT int CALLCONVENTION tqsl_nextStationLocationCapture(tQSL_Location locp) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_nextStationLocationCapture", "check_loc error %d", tQSL_Error); return 1; } if (find_next_page(loc)) return 0; TQSL_LOCATION_PAGE &p = loc->pagelist[loc->page-1]; if (p.next > 0) loc->page = p.next; update_page(loc->page, loc); return 0; } DLLEXPORT int CALLCONVENTION tqsl_prevStationLocationCapture(tQSL_Location locp) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_prevStationLocationCapture", "check_loc error %d", tQSL_Error); return 1; } TQSL_LOCATION_PAGE &p = loc->pagelist[loc->page-1]; if (p.prev > 0) loc->page = p.prev; return 0; } DLLEXPORT int CALLCONVENTION tqsl_hasNextStationLocationCapture(tQSL_Location locp, int *rval) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_hasNextStationLocationCapture", "check_loc error %d", tQSL_Error); return 1; } if (rval == NULL) { tqslTrace("tqsl_hasNextStationLocationCapture", "Arg error rval=NULL"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (find_next_page(loc)) { tqslTrace("tqsl_hasNextStationLocationCapture", "find_next_page error %d", tQSL_Error); return 1; } *rval = (loc->pagelist[loc->page-1].next > 0); return 0; } DLLEXPORT int CALLCONVENTION tqsl_hasPrevStationLocationCapture(tQSL_Location locp, int *rval) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_hasPrevStationLocationCapture", "check_loc error %d", tQSL_Error); return 1; } if (rval == NULL) { tqslTrace("tqsl_hasPrevStationLocationCapture", "arg error rval=NULL"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } *rval = (loc->pagelist[loc->page-1].prev > 0); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getNumLocationField(tQSL_Location locp, int *numf) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_getNumLocationField", "check_loc error %d", tQSL_Error); return 1; } if (numf == NULL) { tqslTrace("tqsl_getNumLocationField", "arg error numf=NULL"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist; *numf = fl.size(); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataLabelSize(tQSL_Location locp, int field_num, int *rval) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_getLocationFieldDataLabelSize", "check_loc error %d", tQSL_Error); return 1; } TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist; if (rval == NULL || field_num < 0 || field_num >= static_cast(fl.size())) { tqslTrace("tqsl_getLocationFieldDataLabelSize", "arg error rval=0x%lx, field_num=%d", rval, field_num); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } *rval = fl[field_num].label.size()+1; return 0; } DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataLabel(tQSL_Location locp, int field_num, char *buf, int bufsiz) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_getLocationFieldDataLabel", "check_loc error %d", tQSL_Error); return 1; } TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist; if (buf == NULL || field_num < 0 || field_num >= static_cast(fl.size())) { tqslTrace("tqsl_getLocationFieldDataLabel", "arg error buf=0x%lx, field_num=%d", buf, field_num); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } strncpy(buf, fl[field_num].label.c_str(), bufsiz); buf[bufsiz-1] = 0; return 0; } DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataGABBISize(tQSL_Location locp, int field_num, int *rval) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_getLocationFieldDataGABBISize", "check_loc error %d", tQSL_Error); return 1; } TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist; if (rval == NULL || field_num < 0 || field_num >= static_cast(fl.size())) { tqslTrace("tqsl_getLocationFieldDataGABBISize", "arg error rval=0x%lx, field_num=%d", rval, field_num); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } *rval = fl[field_num].gabbi_name.size()+1; return 0; } DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataGABBI(tQSL_Location locp, int field_num, char *buf, int bufsiz) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_getLocationFieldDataGABBI", "check_loc error %d", tQSL_Error); return 1; } TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist; if (buf == NULL || field_num < 0 || field_num >= static_cast(fl.size())) { tqslTrace("tqsl_getLocationFieldDataGABBI", "arg error buf=0x%lx, field_num=%d", buf, field_num); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } strncpy(buf, fl[field_num].gabbi_name.c_str(), bufsiz); buf[bufsiz-1] = 0; return 0; } DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldInputType(tQSL_Location locp, int field_num, int *type) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_getLocationFieldInputType", "check_loc error %d", tQSL_Error); return 1; } TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist; if (type == NULL || field_num < 0 || field_num >= static_cast(fl.size())) { tqslTrace("tqsl_getLocationFieldInputType", "arg error type=0x%lx, field_num=%d", type, field_num); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } *type = fl[field_num].input_type; return 0; } DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldChanged(tQSL_Location locp, int field_num, int *changed) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_getLocationFieldChanged", "check_loc error %d", tQSL_Error); return 1; } TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist; if (changed == NULL || field_num < 0 || field_num >= static_cast(fl.size())) { tqslTrace("tqsl_getLocationFieldChanged", "arg error changed=0x%lx, field_num=%d", changed, field_num); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } *changed = fl[field_num].changed; return 0; } DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataType(tQSL_Location locp, int field_num, int *type) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_getLocationFieldDataType", "check_loc error %d", tQSL_Error); return 1; } TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist; if (type == NULL || field_num < 0 || field_num >= static_cast(fl.size())) { tqslTrace("tqsl_getLocationFieldDataType", "arg error type=0x%lx, field_num=%d", type, field_num); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } *type = fl[field_num].data_type; return 0; } DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldFlags(tQSL_Location locp, int field_num, int *flags) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_getLocationFieldFlags", "check_loc error %d", tQSL_Error); return 1; } TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist; if (flags == NULL || field_num < 0 || field_num >= static_cast(fl.size())) { tqslTrace("tqsl_getLocationFieldFlags", "arg error flags=0x%lx, field_num=%d", flags, field_num); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } *flags = fl[field_num].flags; return 0; } DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataLength(tQSL_Location locp, int field_num, int *rval) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_getLocationFieldDataLength", "check_loc error %d", tQSL_Error); return 1; } TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist; if (rval == NULL || field_num < 0 || field_num >= static_cast(fl.size())) { tqslTrace("tqsl_getLocationFieldDataLength", "arg error rval=0x%lx, field_num=%d", rval, field_num); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } *rval = fl[field_num].data_len; return 0; } DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldCharData(tQSL_Location locp, int field_num, char *buf, int bufsiz) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_getLocationFieldCharData", "check_loc error %d", tQSL_Error); return 1; } TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist; if (buf == NULL || field_num < 0 || field_num >= static_cast(fl.size())) { tqslTrace("tqsl_getLocationFieldCharData", "arg errror buf=0x%lx, field_num=%d", buf, field_num); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (fl[field_num].flags & TQSL_LOCATION_FIELD_UPPER) strncpy(buf, string_toupper(fl[field_num].cdata).c_str(), bufsiz); else strncpy(buf, fl[field_num].cdata.c_str(), bufsiz); buf[bufsiz-1] = 0; return 0; } DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldIntData(tQSL_Location locp, int field_num, int *dat) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_getLocationFieldIntData", "check_loc error %d", tQSL_Error); return 1; } TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist; if (dat == NULL || field_num < 0 || field_num >= static_cast(fl.size())) { tqslTrace("tqsl_getLocationFieldIntData", "arg error dat=0x%lx, field_num=%d", dat, field_num); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } *dat = fl[field_num].idata; return 0; } DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldIndex(tQSL_Location locp, int field_num, int *dat) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_getLocationFieldIndex", "check_loc error %d", tQSL_Error); return 1; } TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist; if (dat == NULL || field_num < 0 || field_num >= static_cast(fl.size())) { tqslTrace("tqsl_getLocationFieldIndex", "arg error dat=0x%lx, field_num=%d", dat, field_num); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (fl[field_num].input_type != TQSL_LOCATION_FIELD_DDLIST && fl[field_num].input_type != TQSL_LOCATION_FIELD_LIST) { tqslTrace("tqsl_getLocationFieldIndex", "arg error input type mismatch"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } *dat = fl[field_num].idx; return 0; } DLLEXPORT int CALLCONVENTION tqsl_setLocationFieldCharData(tQSL_Location locp, int field_num, const char *buf) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_setLocationFieldCharData", "check_loc error %d", tQSL_Error); return 1; } TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist; if (buf == NULL || field_num < 0 || field_num >= static_cast(fl.size())) { tqslTrace("tqsl_setLocationFieldCharData", "arg error buf=0x%lx, field_num=%d", buf, field_num); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } fl[field_num].cdata = string(buf).substr(0, fl[field_num].data_len); if (fl[field_num].flags & TQSL_LOCATION_FIELD_UPPER) fl[field_num].cdata = string_toupper(fl[field_num].cdata); if (fl[field_num].input_type == TQSL_LOCATION_FIELD_DDLIST || fl[field_num].input_type == TQSL_LOCATION_FIELD_LIST) { if (fl[field_num].cdata == "") { fl[field_num].idx = 0; fl[field_num].idata = fl[field_num].items[0].ivalue; } else { bool found = false; for (int i = 0; i < static_cast(fl[field_num].items.size()); i++) { if (fl[field_num].items[i].text == fl[field_num].cdata) { fl[field_num].idx = i; fl[field_num].idata = fl[field_num].items[i].ivalue; found = true; break; } } if (!found) { // There's no entry in the list that matches! fl[field_num].cdata = ""; fl[field_num].idx = 0; fl[field_num].idata = 0; } } } return 0; } /* Set the field's index. For pick lists, this is the index into * 'items'. In that case, also set the field's data to the picked value. */ DLLEXPORT int CALLCONVENTION tqsl_setLocationFieldIndex(tQSL_Location locp, int field_num, int dat) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_setLocationFieldIndex", "check_loc error %d", tQSL_Error); return 1; } TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist; if (field_num < 0 || field_num >= static_cast(fl.size())) { tqslTrace("tqsl_setLocationFieldIndex", "arg error field_num=%d, dat=%d", field_num, dat); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } fl[field_num].idx = dat; if (fl[field_num].input_type == TQSL_LOCATION_FIELD_DDLIST || fl[field_num].input_type == TQSL_LOCATION_FIELD_LIST) { if (dat >= 0 && dat < static_cast(fl[field_num].items.size())) { fl[field_num].idx = dat; fl[field_num].cdata = fl[field_num].items[dat].text; fl[field_num].idata = fl[field_num].items[dat].ivalue; } else { tqslTrace("tqsl_setLocationFieldIndex", "arg error field_num=%d", field_num); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } } return 0; } /* Set the field's integer data. */ DLLEXPORT int CALLCONVENTION tqsl_setLocationFieldIntData(tQSL_Location locp, int field_num, int dat) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_setLocationFieldIntData", "check_loc error %d", tQSL_Error); return 1; } TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist; if (field_num < 0 || field_num >= static_cast(fl.size())) { tqslTrace("tqsl_setLocationFieldIntData", "arg error field_num=%d, dat=%d", field_num, dat); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } fl[field_num].idata = dat; return 0; } /* For pick lists, this is the index into * 'items'. In that case, also set the field's char data to the picked value. */ DLLEXPORT int CALLCONVENTION tqsl_getNumLocationFieldListItems(tQSL_Location locp, int field_num, int *rval) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_getNumLocationFieldListItems", "check_loc error %d", tQSL_Error); return 1; } if (rval == NULL) { tqslTrace("tqsl_getNumLocationFieldListItems", "arg error rval=NULL"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist; *rval = fl[field_num].items.size(); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldListItem(tQSL_Location locp, int field_num, int item_idx, char *buf, int bufsiz) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_getLocationFieldListItem", "check_loc error %d", tQSL_Error); return 1; } TQSL_LOCATION_FIELDLIST &fl = loc->pagelist[loc->page-1].fieldlist; if (buf == NULL || field_num < 0 || field_num >= static_cast(fl.size()) || (fl[field_num].input_type != TQSL_LOCATION_FIELD_LIST && fl[field_num].input_type != TQSL_LOCATION_FIELD_DDLIST)) { tqslTrace("tqsl_getLocationFieldListItem", "arg error buf=0x%lx, field_num=%d", buf, field_num); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (item_idx < 0 || item_idx >= static_cast(fl[field_num].items.size())) { tqslTrace("tqsl_getLocationFieldListItem", "arg error item_idx=%d", item_idx); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } string& str = (fl[field_num].items[item_idx].label == "") ? fl[field_num].items[item_idx].text : fl[field_num].items[item_idx].label; strncpy(buf, str.c_str(), bufsiz); buf[bufsiz - 1] = '\0'; return 0; } static string tqsl_station_data_filename(bool deleted = false) { const char *f; if (deleted) f = "station_data_trash"; else f = "station_data"; string s = tQSL_BaseDir; #ifdef _WIN32 s += "\\"; #else s += "/"; #endif s += f; return s; } static int tqsl_load_station_data(XMLElement &xel, bool deleted = false) { int status = xel.parseFile(tqsl_station_data_filename(deleted).c_str()); tqslTrace("tqsl_load_station_data", "file %s parse status %d", tqsl_station_data_filename(deleted).c_str(), status); if (status) { if (errno == ENOENT) { // If there's no file, no error. tqslTrace("tqsl_load_station_data", "File does not exist"); return 0; } strncpy(tQSL_ErrorFile, tqsl_station_data_filename(deleted).c_str(), sizeof tQSL_ErrorFile); if (status == XML_PARSE_SYSTEM_ERROR) { tQSL_Error = TQSL_FILE_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_load_station_data", "parse error, errno=%d", tQSL_Errno); } else { tqslTrace("tqsl_load_station_data", "syntax error"); tQSL_Error = TQSL_FILE_SYNTAX_ERROR; } return 1; } return status; } static int tqsl_dump_station_data(XMLElement &xel, bool deleted = false) { ofstream out; string fn = tqsl_station_data_filename(deleted); out.exceptions(ios::failbit | ios::eofbit | ios::badbit); try { #ifdef _WIN32 wchar_t* wfn = utf8_to_wchar(fn.c_str()); out.open(wfn); free_wchar(wfn); #else out.open(fn.c_str()); #endif out << xel << endl; out.close(); } catch(exception& x) { tQSL_Error = TQSL_CUSTOM_ERROR; snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "Unable to save new station location file (%s): %s/%s", fn.c_str(), x.what(), strerror(errno)); tqslTrace("tqsl_dump_station_data", "file error %s %s", fn.c_str(), tQSL_CustomError); return 1; } return 0; } static int tqsl_load_loc(TQSL_LOCATION *loc, XMLElementList::iterator ep, bool ignoreZones) { bool exists; loc->page = 1; loc->data_errors[0] = '\0'; int bad_ituz = 0; int bad_cqz = 0; tqslTrace("tqsl_load_loc", NULL); while(1) { TQSL_LOCATION_PAGE& page = loc->pagelist[loc->page-1]; for (int fidx = 0; fidx < static_cast(page.fieldlist.size()); fidx++) { TQSL_LOCATION_FIELD& field = page.fieldlist[fidx]; if (field.gabbi_name != "") { // A field that may exist XMLElement el; if (ep->second->getFirstElement(field.gabbi_name, el)) { field.cdata = el.getText(); switch (field.input_type) { case TQSL_LOCATION_FIELD_DDLIST: case TQSL_LOCATION_FIELD_LIST: exists = false; for (int i = 0; i < static_cast(field.items.size()); i++) { string cp = field.items[i].text; int q = strcasecmp(field.cdata.c_str(), cp.c_str()); if (q == 0) { field.idx = i; field.cdata = cp; field.idata = field.items[i].ivalue; exists = true; break; } } if (!exists) { if (field.gabbi_name == "CQZ") bad_cqz = strtol(field.cdata.c_str(), NULL, 10); else if (field.gabbi_name == "ITUZ") bad_ituz = strtol(field.cdata.c_str(), NULL, 10); else if (field.gabbi_name == "CALL" || field.gabbi_name == "DXCC") field.idx = -1; } break; case TQSL_LOCATION_FIELD_TEXT: field.cdata = trim(field.cdata); if (field.data_type == TQSL_LOCATION_FIELD_INT) field.idata = strtol(field.cdata.c_str(), NULL, 10); break; } } } if (update_page(loc->page, loc)) return 1; } int rval; if (tqsl_hasNextStationLocationCapture(loc, &rval) || !rval) break; tqsl_nextStationLocationCapture(loc); } if (ignoreZones) return 0; if (bad_cqz && bad_ituz) { snprintf(loc->data_errors, sizeof(loc->data_errors), "This station location is configured with invalid CQ zone %d and invalid ITU zone %d.", bad_cqz, bad_ituz); } else if (bad_cqz) { snprintf(loc->data_errors, sizeof(loc->data_errors), "This station location is configured with invalid CQ zone %d.", bad_cqz); } else if (bad_ituz) { snprintf(loc->data_errors, sizeof(loc->data_errors), "This station location is configured with invalid ITU zone %d.", bad_ituz); } tqslTrace("tqsl_load_loc", "data_errors=%s", loc->data_errors); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getStationDataEnc(tQSL_StationDataEnc *sdata) { char *dbuf = NULL; size_t dlen = 0; gzFile in = NULL; #ifdef _WIN32 wchar_t *fn = utf8_to_wchar(tqsl_station_data_filename().c_str()); int fd = _wopen(fn, _O_RDONLY|_O_BINARY); free_wchar(fn); if (fd != -1) in = gzdopen(fd, "rb"); #else in = gzopen(tqsl_station_data_filename().c_str(), "rb"); #endif if (!in) { if (errno == ENOENT) { *sdata = NULL; tqslTrace("tqsl_getStationDataEnc", "File %s does not exist", tqsl_station_data_filename().c_str()); return 0; } tQSL_Error = TQSL_SYSTEM_ERROR; tQSL_Errno = errno; strncpy(tQSL_ErrorFile, tqsl_station_data_filename().c_str(), sizeof tQSL_ErrorFile); tqslTrace("tqsl_getStationDataEnc", "File %s open error %s", tqsl_station_data_filename().c_str(), strerror(tQSL_Error) ); return 1; } char buf[2048]; int rcount; while ((rcount = gzread(in, buf, sizeof buf)) > 0) { dlen += rcount; } dbuf = reinterpret_cast(malloc(dlen + 2)); if (!dbuf) { tqslTrace("tqsl_getStationDataEnc", "memory allocation error %d", dlen+2); return 1; } *sdata = dbuf; gzrewind(in); while ((rcount = gzread(in, dbuf, sizeof buf)) > 0) { dbuf += rcount; } *dbuf = '\0'; gzclose(in); return 0; } DLLEXPORT int CALLCONVENTION tqsl_freeStationDataEnc(tQSL_StationDataEnc sdata) { if (sdata) free(sdata); return 0; //can never fail } DLLEXPORT int CALLCONVENTION tqsl_mergeStationLocations(const char *locdata) { XMLElement new_data; XMLElement old_data; XMLElement new_top_el; XMLElement old_top_el; vector locnames; tqslTrace("tqsl_mergeStationLocations", NULL); // Load the current station data if (tqsl_load_station_data(old_top_el)) { tqslTrace("tqsl_mergeStationLocations", "error loading station data"); return 1; } // Parse the data to be merged new_top_el.parseString(locdata); if (!new_top_el.getFirstElement(new_data)) new_data.setElementName("StationDataFile"); if (!old_top_el.getFirstElement(old_data)) old_data.setElementName("StationDataFile"); // Build a list of existing station locations XMLElementList& namelist = old_data.getElementList(); XMLElementList::iterator nameiter; XMLElement locname; for (nameiter = namelist.find("StationData"); nameiter != namelist.end(); nameiter++) { if (nameiter->first != "StationData") break; pair rval = nameiter->second->getAttribute("name"); if (rval.second) { locnames.push_back(rval.first); } } // Iterate the new locations XMLElementList& ellist = new_data.getElementList(); XMLElementList::iterator ep; old_data.setPretext(old_data.getPretext() + " "); for (ep = ellist.find("StationData"); ep != ellist.end(); ep++) { if (ep->first != "StationData") break; pair rval = ep->second->getAttribute("name"); bool found = false; if (rval.second) { for (size_t j = 0; j < locnames.size(); j++) { if (locnames[j] == rval.first) { found = true; break; } } } if (!found) { // Add this one to the station data file XMLElement *newtop = new XMLElement("StationData"); newtop->setPretext("\n "); newtop->setAttribute("name", rval.first); newtop->setText("\n "); XMLElement el; bool elok = ep->second->getFirstElement(el); while (elok) { XMLElement *sub = new XMLElement; sub->setPretext(newtop->getPretext() + " "); sub->setElementName(el.getElementName()); sub->setText(el.getText()); newtop->addElement(sub); elok = ep->second->getNextElement(el); } old_data.addElement(newtop); old_data.setText("\n"); } } return tqsl_dump_station_data(old_data); } // Move a station location to or from the trash static int tqsl_move_station_location(const char *name, bool fromtrash) { tqslTrace("tqsl_move_station_location", "name=%s, fromtrash=%d", name, fromtrash); XMLElement from_top_el; XMLElement to_top_el; if (tqsl_load_station_data(from_top_el, fromtrash)) { tqslTrace("tqsl_move_station_location", "error %d loading data", tQSL_Error); return 1; } if (tqsl_load_station_data(to_top_el, !fromtrash)) { tqslTrace("tqsl_move_station_location", "error %d loading data", tQSL_Error); return 1; } XMLElement from_sfile; XMLElement to_sfile; if (!from_top_el.getFirstElement(from_sfile)) from_sfile.setElementName("StationDataFile"); if (!to_top_el.getFirstElement(to_sfile)) to_sfile.setElementName("StationDataFile"); XMLElementList& from_ellist = from_sfile.getElementList(); XMLElementList::iterator from_ep; for (from_ep = from_ellist.find("StationData"); from_ep != from_ellist.end(); from_ep++) { if (from_ep->first != "StationData") break; pair from_rval = from_ep->second->getAttribute("name"); if (from_rval.second && !strcasecmp(from_rval.first.c_str(), name)) { // Match, move it. // First, delete any old backup for this station location XMLElementList& to_ellist = to_sfile.getElementList(); XMLElementList::iterator to_ep; for (to_ep = to_ellist.find("StationData"); to_ep != to_ellist.end(); to_ep++) { if (to_ep->first != "StationData") break; pair to_rval = to_ep->second->getAttribute("name"); if (to_rval.second && !strcasecmp(to_rval.first.c_str(), name)) { to_ellist.erase(to_ep); break; } } // Now add it to the target XMLElement *newtop = new XMLElement("StationData"); newtop->setPretext("\n "); newtop->setAttribute("name", from_rval.first); newtop->setText("\n "); XMLElement el; bool elok = from_ep->second->getFirstElement(el); while (elok) { XMLElement *sub = new XMLElement; sub->setPretext(newtop->getPretext() + " "); sub->setElementName(el.getElementName()); sub->setText(el.getText()); newtop->addElement(sub); elok = from_ep->second->getNextElement(el); } to_sfile.addElement(newtop); to_sfile.setText("\n"); tqsl_dump_station_data(to_sfile, !fromtrash); from_ellist.erase(from_ep); return tqsl_dump_station_data(from_sfile, fromtrash); } } tqslTrace("tqsl_move_station_location", "location not found"); tQSL_Error = TQSL_LOCATION_NOT_FOUND; return 1; } DLLEXPORT int CALLCONVENTION tqsl_deleteStationLocation(const char *name) { tqslTrace("tqsl_deleteStationLocation", "name=%s", name); return tqsl_move_station_location(name, false); } DLLEXPORT int CALLCONVENTION tqsl_restoreStationLocation(const char *name) { tqslTrace("tqsl_restoreStationLocation", "name=%s", name); return tqsl_move_station_location(name, true); } DLLEXPORT int CALLCONVENTION tqsl_getStationLocation(tQSL_Location *locp, const char *name) { if (tqsl_initStationLocationCapture(locp)) { tqslTrace("tqsl_getStationLocation", "name=%s error=%d", name, tQSL_Error); return 1; } TQSL_LOCATION *loc; if (!(loc = check_loc(*locp))) { tqslTrace("tqsl_getStationLocation", "loc error %d", tQSL_Error); return 1; } loc->name = name; XMLElement top_el; if (tqsl_load_station_data(top_el)) { tqslTrace("tqsl_getStationLocation", "load station data error %d", tQSL_Error); return 1; } XMLElement sfile; if (!top_el.getFirstElement(sfile)) sfile.setElementName("StationDataFile"); XMLElementList& ellist = sfile.getElementList(); bool exists = false; XMLElementList::iterator ep; for (ep = ellist.find("StationData"); ep != ellist.end(); ep++) { if (ep->first != "StationData") break; pair rval = ep->second->getAttribute("name"); if (rval.second && !strcasecmp(trim(rval.first).c_str(), trim(loc->name).c_str())) { exists = true; break; } } if (!exists) { tQSL_Error = TQSL_LOCATION_NOT_FOUND; tqslTrace("tqsl_getStationLocation", "location %s does not exist", name); return 1; } return tqsl_load_loc(loc, ep, false); } DLLEXPORT int CALLCONVENTION tqsl_getStationLocationErrors(tQSL_Location locp, char *buf, int bufsiz) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_getStationLocation", "loc error %d", tQSL_Error); return 1; } if (buf == NULL) { tQSL_Error = TQSL_ARGUMENT_ERROR; tqslTrace("tqsl_getStationLocation", "buf = NULL"); return 1; } strncpy(buf, loc->data_errors, bufsiz); buf[bufsiz-1] = 0; return 0; } DLLEXPORT int CALLCONVENTION tqsl_getNumStationLocations(tQSL_Location locp, int *nloc) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_getNumStationLocations", "loc error %d", tQSL_Error); return 1; } if (nloc == NULL) { tqslTrace("tqsl_getNumStationLocations", "arg error nloc=NULL"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } loc->names.clear(); XMLElement top_el; if (tqsl_load_station_data(top_el)) { tqslTrace("tqsl_getNumStationLocations", "error %d loading station data", tQSL_Error); return 1; } XMLElement sfile; if (top_el.getFirstElement(sfile)) { XMLElement sd; bool ok = sfile.getFirstElement("StationData", sd); while (ok && sd.getElementName() == "StationData") { pair name = sd.getAttribute("name"); if (name.second) { XMLElement xc; string call; if (sd.getFirstElement("CALL", xc)) call = xc.getText(); loc->names.push_back(TQSL_NAME(name.first, call)); } ok = sfile.getNextElement(sd); } } *nloc = loc->names.size(); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getStationLocationName(tQSL_Location locp, int idx, char *buf, int bufsiz) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_getStationLocationName", "loc error %d", tQSL_Error); return 1; } if (buf == NULL || idx < 0 || idx >= static_cast(loc->names.size())) { tqslTrace("tqsl_getStationLocationName", "arg error buf=0x%lx, idx=%d", buf, idx); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } strncpy(buf, loc->names[idx].name.c_str(), bufsiz); buf[bufsiz-1] = 0; return 0; } DLLEXPORT int CALLCONVENTION tqsl_getStationLocationCallSign(tQSL_Location locp, int idx, char *buf, int bufsiz) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_getStationLocationCallSign", "loc error %d", tQSL_Error); return 1; } if (buf == NULL || idx < 0 || idx >= static_cast(loc->names.size())) { tqslTrace("tqsl_getStationLocationCallSign", "arg error buf=0x%lx, idx=%d", buf, idx); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } strncpy(buf, loc->names[idx].call.c_str(), bufsiz); buf[bufsiz-1] = 0; return 0; } DLLEXPORT int CALLCONVENTION tqsl_getStationLocationField(tQSL_Location locp, const char *name, char *namebuf, int bufsize) { int old_page; TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_getStationLocationField", "loc error %d", tQSL_Error); return 1; } if (name == NULL || namebuf == NULL) { tqslTrace("tqsl_getStationLocationField", "arg error name=0x%lx, namebuf=0x%lx", name, namebuf); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (tqsl_getStationLocationCapturePage(loc, &old_page)) { tqslTrace("tqsl_getStationLocationField", "get cap page error %d", tQSL_Error); return 1; } string find = name; tqsl_setStationLocationCapturePage(loc, 1); do { int numf; if (tqsl_getNumLocationField(loc, &numf)) { tqslTrace("tqsl_getStationLocationField", "erro getting num fields %d", tQSL_Error); return 1; } for (int i = 0; i < numf; i++) { TQSL_LOCATION_FIELD& field = loc->pagelist[loc->page-1].fieldlist[i]; if (find == field.gabbi_name) { // Found it switch (field.input_type) { case TQSL_LOCATION_FIELD_DDLIST: case TQSL_LOCATION_FIELD_LIST: if (field.data_type == TQSL_LOCATION_FIELD_INT) { char numbuf[20]; if (static_cast(field.items.size()) <= field.idx) { strncpy(namebuf, field.cdata.c_str(), bufsize); } else if (field.idx == 0 && field.items[field.idx].label == "[None]") { strncpy(namebuf, "", bufsize); } else { snprintf(numbuf, sizeof numbuf, "%d", field.items[field.idx].ivalue); strncpy(namebuf, numbuf, bufsize); } } else if (field.idx < 0 || field.idx >= static_cast(field.items.size())) { // Allow CALL to not be in the items list if (field.idx == -1 && i == 0) strncpy(namebuf, field.cdata.c_str(), bufsize); else strncpy(namebuf, "", bufsize); } else { if (field.items[field.idx].label == "") { strncpy(namebuf, field.items[field.idx].text.c_str(), bufsize); } else { strncpy(namebuf, field.items[field.idx].label.c_str(), bufsize); } } break; case TQSL_LOCATION_FIELD_TEXT: field.cdata = trim(field.cdata); if (field.flags & TQSL_LOCATION_FIELD_UPPER) field.cdata = string_toupper(field.cdata); strncpy(namebuf, field.cdata.c_str(), bufsize); break; } goto done; } } int rval; if (tqsl_hasNextStationLocationCapture(loc, &rval) || !rval) break; if (tqsl_nextStationLocationCapture(loc)) { tqslTrace("tqsl_getStationLocationField", "error in nextStationLocationCapture %d", tQSL_Error); return 1; } } while (1); strncpy(namebuf, "", bufsize); // Did not find it done: tqsl_setStationLocationCapturePage(loc, old_page); return 0; } static int tqsl_location_to_xml(TQSL_LOCATION *loc, XMLElement& sd) { int old_page; if (tqsl_getStationLocationCapturePage(loc, &old_page)) { tqslTrace("tqsl_location_to_xml", "get_sta_loc_cap_page error %d", tQSL_Error); return 1; } tqsl_setStationLocationCapturePage(loc, 1); do { int numf; if (tqsl_getNumLocationField(loc, &numf)) { tqslTrace("tqsl_location_to_xml", "get num loc field error %d", tQSL_Error); return 1; } for (int i = 0; i < numf; i++) { TQSL_LOCATION_FIELD& field = loc->pagelist[loc->page-1].fieldlist[i]; XMLElement *fd = new XMLElement; fd->setPretext(sd.getPretext() + " "); fd->setElementName(field.gabbi_name); switch (field.input_type) { case TQSL_LOCATION_FIELD_DDLIST: case TQSL_LOCATION_FIELD_LIST: if (field.idx < 0 || field.idx >= static_cast(field.items.size())) { fd->setText(""); if (field.gabbi_name == "CALL") { fd->setText("NONE"); } } else if (field.data_type == TQSL_LOCATION_FIELD_INT) { char numbuf[20]; snprintf(numbuf, sizeof numbuf, "%d", field.items[field.idx].ivalue); fd->setText(numbuf); } else { fd->setText(field.items[field.idx].text); } break; case TQSL_LOCATION_FIELD_TEXT: field.cdata = trim(field.cdata); if (field.flags & TQSL_LOCATION_FIELD_UPPER) field.cdata = string_toupper(field.cdata); fd->setText(field.cdata); break; } if (strcmp(fd->getText().c_str(), "")) sd.addElement(fd); } int rval; if (tqsl_hasNextStationLocationCapture(loc, &rval) || !rval) break; if (tqsl_nextStationLocationCapture(loc)) return 1; } while (1); tqsl_setStationLocationCapturePage(loc, old_page); return 0; } DLLEXPORT int CALLCONVENTION tqsl_setStationLocationCaptureName(tQSL_Location locp, const char *name) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_setStationLocationCaptureName", "loc error %d", tQSL_Error); return 1; } if (name == NULL) { tqslTrace("tqsl_setStationLocationCaptureName", "arg error name=null"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } loc->name = name; return 0; } DLLEXPORT int CALLCONVENTION tqsl_getStationLocationCaptureName(tQSL_Location locp, char *namebuf, int bufsize) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_getStationLocationCaptureName", "loc error %d", tQSL_Error); return 1; } if (namebuf == NULL) { tqslTrace("tqsl_getStationLocationCaptureName", "arg error namebuf=null"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } strncpy(namebuf, loc->name.c_str(), bufsize); namebuf[bufsize-1] = 0; return 0; } DLLEXPORT int CALLCONVENTION tqsl_saveStationLocationCapture(tQSL_Location locp, int overwrite) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp))) { tqslTrace("tqsl_saveStationLocationCaptureName", "loc error %d", tQSL_Error); return 1; } if (loc->name == "") { tqslTrace("tqsl_saveStationLocationCaptureName", "name empty"); tQSL_Error = TQSL_EXPECTED_NAME; return 1; } XMLElement top_el; if (tqsl_load_station_data(top_el)) { tqslTrace("tqsl_saveStationLocationCaptureName", "error %d loading station data", tQSL_Error); return 1; } XMLElement sfile; if (!top_el.getFirstElement(sfile)) sfile.setElementName("StationDataFile"); XMLElementList& ellist = sfile.getElementList(); bool exists = false; XMLElementList::iterator ep; for (ep = ellist.find("StationData"); ep != ellist.end(); ep++) { if (ep->first != "StationData") break; pair rval = ep->second->getAttribute("name"); if (rval.second && !strcasecmp(rval.first.c_str(), loc->name.c_str())) { exists = true; break; } } if (exists && !overwrite) { tqslTrace("tqsl_saveStationLocationCaptureName", "exists, no overwrite"); tQSL_Error = TQSL_NAME_EXISTS; return 1; } XMLElement *sd = new XMLElement("StationData"); sd->setPretext("\n "); if (tqsl_location_to_xml(loc, *sd)) { tqslTrace("tqsl_saveStationLocationCaptureName", "error in loc_to_xml %d", tQSL_Error); return 1; } sd->setAttribute("name", loc->name); sd->setText("\n "); // If 'exists', ep points to the existing station record if (exists) ellist.erase(ep); sfile.addElement(sd); sfile.setText("\n"); return tqsl_dump_station_data(sfile); } DLLEXPORT int CALLCONVENTION tqsl_signQSORecord(tQSL_Cert cert, tQSL_Location locp, TQSL_QSO_RECORD *rec, unsigned char *sig, int *siglen) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp, false))) { tqslTrace("tqsl_signQSORecord", "loc error %d", tQSL_Error); return 1; } if (make_sign_data(loc)) { tqslTrace("tqsl_signQSORecord", "error %d making sign data", tQSL_Error); return 1; } XMLElement specfield; bool ok = tCONTACT_sign.getFirstElement(specfield); string rec_sign_data = loc->signdata; while (ok) { string eln = specfield.getElementName(); const char *elname = eln.c_str(); const char *value = 0; char buf[100]; if (!strcmp(elname, "CALL")) { value = rec->callsign; } else if (!strcmp(elname, "BAND")) { value = rec->band; } else if (!strcmp(elname, "BAND_RX")) { value = rec->rxband; } else if (!strcmp(elname, "MODE")) { value = rec->mode; } else if (!strcmp(elname, "FREQ")) { value = rec->freq; } else if (!strcmp(elname, "FREQ_RX")) { value = rec->rxfreq; } else if (!strcmp(elname, "PROP_MODE")) { value = rec->propmode; } else if (!strcmp(elname, "SAT_NAME")) { value = rec->satname; } else if (!strcmp(elname, "QSO_DATE")) { if (tqsl_isDateValid(&(rec->date))) value = tqsl_convertDateToText(&(rec->date), buf, sizeof buf); } else if (!strcmp(elname, "QSO_TIME")) { if (tqsl_isTimeValid(&(rec->time))) value = tqsl_convertTimeToText(&(rec->time), buf, sizeof buf); } else { tQSL_Error = TQSL_CUSTOM_ERROR; snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "Unknown field in signing specification: %s", elname); tqslTrace("tqsl_signQSORecord", "field err %s", tQSL_CustomError); return 1; } if (value == 0 || value[0] == 0) { pair attr = specfield.getAttribute("required"); if (attr.second && strtol(attr.first.c_str(), NULL, 10)) { string err = specfield.getElementName() + " field required by signature specification not found"; tQSL_Error = TQSL_CUSTOM_ERROR; strncpy(tQSL_CustomError, err.c_str(), sizeof tQSL_CustomError); tqslTrace("tqsl_signQSORecord", "val err %s", tQSL_CustomError); return 1; } } else { string v(value); rec_sign_data += trim(v); } ok = tCONTACT_sign.getNextElement(specfield); } return tqsl_signDataBlock(cert, (const unsigned char *)rec_sign_data.c_str(), rec_sign_data.size(), sig, siglen); } DLLEXPORT const char* CALLCONVENTION tqsl_getGABBItCERT(tQSL_Cert cert, int uid) { static string s; s = ""; char buf[3000]; if (tqsl_getCertificateEncoded(cert, buf, sizeof buf)) return 0; char *cp = strstr(buf, "-----END CERTIFICATE-----"); if (cp) *cp = 0; if ((cp = strstr(buf, "\n"))) cp++; else cp = buf; s = "tCERT\n"; char sbuf[10], lbuf[40]; snprintf(sbuf, sizeof sbuf, "%d", uid); snprintf(lbuf, sizeof lbuf, "%s\n", static_cast(strlen(sbuf)), sbuf); s += lbuf; snprintf(lbuf, sizeof lbuf, "", static_cast(strlen(cp))); s += lbuf; s += cp; s += "\n"; return s.c_str(); //KC2YWE 1/26 - dangerous but might work since s is static } DLLEXPORT const char* CALLCONVENTION tqsl_getGABBItSTATION(tQSL_Location locp, int uid, int certuid) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp, false))) { tqslTrace("tqsl_getGABBItSTATION", "loc error %d", tQSL_Error); return 0; } unsigned char *buf = 0; int bufsiz = 0; loc->tSTATION = "tSTATION\n"; char sbuf[10], lbuf[40]; snprintf(sbuf, sizeof sbuf, "%d", uid); snprintf(lbuf, sizeof lbuf, "%s\n", static_cast(strlen(sbuf)), sbuf); loc->tSTATION += lbuf; snprintf(sbuf, sizeof sbuf, "%d", certuid); snprintf(lbuf, sizeof lbuf, "%s\n", static_cast(strlen(sbuf)), sbuf); loc->tSTATION += lbuf; int old_page = loc->page; tqsl_setStationLocationCapturePage(loc, 1); do { TQSL_LOCATION_PAGE& p = loc->pagelist[loc->page-1]; for (int i = 0; i < static_cast(p.fieldlist.size()); i++) { TQSL_LOCATION_FIELD& f = p.fieldlist[i]; string s; if (f.input_type == TQSL_LOCATION_FIELD_BADZONE) // Don't output these to tSTATION continue; if (f.input_type == TQSL_LOCATION_FIELD_DDLIST || f.input_type == TQSL_LOCATION_FIELD_LIST) { if (f.idx < 0 || f.idx >= static_cast(f.items.size())) { s = ""; } else { s = f.items[f.idx].text; } } else if (f.data_type == TQSL_LOCATION_FIELD_INT) { char buf[20]; snprintf(buf, sizeof buf, "%d", f.idata); s = buf; } else { s = f.cdata; } if (s.size() == 0) continue; int wantsize = s.size() + f.gabbi_name.size() + 20; if (buf == 0 || bufsiz < wantsize) { if (buf != 0) delete[] buf; buf = new unsigned char[wantsize]; bufsiz = wantsize; } if (tqsl_adifMakeField(f.gabbi_name.c_str(), 0, (unsigned char *)s.c_str(), s.size(), buf, bufsiz)) { delete[] buf; return 0; } loc->tSTATION += (const char *)buf; loc->tSTATION += "\n"; } int rval; if (tqsl_hasNextStationLocationCapture(loc, &rval) || !rval) break; tqsl_nextStationLocationCapture(loc); } while (1); tqsl_setStationLocationCapturePage(loc, old_page); if (buf != 0) delete[] buf; loc->tSTATION += "\n"; return loc->tSTATION.c_str(); } DLLEXPORT const char* CALLCONVENTION tqsl_getGABBItCONTACTData(tQSL_Cert cert, tQSL_Location locp, TQSL_QSO_RECORD *qso, int stationuid, char* signdata, int sdlen) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp, false))) { tqslTrace("tqsl_getGABBItCONTACTData", "loc error %d", tQSL_Error); return 0; } if (make_sign_data(loc)) { tqslTrace("tqsl_getGABBItCONTACTData", "make_sign_data error %d", tQSL_Error); return 0; } XMLElement specfield; bool ok = tCONTACT_sign.getFirstElement(specfield); string rec_sign_data = loc->signdata; loc->qso_details = ""; while(ok) { string en = specfield.getElementName(); const char *elname = en.c_str(); const char *value = 0; char buf[100]; if (!strcmp(elname, "CALL")) { value = qso->callsign; } else if (!strcmp(elname, "BAND")) { value = qso->band; } else if (!strcmp(elname, "BAND_RX")) { value = qso->rxband; } else if (!strcmp(elname, "MODE")) { value = qso->mode; } else if (!strcmp(elname, "FREQ")) { value = qso->freq; } else if (!strcmp(elname, "FREQ_RX")) { value = qso->rxfreq; } else if (!strcmp(elname, "PROP_MODE")) { value = qso->propmode; } else if (!strcmp(elname, "SAT_NAME")) { value = qso->satname; } else if (!strcmp(elname, "QSO_DATE")) { if (tqsl_isDateValid(&(qso->date))) value = tqsl_convertDateToText(&(qso->date), buf, sizeof buf); } else if (!strcmp(elname, "QSO_TIME")) { if (tqsl_isTimeValid(&(qso->time))) value = tqsl_convertTimeToText(&(qso->time), buf, sizeof buf); } else { tQSL_Error = TQSL_CUSTOM_ERROR; snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "Unknown field in signing specification: %s", elname); tqslTrace("tqsl_getGABBItCONTACTData", "field err %s", tQSL_CustomError); return 0; } if (value == 0 || value[0] == 0) { pair attr = specfield.getAttribute("required"); if (attr.second && strtol(attr.first.c_str(), NULL, 10)) { string err = specfield.getElementName() + " field required by signature specification not found"; tQSL_Error = TQSL_CUSTOM_ERROR; strncpy(tQSL_CustomError, err.c_str(), sizeof tQSL_CustomError); tqslTrace("tqsl_getGABBItCONTACTData", "field err %s", tQSL_CustomError); return 0; } } else { string v(value); rec_sign_data += trim(v); loc->qso_details += trim(v); } ok = tCONTACT_sign.getNextElement(specfield); } unsigned char sig[129]; int siglen = sizeof sig; rec_sign_data = string_toupper(rec_sign_data); if (tqsl_signDataBlock(cert, (const unsigned char *)rec_sign_data.c_str(), rec_sign_data.size(), sig, &siglen)) return 0; char b64[512]; if (tqsl_encodeBase64(sig, siglen, b64, sizeof b64)) return 0; loc->tCONTACT = "tCONTACT\n"; char sbuf[10], lbuf[40]; snprintf(sbuf, sizeof sbuf, "%d", stationuid); snprintf(lbuf, sizeof lbuf, "%s\n", static_cast(strlen(sbuf)), sbuf); loc->tCONTACT += lbuf; char buf[256]; tqsl_adifMakeField("CALL", 0, (const unsigned char *)qso->callsign, -1, (unsigned char *)buf, sizeof buf); loc->tCONTACT += buf; loc->tCONTACT += "\n"; tqsl_adifMakeField("BAND", 0, (const unsigned char *)qso->band, -1, (unsigned char *)buf, sizeof buf); loc->tCONTACT += buf; loc->tCONTACT += "\n"; tqsl_adifMakeField("MODE", 0, (const unsigned char *)qso->mode, -1, (unsigned char *)buf, sizeof buf); loc->tCONTACT += buf; loc->tCONTACT += "\n"; // Optional fields if (qso->freq[0] != 0) { tqsl_adifMakeField("FREQ", 0, (const unsigned char *)qso->freq, -1, (unsigned char *)buf, sizeof buf); loc->tCONTACT += buf; loc->tCONTACT += "\n"; } if (qso->rxfreq[0] != 0) { tqsl_adifMakeField("FREQ_RX", 0, (const unsigned char *)qso->rxfreq, -1, (unsigned char *)buf, sizeof buf); loc->tCONTACT += buf; loc->tCONTACT += "\n"; } if (qso->propmode[0] != 0) { tqsl_adifMakeField("PROP_MODE", 0, (const unsigned char *)qso->propmode, -1, (unsigned char *)buf, sizeof buf); loc->tCONTACT += buf; loc->tCONTACT += "\n"; } if (qso->satname[0] != 0) { tqsl_adifMakeField("SAT_NAME", 0, (const unsigned char *)qso->satname, -1, (unsigned char *)buf, sizeof buf); loc->tCONTACT += buf; loc->tCONTACT += "\n"; } if (qso->rxband[0] != 0) { tqsl_adifMakeField("BAND_RX", 0, (const unsigned char *)qso->rxband, -1, (unsigned char *)buf, sizeof buf); loc->tCONTACT += buf; loc->tCONTACT += "\n"; } // Date and Time char date_buf[40] = ""; tqsl_convertDateToText(&(qso->date), date_buf, sizeof date_buf); tqsl_adifMakeField("QSO_DATE", 0, (const unsigned char *)date_buf, -1, (unsigned char *)buf, sizeof buf); loc->tCONTACT += buf; loc->tCONTACT += "\n"; date_buf[0] = 0; tqsl_convertTimeToText(&(qso->time), date_buf, sizeof date_buf); tqsl_adifMakeField("QSO_TIME", 0, (const unsigned char *)date_buf, -1, (unsigned char *)buf, sizeof buf); loc->tCONTACT += buf; loc->tCONTACT += "\n"; tqsl_adifMakeField(loc->sigspec.c_str(), '6', (const unsigned char *)b64, -1, (unsigned char *)buf, sizeof buf); loc->tCONTACT += buf; // Signature tqsl_adifMakeField("SIGNDATA", 0, (const unsigned char *)rec_sign_data.c_str(), -1, (unsigned char *)buf, sizeof buf); loc->tCONTACT += buf; loc->tCONTACT += "\n"; loc->tCONTACT += "\n"; if (signdata) strncpy(signdata, rec_sign_data.c_str(), sdlen); return loc->tCONTACT.c_str(); } DLLEXPORT const char* CALLCONVENTION tqsl_getGABBItCONTACT(tQSL_Cert cert, tQSL_Location locp, TQSL_QSO_RECORD *qso, int stationuid) { return tqsl_getGABBItCONTACTData(cert, locp, qso, stationuid, NULL, 0); } DLLEXPORT int CALLCONVENTION tqsl_getLocationCallSign(tQSL_Location locp, char *buf, int bufsiz) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp, false))) { tqslTrace("tqsl_getLocationCallSign", "loc error %d", tQSL_Error); return 1; } if (buf == NULL || bufsiz <= 0) { tqslTrace("tqsl_getLocationCallSign", "arg error buf=0x%lx, bufsiz=%d", buf, bufsiz); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } TQSL_LOCATION_PAGE& p = loc->pagelist[0]; for (int i = 0; i < static_cast(p.fieldlist.size()); i++) { TQSL_LOCATION_FIELD f = p.fieldlist[i]; if (f.gabbi_name == "CALL") { strncpy(buf, f.cdata.c_str(), bufsiz); buf[bufsiz-1] = 0; if (static_cast(f.cdata.size()) >= bufsiz) { tqslTrace("tqsl_getLocationCallSign", "buf error req=%d avail=%d", static_cast(f.cdata.size()), bufsiz); tQSL_Error = TQSL_BUFFER_ERROR; return 1; } return 0; } } tQSL_Error = TQSL_CALL_NOT_FOUND; return 1; } DLLEXPORT int CALLCONVENTION tqsl_setLocationCallSign(tQSL_Location locp, const char *buf) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp, false))) { tqslTrace("tqsl_setLocationCallSign", "loc error %d", tQSL_Error); return 1; } if (buf == NULL) { tqslTrace("tqsl_setLocationCallSign", "arg error buf=null"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } TQSL_LOCATION_PAGE& p = loc->pagelist[0]; for (int i = 0; i < static_cast(p.fieldlist.size()); i++) { TQSL_LOCATION_FIELD f = p.fieldlist[i]; if (f.gabbi_name == "CALL") { for (int j = 0; j < static_cast(f.items.size()); j++) { if (f.items[j].text == buf) { loc->pagelist[0].fieldlist[i].idx = j; loc->pagelist[0].fieldlist[i].cdata = buf; break; } } return 0; } } tQSL_Error = TQSL_CALL_NOT_FOUND; return 1; } DLLEXPORT int CALLCONVENTION tqsl_getLocationDXCCEntity(tQSL_Location locp, int *dxcc) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp, false))) { tqslTrace("tqsl_getLocationDXCCEntity", "loc error %d", tQSL_Error); return 1; } if (dxcc == NULL) { tqslTrace("tqsl_getLocationDXCCEntity", "arg err dxcc=null"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } TQSL_LOCATION_PAGE& p = loc->pagelist[0]; for (int i = 0; i < static_cast(p.fieldlist.size()); i++) { TQSL_LOCATION_FIELD f = p.fieldlist[i]; if (f.gabbi_name == "DXCC") { if (f.idx < 0 || f.idx >= static_cast(f.items.size())) break; // No matching DXCC entity *dxcc = f.items[f.idx].ivalue; return 0; } } tqslTrace("tqsl_getLocationDXCCEntity", "name not found"); tQSL_Error = TQSL_NAME_NOT_FOUND; return 1; } DLLEXPORT int CALLCONVENTION tqsl_getNumProviders(int *n) { if (n == NULL) { tqslTrace("tqsl_getNumProviders", "arg error n=null"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } vector plist; if (tqsl_load_provider_list(plist)) { tqslTrace("tqsl_getNumProviders", "error loading providers %d", tQSL_Error); return 1; } if (plist.size() == 0) { tqslTrace("tqsl_getNumProviders", "prov not found"); tQSL_Error = TQSL_PROVIDER_NOT_FOUND; return 1; } *n = plist.size(); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getProvider(int idx, TQSL_PROVIDER *provider) { if (provider == NULL || idx < 0) { tqslTrace("tqsl_getProvider", "arg error provider=0x%lx, idx=%d", provider, idx); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } vector plist; if (tqsl_load_provider_list(plist)) { tqslTrace("tqsl_getProvider", "err %d loading list", tQSL_Error); return 1; } if (idx >= static_cast(plist.size())) { tqslTrace("tqsl_getProvider", "prov not found"); tQSL_Error = TQSL_PROVIDER_NOT_FOUND; return 1; } *provider = plist[idx]; return 0; } DLLEXPORT int CALLCONVENTION tqsl_importTQSLFile(const char *file, int(*cb)(int type, const char *, void *), void *userdata) { bool foundcerts = false; tQSL_ImportCall[0] = '\0'; tQSL_ImportSerial = 0; int rval = 0; if (file == NULL) { tqslTrace("tqsl_importTQSLFile", "file=NULL"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } XMLElement topel; int status = topel.parseFile(file); if (status) { strncpy(tQSL_ErrorFile, file, sizeof tQSL_ErrorFile); if (status == XML_PARSE_SYSTEM_ERROR) { tQSL_Error = TQSL_FILE_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_importTQSLFile", "system error file=%s err=%s", file, strerror(tQSL_Errno)); } else { tQSL_Error = TQSL_FILE_SYNTAX_ERROR; tqslTrace("tqsl_importTQSLFile", "file %s syntax error", file); } return 1; } XMLElement tqsldata; if (!topel.getFirstElement("tqsldata", tqsldata)) { strncpy(tQSL_ErrorFile, file, sizeof tQSL_ErrorFile); tQSL_Error = TQSL_FILE_SYNTAX_ERROR; return 1; } XMLElement section; bool stat = tqsldata.getFirstElement("tqslcerts", section); if (stat) { XMLElement cert; bool cstat = section.getFirstElement("rootcert", cert); while (cstat) { foundcerts = true; if (tqsl_import_cert(cert.getText().c_str(), ROOTCERT, cb, userdata)) { tqslTrace("tqsl_importTQSLFile", "duplicate root cert"); } cstat = section.getNextElement(cert); } cstat = section.getFirstElement("cacert", cert); while (cstat) { foundcerts = true; if (tqsl_import_cert(cert.getText().c_str(), CACERT, cb, userdata)) { tqslTrace("tqsl_importTQSLFile", "duplicate ca cert"); } cstat = section.getNextElement(cert); } cstat = section.getFirstElement("usercert", cert); while (cstat) { foundcerts = true; if (tqsl_import_cert(cert.getText().c_str(), USERCERT, cb, userdata)) { tqslTrace("tqsl_importTQSLFile", "error importing user cert"); tQSL_Error = TQSL_CERT_ERROR; rval = 1; } cstat = section.getNextElement(cert); } } // If any of the user certificates failed import, return the error status. if (rval) { return rval; } stat = tqsldata.getFirstElement("tqslconfig", section); if (stat) { // Check to make sure we aren't overwriting newer version int major = strtol(section.getAttribute("majorversion").first.c_str(), NULL, 10); int minor = strtol(section.getAttribute("minorversion").first.c_str(), NULL, 10); int curmajor, curminor; if (tqsl_getConfigVersion(&curmajor, &curminor)) { tqslTrace("tqsl_importTQSLFile", "Get config ver error %d", tQSL_Error); return 1; } if (major < curmajor) { if (foundcerts) { tqslTrace("tqsl_importTQSLFile", "Suppressing update from V%d.%d to V%d.%d", curmajor, curminor, major, minor); return rval; } tQSL_Error = TQSL_CUSTOM_ERROR; snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "This configuration file (V%d.%d) is older than the currently installed one (V%d.%d). It will not be installed.", major, minor, curmajor, curminor); tqslTrace("tqsl_importTQSLFile", "Config update error: %s", tQSL_CustomError); return 1; } if (major == curmajor) { if (minor == curminor) { // Same rev as already installed tqslTrace("tqsl_importTQSLFile", "Suppressing update from V%d.%d to V%d.%d", curmajor, curminor, major, minor); return rval; } if (minor < curminor) { if (foundcerts) { tqslTrace("tqsl_importTQSLFile", "Suppressing update from V%d.%d to V%d.%d", curmajor, curminor, major, minor); return rval; } tQSL_Error = TQSL_CUSTOM_ERROR; snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "This configuration file (V%d.%d) is older than the currently installed one (V%d.%d). It will not be installed.", major, minor, curmajor, curminor); tqslTrace("tqsl_importTQSLFile", "Config update error: %s", tQSL_CustomError); return rval; } } // Save the configuration file ofstream out; #ifdef _WIN32 string fn = string(tQSL_BaseDir) + "\\config.xml"; #else string fn = string(tQSL_BaseDir) + "/config.xml"; #endif out.exceptions(ios::failbit | ios::eofbit | ios::badbit); try { #ifdef _WIN32 wchar_t *wfn = utf8_to_wchar(fn.c_str()); out.open(wfn); free_wchar(wfn); #else out.open(fn.c_str()); #endif out << section << endl; out.close(); } catch(exception& x) { tQSL_Error = TQSL_CUSTOM_ERROR; snprintf(tQSL_CustomError, sizeof tQSL_CustomError, "Error writing new configuration file (%s): %s/%s", fn.c_str(), x.what(), strerror(errno)); tqslTrace("tqsl_importTQSLFile", "I/O error: %s", tQSL_CustomError); if (cb) return (*cb)(TQSL_CERT_CB_RESULT | TQSL_CERT_CB_ERROR | TQSL_CERT_CB_CONFIG, fn.c_str(), userdata); if (tQSL_Error == 0) { tQSL_Error = TQSL_CERT_ERROR; } return 1; } // Clear stored config data to force re-reading new config tqsl_xml_config.clear(); DXCCMap.clear(); DXCCList.clear(); BandList.clear(); ModeList.clear(); tqsl_page_map.clear(); tqsl_field_map.clear(); tqsl_adif_map.clear(); tqsl_cabrillo_map.clear(); string version = "Configuration V" + section.getAttribute("majorversion").first + "." + section.getAttribute("minorversion").first + "\n" + fn; if (cb) { int cbret = (*cb)(TQSL_CERT_CB_RESULT | TQSL_CERT_CB_LOADED | TQSL_CERT_CB_CONFIG, version.c_str(), userdata); if (cbret || rval) { if (tQSL_Error == 0) { tQSL_Error = TQSL_CERT_ERROR; } return 1; } } } if (rval && tQSL_Error == 0) { tQSL_Error = TQSL_CERT_ERROR; } return rval; } /* * Get the first user certificate from a .tq6 file */ DLLEXPORT int CALLCONVENTION tqsl_getSerialFromTQSLFile(const char *file, long *serial) { XMLElement topel; if (file == NULL || serial == NULL) { tqslTrace("tqsl_getSerialFromTQSLFile", "Arg error file=0x%lx, serial=0x%lx", file, serial); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } int status = topel.parseFile(file); if (status) { strncpy(tQSL_ErrorFile, file, sizeof tQSL_ErrorFile); if (status == XML_PARSE_SYSTEM_ERROR) { tQSL_Error = TQSL_FILE_SYSTEM_ERROR; tQSL_Errno = errno; tqslTrace("tqsl_getSerialFromTQSLFile", "parse error %d, error %s", tQSL_Error, strerror(tQSL_Errno)); } else { tQSL_Error = TQSL_FILE_SYNTAX_ERROR; tqslTrace("tqsl_getSerialFromTQSLFile", "parse syntax error %d", tQSL_Error); } return 1; } XMLElement tqsldata; if (!topel.getFirstElement("tqsldata", tqsldata)) { strncpy(tQSL_ErrorFile, file, sizeof tQSL_ErrorFile); tqslTrace("tqsl_getSerialFromTQSLFile", "parse syntax error %d", tQSL_Error); tQSL_Error = TQSL_FILE_SYNTAX_ERROR; return 1; } XMLElement section; bool stat = tqsldata.getFirstElement("tqslcerts", section); if (stat) { XMLElement cert; bool cstat = section.getFirstElement("usercert", cert); if (cstat) { if (tqsl_get_pem_serial(cert.getText().c_str(), serial)) { strncpy(tQSL_ErrorFile, file, sizeof tQSL_ErrorFile); tqslTrace("tqsl_getSerialFromTQSLFile", "parse syntax error %d", tQSL_Error); tQSL_Error = TQSL_FILE_SYNTAX_ERROR; return 1; } return 0; } } tqslTrace("tqsl_getSerialFromTQSLFile", "no usercert in file %s", file); return 1; } DLLEXPORT int CALLCONVENTION tqsl_getDeletedStationLocations(char ***locp, int *nloc) { if (locp == NULL) { tqslTrace("tqsl_getDeletedStationLocations", "arg error locp=NULL"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } if (nloc == NULL) { tqslTrace("tqsl_getDeletedStationLocations", "arg error nloc=NULL"); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } *locp = NULL; *nloc = 0; vector namelist; XMLElement top_el; if (tqsl_load_station_data(top_el, true)) { tqslTrace("tqsl_getDeletedStationLocations", "error %d loading station data", tQSL_Error); return 1; } XMLElement sfile; if (top_el.getFirstElement(sfile)) { XMLElement sd; bool ok = sfile.getFirstElement("StationData", sd); while (ok && sd.getElementName() == "StationData") { pair name = sd.getAttribute("name"); if (name.second) { namelist.push_back(name.first); } ok = sfile.getNextElement(sd); } } *nloc = namelist.size(); if (*nloc == 0) { *locp = NULL; return 0; } *locp = reinterpret_cast(calloc(*nloc, sizeof(**locp))); vector::iterator it; char **p = *locp; for (it = namelist.begin(); it != namelist.end(); it++) { *p++ = strdup((*it).c_str()); } return 0; } DLLEXPORT void CALLCONVENTION tqsl_freeDeletedLocationList(char** list, int nloc) { if (!list) return; for (int i = 0; i < nloc; i++) if (list[i]) free(list[i]); if (list) free(list); } DLLEXPORT int CALLCONVENTION tqsl_getLocationQSODetails(tQSL_Location locp, char *buf, int buflen) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp, false))) { tqslTrace("tqsl_getLocationDXCCEntity", "loc error %d", tQSL_Error); return 1; } if (buf == NULL) { tqslTrace("tqsl_getLocationQSODetails", "Argument error, buf = 0x%lx", buf); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } strncpy(buf, loc->qso_details.c_str(), buflen); return 0; } DLLEXPORT int CALLCONVENTION tqsl_getLocationStationDetails(tQSL_Location locp, char *buf, int buflen) { TQSL_LOCATION *loc; if (!(loc = check_loc(locp, false))) { tqslTrace("tqsl_getLocationStationDetails", "loc error %d", tQSL_Error); return 1; } if (buf == NULL) { tqslTrace("tqsl_getLocationStationDetails", "Argument error, buf = 0x%lx", buf); tQSL_Error = TQSL_ARGUMENT_ERROR; return 1; } strncpy(buf, loc->loc_details.c_str(), buflen); return 0; } tqsl-2.5.1/src/load_cert.cpp0000664000076400007640000000201113563134442015740 0ustar rmurphyrmurphy/*************************************************************************** load_cert.cpp - description ------------------- begin : Sat Dec 14 2002 copyright : (C) 2002 by ARRL author : Jon Bloom email : jbloom@arrl.org revision : $Id$ ***************************************************************************/ #ifdef HAVE_CONFIG_H #include "sysconfig.h" #endif #include #include #include "tqsllib.h" using std::string; using std::ios; using std::cerr; using std::cout; using std::endl; int cb(int, const char *msg, void *) { cout << msg << endl; return 0; } int main(int argc, char *argv[]) { if (tqsl_init()) { cerr << tqsl_getErrorString() << endl; return EXIT_FAILURE; } for (int i = 1; i < argc; i++) { if (tqsl_importTQSLFile(argv[i], cb, 0)) { cerr << tqsl_getErrorString() << endl; return EXIT_FAILURE; } } return EXIT_SUCCESS; } tqsl-2.5.1/src/gen_crq.cpp0000664000076400007640000000752013563134442015434 0ustar rmurphyrmurphy/*************************************************************************** gen_crq.cpp - description ------------------- begin : Sat Dec 14 2002 copyright : (C) 2002 by ARRL author : Jon Bloom email : jbloom@arrl.org revision : $Id$ Generates a set of certificate-request files. ***************************************************************************/ #ifdef HAVE_CONFIG_H #include "sysconfig.h" #endif #include #include #include #include #include #include #ifdef HAVE_GETOPT_H #include #endif #include #include #include "tqsllib.h" #include "tqslexc.h" using std::cerr; using std::endl; int usage() { std::cerr << "Usage: -e email -d dxcc [-c sign_call] [-x sign_dxcc] call1 [call2 ...]" << endl; exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { string sign_call, email_addr; int dxcc = 0, sign_dxcc = 0; tQSL_Cert sign_cert = 0; try { if (tqsl_init()) throw tqslexc(); int c; while ((c = getopt(argc, argv, "c:x:e:d:")) != -1) { switch (c) { case 'c': sign_call = optarg; break; case 'x': sign_dxcc = strtol(optarg, NULL, 10); break; case 'd': dxcc = strtol(optarg, NULL, 10); break; case 'e': email_addr = optarg; break; default: usage(); } } if (optind >= argc || email_addr == "" || dxcc == 0) usage(); if (sign_call != "") { // if (sign_dxcc == 0) // usage(); tQSL_Cert *list; int ncerts; if (tqsl_selectCertificates(&list, &ncerts, sign_call.c_str(), sign_dxcc, 0, 0, 1)) throw tqslexc(); if (ncerts < 1) { string erm = "No signing certificate found for " + sign_call; if (sign_dxcc) { const char *entity; tqsl_getDXCCEntityName(sign_dxcc, &entity); erm += " with DXCC Entity="; erm += entity; } throw myexc(erm); } sign_cert = *list; } else if (sign_dxcc != 0) { usage(); } if (sign_cert) { char buf[512]; long serial; int cdxcc; if (tqsl_getCertificateIssuer(sign_cert, buf, sizeof buf)) throw tqslexc(); if (tqsl_getCertificateSerial(sign_cert, &serial)) throw tqslexc(); if (tqsl_getCertificateDXCCEntity(sign_cert, &cdxcc)) throw tqslexc(); std::cout << "Signing certificate issuer: " << buf << endl; std::cout << "Signing certificate serial: " << serial << endl; std::cout << " Signing certificate DXCC: " << cdxcc << endl; if (tqsl_beginSigning(sign_cert, const_cast(""), 0, 0)) throw tqslexc(); } TQSL_CERT_REQ crq; memset(&crq, 0, sizeof crq); strncpy(crq.name, "Ish Kabibble", sizeof crq.name); strncpy(crq.address1, "1 No Place", sizeof crq.address1); strncpy(crq.city, "City", sizeof crq.city); strncpy(crq.state, "ST", sizeof crq.state); strncpy(crq.country, "USA", sizeof crq.country); strncpy(crq.emailAddress, email_addr.c_str(), sizeof crq.emailAddress); crq.dxccEntity = dxcc; tqsl_initDate(&crq.qsoNotBefore, "1945-11-15"); crq.signer = sign_cert; for (; optind < argc; optind++) { string call = argv[optind]; strncpy(crq.callSign, call.c_str(), sizeof crq.callSign); for (char *cp = argv[optind]; *cp; cp++) { if (*cp == '/') *cp = '_'; } string filename = string(argv[optind]) + ".tq5"; std::cout << "Creating CRQ for " << crq.callSign << " DXCC=" << crq.dxccEntity << endl; if (tqsl_createCertRequest(filename.c_str(), &crq, 0, 0)) throw tqslexc(); } return EXIT_SUCCESS; } catch(exception& x) { std::cerr << "Aborting: " << x.what() << endl; return EXIT_FAILURE; } return EXIT_SUCCESS; } tqsl-2.5.1/src/dumptqsldata.cpp0000664000076400007640000000407013563134442016516 0ustar rmurphyrmurphy/*************************************************************************** dumptqsldata.c - description ------------------- begin : Mon Mar 3 2003 copyright : (C) 2003 by ARRL author : Jon Bloom email : jbloom@arrl.org revision : $Id$ ***************************************************************************/ /* Dumps the config data from the TQSL library */ #include #include #include "tqsllib.h" void errchk(int stat) { if (stat) { printf("ERROR: %s\n", tqsl_getErrorString()); exit(1); } } int main() { int count, i; const char *cp1, *cp2; tQSL_Date start, end; int low, high; char buf1[20], buf2[20]; errchk(tqsl_init()); puts("===== MODES =====\n Mode Group"); errchk(tqsl_getNumMode(&count)); for (i = 0; i < count; i++) { errchk(tqsl_getMode(i, &cp1, &cp2)); printf(" %-10.10s %s\n", cp1, cp2); } puts("\n===== BANDS =====\n Band Spectrum Low High"); errchk(tqsl_getNumBand(&count)); for (i = 0; i < count; i++) { errchk(tqsl_getBand(i, &cp1, &cp2, &low, &high)); printf(" %-10.10s %-8.8s %-8d %d\n", cp1, cp2, low, high); } puts("\n===== DXCC =====\n Entity Name"); errchk(tqsl_getNumDXCCEntity(&count)); for (i = 0; i < count; i++) { errchk(tqsl_getDXCCEntity(i, &low, &cp1)); printf(" %-6d %s\n", low, cp1); } puts("\n===== PROP_MODES =====\n Mode Descrip"); errchk(tqsl_getNumPropagationMode(&count)); for (i = 0; i < count; i++) { errchk(tqsl_getPropagationMode(i, &cp1, &cp2)); printf(" %-6s %s\n", cp1, cp2); } puts("\n===== SATELLITES =====\n Sat Start Date End Date Descrip"); errchk(tqsl_getNumSatellite(&count)); for (i = 0; i < count; i++) { errchk(tqsl_getSatellite(i, &cp1, &cp2, &start, &end)); buf1[0] = buf2[0] = '\0'; tqsl_convertDateToText(&start, buf1, sizeof buf1); tqsl_convertDateToText(&end, buf2, sizeof buf2); printf(" %-6s %-10s %-10s %s\n", cp1, buf1, buf2, cp2); } return 0; } tqsl-2.5.1/src/doxygen/0000775000076400007640000000000013563134442014763 5ustar rmurphyrmurphytqsl-2.5.1/src/doxygen/html/0000775000076400007640000000000013563134442015727 5ustar rmurphyrmurphytqsl-2.5.1/src/doxygen/html/tqsllib_8h_source.html0000664000076400007640000066037613563134442022270 0ustar rmurphyrmurphy TrustedQSL Library API: tqsllib.h Source File
TrustedQSL Library API
tqsllib.h
Go to the documentation of this file.
1 /***************************************************************************
2  tqsllib.h - description
3  -------------------
4  begin : Mon May 20 2002
5  copyright : (C) 2002 by ARRL
6  author : Jon Bloom
7  email : jbloom@arrl.org
8  revision : $Id: tqsllib.h,v 1.14 2013/03/01 13:26:44 k1mu Exp $
9  ***************************************************************************/
10 
11 #ifndef TQSLLIB_H
12 #define TQSLLIB_H
13 
14 #if defined(_WIN32) && !defined(TQSL_NODLL)
15  #ifdef TQSLLIB_DEF
16  #define DLLEXPORT __declspec(dllexport)
17  #define DLLEXPORTDATA __declspec(dllexport)
18  #define CALLCONVENTION __stdcall
19  #else
20  #define DLLEXPORT __declspec(dllimport)
21  #define DLLEXPORTDATA __declspec(dllimport)
22  #define CALLCONVENTION __stdcall
23  #endif
24 #else
25  #define DLLEXPORT
26  #define DLLEXPORTDATA
27  #define CALLCONVENTION
28 #endif
29 
30 #include "adif.h"
31 #include "cabrillo.h"
32 
37 /* Sizes */
38 #define TQSL_MAX_PATH_LEN 256
39 #define TQSL_PASSWORD_MAX 80
40 #define TQSL_NAME_ELEMENT_MAX 256
41 #define TQSL_CALLSIGN_MAX 13
42 #define TQSL_CRQ_NAME_MAX 60
43 #define TQSL_CRQ_ADDR_MAX 80
44 #define TQSL_CRQ_CITY_MAX 80
45 #define TQSL_CRQ_STATE_MAX 80
46 #define TQSL_CRQ_POSTAL_MAX 20
47 #define TQSL_CRQ_COUNTRY_MAX 80
48 #define TQSL_CRQ_EMAIL_MAX 180
49 #define TQSL_BAND_MAX 6
50 #define TQSL_MODE_MAX 16
51 #define TQSL_FREQ_MAX 20
52 #define TQSL_SATNAME_MAX 20
53 #define TQSL_PROPMODE_MAX 20
54 
55 #define TQSL_CERT_CB_USER 0
56 #define TQSL_CERT_CB_CA 1
57 #define TQSL_CERT_CB_ROOT 2
58 #define TQSL_CERT_CB_PKEY 3
59 #define TQSL_CERT_CB_CONFIG 4
60 #define TQSL_CERT_CB_CERT_TYPE(x) ((x) & 0xf)
61 #define TQSL_CERT_CB_MILESTONE 0
62 #define TQSL_CERT_CB_RESULT 0x10
63 #define TQSL_CERT_CB_CALL_TYPE(x) ((x) & TQSL_CERT_CB_RESULT)
64 #define TQSL_CERT_CB_PROMPT 0
65 #define TQSL_CERT_CB_DUPLICATE 0x100
66 #define TQSL_CERT_CB_ERROR 0x200
67 #define TQSL_CERT_CB_LOADED 0x300
68 #define TQSL_CERT_CB_RESULT_TYPE(x) ((x) & 0x0f00)
69 
70 typedef void * tQSL_Cert;
71 typedef void * tQSL_Location;
72 typedef char * tQSL_StationDataEnc;
73 
75 typedef struct {
76  int year;
77  int month;
78  int day;
79 } tQSL_Date;
80 
82 typedef struct {
83  int hour;
84  int minute;
85  int second;
86 } tQSL_Time;
87 
89 typedef struct tqsl_provider_st {
95 
97 typedef struct tqsl_cert_req_st {
98  char providerName[TQSL_NAME_ELEMENT_MAX+1];
99  char providerUnit[TQSL_NAME_ELEMENT_MAX+1];
100  char callSign[TQSL_CALLSIGN_MAX+1];
101  char name[TQSL_CRQ_NAME_MAX+1];
102  char address1[TQSL_CRQ_ADDR_MAX+1];
103  char address2[TQSL_CRQ_ADDR_MAX+1];
104  char city[TQSL_CRQ_CITY_MAX+1];
105  char state[TQSL_CRQ_STATE_MAX+1];
106  char postalCode[TQSL_CRQ_POSTAL_MAX+1];
107  char country[TQSL_CRQ_COUNTRY_MAX+1];
112  char password[TQSL_PASSWORD_MAX+1];
114  char renew;
115 } TQSL_CERT_REQ;
116 
118 typedef struct {
119  char callsign[TQSL_CALLSIGN_MAX+1];
120  char band[TQSL_BAND_MAX+1];
121  char mode[TQSL_MODE_MAX+1];
122  char submode[TQSL_MODE_MAX+1];
125  char freq[TQSL_FREQ_MAX+1];
126  char rxfreq[TQSL_FREQ_MAX+1];
127  char rxband[TQSL_BAND_MAX+1];
128  char propmode[TQSL_PROPMODE_MAX+1];
129  char satname[TQSL_SATNAME_MAX+1];
131  bool mode_set;
132  bool band_set;
133  bool date_set;
134  bool time_set;
136 
138 DLLEXPORTDATA extern const char *tQSL_BaseDir;
139 DLLEXPORTDATA extern const char *tQSL_RsrcDir;
140 
141 #ifdef __cplusplus
142 extern "C" {
143 #endif
144 
149 
152 DLLEXPORTDATA extern int tQSL_Error;
158 DLLEXPORTDATA extern char tQSL_ErrorFile[256];
160 DLLEXPORTDATA extern char tQSL_CustomError[256];
162 DLLEXPORTDATA extern int tQSL_Errno;
164 DLLEXPORTDATA extern char tQSL_ImportCall[256];
166 DLLEXPORTDATA extern long tQSL_ImportSerial;
168 DLLEXPORTDATA extern FILE* tQSL_DiagFile;
169 
175 
185 DLLEXPORT int CALLCONVENTION tqsl_setDirectory(const char *dir);
186 
191 
196 DLLEXPORT const char* CALLCONVENTION tqsl_getErrorString_v(int err);
197 
205 DLLEXPORT int CALLCONVENTION tqsl_encodeBase64(const unsigned char *data, int datalen, char *output, int outputlen);
206 
215 DLLEXPORT int CALLCONVENTION tqsl_decodeBase64(const char *input, unsigned char *data, int *datalen);
216 
223 DLLEXPORT int CALLCONVENTION tqsl_initDate(tQSL_Date *date, const char *str);
224 
231 DLLEXPORT int CALLCONVENTION tqsl_initTime(tQSL_Time *time, const char *str);
232 
243 
249 DLLEXPORT int CALLCONVENTION tqsl_subtractDates(const tQSL_Date *a, const tQSL_Date *b, int *diff);
250 
255 DLLEXPORT char* CALLCONVENTION tqsl_convertDateToText(const tQSL_Date *date, char *buf, int bufsiz);
256 
262 
268 
274 
279 DLLEXPORT char* CALLCONVENTION tqsl_convertTimeToText(const tQSL_Time *time, char *buf, int bufsiz);
280 
283 DLLEXPORT int CALLCONVENTION tqsl_getVersion(int *major, int *minor);
284 
287 DLLEXPORT int CALLCONVENTION tqsl_getConfigVersion(int *major, int *minor);
288 
317 #define TQSL_SELECT_CERT_WITHKEYS 1
318 #define TQSL_SELECT_CERT_EXPIRED 2
319 #define TQSL_SELECT_CERT_SUPERCEDED 4
320 
321 
348 DLLEXPORT int CALLCONVENTION tqsl_selectCertificates(tQSL_Cert **certlist, int *ncerts,
349  const char *callsign, int dxcc, const tQSL_Date *date, const TQSL_PROVIDER *issuer, int flag);
350 
362 DLLEXPORT int CALLCONVENTION tqsl_selectCACertificates(tQSL_Cert **certlist, int *ncerts, const char *type);
363 
373  int idx);
374 
378 
382 
386 
389 DLLEXPORT int CALLCONVENTION tqsl_getCertificateEncoded(tQSL_Cert cert, char *buf, int bufsiz);
390 
393 DLLEXPORT int CALLCONVENTION tqsl_getKeyEncoded(tQSL_Cert cert, char *buf, int bufsiz);
394 
397 DLLEXPORT int CALLCONVENTION tqsl_importKeyPairEncoded(const char *callsign, const char *type, const char *keybuf, const char *certbuf);
398 
402 
406 DLLEXPORT int CALLCONVENTION tqsl_getCertificateSerialExt(tQSL_Cert cert, char *serial, int serialsiz);
407 
412 
422 DLLEXPORT int CALLCONVENTION tqsl_getCertificateIssuer(tQSL_Cert cert, char *buf, int bufsiz);
423 
453 DLLEXPORT int CALLCONVENTION tqsl_getCertificateCallSign(tQSL_Cert cert, char *buf, int bufsiz);
463 DLLEXPORT int CALLCONVENTION tqsl_getCertificateAROName(tQSL_Cert cert, char *buf, int bufsiz);
464 
474 DLLEXPORT int CALLCONVENTION tqsl_getCertificateEmailAddress(tQSL_Cert cert, char *buf, int bufsiz);
475 
485 
495 
505 
515 
525 
537 
549 
560 DLLEXPORT int CALLCONVENTION tqsl_getCertificateRequestCity(tQSL_Cert cert, char *str, int bufsiz);
561 
572 DLLEXPORT int CALLCONVENTION tqsl_getCertificateRequestState(tQSL_Cert cert, char *str, int bufsiz);
573 
585 
597 
598 #define TQSL_PK_TYPE_ERR 0
599 #define TQSL_PK_TYPE_NONE 1
600 #define TQSL_PK_TYPE_UNENC 2
601 #define TQSL_PK_TYPE_ENC 3
602 
603 
618 
619 
624 
631 
632 #define TQSL_CERT_STATUS_UNK 0
633 #define TQSL_CERT_STATUS_SUP 1
634 #define TQSL_CERT_STATUS_EXP 2
635 #define TQSL_CERT_STATUS_OK 3
636 #define TQSL_CERT_STATUS_INV 4
637 
638 
653 
658 DLLEXPORT int CALLCONVENTION tqsl_setCertificateStatus(long serial, const char *status);
659 
660 /* int tqsl_checkCertificate(tQSL_Cert); */
661 
679 DLLEXPORT int CALLCONVENTION tqsl_importTQSLFile(const char *file, int(*cb)(int type, const char *message, void *userdata), void *user);
680 
689 DLLEXPORT int CALLCONVENTION tqsl_getSerialFromTQSLFile(const char *file, long *serial);
690 
694 
700 
713 DLLEXPORT int CALLCONVENTION tqsl_createCertRequest(const char *filename, TQSL_CERT_REQ *req,
714  int(*pwcb)(char *pwbuf, int pwsize, void *userdata), void *user);
715 
723 DLLEXPORT int CALLCONVENTION tqsl_exportPKCS12File(tQSL_Cert cert, const char *filename, const char *p12password);
724 
733 DLLEXPORT int CALLCONVENTION tqsl_exportPKCS12Base64(tQSL_Cert cert, char *base64, int b64len, const char *p12password);
734 
737 DLLEXPORT int CALLCONVENTION tqsl_importPKCS12File(const char *filename, const char *p12password, const char *password,
738  int (*pwcb)(char *buf, int bufsiz, void *userdata), int(*cb)(int type , const char *message, void *userdata), void *user);
739 
742 DLLEXPORT int CALLCONVENTION tqsl_importPKCS12Base64(const char *base64, const char *p12password, const char *password,
743  int (*pwcb)(char *buf, int bufsiz, void *userdata), int(*cb)(int type , const char *message, void *userdata), void *user);
744 
746 DLLEXPORT int CALLCONVENTION tqsl_getDeletedCallsignCertificates(char ***calls, int *ncall, const char *filter);
747 
749 DLLEXPORT void CALLCONVENTION tqsl_freeDeletedCertificateList(char **list, int nloc);
750 
753 
757 
780 DLLEXPORT int CALLCONVENTION tqsl_beginSigning(tQSL_Cert cert, char *password, int(*pwcb)(char *pwbuf, int pwsize, void *userdata), void *user);
781 
787 
793 
799 DLLEXPORT int CALLCONVENTION tqsl_signDataBlock(tQSL_Cert cert, const unsigned char *data, int datalen, unsigned char *sig, int *siglen);
800 
805 DLLEXPORT int CALLCONVENTION tqsl_verifyDataBlock(tQSL_Cert cert, const unsigned char *data, int datalen, unsigned char *sig, int siglen);
806 
814 DLLEXPORT int CALLCONVENTION tqsl_signQSORecord(tQSL_Cert cert, tQSL_Location loc, TQSL_QSO_RECORD *rec, unsigned char *sig, int *siglen);
815 
821 
926 /* Location field input types */
927 
928 #define TQSL_LOCATION_FIELD_TEXT 1
929 #define TQSL_LOCATION_FIELD_DDLIST 2
930 #define TQSL_LOCATION_FIELD_LIST 3
931 #define TQSL_LOCATION_FIELD_BADZONE 4
932 
933 /* Location field data types */
934 #define TQSL_LOCATION_FIELD_CHAR 1
935 #define TQSL_LOCATION_FIELD_INT 2
936 
937 
939 
945 
948 
952 
955 
961 
966 
969 
972 
975 
978 
986 
989 
992 
995 
997 DLLEXPORT int CALLCONVENTION tqsl_getStationLocationName(tQSL_Location loc, int idx, char *buf, int bufsiz);
998 
1000 DLLEXPORT int CALLCONVENTION tqsl_getStationLocationCallSign(tQSL_Location loc, int idx, char *buf, int bufsiz);
1001 
1003 DLLEXPORT int CALLCONVENTION tqsl_getStationLocationField(tQSL_Location locp, const char *name, char *namebuf, int bufsize);
1004 
1010 
1018 
1023 
1027 
1029 DLLEXPORT int CALLCONVENTION tqsl_mergeStationLocations(const char *locdata);
1030 
1033 
1036 
1038 DLLEXPORT int CALLCONVENTION tqsl_getDeletedStationLocations(char ***locp, int *nloc);
1039 
1041 DLLEXPORT void CALLCONVENTION tqsl_freeDeletedLocationList(char **list, int nloc);
1042 
1045 
1048 
1050 DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataLabel(tQSL_Location loc, int field_num, char *buf, int bufsiz);
1051 
1054 
1056 DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataGABBI(tQSL_Location loc, int field_num, char *buf, int bufsiz);
1057 
1065 
1068 
1070 DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataLabel(tQSL_Location loc, int field_num, char *buf, int bufsiz);
1071 
1074 
1076 DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataGABBI(tQSL_Location loc, int field_num, char *buf, int bufsiz);
1077 
1083 DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldInputType(tQSL_Location loc, int field_num, int *type);
1084 
1089 DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataType(tQSL_Location loc, int field_num, int *type);
1090 
1099 DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldFlags(tQSL_Location loc, int field_num, int *flags);
1100 
1102 DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataLength(tQSL_Location loc, int field_num, int *rval);
1103 
1110 DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldCharData(tQSL_Location loc, int field_num, char *buf, int bufsiz);
1111 
1117 DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldIntData(tQSL_Location loc, int field_num, int *dat);
1118 
1123 DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldIndex(tQSL_Location loc, int field_num, int *dat);
1124 
1126 DLLEXPORT int CALLCONVENTION tqsl_getNumLocationFieldListItems(tQSL_Location loc, int field_num, int *rval);
1127 
1129 DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldListItem(tQSL_Location loc, int field_num, int item_idx, char *buf, int bufsiz);
1130 
1132 DLLEXPORT int CALLCONVENTION tqsl_setLocationFieldCharData(tQSL_Location loc, int field_num, const char *buf);
1133 
1136 DLLEXPORT int CALLCONVENTION tqsl_setLocationFieldIntData(tQSL_Location loc, int field_num, int dat);
1137 
1142 DLLEXPORT int CALLCONVENTION tqsl_setLocationFieldIndex(tQSL_Location loc, int field_num, int dat);
1143 
1148 DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldChanged(tQSL_Location loc, int field_num, int *changed);
1149 
1151 DLLEXPORT int CALLCONVENTION tqsl_getLocationCallSign(tQSL_Location loc, char *buf, int bufsiz);
1152 
1155 
1158 
1160 DLLEXPORT int CALLCONVENTION tqsl_getLocationQSODetails(tQSL_Location locp, char *buf, int buflen);
1161 
1163 DLLEXPORT int CALLCONVENTION tqsl_getLocationStationDetails(tQSL_Location locp, char *buf, int buflen);
1164 
1166 DLLEXPORT int CALLCONVENTION tqsl_saveCallsignLocationInfo(const char *callsign, const char *json);
1167 
1169 DLLEXPORT int CALLCONVENTION tqsl_getCallsignLocationInfo(const char *callsign, char **buf);
1170 
1174 
1177 DLLEXPORT int CALLCONVENTION tqsl_getDXCCEntity(int index, int *number, const char **name);
1178 
1181 DLLEXPORT int CALLCONVENTION tqsl_getDXCCEntityName(int number, const char **name);
1182 
1185 DLLEXPORT int CALLCONVENTION tqsl_getDXCCZoneMap(int number, const char **zonemap);
1186 
1190 
1194 
1197 DLLEXPORT int CALLCONVENTION tqsl_getDXCCDeleted(int number, int *deleted);
1198 
1200 DLLEXPORT int CALLCONVENTION tqsl_getNumBand(int *number);
1201 
1211 DLLEXPORT int CALLCONVENTION tqsl_getBand(int index, const char **name, const char **spectrum, int *low, int *high);
1212 
1214 DLLEXPORT int CALLCONVENTION tqsl_getNumMode(int *number);
1215 
1223 DLLEXPORT int CALLCONVENTION tqsl_getMode(int index, const char **mode, const char **group);
1224 
1227 
1235 DLLEXPORT int CALLCONVENTION tqsl_getPropagationMode(int index, const char **name, const char **descrip);
1236 
1239 
1249 DLLEXPORT int CALLCONVENTION tqsl_getSatellite(int index, const char **name, const char **descrip,
1250  tQSL_Date *start, tQSL_Date *end);
1251 
1255 
1266 DLLEXPORT int CALLCONVENTION tqsl_setCabrilloMapEntry(const char *contest, int field, int contest_type);
1267 
1279 DLLEXPORT int CALLCONVENTION tqsl_getCabrilloMapEntry(const char *contest, int *fieldnum, int *contest_type);
1280 
1284 
1287 DLLEXPORT int CALLCONVENTION tqsl_setADIFMode(const char *adif_item, const char *mode);
1288 
1291 DLLEXPORT int CALLCONVENTION tqsl_getADIFMode(const char *adif_item, char *mode, int nmode);
1292 
1295 DLLEXPORT int CALLCONVENTION tqsl_getADIFSubMode(const char *adif_item, char *mode, char *submode, int nmode);
1296 
1306 DLLEXPORT const char* CALLCONVENTION tqsl_getGABBItCERT(tQSL_Cert cert, int uid);
1307 
1318 DLLEXPORT const char* CALLCONVENTION tqsl_getGABBItSTATION(tQSL_Location loc, int uid, int certuid);
1319 
1333  int stationuid);
1334 
1349  int stationuid, char *signdata, int sdlen);
1350 
1357 DLLEXPORT void CALLCONVENTION tqslTrace(const char *name, const char *format, ...);
1367 DLLEXPORT int CALLCONVENTION tqsl_openDiagFile(const char* file);
1368 
1369 #ifdef _WIN32
1370 DLLEXPORT wchar_t* CALLCONVENTION utf8_to_wchar(const char* str);
1371 DLLEXPORT char* CALLCONVENTION wchar_to_utf8(const wchar_t* str, bool forceUTF8);
1372 DLLEXPORT void CALLCONVENTION free_wchar(wchar_t* ptr);
1373 #endif
1374 
1375 #ifdef __cplusplus
1376 }
1377 #endif
1378 
1379 /* Useful defines */
1380 #define TQSL_MAX_PW_LENGTH 32
1381 
1382 #endif /* TQSLLIB_H */
DLLEXPORT int CALLCONVENTION tqsl_getCertificateQSONotBeforeDate(tQSL_Cert cert, tQSL_Date *date)
DLLEXPORT void CALLCONVENTION tqslTrace(const char *name, const char *format,...)
DLLEXPORT int CALLCONVENTION tqsl_freeStationDataEnc(tQSL_StationDataEnc sdata)
DLLEXPORT int CALLCONVENTION tqsl_getCertificateStatus(long serial)
DLLEXPORT int CALLCONVENTION tqsl_getCertificateIssuerOrganization(tQSL_Cert cert, char *buf, int bufsiz)
int month
Numeric month.
Definition: tqsllib.h:77
DLLEXPORT int CALLCONVENTION tqsl_getDXCCEndDate(int number, tQSL_Date *d)
DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldInputType(tQSL_Location loc, int field_num, int *type)
Definition: tqsllib.h:118
DLLEXPORT int CALLCONVENTION tqsl_getLocationQSODetails(tQSL_Location locp, char *buf, int buflen)
DLLEXPORT int CALLCONVENTION tqsl_getCertificateRequestAddress2(tQSL_Cert cert, char *str, int bufsiz)
DLLEXPORT int CALLCONVENTION tqsl_setStationLocationCaptureName(tQSL_Location loc, const char *name)
DLLEXPORT int CALLCONVENTION tqsl_getADIFSubMode(const char *adif_item, char *mode, char *submode, int nmode)
DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataGABBI(tQSL_Location loc, int field_num, char *buf, int bufsiz)
#define TQSL_BAND_MAX
Max length of a band name.
Definition: tqsllib.h:49
DLLEXPORT int CALLCONVENTION tqsl_exportPKCS12File(tQSL_Cert cert, const char *filename, const char *p12password)
DLLEXPORT int CALLCONVENTION tqsl_getCertificateKeyOnly(tQSL_Cert cert, int *keyonly)
DLLEXPORT int CALLCONVENTION tqsl_getCertificateSerial(tQSL_Cert cert, long *serial)
int second
Time seconds field.
Definition: tqsllib.h:85
DLLEXPORT int CALLCONVENTION tqsl_getLocationCallSign(tQSL_Location loc, char *buf, int bufsiz)
DLLEXPORT int CALLCONVENTION tqsl_getLocationDXCCEntity(tQSL_Location loc, int *dxcc)
#define TQSL_PASSWORD_MAX
Max password length.
Definition: tqsllib.h:39
DLLEXPORT int CALLCONVENTION tqsl_diagFileOpen(void)
DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldListItem(tQSL_Location loc, int field_num, int item_idx, char *buf, int bufsiz)
DLLEXPORT int CALLCONVENTION tqsl_getCertificateRequestAddress1(tQSL_Cert cert, char *str, int bufsiz)
int minute
Time minute field.
Definition: tqsllib.h:84
DLLEXPORT int CALLCONVENTION tqsl_getNumStationLocations(tQSL_Location loc, int *nloc)
DLLEXPORT int CALLCONVENTION tqsl_isDateValid(const tQSL_Date *d)
void * tQSL_Location
Opaque location type.
Definition: tqsllib.h:71
#define TQSL_CRQ_POSTAL_MAX
Max length of request zip.
Definition: tqsllib.h:46
DLLEXPORT int CALLCONVENTION tqsl_isCertificateExpired(tQSL_Cert cert, int *status)
DLLEXPORTDATA char tQSL_CustomError[256]
Custom error message string.
DLLEXPORT void CALLCONVENTION tqsl_freeDeletedCertificateList(char **list, int nloc)
DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldCharData(tQSL_Location loc, int field_num, char *buf, int bufsiz)
char url[TQSL_NAME_ELEMENT_MAX+1]
Provider URL.
Definition: tqsllib.h:93
DLLEXPORT int CALLCONVENTION tqsl_endStationLocationCapture(tQSL_Location *locp)
Definition: tqsllib.h:89
DLLEXPORT int CALLCONVENTION tqsl_getCertificateRequestCity(tQSL_Cert cert, char *str, int bufsiz)
#define TQSL_NAME_ELEMENT_MAX
Max Org name length.
Definition: tqsllib.h:40
DLLEXPORTDATA int tQSL_Error
Error code from most recent tQSL library call.
DLLEXPORT int CALLCONVENTION tqsl_restoreCallsignCertificate(const char *callsign)
DLLEXPORT int CALLCONVENTION tqsl_getCertificateAROName(tQSL_Cert cert, char *buf, int bufsiz)
DLLEXPORT int CALLCONVENTION tqsl_getNumPropagationMode(int *number)
char emailAddress[TQSL_NAME_ELEMENT_MAX+1]
Provider e-mail.
Definition: tqsllib.h:92
DLLEXPORT int CALLCONVENTION tqsl_getNumSatellite(int *number)
DLLEXPORT int CALLCONVENTION tqsl_getCabrilloMapEntry(const char *contest, int *fieldnum, int *contest_type)
#define TQSL_CRQ_CITY_MAX
Max length of request city.
Definition: tqsllib.h:44
DLLEXPORT int CALLCONVENTION tqsl_createCertRequest(const char *filename, TQSL_CERT_REQ *req, int(*pwcb)(char *pwbuf, int pwsize, void *userdata), void *user)
DLLEXPORT int CALLCONVENTION tqsl_getDXCCStartDate(int number, tQSL_Date *d)
DLLEXPORT int CALLCONVENTION tqsl_getStationDataEnc(tQSL_StationDataEnc *sdata)
bool callsign_set
QSO specifies a call worked.
Definition: tqsllib.h:130
DLLEXPORT int CALLCONVENTION tqsl_getCertificateRequestPostalCode(tQSL_Cert cert, char *str, int bufsiz)
int year
Numeric year.
Definition: tqsllib.h:76
DLLEXPORT int CALLCONVENTION tqsl_openDiagFile(const char *file)
DLLEXPORTDATA FILE * tQSL_DiagFile
Diagnostic log file.
DLLEXPORT int CALLCONVENTION tqsl_getSelectedCertificate(tQSL_Cert *cert, const tQSL_Cert **certlist, int idx)
DLLEXPORT int CALLCONVENTION tqsl_getStationLocationCaptureName(tQSL_Location loc, char *namebuf, int bufsiz)
DLLEXPORT int CALLCONVENTION tqsl_initStationLocationCapture(tQSL_Location *locp)
DLLEXPORT int CALLCONVENTION tqsl_getDXCCDeleted(int number, int *deleted)
DLLEXPORT int CALLCONVENTION tqsl_updateStationLocationCapture(tQSL_Location loc)
DLLEXPORTDATA char tQSL_ErrorFile[256]
File name of file giving error. (May be empty.)
DLLEXPORT int CALLCONVENTION tqsl_getKeyEncoded(tQSL_Cert cert, char *buf, int bufsiz)
DLLEXPORT int CALLCONVENTION tqsl_importPKCS12File(const char *filename, const char *p12password, const char *password, int(*pwcb)(char *buf, int bufsiz, void *userdata), int(*cb)(int type, const char *message, void *userdata), void *user)
DLLEXPORT int CALLCONVENTION tqsl_getCallsignLocationInfo(const char *callsign, char **buf)
#define TQSL_CRQ_STATE_MAX
Max length of request state.
Definition: tqsllib.h:45
DLLEXPORT const char *CALLCONVENTION tqsl_getGABBItCONTACT(tQSL_Cert cert, tQSL_Location loc, TQSL_QSO_RECORD *qso, int stationuid)
DLLEXPORT int CALLCONVENTION tqsl_getCertificateRequestState(tQSL_Cert cert, char *str, int bufsiz)
DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataType(tQSL_Location loc, int field_num, int *type)
DLLEXPORT int CALLCONVENTION tqsl_deleteCertificate(tQSL_Cert cert)
TQSL_ADIF_GET_FIELD_ERROR
Response values returned from tqsl_getADIFField()
Definition: adif.h:52
DLLEXPORT int CALLCONVENTION tqsl_getStationLocationCapturePage(tQSL_Location loc, int *page)
DLLEXPORT void CALLCONVENTION tqsl_freeCertificate(tQSL_Cert cert)
char organizationName[TQSL_NAME_ELEMENT_MAX+1]
Provider name.
Definition: tqsllib.h:90
int dxccEntity
DXCC Entity code.
Definition: tqsllib.h:109
TQSL_CABRILLO_ERROR_TYPE
Cabrillo status values.
Definition: cabrillo.h:40
#define TQSL_CRQ_COUNTRY_MAX
Max length of req entity.
Definition: tqsllib.h:47
DLLEXPORT int CALLCONVENTION tqsl_setDirectory(const char *dir)
DLLEXPORT int CALLCONVENTION tqsl_setLocationFieldCharData(tQSL_Location loc, int field_num, const char *buf)
DLLEXPORT int CALLCONVENTION tqsl_getStationLocation(tQSL_Location *loc, const char *name)
DLLEXPORT char *CALLCONVENTION tqsl_convertTimeToText(const tQSL_Time *time, char *buf, int bufsiz)
DLLEXPORT int CALLCONVENTION tqsl_getDXCCEntity(int index, int *number, const char **name)
DLLEXPORT int CALLCONVENTION tqsl_importTQSLFile(const char *file, int(*cb)(int type, const char *message, void *userdata), void *user)
DLLEXPORTDATA TQSL_CABRILLO_ERROR_TYPE tQSL_Cabrillo_Error
The ADIF error code.
bool date_set
QSO specifies a date.
Definition: tqsllib.h:133
DLLEXPORT int CALLCONVENTION tqsl_setLocationFieldIndex(tQSL_Location loc, int field_num, int dat)
void * tQSL_Cert
Opaque certificate type.
Definition: tqsllib.h:70
DLLEXPORT int CALLCONVENTION tqsl_getLocationStationDetails(tQSL_Location locp, char *buf, int buflen)
char renew
Rewewal reference.
Definition: tqsllib.h:114
DLLEXPORT int CALLCONVENTION tqsl_isDateNull(const tQSL_Date *d)
DLLEXPORT int CALLCONVENTION tqsl_deleteStationLocation(const char *name)
DLLEXPORT int CALLCONVENTION tqsl_getDeletedCallsignCertificates(char ***calls, int *ncall, const char *filter)
DLLEXPORT int CALLCONVENTION tqsl_hasPrevStationLocationCapture(tQSL_Location loc, int *rval)
DLLEXPORT int CALLCONVENTION tqsl_decodeBase64(const char *input, unsigned char *data, int *datalen)
DLLEXPORT int CALLCONVENTION tqsl_setLocationCallSign(tQSL_Location loc, const char *buf)
DLLEXPORT int CALLCONVENTION tqsl_setStationLocationCertFlags(tQSL_Location loc, int flags)
DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldIndex(tQSL_Location loc, int field_num, int *dat)
DLLEXPORT int CALLCONVENTION tqsl_clearCabrilloMap()
DLLEXPORT int CALLCONVENTION tqsl_encodeBase64(const unsigned char *data, int datalen, char *output, int outputlen)
DLLEXPORT int CALLCONVENTION tqsl_prevStationLocationCapture(tQSL_Location loc)
DLLEXPORT int CALLCONVENTION tqsl_selectCertificates(tQSL_Cert **certlist, int *ncerts, const char *callsign, int dxcc, const tQSL_Date *date, const TQSL_PROVIDER *issuer, int flag)
char * tQSL_StationDataEnc
Opaque station data type.
Definition: tqsllib.h:72
DLLEXPORT int CALLCONVENTION tqsl_selectCACertificates(tQSL_Cert **certlist, int *ncerts, const char *type)
DLLEXPORT int CALLCONVENTION tqsl_getNumBand(int *number)
DLLEXPORT int CALLCONVENTION tqsl_checkSigningStatus(tQSL_Cert cert)
DLLEXPORT int CALLCONVENTION tqsl_saveCallsignLocationInfo(const char *callsign, const char *json)
tQSL_Cert signer
Signing cert.
Definition: tqsllib.h:113
DLLEXPORT int CALLCONVENTION tqsl_getCertificatePrivateKeyType(tQSL_Cert cert)
DLLEXPORT const char *CALLCONVENTION tqsl_getErrorString()
DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldChanged(tQSL_Location loc, int field_num, int *changed)
DLLEXPORTDATA long tQSL_ImportSerial
Serial number of recent certificate import.
DLLEXPORT int CALLCONVENTION tqsl_getStationLocationErrors(tQSL_Location loc, char *buf, int bufsiz)
DLLEXPORT int CALLCONVENTION tqsl_getConfigVersion(int *major, int *minor)
DLLEXPORT const char *CALLCONVENTION tqsl_getErrorString_v(int err)
tQSL_Time time
QSO time.
Definition: tqsllib.h:124
Definition: tqsllib.h:82
DLLEXPORT int CALLCONVENTION tqsl_getDXCCZoneMap(int number, const char **zonemap)
DLLEXPORT int CALLCONVENTION tqsl_getMaxSignatureSize(tQSL_Cert cert, int *sigsize)
DLLEXPORT int CALLCONVENTION tqsl_getCertificateCallSign(tQSL_Cert cert, char *buf, int bufsiz)
DLLEXPORT void CALLCONVENTION tqsl_closeDiagFile(void)
tQSL_Date qsoNotBefore
QSOs not before date.
Definition: tqsllib.h:110
#define DLLEXPORTDATA
Symbol exports - Windows only.
Definition: tqsllib.h:26
DLLEXPORT int CALLCONVENTION tqsl_getCertificateDXCCEntity(tQSL_Cert cert, int *dxcc)
bool mode_set
QSO specifies a mode.
Definition: tqsllib.h:131
DLLEXPORT int CALLCONVENTION tqsl_verifyDataBlock(tQSL_Cert cert, const unsigned char *data, int datalen, unsigned char *sig, int siglen)
DLLEXPORT int CALLCONVENTION tqsl_getStationLocationName(tQSL_Location loc, int idx, char *buf, int bufsiz)
DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataLength(tQSL_Location loc, int field_num, int *rval)
#define CALLCONVENTION
Symbol exports - Windows only.
Definition: tqsllib.h:27
struct tqsl_cert_req_st TQSL_CERT_REQ
DLLEXPORT int CALLCONVENTION tqsl_initDate(tQSL_Date *date, const char *str)
DLLEXPORTDATA int tQSL_Errno
System errno - stored when tQSL_Error == TQSL_SYSTEM_ERROR.
#define TQSL_CALLSIGN_MAX
Max callsign length.
Definition: tqsllib.h:41
DLLEXPORT int CALLCONVENTION tqsl_mergeStationLocations(const char *locdata)
#define TQSL_PROPMODE_MAX
Max length of a prop mode.
Definition: tqsllib.h:53
DLLEXPORT int CALLCONVENTION tqsl_getCertificateIssuerOrganizationalUnit(tQSL_Cert cert, char *buf, int bufsiz)
DLLEXPORTDATA const char * tQSL_BaseDir
Base directory for tQSL library working files.
DLLEXPORTDATA TQSL_ADIF_GET_FIELD_ERROR tQSL_ADIF_Error
The ADIF error code.
DLLEXPORT int CALLCONVENTION tqsl_nextStationLocationCapture(tQSL_Location loc)
DLLEXPORT int CALLCONVENTION tqsl_importKeyPairEncoded(const char *callsign, const char *type, const char *keybuf, const char *certbuf)
DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataLabel(tQSL_Location loc, int field_num, char *buf, int bufsiz)
DLLEXPORT int CALLCONVENTION tqsl_getCertificateNotBeforeDate(tQSL_Cert cert, tQSL_Date *date)
DLLEXPORT int CALLCONVENTION tqsl_getNumStationLocationCapturePages(tQSL_Location loc, int *npages)
DLLEXPORT int CALLCONVENTION tqsl_endSigning(tQSL_Cert cert)
DLLEXPORT int CALLCONVENTION tqsl_getVersion(int *major, int *minor)
DLLEXPORT int CALLCONVENTION tqsl_setCertificateStatus(long serial, const char *status)
DLLEXPORT int CALLCONVENTION tqsl_getADIFMode(const char *adif_item, char *mode, int nmode)
DLLEXPORT int CALLCONVENTION tqsl_setCabrilloMapEntry(const char *contest, int field, int contest_type)
#define TQSL_CRQ_NAME_MAX
Max length of request name.
Definition: tqsllib.h:42
DLLEXPORT int CALLCONVENTION tqsl_clearADIFModes()
tQSL_Date date
QSO date.
Definition: tqsllib.h:123
DLLEXPORT int CALLCONVENTION tqsl_getNumLocationFieldListItems(tQSL_Location loc, int field_num, int *rval)
DLLEXPORT void CALLCONVENTION tqsl_freeDeletedLocationList(char **list, int nloc)
DLLEXPORT void CALLCONVENTION tqsl_freeCertificateList(tQSL_Cert *list, int ncerts)
DLLEXPORT int CALLCONVENTION tqsl_setLocationFieldIntData(tQSL_Location loc, int field_num, int dat)
DLLEXPORT const char *CALLCONVENTION tqsl_getGABBItCONTACTData(tQSL_Cert cert, tQSL_Location loc, TQSL_QSO_RECORD *qso, int stationuid, char *signdata, int sdlen)
tQSL_Date qsoNotAfter
QSOs not after date.
Definition: tqsllib.h:111
DLLEXPORT int CALLCONVENTION tqsl_getSatellite(int index, const char **name, const char **descrip, tQSL_Date *start, tQSL_Date *end)
DLLEXPORT int CALLCONVENTION tqsl_getStationLocationField(tQSL_Location locp, const char *name, char *namebuf, int bufsize)
#define TQSL_MODE_MAX
Max length of a mode name.
Definition: tqsllib.h:50
DLLEXPORT int CALLCONVENTION tqsl_getDXCCEntityName(int number, const char **name)
DLLEXPORT int CALLCONVENTION tqsl_importPKCS12Base64(const char *base64, const char *p12password, const char *password, int(*pwcb)(char *buf, int bufsiz, void *userdata), int(*cb)(int type, const char *message, void *userdata), void *user)
DLLEXPORT int CALLCONVENTION tqsl_getCertificateNotAfterDate(tQSL_Cert cert, tQSL_Date *date)
DLLEXPORT int CALLCONVENTION tqsl_getCertificateEmailAddress(tQSL_Cert cert, char *buf, int bufsiz)
DLLEXPORT int CALLCONVENTION tqsl_saveStationLocationCapture(tQSL_Location loc, int overwrite)
DLLEXPORT int CALLCONVENTION tqsl_getStationLocationCallSign(tQSL_Location loc, int idx, char *buf, int bufsiz)
DLLEXPORT int CALLCONVENTION tqsl_getNumDXCCEntity(int *number)
DLLEXPORT int CALLCONVENTION tqsl_subtractDates(const tQSL_Date *a, const tQSL_Date *b, int *diff)
DLLEXPORT int CALLCONVENTION tqsl_getDeletedStationLocations(char ***locp, int *nloc)
DLLEXPORT int CALLCONVENTION tqsl_signDataBlock(tQSL_Cert cert, const unsigned char *data, int datalen, unsigned char *sig, int *siglen)
DLLEXPORT int CALLCONVENTION tqsl_isTimeValid(const tQSL_Time *t)
DLLEXPORT const char *CALLCONVENTION tqsl_getGABBItSTATION(tQSL_Location loc, int uid, int certuid)
DLLEXPORT const char *CALLCONVENTION tqsl_getGABBItCERT(tQSL_Cert cert, int uid)
DLLEXPORTDATA char tQSL_ImportCall[256]
Callsign used in import - used for missing public key error.
DLLEXPORT int CALLCONVENTION tqsl_hasNextStationLocationCapture(tQSL_Location loc, int *rval)
DLLEXPORT int CALLCONVENTION tqsl_getProvider(int idx, TQSL_PROVIDER *provider)
#define DLLEXPORT
Symbol exports - Windows only.
Definition: tqsllib.h:25
DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataGABBISize(tQSL_Location loc, int field_num, int *rval)
DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldFlags(tQSL_Location loc, int field_num, int *flags)
DLLEXPORT int CALLCONVENTION tqsl_restoreStationLocation(const char *name)
#define TQSL_CRQ_EMAIL_MAX
Max length of req email.
Definition: tqsllib.h:48
DLLEXPORT int CALLCONVENTION tqsl_getCertificateRequestCountry(tQSL_Cert cert, char *str, int bufsiz)
#define TQSL_FREQ_MAX
Max length of a frequency.
Definition: tqsllib.h:51
DLLEXPORT int CALLCONVENTION tqsl_getCertificateSerialLength(tQSL_Cert cert)
DLLEXPORT int CALLCONVENTION tqsl_signQSORecord(tQSL_Cert cert, tQSL_Location loc, TQSL_QSO_RECORD *rec, unsigned char *sig, int *siglen)
DLLEXPORT int CALLCONVENTION tqsl_exportPKCS12Base64(tQSL_Cert cert, char *base64, int b64len, const char *p12password)
char organizationalUnitName[TQSL_NAME_ELEMENT_MAX+1]
Provider unit.
Definition: tqsllib.h:91
DLLEXPORT int CALLCONVENTION tqsl_getNumMode(int *number)
int hour
Time hour field.
Definition: tqsllib.h:83
#define TQSL_SATNAME_MAX
Max length of a sat name.
Definition: tqsllib.h:52
Definition: tqsllib.h:75
DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataLabelSize(tQSL_Location loc, int field_num, int *rval)
bool time_set
QSO specifies a time.
Definition: tqsllib.h:134
DLLEXPORT int CALLCONVENTION tqsl_compareDates(const tQSL_Date *a, const tQSL_Date *b)
DLLEXPORT int CALLCONVENTION tqsl_getCertificateSerialExt(tQSL_Cert cert, char *serial, int serialsiz)
#define TQSL_CRQ_ADDR_MAX
Max length of request addr.
Definition: tqsllib.h:43
int day
Numeric day.
Definition: tqsllib.h:78
bool band_set
QSO specifies a band or frequency.
Definition: tqsllib.h:132
DLLEXPORT int CALLCONVENTION tqsl_getCertificateIssuer(tQSL_Cert cert, char *buf, int bufsiz)
DLLEXPORT int CALLCONVENTION tqsl_getSerialFromTQSLFile(const char *file, long *serial)
DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldIntData(tQSL_Location loc, int field_num, int *dat)
DLLEXPORT int CALLCONVENTION tqsl_getCertificateQSONotAfterDate(tQSL_Cert cert, tQSL_Date *date)
DLLEXPORT int CALLCONVENTION tqsl_initTime(tQSL_Time *time, const char *str)
DLLEXPORT int CALLCONVENTION tqsl_beginSigning(tQSL_Cert cert, char *password, int(*pwcb)(char *pwbuf, int pwsize, void *userdata), void *user)
DLLEXPORT int CALLCONVENTION tqsl_getPropagationMode(int index, const char **name, const char **descrip)
struct tqsl_provider_st TQSL_PROVIDER
DLLEXPORT int CALLCONVENTION tqsl_getNumLocationField(tQSL_Location loc, int *numf)
Definition: tqsllib.h:97
DLLEXPORT int CALLCONVENTION tqsl_getMode(int index, const char **mode, const char **group)
DLLEXPORT int CALLCONVENTION tqsl_getBand(int index, const char **name, const char **spectrum, int *low, int *high)
DLLEXPORT int CALLCONVENTION tqsl_getCertificateEncoded(tQSL_Cert cert, char *buf, int bufsiz)
DLLEXPORT int CALLCONVENTION tqsl_getNumProviders(int *n)
DLLEXPORT int CALLCONVENTION tqsl_init()
DLLEXPORT int CALLCONVENTION tqsl_setADIFMode(const char *adif_item, const char *mode)
DLLEXPORT int CALLCONVENTION tqsl_isCertificateSuperceded(tQSL_Cert cert, int *status)
DLLEXPORT char *CALLCONVENTION tqsl_convertDateToText(const tQSL_Date *date, char *buf, int bufsiz)
DLLEXPORT int CALLCONVENTION tqsl_setStationLocationCapturePage(tQSL_Location loc, int page)
tqsl-2.5.1/src/doxygen/html/tqsllib_8h.html0000664000076400007640000044563513563134442020707 0ustar rmurphyrmurphy TrustedQSL Library API: tqsllib.h File Reference
TrustedQSL Library API
tqsllib.h File Reference
#include "adif.h"
#include "cabrillo.h"

Go to the source code of this file.

Classes

struct  tQSL_Date
 
struct  tQSL_Time
 
struct  tqsl_provider_st
 
struct  tqsl_cert_req_st
 
struct  TQSL_QSO_RECORD
 

Macros

#define DLLEXPORT
 Symbol exports - Windows only.
 
#define DLLEXPORTDATA
 Symbol exports - Windows only.
 
#define CALLCONVENTION
 Symbol exports - Windows only.
 
#define TQSL_MAX_PATH_LEN   256
 Max length of a FS path.
 
#define TQSL_PASSWORD_MAX   80
 Max password length.
 
#define TQSL_NAME_ELEMENT_MAX   256
 Max Org name length.
 
#define TQSL_CALLSIGN_MAX   13
 Max callsign length.
 
#define TQSL_CRQ_NAME_MAX   60
 Max length of request name.
 
#define TQSL_CRQ_ADDR_MAX   80
 Max length of request addr.
 
#define TQSL_CRQ_CITY_MAX   80
 Max length of request city.
 
#define TQSL_CRQ_STATE_MAX   80
 Max length of request state.
 
#define TQSL_CRQ_POSTAL_MAX   20
 Max length of request zip.
 
#define TQSL_CRQ_COUNTRY_MAX   80
 Max length of req entity.
 
#define TQSL_CRQ_EMAIL_MAX   180
 Max length of req email.
 
#define TQSL_BAND_MAX   6
 Max length of a band name.
 
#define TQSL_MODE_MAX   16
 Max length of a mode name.
 
#define TQSL_FREQ_MAX   20
 Max length of a frequency.
 
#define TQSL_SATNAME_MAX   20
 Max length of a sat name.
 
#define TQSL_PROPMODE_MAX   20
 Max length of a prop mode.
 
#define TQSL_CERT_CB_USER   0
 Callback is for user cert.
 
#define TQSL_CERT_CB_CA   1
 Callback is for CA cert.
 
#define TQSL_CERT_CB_ROOT   2
 Callback is for root cert.
 
#define TQSL_CERT_CB_PKEY   3
 Callback is for private key.
 
#define TQSL_CERT_CB_CONFIG   4
 Callback for config file.
 
#define TQSL_CERT_CB_CERT_TYPE(x)   ((x) & 0xf)
 Type of the cert.
 
#define TQSL_CERT_CB_MILESTONE   0
 New certificate.
 
#define TQSL_CERT_CB_RESULT   0x10
 Cert import result.
 
#define TQSL_CERT_CB_CALL_TYPE(x)   ((x) & TQSL_CERT_CB_RESULT)
 Callback type.
 
#define TQSL_CERT_CB_PROMPT   0
 Callback prompt.
 
#define TQSL_CERT_CB_DUPLICATE   0x100
 Dupe cert callback.
 
#define TQSL_CERT_CB_ERROR   0x200
 Error import callback.
 
#define TQSL_CERT_CB_LOADED   0x300
 Cert loaded callback.
 
#define TQSL_CERT_CB_RESULT_TYPE(x)   ((x) & 0x0f00)
 Result type mask.
 
#define TQSL_SELECT_CERT_WITHKEYS   1
 Private keys only (no cert)
 
#define TQSL_SELECT_CERT_EXPIRED   2
 Include expired certs.
 
#define TQSL_SELECT_CERT_SUPERCEDED   4
 Include superseded certs.
 
#define TQSL_PK_TYPE_ERR   0
 Error retrieving private key.
 
#define TQSL_PK_TYPE_NONE   1
 No private key.
 
#define TQSL_PK_TYPE_UNENC   2
 Private key is not encrypted.
 
#define TQSL_PK_TYPE_ENC   3
 Private key is encrypted.
 
#define TQSL_CERT_STATUS_UNK   0
 Status is unknown.
 
#define TQSL_CERT_STATUS_SUP   1
 Certificate is superceded.
 
#define TQSL_CERT_STATUS_EXP   2
 Certificate is expired.
 
#define TQSL_CERT_STATUS_OK   3
 Certificate is valid.
 
#define TQSL_CERT_STATUS_INV   4
 Invalid serial number.
 
#define TQSL_LOCATION_FIELD_TEXT   1
 Text type input field.
 
#define TQSL_LOCATION_FIELD_DDLIST   2
 Dropdown list input field.
 
#define TQSL_LOCATION_FIELD_LIST   3
 List type input field.
 
#define TQSL_LOCATION_FIELD_BADZONE   4
 Used to return zone selection errors.
 
#define TQSL_LOCATION_FIELD_CHAR   1
 Character field.
 
#define TQSL_LOCATION_FIELD_INT   2
 Integer field.
 
#define TQSL_MAX_PW_LENGTH   32
 Password buffer length.
 

Typedefs

typedef void * tQSL_Cert
 Opaque certificate type.
 
typedef void * tQSL_Location
 Opaque location type.
 
typedef char * tQSL_StationDataEnc
 Opaque station data type.
 
typedef struct tqsl_provider_st TQSL_PROVIDER
 
typedef struct tqsl_cert_req_st TQSL_CERT_REQ
 

Functions

DLLEXPORT int CALLCONVENTION tqsl_init ()
 
DLLEXPORT int CALLCONVENTION tqsl_setDirectory (const char *dir)
 
DLLEXPORT const char *CALLCONVENTION tqsl_getErrorString ()
 
DLLEXPORT const char *CALLCONVENTION tqsl_getErrorString_v (int err)
 
DLLEXPORT int CALLCONVENTION tqsl_encodeBase64 (const unsigned char *data, int datalen, char *output, int outputlen)
 
DLLEXPORT int CALLCONVENTION tqsl_decodeBase64 (const char *input, unsigned char *data, int *datalen)
 
DLLEXPORT int CALLCONVENTION tqsl_initDate (tQSL_Date *date, const char *str)
 
DLLEXPORT int CALLCONVENTION tqsl_initTime (tQSL_Time *time, const char *str)
 
DLLEXPORT int CALLCONVENTION tqsl_compareDates (const tQSL_Date *a, const tQSL_Date *b)
 
DLLEXPORT int CALLCONVENTION tqsl_subtractDates (const tQSL_Date *a, const tQSL_Date *b, int *diff)
 
DLLEXPORT char *CALLCONVENTION tqsl_convertDateToText (const tQSL_Date *date, char *buf, int bufsiz)
 
DLLEXPORT int CALLCONVENTION tqsl_isDateValid (const tQSL_Date *d)
 
DLLEXPORT int CALLCONVENTION tqsl_isDateNull (const tQSL_Date *d)
 
DLLEXPORT int CALLCONVENTION tqsl_isTimeValid (const tQSL_Time *t)
 
DLLEXPORT char *CALLCONVENTION tqsl_convertTimeToText (const tQSL_Time *time, char *buf, int bufsiz)
 
DLLEXPORT int CALLCONVENTION tqsl_getVersion (int *major, int *minor)
 
DLLEXPORT int CALLCONVENTION tqsl_getConfigVersion (int *major, int *minor)
 
DLLEXPORT int CALLCONVENTION tqsl_selectCertificates (tQSL_Cert **certlist, int *ncerts, const char *callsign, int dxcc, const tQSL_Date *date, const TQSL_PROVIDER *issuer, int flag)
 
DLLEXPORT int CALLCONVENTION tqsl_selectCACertificates (tQSL_Cert **certlist, int *ncerts, const char *type)
 
DLLEXPORT int CALLCONVENTION tqsl_getSelectedCertificate (tQSL_Cert *cert, const tQSL_Cert **certlist, int idx)
 
DLLEXPORT int CALLCONVENTION tqsl_isCertificateExpired (tQSL_Cert cert, int *status)
 
DLLEXPORT int CALLCONVENTION tqsl_isCertificateSuperceded (tQSL_Cert cert, int *status)
 
DLLEXPORT int CALLCONVENTION tqsl_getCertificateKeyOnly (tQSL_Cert cert, int *keyonly)
 
DLLEXPORT int CALLCONVENTION tqsl_getCertificateEncoded (tQSL_Cert cert, char *buf, int bufsiz)
 
DLLEXPORT int CALLCONVENTION tqsl_getKeyEncoded (tQSL_Cert cert, char *buf, int bufsiz)
 
DLLEXPORT int CALLCONVENTION tqsl_importKeyPairEncoded (const char *callsign, const char *type, const char *keybuf, const char *certbuf)
 
DLLEXPORT int CALLCONVENTION tqsl_getCertificateSerial (tQSL_Cert cert, long *serial)
 
DLLEXPORT int CALLCONVENTION tqsl_getCertificateSerialExt (tQSL_Cert cert, char *serial, int serialsiz)
 
DLLEXPORT int CALLCONVENTION tqsl_getCertificateSerialLength (tQSL_Cert cert)
 
DLLEXPORT int CALLCONVENTION tqsl_getCertificateIssuer (tQSL_Cert cert, char *buf, int bufsiz)
 
DLLEXPORT int CALLCONVENTION tqsl_getCertificateIssuerOrganization (tQSL_Cert cert, char *buf, int bufsiz)
 
DLLEXPORT int CALLCONVENTION tqsl_getCertificateIssuerOrganizationalUnit (tQSL_Cert cert, char *buf, int bufsiz)
 
DLLEXPORT int CALLCONVENTION tqsl_getCertificateCallSign (tQSL_Cert cert, char *buf, int bufsiz)
 
DLLEXPORT int CALLCONVENTION tqsl_getCertificateAROName (tQSL_Cert cert, char *buf, int bufsiz)
 
DLLEXPORT int CALLCONVENTION tqsl_getCertificateEmailAddress (tQSL_Cert cert, char *buf, int bufsiz)
 
DLLEXPORT int CALLCONVENTION tqsl_getCertificateQSONotBeforeDate (tQSL_Cert cert, tQSL_Date *date)
 
DLLEXPORT int CALLCONVENTION tqsl_getCertificateQSONotAfterDate (tQSL_Cert cert, tQSL_Date *date)
 
DLLEXPORT int CALLCONVENTION tqsl_getCertificateNotBeforeDate (tQSL_Cert cert, tQSL_Date *date)
 
DLLEXPORT int CALLCONVENTION tqsl_getCertificateNotAfterDate (tQSL_Cert cert, tQSL_Date *date)
 
DLLEXPORT int CALLCONVENTION tqsl_getCertificateDXCCEntity (tQSL_Cert cert, int *dxcc)
 
DLLEXPORT int CALLCONVENTION tqsl_getCertificateRequestAddress1 (tQSL_Cert cert, char *str, int bufsiz)
 
DLLEXPORT int CALLCONVENTION tqsl_getCertificateRequestAddress2 (tQSL_Cert cert, char *str, int bufsiz)
 
DLLEXPORT int CALLCONVENTION tqsl_getCertificateRequestCity (tQSL_Cert cert, char *str, int bufsiz)
 
DLLEXPORT int CALLCONVENTION tqsl_getCertificateRequestState (tQSL_Cert cert, char *str, int bufsiz)
 
DLLEXPORT int CALLCONVENTION tqsl_getCertificateRequestPostalCode (tQSL_Cert cert, char *str, int bufsiz)
 
DLLEXPORT int CALLCONVENTION tqsl_getCertificateRequestCountry (tQSL_Cert cert, char *str, int bufsiz)
 
DLLEXPORT int CALLCONVENTION tqsl_getCertificatePrivateKeyType (tQSL_Cert cert)
 
DLLEXPORT void CALLCONVENTION tqsl_freeCertificate (tQSL_Cert cert)
 
DLLEXPORT void CALLCONVENTION tqsl_freeCertificateList (tQSL_Cert *list, int ncerts)
 
DLLEXPORT int CALLCONVENTION tqsl_getCertificateStatus (long serial)
 
DLLEXPORT int CALLCONVENTION tqsl_setCertificateStatus (long serial, const char *status)
 
DLLEXPORT int CALLCONVENTION tqsl_importTQSLFile (const char *file, int(*cb)(int type, const char *message, void *userdata), void *user)
 
DLLEXPORT int CALLCONVENTION tqsl_getSerialFromTQSLFile (const char *file, long *serial)
 
DLLEXPORT int CALLCONVENTION tqsl_getNumProviders (int *n)
 
DLLEXPORT int CALLCONVENTION tqsl_getProvider (int idx, TQSL_PROVIDER *provider)
 
DLLEXPORT int CALLCONVENTION tqsl_createCertRequest (const char *filename, TQSL_CERT_REQ *req, int(*pwcb)(char *pwbuf, int pwsize, void *userdata), void *user)
 
DLLEXPORT int CALLCONVENTION tqsl_exportPKCS12File (tQSL_Cert cert, const char *filename, const char *p12password)
 
DLLEXPORT int CALLCONVENTION tqsl_exportPKCS12Base64 (tQSL_Cert cert, char *base64, int b64len, const char *p12password)
 
DLLEXPORT int CALLCONVENTION tqsl_importPKCS12File (const char *filename, const char *p12password, const char *password, int(*pwcb)(char *buf, int bufsiz, void *userdata), int(*cb)(int type, const char *message, void *userdata), void *user)
 
DLLEXPORT int CALLCONVENTION tqsl_importPKCS12Base64 (const char *base64, const char *p12password, const char *password, int(*pwcb)(char *buf, int bufsiz, void *userdata), int(*cb)(int type, const char *message, void *userdata), void *user)
 
DLLEXPORT int CALLCONVENTION tqsl_getDeletedCallsignCertificates (char ***calls, int *ncall, const char *filter)
 
DLLEXPORT void CALLCONVENTION tqsl_freeDeletedCertificateList (char **list, int nloc)
 
DLLEXPORT int CALLCONVENTION tqsl_restoreCallsignCertificate (const char *callsign)
 
DLLEXPORT int CALLCONVENTION tqsl_deleteCertificate (tQSL_Cert cert)
 
DLLEXPORT int CALLCONVENTION tqsl_beginSigning (tQSL_Cert cert, char *password, int(*pwcb)(char *pwbuf, int pwsize, void *userdata), void *user)
 
DLLEXPORT int CALLCONVENTION tqsl_checkSigningStatus (tQSL_Cert cert)
 
DLLEXPORT int CALLCONVENTION tqsl_getMaxSignatureSize (tQSL_Cert cert, int *sigsize)
 
DLLEXPORT int CALLCONVENTION tqsl_signDataBlock (tQSL_Cert cert, const unsigned char *data, int datalen, unsigned char *sig, int *siglen)
 
DLLEXPORT int CALLCONVENTION tqsl_verifyDataBlock (tQSL_Cert cert, const unsigned char *data, int datalen, unsigned char *sig, int siglen)
 
DLLEXPORT int CALLCONVENTION tqsl_signQSORecord (tQSL_Cert cert, tQSL_Location loc, TQSL_QSO_RECORD *rec, unsigned char *sig, int *siglen)
 
DLLEXPORT int CALLCONVENTION tqsl_endSigning (tQSL_Cert cert)
 
DLLEXPORT int CALLCONVENTION tqsl_initStationLocationCapture (tQSL_Location *locp)
 
DLLEXPORT int CALLCONVENTION tqsl_endStationLocationCapture (tQSL_Location *locp)
 
DLLEXPORT int CALLCONVENTION tqsl_updateStationLocationCapture (tQSL_Location loc)
 
DLLEXPORT int CALLCONVENTION tqsl_getNumStationLocationCapturePages (tQSL_Location loc, int *npages)
 
DLLEXPORT int CALLCONVENTION tqsl_getStationLocationCapturePage (tQSL_Location loc, int *page)
 
DLLEXPORT int CALLCONVENTION tqsl_setStationLocationCapturePage (tQSL_Location loc, int page)
 
DLLEXPORT int CALLCONVENTION tqsl_setStationLocationCertFlags (tQSL_Location loc, int flags)
 
DLLEXPORT int CALLCONVENTION tqsl_nextStationLocationCapture (tQSL_Location loc)
 
DLLEXPORT int CALLCONVENTION tqsl_prevStationLocationCapture (tQSL_Location loc)
 
DLLEXPORT int CALLCONVENTION tqsl_hasNextStationLocationCapture (tQSL_Location loc, int *rval)
 
DLLEXPORT int CALLCONVENTION tqsl_hasPrevStationLocationCapture (tQSL_Location loc, int *rval)
 
DLLEXPORT int CALLCONVENTION tqsl_saveStationLocationCapture (tQSL_Location loc, int overwrite)
 
DLLEXPORT int CALLCONVENTION tqsl_getStationLocationCaptureName (tQSL_Location loc, char *namebuf, int bufsiz)
 
DLLEXPORT int CALLCONVENTION tqsl_setStationLocationCaptureName (tQSL_Location loc, const char *name)
 
DLLEXPORT int CALLCONVENTION tqsl_getNumStationLocations (tQSL_Location loc, int *nloc)
 
DLLEXPORT int CALLCONVENTION tqsl_getStationLocationName (tQSL_Location loc, int idx, char *buf, int bufsiz)
 
DLLEXPORT int CALLCONVENTION tqsl_getStationLocationCallSign (tQSL_Location loc, int idx, char *buf, int bufsiz)
 
DLLEXPORT int CALLCONVENTION tqsl_getStationLocationField (tQSL_Location locp, const char *name, char *namebuf, int bufsize)
 
DLLEXPORT int CALLCONVENTION tqsl_getStationLocation (tQSL_Location *loc, const char *name)
 
DLLEXPORT int CALLCONVENTION tqsl_getStationLocationErrors (tQSL_Location loc, char *buf, int bufsiz)
 
DLLEXPORT int CALLCONVENTION tqsl_getStationDataEnc (tQSL_StationDataEnc *sdata)
 
DLLEXPORT int CALLCONVENTION tqsl_freeStationDataEnc (tQSL_StationDataEnc sdata)
 
DLLEXPORT int CALLCONVENTION tqsl_mergeStationLocations (const char *locdata)
 
DLLEXPORT int CALLCONVENTION tqsl_deleteStationLocation (const char *name)
 
DLLEXPORT int CALLCONVENTION tqsl_restoreStationLocation (const char *name)
 
DLLEXPORT int CALLCONVENTION tqsl_getDeletedStationLocations (char ***locp, int *nloc)
 
DLLEXPORT void CALLCONVENTION tqsl_freeDeletedLocationList (char **list, int nloc)
 
DLLEXPORT int CALLCONVENTION tqsl_getNumLocationField (tQSL_Location loc, int *numf)
 
DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataLabelSize (tQSL_Location loc, int field_num, int *rval)
 
DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataLabel (tQSL_Location loc, int field_num, char *buf, int bufsiz)
 
DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataGABBISize (tQSL_Location loc, int field_num, int *rval)
 
DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataGABBI (tQSL_Location loc, int field_num, char *buf, int bufsiz)
 
DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldInputType (tQSL_Location loc, int field_num, int *type)
 
DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataType (tQSL_Location loc, int field_num, int *type)
 
DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldFlags (tQSL_Location loc, int field_num, int *flags)
 
DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldDataLength (tQSL_Location loc, int field_num, int *rval)
 
DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldCharData (tQSL_Location loc, int field_num, char *buf, int bufsiz)
 
DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldIntData (tQSL_Location loc, int field_num, int *dat)
 
DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldIndex (tQSL_Location loc, int field_num, int *dat)
 
DLLEXPORT int CALLCONVENTION tqsl_getNumLocationFieldListItems (tQSL_Location loc, int field_num, int *rval)
 
DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldListItem (tQSL_Location loc, int field_num, int item_idx, char *buf, int bufsiz)
 
DLLEXPORT int CALLCONVENTION tqsl_setLocationFieldCharData (tQSL_Location loc, int field_num, const char *buf)
 
DLLEXPORT int CALLCONVENTION tqsl_setLocationFieldIntData (tQSL_Location loc, int field_num, int dat)
 
DLLEXPORT int CALLCONVENTION tqsl_setLocationFieldIndex (tQSL_Location loc, int field_num, int dat)
 
DLLEXPORT int CALLCONVENTION tqsl_getLocationFieldChanged (tQSL_Location loc, int field_num, int *changed)
 
DLLEXPORT int CALLCONVENTION tqsl_getLocationCallSign (tQSL_Location loc, char *buf, int bufsiz)
 
DLLEXPORT int CALLCONVENTION tqsl_setLocationCallSign (tQSL_Location loc, const char *buf)
 
DLLEXPORT int CALLCONVENTION tqsl_getLocationDXCCEntity (tQSL_Location loc, int *dxcc)
 
DLLEXPORT int CALLCONVENTION tqsl_getLocationQSODetails (tQSL_Location locp, char *buf, int buflen)
 
DLLEXPORT int CALLCONVENTION tqsl_getLocationStationDetails (tQSL_Location locp, char *buf, int buflen)
 
DLLEXPORT int CALLCONVENTION tqsl_saveCallsignLocationInfo (const char *callsign, const char *json)
 
DLLEXPORT int CALLCONVENTION tqsl_getCallsignLocationInfo (const char *callsign, char **buf)
 
DLLEXPORT int CALLCONVENTION tqsl_getNumDXCCEntity (int *number)
 
DLLEXPORT int CALLCONVENTION tqsl_getDXCCEntity (int index, int *number, const char **name)
 
DLLEXPORT int CALLCONVENTION tqsl_getDXCCEntityName (int number, const char **name)
 
DLLEXPORT int CALLCONVENTION tqsl_getDXCCZoneMap (int number, const char **zonemap)
 
DLLEXPORT int CALLCONVENTION tqsl_getDXCCStartDate (int number, tQSL_Date *d)
 
DLLEXPORT int CALLCONVENTION tqsl_getDXCCEndDate (int number, tQSL_Date *d)
 
DLLEXPORT int CALLCONVENTION tqsl_getDXCCDeleted (int number, int *deleted)
 
DLLEXPORT int CALLCONVENTION tqsl_getNumBand (int *number)
 
DLLEXPORT int CALLCONVENTION tqsl_getBand (int index, const char **name, const char **spectrum, int *low, int *high)
 
DLLEXPORT int CALLCONVENTION tqsl_getNumMode (int *number)
 
DLLEXPORT int CALLCONVENTION tqsl_getMode (int index, const char **mode, const char **group)
 
DLLEXPORT int CALLCONVENTION tqsl_getNumPropagationMode (int *number)
 
DLLEXPORT int CALLCONVENTION tqsl_getPropagationMode (int index, const char **name, const char **descrip)
 
DLLEXPORT int CALLCONVENTION tqsl_getNumSatellite (int *number)
 
DLLEXPORT int CALLCONVENTION tqsl_getSatellite (int index, const char **name, const char **descrip, tQSL_Date *start, tQSL_Date *end)
 
DLLEXPORT int CALLCONVENTION tqsl_clearCabrilloMap ()
 
DLLEXPORT int CALLCONVENTION tqsl_setCabrilloMapEntry (const char *contest, int field, int contest_type)
 
DLLEXPORT int CALLCONVENTION tqsl_getCabrilloMapEntry (const char *contest, int *fieldnum, int *contest_type)
 
DLLEXPORT int CALLCONVENTION tqsl_clearADIFModes ()
 
DLLEXPORT int CALLCONVENTION tqsl_setADIFMode (const char *adif_item, const char *mode)
 
DLLEXPORT int CALLCONVENTION tqsl_getADIFMode (const char *adif_item, char *mode, int nmode)
 
DLLEXPORT int CALLCONVENTION tqsl_getADIFSubMode (const char *adif_item, char *mode, char *submode, int nmode)
 
DLLEXPORT const char *CALLCONVENTION tqsl_getGABBItCERT (tQSL_Cert cert, int uid)
 
DLLEXPORT const char *CALLCONVENTION tqsl_getGABBItSTATION (tQSL_Location loc, int uid, int certuid)
 
DLLEXPORT const char *CALLCONVENTION tqsl_getGABBItCONTACT (tQSL_Cert cert, tQSL_Location loc, TQSL_QSO_RECORD *qso, int stationuid)
 
DLLEXPORT const char *CALLCONVENTION tqsl_getGABBItCONTACTData (tQSL_Cert cert, tQSL_Location loc, TQSL_QSO_RECORD *qso, int stationuid, char *signdata, int sdlen)
 
DLLEXPORT void CALLCONVENTION tqslTrace (const char *name, const char *format,...)
 
DLLEXPORT void CALLCONVENTION tqsl_closeDiagFile (void)
 
DLLEXPORT int CALLCONVENTION tqsl_diagFileOpen (void)
 
DLLEXPORT int CALLCONVENTION tqsl_openDiagFile (const char *file)
 

Variables

DLLEXPORTDATA const char * tQSL_BaseDir
 Base directory for tQSL library working files.
 
DLLEXPORTDATA const char * tQSL_RsrcDir
 
DLLEXPORTDATA int tQSL_Error
 Error code from most recent tQSL library call. More...
 
DLLEXPORTDATA TQSL_ADIF_GET_FIELD_ERROR tQSL_ADIF_Error
 The ADIF error code.
 
DLLEXPORTDATA TQSL_CABRILLO_ERROR_TYPE tQSL_Cabrillo_Error
 The ADIF error code.
 
DLLEXPORTDATA char tQSL_ErrorFile [256]
 File name of file giving error. (May be empty.)
 
DLLEXPORTDATA char tQSL_CustomError [256]
 Custom error message string.
 
DLLEXPORTDATA int tQSL_Errno
 System errno - stored when tQSL_Error == TQSL_SYSTEM_ERROR.
 
DLLEXPORTDATA char tQSL_ImportCall [256]
 Callsign used in import - used for missing public key error.
 
DLLEXPORTDATA long tQSL_ImportSerial
 Serial number of recent certificate import.
 
DLLEXPORTDATA FILE * tQSL_DiagFile
 Diagnostic log file.
 

Detailed Description

tQSL library functions.

Typedef Documentation

◆ TQSL_CERT_REQ

Certificate request data

◆ TQSL_PROVIDER

Certificate provider data

Function Documentation

◆ tqsl_closeDiagFile()

DLLEXPORT void CALLCONVENTION tqsl_closeDiagFile ( void  )

Close the diagnostic trace file (if it is open)

◆ tqsl_diagFileOpen()

DLLEXPORT int CALLCONVENTION tqsl_diagFileOpen ( void  )

Close the diagnostic trace file (if it is open)

◆ tqsl_openDiagFile()

DLLEXPORT int CALLCONVENTION tqsl_openDiagFile ( const char *  file)

Returns true if the log file is open

◆ tqslTrace()

DLLEXPORT void CALLCONVENTION tqslTrace ( const char *  name,
const char *  format,
  ... 
)

Output to a diagnostic trace file (if one is opened.

  • name is the name of the function being executed
tqsl-2.5.1/src/doxygen/html/tqsllib.css0000664000076400007640000000211613563134442020113 0ustar rmurphyrmurphyBODY { font-family: Helvetica,Arial,sans-serif; font-size: small } H1 { text-align: center; font-size: x-large } H2 { font-size: large } CODE { font-size: medium } A.qindex {} A.qindexRef {} A.el { text-decoration: none; font-weight: bold; } A.elRef { font-weight: bold } A.code { text-decoration: none; font-weight: normal; color: #4444ee } A.codeRef { font-weight: normal; color: #4444ee } A:link { color: #0000ff } A:visited { color: #0066cc } DL.el { margin-left: -1cm } DIV.fragment { width: 100%; border: none; background-color: #eeeeee } DIV.ah { background-color: black; margin-bottom: 3; margin-top: 3 } TD.md { background-color: #f2f2ff } DIV.groupHeader { margin-left: 16; margin-top: 12; margin-bottom: 6; font-weight: bold } DIV.groupText { margin-left: 16; font-style: italic; font-size: smaller } FONT.keyword { color: #008000 } FONT.keywordtype { color: #604020 } FONT.keywordflow { color: #e08000 } FONT.comment { color: #800000 } FONT.preprocessor { color: #806020 } FONT.stringliteral { color: #002080 } FONT.charliteral { color: #008080 } TD { font-size: small } tqsl-2.5.1/src/doxygen/html/tqsllib-doc_8h_source.html0000664000076400007640000000647413563134442023024 0ustar rmurphyrmurphy TrustedQSL Library API: tqsllib-doc.h Source File
TrustedQSL Library API
tqsllib-doc.h
1 /***************************************************************************
2  tqsllib-doc.h - description
3  -------------------
4  begin : Tue Jun 4 2002
5  copyright : (C) 2002 by ARRL
6  author : Jon Bloom
7  email : jbloom@arrl.org
8  revision : $Id$
9  ***************************************************************************/
10 
tqsl-2.5.1/src/doxygen/html/tqslerrno_8h_source.html0000664000076400007640000003062413563134442022632 0ustar rmurphyrmurphy TrustedQSL Library API: tqslerrno.h Source File
TrustedQSL Library API
tqslerrno.h
Go to the documentation of this file.
1 /***************************************************************************
2  tqslerrno.h - description
3  -------------------
4  begin : Tue May 28 2002
5  copyright : (C) 2002 by ARRL
6  author : Jon Bloom
7  email : jbloom@arrl.org
8  revision : $Id$
9  ***************************************************************************/
10 
11 #ifndef __TQSLERRNO_H
12 #define __TQSLERRNO_H
13 
18 #define TQSL_NO_ERROR 0
19 #define TQSL_SYSTEM_ERROR 1
20 #define TQSL_OPENSSL_ERROR 2
21 #define TQSL_ADIF_ERROR 3
22 #define TQSL_CUSTOM_ERROR 4
23 #define TQSL_CABRILLO_ERROR 5
24 #define TQSL_OPENSSL_VERSION_ERROR 6
25 #define TQSL_ERROR_ENUM_BASE 16
26 #define TQSL_ALLOC_ERROR 16
27 #define TQSL_RANDOM_ERROR 17
28 #define TQSL_ARGUMENT_ERROR 18
29 #define TQSL_OPERATOR_ABORT 19
30 #define TQSL_NOKEY_ERROR 20
31 #define TQSL_BUFFER_ERROR 21
32 #define TQSL_INVALID_DATE 22
33 #define TQSL_SIGNINIT_ERROR 23
34 #define TQSL_PASSWORD_ERROR 24
35 #define TQSL_EXPECTED_NAME 25
36 #define TQSL_NAME_EXISTS 26
37 #define TQSL_NAME_NOT_FOUND 27
38 #define TQSL_INVALID_TIME 28
39 #define TQSL_CERT_DATE_MISMATCH 29
40 #define TQSL_PROVIDER_NOT_FOUND 30
41 #define TQSL_CERT_KEY_ONLY 31
42 #define TQSL_CONFIG_ERROR 32
43 #define TQSL_CERT_NOT_FOUND 33
44 #define TQSL_PKCS12_ERROR 34
45 #define TQSL_CERT_TYPE_ERROR 35
46 #define TQSL_DATE_OUT_OF_RANGE 36
47 #define TQSL_DUPLICATE_QSO 37
48 #define TQSL_DB_ERROR 38
49 #define TQSL_LOCATION_NOT_FOUND 39
50 #define TQSL_CALL_NOT_FOUND 40
51 #define TQSL_CONFIG_SYNTAX_ERROR 41
52 #define TQSL_FILE_SYSTEM_ERROR 42
53 #define TQSL_FILE_SYNTAX_ERROR 43
54 #define TQSL_CERT_ERROR 44
55 
56 #endif /* __TQSLERRNO_H */
tqsl-2.5.1/src/doxygen/html/tqslerrno_8h.html0000664000076400007640000006022513563134442021252 0ustar rmurphyrmurphy TrustedQSL Library API: tqslerrno.h File Reference
TrustedQSL Library API
tqslerrno.h File Reference

Go to the source code of this file.

Macros

#define TQSL_NO_ERROR   0
 No error.
 
#define TQSL_SYSTEM_ERROR   1
 System Error.
 
#define TQSL_OPENSSL_ERROR   2
 Error in OpenSSL calls.
 
#define TQSL_ADIF_ERROR   3
 ADIF Errors.
 
#define TQSL_CUSTOM_ERROR   4
 Custom errors - output to tQSL_CustomError.
 
#define TQSL_CABRILLO_ERROR   5
 Cabrillo handler error.
 
#define TQSL_OPENSSL_VERSION_ERROR   6
 OpenSSL version obsolete.
 
#define TQSL_ERROR_ENUM_BASE   16
 Base for enumerated errors.
 
#define TQSL_ALLOC_ERROR   16
 Memory allocation error.
 
#define TQSL_RANDOM_ERROR   17
 Error initializing random number generator.
 
#define TQSL_ARGUMENT_ERROR   18
 Invalid arguments.
 
#define TQSL_OPERATOR_ABORT   19
 Aborted by operator.
 
#define TQSL_NOKEY_ERROR   20
 No key available.
 
#define TQSL_BUFFER_ERROR   21
 Insufficient buffer space.
 
#define TQSL_INVALID_DATE   22
 Date string invalid.
 
#define TQSL_SIGNINIT_ERROR   23
 Error initializing signing routine.
 
#define TQSL_PASSWORD_ERROR   24
 Invalid password.
 
#define TQSL_EXPECTED_NAME   25
 Name expected but not supplied.
 
#define TQSL_NAME_EXISTS   26
 Entity name exists already.
 
#define TQSL_NAME_NOT_FOUND   27
 Entity name does not exist.
 
#define TQSL_INVALID_TIME   28
 Time format is invalid.
 
#define TQSL_CERT_DATE_MISMATCH   29
 Certificate date mismatch.
 
#define TQSL_PROVIDER_NOT_FOUND   30
 Certificate provider unknown.
 
#define TQSL_CERT_KEY_ONLY   31
 No signed public key is installed.
 
#define TQSL_CONFIG_ERROR   32
 There is an error in the configuration file.
 
#define TQSL_CERT_NOT_FOUND   33
 The certificate could not be found.
 
#define TQSL_PKCS12_ERROR   34
 There is an error parsing the .p12 file.
 
#define TQSL_CERT_TYPE_ERROR   35
 The certificate type is invalid.
 
#define TQSL_DATE_OUT_OF_RANGE   36
 The date is out of the valid range.
 
#define TQSL_DUPLICATE_QSO   37
 This QSO is already uploaded.
 
#define TQSL_DB_ERROR   38
 The dupe database could not be accessed.
 
#define TQSL_LOCATION_NOT_FOUND   39
 The station location is invalid.
 
#define TQSL_CALL_NOT_FOUND   40
 The callsign could not be located.
 
#define TQSL_CONFIG_SYNTAX_ERROR   41
 The config file has a syntax error.
 
#define TQSL_FILE_SYSTEM_ERROR   42
 There was a file system I/O error.
 
#define TQSL_FILE_SYNTAX_ERROR   43
 The file format is invalid.
 
#define TQSL_CERT_ERROR   44
 Callsign certificate could not be installed.
 

Detailed Description

tQSL_Error values

tqsl-2.5.1/src/doxygen/html/tqslconvert_8h_source.html0000664000076400007640000006173413563134442023173 0ustar rmurphyrmurphy TrustedQSL Library API: tqslconvert.h Source File
TrustedQSL Library API
tqslconvert.h
Go to the documentation of this file.
1 /***************************************************************************
2  convert.h - description
3  -------------------
4  begin : Sun Nov 17 2002
5  copyright : (C) 2002 by ARRL
6  author : Jon Bloom
7  email : jbloom@arrl.org
8  revision : $Id$
9  ***************************************************************************/
10 
11 #ifndef __tqslconvert_h
12 #define __tqslconvert_h
13 
14 #include "tqsllib.h"
15 
27 typedef void * tQSL_Converter;
28 
31 #ifdef __cplusplus
32 extern "C" {
33 #endif
34 
42 
52 DLLEXPORT int CALLCONVENTION tqsl_beginADIFConverter(tQSL_Converter *conv, const char *filename,
53  tQSL_Cert *certs, int ncerts, tQSL_Location loc);
54 
65  tQSL_Cert *certs, int ncerts, tQSL_Location loc);
66 
69 
79 
89 
97 
105 
112 
120 tqsl_getDuplicateRecords(tQSL_Converter convp, char *key, char *data, int keylen);
121 
130 tqsl_getDuplicateRecordsV2(tQSL_Converter convp, char *key, char *data, int keylen);
131 
139 tqsl_putDuplicateRecord(tQSL_Converter convp, const char *key, const char *data, int keylen);
140 
150  tQSL_Date *end);
151 
174 
177 
181 
187 
190 #ifdef __cplusplus
191 }
192 #endif
193 
194 #endif /* __tqslconvert_h */
195 
DLLEXPORT const char *CALLCONVENTION tqsl_getConverterGABBI(tQSL_Converter conv)
DLLEXPORT int CALLCONVENTION tqsl_setConverterAllowBadCall(tQSL_Converter conv, int allow)
DLLEXPORT int CALLCONVENTION tqsl_beginCabrilloConverter(tQSL_Converter *conv, const char *filename, tQSL_Cert *certs, int ncerts, tQSL_Location loc)
DLLEXPORT const char *CALLCONVENTION tqsl_getConverterRecordText(tQSL_Converter conv)
void * tQSL_Converter
Definition: tqslconvert.h:27
void * tQSL_Location
Opaque location type.
Definition: tqsllib.h:71
DLLEXPORT int CALLCONVENTION tqsl_putDuplicateRecord(tQSL_Converter convp, const char *key, const char *data, int keylen)
DLLEXPORT int CALLCONVENTION tqsl_setADIFConverterDateFilter(tQSL_Converter conv, tQSL_Date *start, tQSL_Date *end)
DLLEXPORT int CALLCONVENTION tqsl_setConverterAppName(tQSL_Converter convp, const char *app)
DLLEXPORT int CALLCONVENTION tqsl_getDuplicateRecordsV2(tQSL_Converter convp, char *key, char *data, int keylen)
DLLEXPORT int CALLCONVENTION tqsl_converterCommit(tQSL_Converter convp)
void * tQSL_Cert
Opaque certificate type.
Definition: tqsllib.h:70
DLLEXPORT int CALLCONVENTION tqsl_endConverter(tQSL_Converter *conv)
DLLEXPORT int CALLCONVENTION tqsl_setConverterAllowDuplicates(tQSL_Converter convp, int allow)
DLLEXPORT int CALLCONVENTION tqsl_getConverterLine(tQSL_Converter conv, int *lineno)
DLLEXPORT int CALLCONVENTION tqsl_getConverterCert(tQSL_Converter conv, tQSL_Cert *certp)
DLLEXPORT int CALLCONVENTION tqsl_getDuplicateRecords(tQSL_Converter convp, char *key, char *data, int keylen)
#define CALLCONVENTION
Symbol exports - Windows only.
Definition: tqsllib.h:27
DLLEXPORT int CALLCONVENTION tqsl_converterRollBack(tQSL_Converter convp)
DLLEXPORT int CALLCONVENTION tqsl_beginConverter(tQSL_Converter *convp)
#define DLLEXPORT
Symbol exports - Windows only.
Definition: tqsllib.h:25
Definition: tqsllib.h:75
DLLEXPORT int CALLCONVENTION tqsl_beginADIFConverter(tQSL_Converter *conv, const char *filename, tQSL_Cert *certs, int ncerts, tQSL_Location loc)
tqsl-2.5.1/src/doxygen/html/tqslconvert_8h.html0000664000076400007640000003543513563134442021612 0ustar rmurphyrmurphy TrustedQSL Library API: tqslconvert.h File Reference
TrustedQSL Library API
tqslconvert.h File Reference
#include "tqsllib.h"

Go to the source code of this file.

Typedefs

typedef void * tQSL_Converter
 

Functions

DLLEXPORT int CALLCONVENTION tqsl_beginConverter (tQSL_Converter *convp)
 
DLLEXPORT int CALLCONVENTION tqsl_beginADIFConverter (tQSL_Converter *conv, const char *filename, tQSL_Cert *certs, int ncerts, tQSL_Location loc)
 
DLLEXPORT int CALLCONVENTION tqsl_beginCabrilloConverter (tQSL_Converter *conv, const char *filename, tQSL_Cert *certs, int ncerts, tQSL_Location loc)
 
DLLEXPORT int CALLCONVENTION tqsl_endConverter (tQSL_Converter *conv)
 
DLLEXPORT int CALLCONVENTION tqsl_setConverterAllowBadCall (tQSL_Converter conv, int allow)
 
DLLEXPORT int CALLCONVENTION tqsl_setConverterAllowDuplicates (tQSL_Converter convp, int allow)
 
DLLEXPORT int CALLCONVENTION tqsl_setConverterAppName (tQSL_Converter convp, const char *app)
 
DLLEXPORT int CALLCONVENTION tqsl_converterRollBack (tQSL_Converter convp)
 
DLLEXPORT int CALLCONVENTION tqsl_converterCommit (tQSL_Converter convp)
 
DLLEXPORT int CALLCONVENTION tqsl_getDuplicateRecords (tQSL_Converter convp, char *key, char *data, int keylen)
 
DLLEXPORT int CALLCONVENTION tqsl_getDuplicateRecordsV2 (tQSL_Converter convp, char *key, char *data, int keylen)
 
DLLEXPORT int CALLCONVENTION tqsl_putDuplicateRecord (tQSL_Converter convp, const char *key, const char *data, int keylen)
 
DLLEXPORT int CALLCONVENTION tqsl_setADIFConverterDateFilter (tQSL_Converter conv, tQSL_Date *start, tQSL_Date *end)
 
DLLEXPORT const char *CALLCONVENTION tqsl_getConverterGABBI (tQSL_Converter conv)
 
DLLEXPORT int CALLCONVENTION tqsl_getConverterCert (tQSL_Converter conv, tQSL_Cert *certp)
 
DLLEXPORT int CALLCONVENTION tqsl_getConverterLine (tQSL_Converter conv, int *lineno)
 
DLLEXPORT const char *CALLCONVENTION tqsl_getConverterRecordText (tQSL_Converter conv)
 

Detailed Description

tQSL library converter functions.

tqsl-2.5.1/src/doxygen/html/tabs.css0000664000076400007640000002207513563134442017400 0ustar rmurphyrmurphy.sm{position:relative;z-index:9999}.sm,.sm ul,.sm li{display:block;list-style:none;margin:0;padding:0;line-height:normal;direction:ltr;text-align:left;-webkit-tap-highlight-color:rgba(0,0,0,0)}.sm-rtl,.sm-rtl ul,.sm-rtl li{direction:rtl;text-align:right}.sm>li>h1,.sm>li>h2,.sm>li>h3,.sm>li>h4,.sm>li>h5,.sm>li>h6{margin:0;padding:0}.sm ul{display:none}.sm li,.sm a{position:relative}.sm a{display:block}.sm a.disabled{cursor:not-allowed}.sm:after{content:"\00a0";display:block;height:0;font:0/0 serif;clear:both;visibility:hidden;overflow:hidden}.sm,.sm *,.sm *:before,.sm *:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}#doc-content{overflow:auto;display:block;padding:0;margin:0;-webkit-overflow-scrolling:touch}.sm-dox{background-image:url("tab_b.png")}.sm-dox a,.sm-dox a:focus,.sm-dox a:hover,.sm-dox a:active{padding:0 12px;padding-right:43px;font-family:"Lucida Grande","Geneva","Helvetica",Arial,sans-serif;font-size:13px;font-weight:bold;line-height:36px;text-decoration:none;text-shadow:0 1px 1px rgba(255,255,255,0.9);color:#283a5d;outline:0}.sm-dox a:hover{background-image:url("tab_a.png");background-repeat:repeat-x;color:white;text-shadow:0 1px 1px black}.sm-dox a.current{color:#d23600}.sm-dox a.disabled{color:#bbb}.sm-dox a span.sub-arrow{position:absolute;top:50%;margin-top:-14px;left:auto;right:3px;width:28px;height:28px;overflow:hidden;font:bold 12px/28px monospace!important;text-align:center;text-shadow:none;background:rgba(255,255,255,0.5);-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.sm-dox a.highlighted span.sub-arrow:before{display:block;content:'-'}.sm-dox>li:first-child>a,.sm-dox>li:first-child>:not(ul) a{-moz-border-radius:5px 5px 0 0;-webkit-border-radius:5px;border-radius:5px 5px 0 0}.sm-dox>li:last-child>a,.sm-dox>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul{-moz-border-radius:0 0 5px 5px;-webkit-border-radius:0;border-radius:0 0 5px 5px}.sm-dox>li:last-child>a.highlighted,.sm-dox>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0}.sm-dox ul{background:rgba(162,162,162,0.1)}.sm-dox ul a,.sm-dox ul a:focus,.sm-dox ul a:hover,.sm-dox ul a:active{font-size:12px;border-left:8px solid transparent;line-height:36px;text-shadow:none;background-color:white;background-image:none}.sm-dox ul a:hover{background-image:url("tab_a.png");background-repeat:repeat-x;color:white;text-shadow:0 1px 1px black}.sm-dox ul ul a,.sm-dox ul ul a:hover,.sm-dox ul ul a:focus,.sm-dox ul ul a:active{border-left:16px solid transparent}.sm-dox ul ul ul a,.sm-dox ul ul ul a:hover,.sm-dox ul ul ul a:focus,.sm-dox ul ul ul a:active{border-left:24px solid transparent}.sm-dox ul ul ul ul a,.sm-dox ul ul ul ul a:hover,.sm-dox ul ul ul ul a:focus,.sm-dox ul ul ul ul a:active{border-left:32px solid transparent}.sm-dox ul ul ul ul ul a,.sm-dox ul ul ul ul ul a:hover,.sm-dox ul ul ul ul ul a:focus,.sm-dox ul ul ul ul ul a:active{border-left:40px solid transparent}@media(min-width:768px){.sm-dox ul{position:absolute;width:12em}.sm-dox li{float:left}.sm-dox.sm-rtl li{float:right}.sm-dox ul li,.sm-dox.sm-rtl ul li,.sm-dox.sm-vertical li{float:none}.sm-dox a{white-space:nowrap}.sm-dox ul a,.sm-dox.sm-vertical a{white-space:normal}.sm-dox .sm-nowrap>li>a,.sm-dox .sm-nowrap>li>:not(ul) a{white-space:nowrap}.sm-dox{padding:0 10px;background-image:url("tab_b.png");line-height:36px}.sm-dox a span.sub-arrow{top:50%;margin-top:-2px;right:12px;width:0;height:0;border-width:4px;border-style:solid dashed dashed dashed;border-color:#283a5d transparent transparent transparent;background:transparent;-moz-border-radius:0;-webkit-border-radius:0;border-radius:0}.sm-dox a,.sm-dox a:focus,.sm-dox a:active,.sm-dox a:hover,.sm-dox a.highlighted{padding:0 12px;background-image:url("tab_s.png");background-repeat:no-repeat;background-position:right;-moz-border-radius:0!important;-webkit-border-radius:0;border-radius:0!important}.sm-dox a:hover{background-image:url("tab_a.png");background-repeat:repeat-x;color:white;text-shadow:0 1px 1px black}.sm-dox a:hover span.sub-arrow{border-color:white transparent transparent transparent}.sm-dox a.has-submenu{padding-right:24px}.sm-dox li{border-top:0}.sm-dox>li>ul:before,.sm-dox>li>ul:after{content:'';position:absolute;top:-18px;left:30px;width:0;height:0;overflow:hidden;border-width:9px;border-style:dashed dashed solid dashed;border-color:transparent transparent #bbb transparent}.sm-dox>li>ul:after{top:-16px;left:31px;border-width:8px;border-color:transparent transparent #fff transparent}.sm-dox ul{border:1px solid #bbb;padding:5px 0;background:#fff;-moz-border-radius:5px!important;-webkit-border-radius:5px;border-radius:5px!important;-moz-box-shadow:0 5px 9px rgba(0,0,0,0.2);-webkit-box-shadow:0 5px 9px rgba(0,0,0,0.2);box-shadow:0 5px 9px rgba(0,0,0,0.2)}.sm-dox ul a span.sub-arrow{right:8px;top:50%;margin-top:-5px;border-width:5px;border-color:transparent transparent transparent #555;border-style:dashed dashed dashed solid}.sm-dox ul a,.sm-dox ul a:hover,.sm-dox ul a:focus,.sm-dox ul a:active,.sm-dox ul a.highlighted{color:#555;background-image:none;border:0!important;color:#555;background-image:none}.sm-dox ul a:hover{background-image:url("tab_a.png");background-repeat:repeat-x;color:white;text-shadow:0 1px 1px black}.sm-dox ul a:hover span.sub-arrow{border-color:transparent transparent transparent white}.sm-dox span.scroll-up,.sm-dox span.scroll-down{position:absolute;display:none;visibility:hidden;overflow:hidden;background:#fff;height:36px}.sm-dox span.scroll-up:hover,.sm-dox span.scroll-down:hover{background:#eee}.sm-dox span.scroll-up:hover span.scroll-up-arrow,.sm-dox span.scroll-up:hover span.scroll-down-arrow{border-color:transparent transparent #d23600 transparent}.sm-dox span.scroll-down:hover span.scroll-down-arrow{border-color:#d23600 transparent transparent transparent}.sm-dox span.scroll-up-arrow,.sm-dox span.scroll-down-arrow{position:absolute;top:0;left:50%;margin-left:-6px;width:0;height:0;overflow:hidden;border-width:6px;border-style:dashed dashed solid dashed;border-color:transparent transparent #555 transparent}.sm-dox span.scroll-down-arrow{top:8px;border-style:solid dashed dashed dashed;border-color:#555 transparent transparent transparent}.sm-dox.sm-rtl a.has-submenu{padding-right:12px;padding-left:24px}.sm-dox.sm-rtl a span.sub-arrow{right:auto;left:12px}.sm-dox.sm-rtl.sm-vertical a.has-submenu{padding:10px 20px}.sm-dox.sm-rtl.sm-vertical a span.sub-arrow{right:auto;left:8px;border-style:dashed solid dashed dashed;border-color:transparent #555 transparent transparent}.sm-dox.sm-rtl>li>ul:before{left:auto;right:30px}.sm-dox.sm-rtl>li>ul:after{left:auto;right:31px}.sm-dox.sm-rtl ul a.has-submenu{padding:10px 20px!important}.sm-dox.sm-rtl ul a span.sub-arrow{right:auto;left:8px;border-style:dashed solid dashed dashed;border-color:transparent #555 transparent transparent}.sm-dox.sm-vertical{padding:10px 0;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.sm-dox.sm-vertical a{padding:10px 20px}.sm-dox.sm-vertical a:hover,.sm-dox.sm-vertical a:focus,.sm-dox.sm-vertical a:active,.sm-dox.sm-vertical a.highlighted{background:#fff}.sm-dox.sm-vertical a.disabled{background-image:url("tab_b.png")}.sm-dox.sm-vertical a span.sub-arrow{right:8px;top:50%;margin-top:-5px;border-width:5px;border-style:dashed dashed dashed solid;border-color:transparent transparent transparent #555}.sm-dox.sm-vertical>li>ul:before,.sm-dox.sm-vertical>li>ul:after{display:none}.sm-dox.sm-vertical ul a{padding:10px 20px}.sm-dox.sm-vertical ul a:hover,.sm-dox.sm-vertical ul a:focus,.sm-dox.sm-vertical ul a:active,.sm-dox.sm-vertical ul a.highlighted{background:#eee}.sm-dox.sm-vertical ul a.disabled{background:#fff}}tqsl-2.5.1/src/doxygen/html/tab_s.png0000664000076400007640000000027013563134442017524 0ustar rmurphyrmurphyPNG  IHDR$[IDATx݁ @@ѣ?Q"%If6[HQ<]dr s?O=w'F -~rÍ[芭m֬ݯнF)Y% `n,9B!ь\<#IENDB`tqsl-2.5.1/src/doxygen/html/tab_h.png0000664000076400007640000000026113563134442017511 0ustar rmurphyrmurphyPNG  IHDR$[xIDATxM@~ΒEv"!d*rGq={SݧH uO^[_Xvyұ=VCff{R%_rug(?gh\i>|sIENDB`tqsl-2.5.1/src/doxygen/html/tab_b.png0000664000076400007640000000025113563134442017502 0ustar rmurphyrmurphyPNG  IHDR$[pIDATxM EǻԸu`V0}:t]Ds䮂u|x>1&m8SxLU޲iEOsnxKN~jIENDB`tqsl-2.5.1/src/doxygen/html/tab_a.png0000664000076400007640000000021613563134442017502 0ustar rmurphyrmurphyPNG  IHDR$[UIDATxK 0C'o([Ž%x#٩ We# 3t I 3+E~\D9wM}Y_A4Y}IENDB`tqsl-2.5.1/src/doxygen/html/sync_on.png0000664000076400007640000000151513563134442020107 0ustar rmurphyrmurphyPNG  IHDRw=IDATx_HTY8i4-g6&kQ)!0URKڅ/PE>K-+K.YdEPaAZSܝ;3wgfsWK.Da'q_k DQCg 0Y:qZ)~L0HV z-C%g68%wUϿ }? ?3 K@h aaUe s~2&&B*Alji*˨,oƣT,d[3-*> LɟfkҠw#*AEjKUy>&{8m5Ki jjD*Nigw7DmzK۾M!k?o_lX#~XӑR*EՂדE;6e"Q(=Ezæ5Kؼָ_ 1zBJ X96jL^7{J1i@%8'7M_\Q#Uy Wo x8sv|Sn q_m >b[JX,4[T{Ratjjzz'ȶiIws KC^Y%6ꈺ]vhiWvh'̂|[^YrD=ѝlU_?]Y(N8f1qn-etm 0}b%׌=0?1s08;_ W|%\Zð >舽lnp.a{ )t; b n652?>Oдunm`׭ZWjC~>־0+ {{fMŕټ` ݛ%uA6,]kWu]7ihu1 l Ҷ̺:\cxhRQt$ fd<4B[fd7=.M9//O a},j?.5ښm?X2#d p(?c!a1ޗةܾ7dK:)3],H+ku<|`LhC7e םt H$^2%l.aeÉ|s }D^hz~Rá]|#@חև[k<|(*ݹdtM:,]' X_n| /cfOIENDB`tqsl-2.5.1/src/doxygen/html/structtqsl__provider__st.html0000664000076400007640000001215713563134442023771 0ustar rmurphyrmurphy TrustedQSL Library API: tqsl_provider_st Struct Reference
TrustedQSL Library API
tqsl_provider_st Struct Reference

#include <tqsllib.h>

Public Attributes

char organizationName [TQSL_NAME_ELEMENT_MAX+1]
 Provider name.
 
char organizationalUnitName [TQSL_NAME_ELEMENT_MAX+1]
 Provider unit.
 
char emailAddress [TQSL_NAME_ELEMENT_MAX+1]
 Provider e-mail.
 
char url [TQSL_NAME_ELEMENT_MAX+1]
 Provider URL.
 

Detailed Description

Certificate provider data


The documentation for this struct was generated from the following file:
tqsl-2.5.1/src/doxygen/html/structtqsl__provider__st-members.html0000664000076400007640000000572213563134442025421 0ustar rmurphyrmurphy TrustedQSL Library API: Member List
TrustedQSL Library API
tqsl_provider_st Member List
tqsl-2.5.1/src/doxygen/html/structtqsl__cert__req__st.html0000664000076400007640000003353213563134442024102 0ustar rmurphyrmurphy TrustedQSL Library API: tqsl_cert_req_st Struct Reference
TrustedQSL Library API
tqsl_cert_req_st Struct Reference

#include <tqsllib.h>

Public Attributes

char providerName [TQSL_NAME_ELEMENT_MAX+1]
 < Cert request data More...
 
char providerUnit [TQSL_NAME_ELEMENT_MAX+1]
 Provider unit.
 
char callSign [TQSL_CALLSIGN_MAX+1]
 Callsign.
 
char name [TQSL_CRQ_NAME_MAX+1]
 Name.
 
char address1 [TQSL_CRQ_ADDR_MAX+1]
 Address 1.
 
char address2 [TQSL_CRQ_ADDR_MAX+1]
 Address 2.
 
char city [TQSL_CRQ_CITY_MAX+1]
 City.
 
char state [TQSL_CRQ_STATE_MAX+1]
 State.
 
char postalCode [TQSL_CRQ_POSTAL_MAX+1]
 Postal Code.
 
char country [TQSL_CRQ_COUNTRY_MAX+1]
 Country.
 
char emailAddress [TQSL_CRQ_EMAIL_MAX+1]
 e-mail
 
int dxccEntity
 DXCC Entity code.
 
tQSL_Date qsoNotBefore
 QSOs not before date.
 
tQSL_Date qsoNotAfter
 QSOs not after date.
 
char password [TQSL_PASSWORD_MAX+1]
 Password.
 
tQSL_Cert signer
 Signing cert.
 
char renew
 Rewewal reference.
 

Detailed Description

Certificate request data

Member Data Documentation

◆ providerName

char tqsl_cert_req_st::providerName[TQSL_NAME_ELEMENT_MAX+1]

< Cert request data

Provider name


The documentation for this struct was generated from the following file:
tqsl-2.5.1/src/doxygen/html/structtqsl__cert__req__st-members.html0000664000076400007640000001434613563134442025534 0ustar rmurphyrmurphy TrustedQSL Library API: Member List
TrustedQSL Library API
tqsl_cert_req_st Member List
tqsl-2.5.1/src/doxygen/html/structtqsl__cabrilloField.html0000664000076400007640000001117213563134442024021 0ustar rmurphyrmurphy TrustedQSL Library API: tqsl_cabrilloField Struct Reference
TrustedQSL Library API
tqsl_cabrilloField Struct Reference

#include <cabrillo.h>

Public Attributes

char name [TQSL_CABRILLO_FIELD_NAME_LENGTH_MAX+1]
 < Cabrillo field More...
 
char value [TQSL_CABRILLO_FIELD_VALUE_LENGTH_MAX+1]
 Field value.
 

Detailed Description

Cabrillo field data:

  • name - ADIF field name
  • value - Field content

Member Data Documentation

◆ name

char tqsl_cabrilloField::name[TQSL_CABRILLO_FIELD_NAME_LENGTH_MAX+1]

< Cabrillo field

Field name


The documentation for this struct was generated from the following file:
tqsl-2.5.1/src/doxygen/html/structtqsl__cabrilloField-members.html0000664000076400007640000000470413563134442025454 0ustar rmurphyrmurphy TrustedQSL Library API: Member List
TrustedQSL Library API
tqsl_cabrilloField Member List

This is the complete list of members for tqsl_cabrilloField, including all inherited members.

nametqsl_cabrilloField
valuetqsl_cabrilloField
tqsl-2.5.1/src/doxygen/html/structtqsl__adifFieldResults.html0000664000076400007640000001433413563134442024522 0ustar rmurphyrmurphy TrustedQSL Library API: tqsl_adifFieldResults Struct Reference
TrustedQSL Library API
tqsl_adifFieldResults Struct Reference

#include <adif.h>

Public Attributes

char name [TQSL_ADIF_FIELD_NAME_LENGTH_MAX+1]
 Field name.
 
char size [TQSL_ADIF_FIELD_SIZE_LENGTH_MAX+1]
 Size.
 
char type [TQSL_ADIF_FIELD_TYPE_LENGTH_MAX+1]
 Type.
 
unsigned char * data
 data
 
unsigned int adifNameIndex
 Name index.
 
void * userPointer
 User pointer.
 

Detailed Description

Field returned from parsing


The documentation for this struct was generated from the following file:
tqsl-2.5.1/src/doxygen/html/structtqsl__adifFieldResults-members.html0000664000076400007640000000702013563134442026144 0ustar rmurphyrmurphy TrustedQSL Library API: Member List
TrustedQSL Library API
tqsl_adifFieldResults Member List
tqsl-2.5.1/src/doxygen/html/structtqsl__adifFieldDefinitions.html0000664000076400007640000001662113563134442025335 0ustar rmurphyrmurphy TrustedQSL Library API: tqsl_adifFieldDefinitions Struct Reference
TrustedQSL Library API
tqsl_adifFieldDefinitions Struct Reference

#include <adif.h>

Public Attributes

char name [TQSL_ADIF_FIELD_NAME_LENGTH_MAX+1]
 Field name.
 
char type [TQSL_ADIF_FIELD_TYPE_LENGTH_MAX+1]
 Field type.
 
TQSL_ADIF_RANGE_TYPE rangeType
 Range type.
 
unsigned int max_length
 Max length.
 
long signed min_value
 Min value.
 
long signed max_value
 Max value.
 
const char ** enumStrings
 Enumerated values.
 
void * userPointer
 user poitner
 

Detailed Description

An ADIF field definition


The documentation for this struct was generated from the following file:
tqsl-2.5.1/src/doxygen/html/structtqsl__adifFieldDefinitions-members.html0000664000076400007640000001024013563134442026754 0ustar rmurphyrmurphy TrustedQSL Library API: Member List
TrustedQSL Library API
tqsl_adifFieldDefinitions Member List
tqsl-2.5.1/src/doxygen/html/structtQSL__Time.html0000664000076400007640000001013513563134442022022 0ustar rmurphyrmurphy TrustedQSL Library API: tQSL_Time Struct Reference
TrustedQSL Library API
tQSL_Time Struct Reference

#include <tqsllib.h>

Public Attributes

int hour
 Time hour field.
 
int minute
 Time minute field.
 
int second
 Time seconds field.
 

Detailed Description

Struct that holds h-m-s


The documentation for this struct was generated from the following file:
tqsl-2.5.1/src/doxygen/html/structtQSL__Time-members.html0000664000076400007640000000514113563134442023453 0ustar rmurphyrmurphy TrustedQSL Library API: Member List
TrustedQSL Library API
tQSL_Time Member List

This is the complete list of members for tQSL_Time, including all inherited members.

hourtQSL_Time
minutetQSL_Time
secondtQSL_Time
tqsl-2.5.1/src/doxygen/html/structtQSL__Date.html0000664000076400007640000001011313563134442021775 0ustar rmurphyrmurphy TrustedQSL Library API: tQSL_Date Struct Reference
TrustedQSL Library API
tQSL_Date Struct Reference

#include <tqsllib.h>

Public Attributes

int year
 Numeric year.
 
int month
 Numeric month.
 
int day
 Numeric day.
 

Detailed Description

Struct that holds y-m-d


The documentation for this struct was generated from the following file:
tqsl-2.5.1/src/doxygen/html/structtQSL__Date-members.html0000664000076400007640000000513513563134442023435 0ustar rmurphyrmurphy TrustedQSL Library API: Member List
TrustedQSL Library API
tQSL_Date Member List

This is the complete list of members for tQSL_Date, including all inherited members.

daytQSL_Date
monthtQSL_Date
yeartQSL_Date
tqsl-2.5.1/src/doxygen/html/structTQSL__QSO__RECORD.html0000664000076400007640000003041613563134442022727 0ustar rmurphyrmurphy TrustedQSL Library API: TQSL_QSO_RECORD Struct Reference
TrustedQSL Library API
TQSL_QSO_RECORD Struct Reference

#include <tqsllib.h>

Public Attributes

char callsign [TQSL_CALLSIGN_MAX+1]
 QSO callsign.
 
char band [TQSL_BAND_MAX+1]
 QSO band.
 
char mode [TQSL_MODE_MAX+1]
 QSO mode.
 
char submode [TQSL_MODE_MAX+1]
 QSO submode.
 
tQSL_Date date
 QSO date.
 
tQSL_Time time
 QSO time.
 
char freq [TQSL_FREQ_MAX+1]
 QSO frequency.
 
char rxfreq [TQSL_FREQ_MAX+1]
 QSO receive frequency.
 
char rxband [TQSL_BAND_MAX+1]
 QSO RX band.
 
char propmode [TQSL_PROPMODE_MAX+1]
 QSO prop mode.
 
char satname [TQSL_SATNAME_MAX+1]
 QSO satellite name.
 
bool callsign_set
 QSO specifies a call worked.
 
bool mode_set
 QSO specifies a mode.
 
bool band_set
 QSO specifies a band or frequency.
 
bool date_set
 QSO specifies a date.
 
bool time_set
 QSO specifies a time.
 

Detailed Description

QSO data


The documentation for this struct was generated from the following file:
tqsl-2.5.1/src/doxygen/html/structTQSL__QSO__RECORD-members.html0000664000076400007640000001355613563134442024365 0ustar rmurphyrmurphy TrustedQSL Library API: Member List
TrustedQSL Library API
TQSL_QSO_RECORD Member List
tqsl-2.5.1/src/doxygen/html/splitbar.png0000664000076400007640000000047213563134442020260 0ustar rmurphyrmurphyPNG  IHDRMIDATxݡJCa( %4 bȘͶ3v^EL ,b;{Ï/aYկq:\IIIIIIIIIIIIIIIIII-l揊_t/ϻYQVYivk_ۣI@$I@$I@$I@$I@$I@$I@$I@$I@$I@$I@$I@$I@$I@$I@$I@$I@$I@$C[V=[fIENDB`tqsl-2.5.1/src/doxygen/html/open.png0000664000076400007640000000017313563134442017377 0ustar rmurphyrmurphyPNG  IHDR BIDATx 0 ׬ՙ\39b!9{|I>$#ߴ8/z/>2[giU,/~\ 9ٸIENDB`tqsl-2.5.1/src/doxygen/html/nav_h.png0000664000076400007640000000014213563134442017525 0ustar rmurphyrmurphyPNG  IHDR ,@)IDATxA @BQۛТ) ) aܿoRlIENDB`tqsl-2.5.1/src/doxygen/html/nav_g.png0000664000076400007640000000013713563134442017530 0ustar rmurphyrmurphyPNG  IHDR1&IDATx1 OHf_ ->~M iMS<IENDB`tqsl-2.5.1/src/doxygen/html/nav_f.png0000664000076400007640000000023113563134442017522 0ustar rmurphyrmurphyPNG  IHDR8`IDATxK Eі[BmkHprӼ.ꎤR6Z VIE5jliIJ0/u޿6sH yIENDB`tqsl-2.5.1/src/doxygen/html/modules.html0000664000076400007640000000642713563134442020276 0ustar rmurphyrmurphy TrustedQSL Library API: Modules
TrustedQSL Library API
Modules
tqsl-2.5.1/src/doxygen/html/menudata.js0000664000076400007640000000511113563134442020061 0ustar rmurphyrmurphyvar menudata={children:[ {text:"Main Page",url:"index.html"}, {text:"Modules",url:"modules.html"}, {text:"Classes",url:"annotated.html",children:[ {text:"Class List",url:"annotated.html"}, {text:"Class Members",url:"functions.html",children:[ {text:"All",url:"functions.html",children:[ {text:"a",url:"functions.html#index_a"}, {text:"b",url:"functions.html#index_b"}, {text:"c",url:"functions.html#index_c"}, {text:"d",url:"functions.html#index_d"}, {text:"e",url:"functions.html#index_e"}, {text:"f",url:"functions.html#index_f"}, {text:"h",url:"functions.html#index_h"}, {text:"m",url:"functions.html#index_m"}, {text:"n",url:"functions.html#index_n"}, {text:"o",url:"functions.html#index_o"}, {text:"p",url:"functions.html#index_p"}, {text:"q",url:"functions.html#index_q"}, {text:"r",url:"functions.html#index_r"}, {text:"s",url:"functions.html#index_s"}, {text:"t",url:"functions.html#index_t"}, {text:"u",url:"functions.html#index_u"}, {text:"v",url:"functions.html#index_v"}, {text:"y",url:"functions.html#index_y"}]}, {text:"Variables",url:"functions_vars.html",children:[ {text:"a",url:"functions_vars.html#index_a"}, {text:"b",url:"functions_vars.html#index_b"}, {text:"c",url:"functions_vars.html#index_c"}, {text:"d",url:"functions_vars.html#index_d"}, {text:"e",url:"functions_vars.html#index_e"}, {text:"f",url:"functions_vars.html#index_f"}, {text:"h",url:"functions_vars.html#index_h"}, {text:"m",url:"functions_vars.html#index_m"}, {text:"n",url:"functions_vars.html#index_n"}, {text:"o",url:"functions_vars.html#index_o"}, {text:"p",url:"functions_vars.html#index_p"}, {text:"q",url:"functions_vars.html#index_q"}, {text:"r",url:"functions_vars.html#index_r"}, {text:"s",url:"functions_vars.html#index_s"}, {text:"t",url:"functions_vars.html#index_t"}, {text:"u",url:"functions_vars.html#index_u"}, {text:"v",url:"functions_vars.html#index_v"}, {text:"y",url:"functions_vars.html#index_y"}]}]}]}, {text:"Files",url:"files.html",children:[ {text:"File List",url:"files.html"}, {text:"File Members",url:"globals.html",children:[ {text:"All",url:"globals.html",children:[ {text:"c",url:"globals.html#index_c"}, {text:"d",url:"globals_d.html#index_d"}, {text:"t",url:"globals_t.html#index_t"}]}, {text:"Functions",url:"globals_func.html",children:[ {text:"t",url:"globals_func.html#index_t"}]}, {text:"Variables",url:"globals_vars.html"}, {text:"Typedefs",url:"globals_type.html"}, {text:"Enumerations",url:"globals_enum.html"}, {text:"Macros",url:"globals_defs.html",children:[ {text:"c",url:"globals_defs.html#index_c"}, {text:"d",url:"globals_defs.html#index_d"}, {text:"t",url:"globals_defs.html#index_t"}]}]}]}]} tqsl-2.5.1/src/doxygen/html/menu.js0000664000076400007640000000355413563134442017240 0ustar rmurphyrmurphyfunction initMenu(relPath,searchEnabled,serverSide,searchPage,search) { function makeTree(data,relPath) { var result=''; if ('children' in data) { result+=''; } return result; } $('#main-nav').append(makeTree(menudata,relPath)); $('#main-nav').children(':first').addClass('sm sm-dox').attr('id','main-menu'); if (searchEnabled) { if (serverSide) { $('#main-menu').append('
  • '); } else { $('#main-menu').append('
  • '); } } $('#main-menu').smartmenus(); } tqsl-2.5.1/src/doxygen/html/jquery.js0000664000076400007640000052151113563134442017611 0ustar rmurphyrmurphy/*! * jQuery JavaScript Library v1.7.1 * http://jquery.com/ * * Copyright 2011, John Resig * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * Includes Sizzle.js * http://sizzlejs.com/ * Copyright 2011, The Dojo Foundation * Released under the MIT, BSD, and GPL Licenses. * * Date: Mon Nov 21 21:11:03 2011 -0500 */ (function(bb,L){var av=bb.document,bu=bb.navigator,bl=bb.location;var b=(function(){var bF=function(b0,b1){return new bF.fn.init(b0,b1,bD)},bU=bb.jQuery,bH=bb.$,bD,bY=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,bM=/\S/,bI=/^\s+/,bE=/\s+$/,bA=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,bN=/^[\],:{}\s]*$/,bW=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,bP=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,bJ=/(?:^|:|,)(?:\s*\[)+/g,by=/(webkit)[ \/]([\w.]+)/,bR=/(opera)(?:.*version)?[ \/]([\w.]+)/,bQ=/(msie) ([\w.]+)/,bS=/(mozilla)(?:.*? rv:([\w.]+))?/,bB=/-([a-z]|[0-9])/ig,bZ=/^-ms-/,bT=function(b0,b1){return(b1+"").toUpperCase()},bX=bu.userAgent,bV,bC,e,bL=Object.prototype.toString,bG=Object.prototype.hasOwnProperty,bz=Array.prototype.push,bK=Array.prototype.slice,bO=String.prototype.trim,bv=Array.prototype.indexOf,bx={};bF.fn=bF.prototype={constructor:bF,init:function(b0,b4,b3){var b2,b5,b1,b6;if(!b0){return this}if(b0.nodeType){this.context=this[0]=b0;this.length=1;return this}if(b0==="body"&&!b4&&av.body){this.context=av;this[0]=av.body;this.selector=b0;this.length=1;return this}if(typeof b0==="string"){if(b0.charAt(0)==="<"&&b0.charAt(b0.length-1)===">"&&b0.length>=3){b2=[null,b0,null]}else{b2=bY.exec(b0)}if(b2&&(b2[1]||!b4)){if(b2[1]){b4=b4 instanceof bF?b4[0]:b4;b6=(b4?b4.ownerDocument||b4:av);b1=bA.exec(b0);if(b1){if(bF.isPlainObject(b4)){b0=[av.createElement(b1[1])];bF.fn.attr.call(b0,b4,true)}else{b0=[b6.createElement(b1[1])]}}else{b1=bF.buildFragment([b2[1]],[b6]);b0=(b1.cacheable?bF.clone(b1.fragment):b1.fragment).childNodes}return bF.merge(this,b0)}else{b5=av.getElementById(b2[2]);if(b5&&b5.parentNode){if(b5.id!==b2[2]){return b3.find(b0)}this.length=1;this[0]=b5}this.context=av;this.selector=b0;return this}}else{if(!b4||b4.jquery){return(b4||b3).find(b0)}else{return this.constructor(b4).find(b0)}}}else{if(bF.isFunction(b0)){return b3.ready(b0)}}if(b0.selector!==L){this.selector=b0.selector;this.context=b0.context}return bF.makeArray(b0,this)},selector:"",jquery:"1.7.1",length:0,size:function(){return this.length},toArray:function(){return bK.call(this,0)},get:function(b0){return b0==null?this.toArray():(b0<0?this[this.length+b0]:this[b0])},pushStack:function(b1,b3,b0){var b2=this.constructor();if(bF.isArray(b1)){bz.apply(b2,b1)}else{bF.merge(b2,b1)}b2.prevObject=this;b2.context=this.context;if(b3==="find"){b2.selector=this.selector+(this.selector?" ":"")+b0}else{if(b3){b2.selector=this.selector+"."+b3+"("+b0+")"}}return b2},each:function(b1,b0){return bF.each(this,b1,b0)},ready:function(b0){bF.bindReady();bC.add(b0);return this},eq:function(b0){b0=+b0;return b0===-1?this.slice(b0):this.slice(b0,b0+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(bK.apply(this,arguments),"slice",bK.call(arguments).join(","))},map:function(b0){return this.pushStack(bF.map(this,function(b2,b1){return b0.call(b2,b1,b2)}))},end:function(){return this.prevObject||this.constructor(null)},push:bz,sort:[].sort,splice:[].splice};bF.fn.init.prototype=bF.fn;bF.extend=bF.fn.extend=function(){var b9,b2,b0,b1,b6,b7,b5=arguments[0]||{},b4=1,b3=arguments.length,b8=false;if(typeof b5==="boolean"){b8=b5;b5=arguments[1]||{};b4=2}if(typeof b5!=="object"&&!bF.isFunction(b5)){b5={}}if(b3===b4){b5=this;--b4}for(;b40){return}bC.fireWith(av,[bF]);if(bF.fn.trigger){bF(av).trigger("ready").off("ready")}}},bindReady:function(){if(bC){return}bC=bF.Callbacks("once memory");if(av.readyState==="complete"){return setTimeout(bF.ready,1)}if(av.addEventListener){av.addEventListener("DOMContentLoaded",e,false);bb.addEventListener("load",bF.ready,false)}else{if(av.attachEvent){av.attachEvent("onreadystatechange",e);bb.attachEvent("onload",bF.ready);var b0=false;try{b0=bb.frameElement==null}catch(b1){}if(av.documentElement.doScroll&&b0){bw()}}}},isFunction:function(b0){return bF.type(b0)==="function"},isArray:Array.isArray||function(b0){return bF.type(b0)==="array"},isWindow:function(b0){return b0&&typeof b0==="object"&&"setInterval" in b0},isNumeric:function(b0){return !isNaN(parseFloat(b0))&&isFinite(b0)},type:function(b0){return b0==null?String(b0):bx[bL.call(b0)]||"object"},isPlainObject:function(b2){if(!b2||bF.type(b2)!=="object"||b2.nodeType||bF.isWindow(b2)){return false}try{if(b2.constructor&&!bG.call(b2,"constructor")&&!bG.call(b2.constructor.prototype,"isPrototypeOf")){return false}}catch(b1){return false}var b0;for(b0 in b2){}return b0===L||bG.call(b2,b0)},isEmptyObject:function(b1){for(var b0 in b1){return false}return true},error:function(b0){throw new Error(b0)},parseJSON:function(b0){if(typeof b0!=="string"||!b0){return null}b0=bF.trim(b0);if(bb.JSON&&bb.JSON.parse){return bb.JSON.parse(b0)}if(bN.test(b0.replace(bW,"@").replace(bP,"]").replace(bJ,""))){return(new Function("return "+b0))()}bF.error("Invalid JSON: "+b0)},parseXML:function(b2){var b0,b1;try{if(bb.DOMParser){b1=new DOMParser();b0=b1.parseFromString(b2,"text/xml")}else{b0=new ActiveXObject("Microsoft.XMLDOM");b0.async="false";b0.loadXML(b2)}}catch(b3){b0=L}if(!b0||!b0.documentElement||b0.getElementsByTagName("parsererror").length){bF.error("Invalid XML: "+b2)}return b0},noop:function(){},globalEval:function(b0){if(b0&&bM.test(b0)){(bb.execScript||function(b1){bb["eval"].call(bb,b1)})(b0)}},camelCase:function(b0){return b0.replace(bZ,"ms-").replace(bB,bT)},nodeName:function(b1,b0){return b1.nodeName&&b1.nodeName.toUpperCase()===b0.toUpperCase()},each:function(b3,b6,b2){var b1,b4=0,b5=b3.length,b0=b5===L||bF.isFunction(b3);if(b2){if(b0){for(b1 in b3){if(b6.apply(b3[b1],b2)===false){break}}}else{for(;b40&&b0[0]&&b0[b1-1])||b1===0||bF.isArray(b0));if(b3){for(;b21?aJ.call(arguments,0):bG;if(!(--bw)){bC.resolveWith(bC,bx)}}}function bz(bF){return function(bG){bB[bF]=arguments.length>1?aJ.call(arguments,0):bG;bC.notifyWith(bE,bB)}}if(e>1){for(;bv
    a";bI=bv.getElementsByTagName("*");bF=bv.getElementsByTagName("a")[0];if(!bI||!bI.length||!bF){return{}}bG=av.createElement("select");bx=bG.appendChild(av.createElement("option"));bE=bv.getElementsByTagName("input")[0];bJ={leadingWhitespace:(bv.firstChild.nodeType===3),tbody:!bv.getElementsByTagName("tbody").length,htmlSerialize:!!bv.getElementsByTagName("link").length,style:/top/.test(bF.getAttribute("style")),hrefNormalized:(bF.getAttribute("href")==="/a"),opacity:/^0.55/.test(bF.style.opacity),cssFloat:!!bF.style.cssFloat,checkOn:(bE.value==="on"),optSelected:bx.selected,getSetAttribute:bv.className!=="t",enctype:!!av.createElement("form").enctype,html5Clone:av.createElement("nav").cloneNode(true).outerHTML!=="<:nav>",submitBubbles:true,changeBubbles:true,focusinBubbles:false,deleteExpando:true,noCloneEvent:true,inlineBlockNeedsLayout:false,shrinkWrapBlocks:false,reliableMarginRight:true};bE.checked=true;bJ.noCloneChecked=bE.cloneNode(true).checked;bG.disabled=true;bJ.optDisabled=!bx.disabled;try{delete bv.test}catch(bC){bJ.deleteExpando=false}if(!bv.addEventListener&&bv.attachEvent&&bv.fireEvent){bv.attachEvent("onclick",function(){bJ.noCloneEvent=false});bv.cloneNode(true).fireEvent("onclick")}bE=av.createElement("input");bE.value="t";bE.setAttribute("type","radio");bJ.radioValue=bE.value==="t";bE.setAttribute("checked","checked");bv.appendChild(bE);bD=av.createDocumentFragment();bD.appendChild(bv.lastChild);bJ.checkClone=bD.cloneNode(true).cloneNode(true).lastChild.checked;bJ.appendChecked=bE.checked;bD.removeChild(bE);bD.appendChild(bv);bv.innerHTML="";if(bb.getComputedStyle){bA=av.createElement("div");bA.style.width="0";bA.style.marginRight="0";bv.style.width="2px";bv.appendChild(bA);bJ.reliableMarginRight=(parseInt((bb.getComputedStyle(bA,null)||{marginRight:0}).marginRight,10)||0)===0}if(bv.attachEvent){for(by in {submit:1,change:1,focusin:1}){bB="on"+by;bw=(bB in bv);if(!bw){bv.setAttribute(bB,"return;");bw=(typeof bv[bB]==="function")}bJ[by+"Bubbles"]=bw}}bD.removeChild(bv);bD=bG=bx=bA=bv=bE=null;b(function(){var bM,bU,bV,bT,bN,bO,bL,bS,bR,e,bP,bQ=av.getElementsByTagName("body")[0];if(!bQ){return}bL=1;bS="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;";bR="visibility:hidden;border:0;";e="style='"+bS+"border:5px solid #000;padding:0;'";bP="
    ";bM=av.createElement("div");bM.style.cssText=bR+"width:0;height:0;position:static;top:0;margin-top:"+bL+"px";bQ.insertBefore(bM,bQ.firstChild);bv=av.createElement("div");bM.appendChild(bv);bv.innerHTML="
    t
    ";bz=bv.getElementsByTagName("td");bw=(bz[0].offsetHeight===0);bz[0].style.display="";bz[1].style.display="none";bJ.reliableHiddenOffsets=bw&&(bz[0].offsetHeight===0);bv.innerHTML="";bv.style.width=bv.style.paddingLeft="1px";b.boxModel=bJ.boxModel=bv.offsetWidth===2;if(typeof bv.style.zoom!=="undefined"){bv.style.display="inline";bv.style.zoom=1;bJ.inlineBlockNeedsLayout=(bv.offsetWidth===2);bv.style.display="";bv.innerHTML="
    ";bJ.shrinkWrapBlocks=(bv.offsetWidth!==2)}bv.style.cssText=bS+bR;bv.innerHTML=bP;bU=bv.firstChild;bV=bU.firstChild;bN=bU.nextSibling.firstChild.firstChild;bO={doesNotAddBorder:(bV.offsetTop!==5),doesAddBorderForTableAndCells:(bN.offsetTop===5)};bV.style.position="fixed";bV.style.top="20px";bO.fixedPosition=(bV.offsetTop===20||bV.offsetTop===15);bV.style.position=bV.style.top="";bU.style.overflow="hidden";bU.style.position="relative";bO.subtractsBorderForOverflowNotVisible=(bV.offsetTop===-5);bO.doesNotIncludeMarginInBodyOffset=(bQ.offsetTop!==bL);bQ.removeChild(bM);bv=bM=null;b.extend(bJ,bO)});return bJ})();var aS=/^(?:\{.*\}|\[.*\])$/,aA=/([A-Z])/g;b.extend({cache:{},uuid:0,expando:"jQuery"+(b.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:true,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:true},hasData:function(e){e=e.nodeType?b.cache[e[b.expando]]:e[b.expando];return !!e&&!S(e)},data:function(bx,bv,bz,by){if(!b.acceptData(bx)){return}var bG,bA,bD,bE=b.expando,bC=typeof bv==="string",bF=bx.nodeType,e=bF?b.cache:bx,bw=bF?bx[bE]:bx[bE]&&bE,bB=bv==="events";if((!bw||!e[bw]||(!bB&&!by&&!e[bw].data))&&bC&&bz===L){return}if(!bw){if(bF){bx[bE]=bw=++b.uuid}else{bw=bE}}if(!e[bw]){e[bw]={};if(!bF){e[bw].toJSON=b.noop}}if(typeof bv==="object"||typeof bv==="function"){if(by){e[bw]=b.extend(e[bw],bv)}else{e[bw].data=b.extend(e[bw].data,bv)}}bG=bA=e[bw];if(!by){if(!bA.data){bA.data={}}bA=bA.data}if(bz!==L){bA[b.camelCase(bv)]=bz}if(bB&&!bA[bv]){return bG.events}if(bC){bD=bA[bv];if(bD==null){bD=bA[b.camelCase(bv)]}}else{bD=bA}return bD},removeData:function(bx,bv,by){if(!b.acceptData(bx)){return}var bB,bA,bz,bC=b.expando,bD=bx.nodeType,e=bD?b.cache:bx,bw=bD?bx[bC]:bC;if(!e[bw]){return}if(bv){bB=by?e[bw]:e[bw].data;if(bB){if(!b.isArray(bv)){if(bv in bB){bv=[bv]}else{bv=b.camelCase(bv);if(bv in bB){bv=[bv]}else{bv=bv.split(" ")}}}for(bA=0,bz=bv.length;bA-1){return true}}return false},val:function(bx){var e,bv,by,bw=this[0];if(!arguments.length){if(bw){e=b.valHooks[bw.nodeName.toLowerCase()]||b.valHooks[bw.type];if(e&&"get" in e&&(bv=e.get(bw,"value"))!==L){return bv}bv=bw.value;return typeof bv==="string"?bv.replace(aU,""):bv==null?"":bv}return}by=b.isFunction(bx);return this.each(function(bA){var bz=b(this),bB;if(this.nodeType!==1){return}if(by){bB=bx.call(this,bA,bz.val())}else{bB=bx}if(bB==null){bB=""}else{if(typeof bB==="number"){bB+=""}else{if(b.isArray(bB)){bB=b.map(bB,function(bC){return bC==null?"":bC+""})}}}e=b.valHooks[this.nodeName.toLowerCase()]||b.valHooks[this.type];if(!e||!("set" in e)||e.set(this,bB,"value")===L){this.value=bB}})}});b.extend({valHooks:{option:{get:function(e){var bv=e.attributes.value;return !bv||bv.specified?e.value:e.text}},select:{get:function(e){var bA,bv,bz,bx,by=e.selectedIndex,bB=[],bC=e.options,bw=e.type==="select-one";if(by<0){return null}bv=bw?by:0;bz=bw?by+1:bC.length;for(;bv=0});if(!e.length){bv.selectedIndex=-1}return e}}},attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(bA,bx,bB,bz){var bw,e,by,bv=bA.nodeType;if(!bA||bv===3||bv===8||bv===2){return}if(bz&&bx in b.attrFn){return b(bA)[bx](bB)}if(typeof bA.getAttribute==="undefined"){return b.prop(bA,bx,bB)}by=bv!==1||!b.isXMLDoc(bA);if(by){bx=bx.toLowerCase();e=b.attrHooks[bx]||(ao.test(bx)?aY:be)}if(bB!==L){if(bB===null){b.removeAttr(bA,bx);return}else{if(e&&"set" in e&&by&&(bw=e.set(bA,bB,bx))!==L){return bw}else{bA.setAttribute(bx,""+bB);return bB}}}else{if(e&&"get" in e&&by&&(bw=e.get(bA,bx))!==null){return bw}else{bw=bA.getAttribute(bx);return bw===null?L:bw}}},removeAttr:function(bx,bz){var by,bA,bv,e,bw=0;if(bz&&bx.nodeType===1){bA=bz.toLowerCase().split(af);e=bA.length;for(;bw=0)}}})});var bd=/^(?:textarea|input|select)$/i,n=/^([^\.]*)?(?:\.(.+))?$/,J=/\bhover(\.\S+)?\b/,aO=/^key/,bf=/^(?:mouse|contextmenu)|click/,T=/^(?:focusinfocus|focusoutblur)$/,U=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,Y=function(e){var bv=U.exec(e);if(bv){bv[1]=(bv[1]||"").toLowerCase();bv[3]=bv[3]&&new RegExp("(?:^|\\s)"+bv[3]+"(?:\\s|$)")}return bv},j=function(bw,e){var bv=bw.attributes||{};return((!e[1]||bw.nodeName.toLowerCase()===e[1])&&(!e[2]||(bv.id||{}).value===e[2])&&(!e[3]||e[3].test((bv["class"]||{}).value)))},bt=function(e){return b.event.special.hover?e:e.replace(J,"mouseenter$1 mouseleave$1")};b.event={add:function(bx,bC,bJ,bA,by){var bD,bB,bK,bI,bH,bF,e,bG,bv,bz,bw,bE;if(bx.nodeType===3||bx.nodeType===8||!bC||!bJ||!(bD=b._data(bx))){return}if(bJ.handler){bv=bJ;bJ=bv.handler}if(!bJ.guid){bJ.guid=b.guid++}bK=bD.events;if(!bK){bD.events=bK={}}bB=bD.handle;if(!bB){bD.handle=bB=function(bL){return typeof b!=="undefined"&&(!bL||b.event.triggered!==bL.type)?b.event.dispatch.apply(bB.elem,arguments):L};bB.elem=bx}bC=b.trim(bt(bC)).split(" ");for(bI=0;bI=0){bG=bG.slice(0,-1);bw=true}if(bG.indexOf(".")>=0){bx=bG.split(".");bG=bx.shift();bx.sort()}if((!bA||b.event.customEvent[bG])&&!b.event.global[bG]){return}bv=typeof bv==="object"?bv[b.expando]?bv:new b.Event(bG,bv):new b.Event(bG);bv.type=bG;bv.isTrigger=true;bv.exclusive=bw;bv.namespace=bx.join(".");bv.namespace_re=bv.namespace?new RegExp("(^|\\.)"+bx.join("\\.(?:.*\\.)?")+"(\\.|$)"):null;by=bG.indexOf(":")<0?"on"+bG:"";if(!bA){e=b.cache;for(bC in e){if(e[bC].events&&e[bC].events[bG]){b.event.trigger(bv,bD,e[bC].handle.elem,true)}}return}bv.result=L;if(!bv.target){bv.target=bA}bD=bD!=null?b.makeArray(bD):[];bD.unshift(bv);bF=b.event.special[bG]||{};if(bF.trigger&&bF.trigger.apply(bA,bD)===false){return}bB=[[bA,bF.bindType||bG]];if(!bJ&&!bF.noBubble&&!b.isWindow(bA)){bI=bF.delegateType||bG;bH=T.test(bI+bG)?bA:bA.parentNode;bz=null;for(;bH;bH=bH.parentNode){bB.push([bH,bI]);bz=bH}if(bz&&bz===bA.ownerDocument){bB.push([bz.defaultView||bz.parentWindow||bb,bI])}}for(bC=0;bCbA){bH.push({elem:this,matches:bz.slice(bA)})}for(bC=0;bC0?this.on(e,null,bx,bw):this.trigger(e)};if(b.attrFn){b.attrFn[e]=true}if(aO.test(e)){b.event.fixHooks[e]=b.event.keyHooks}if(bf.test(e)){b.event.fixHooks[e]=b.event.mouseHooks}}); /*! * Sizzle CSS Selector Engine * Copyright 2011, The Dojo Foundation * Released under the MIT, BSD, and GPL Licenses. * More information: http://sizzlejs.com/ */ (function(){var bH=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,bC="sizcache"+(Math.random()+"").replace(".",""),bI=0,bL=Object.prototype.toString,bB=false,bA=true,bK=/\\/g,bO=/\r\n/g,bQ=/\W/;[0,0].sort(function(){bA=false;return 0});var by=function(bV,e,bY,bZ){bY=bY||[];e=e||av;var b1=e;if(e.nodeType!==1&&e.nodeType!==9){return[]}if(!bV||typeof bV!=="string"){return bY}var bS,b3,b6,bR,b2,b5,b4,bX,bU=true,bT=by.isXML(e),bW=[],b0=bV;do{bH.exec("");bS=bH.exec(b0);if(bS){b0=bS[3];bW.push(bS[1]);if(bS[2]){bR=bS[3];break}}}while(bS);if(bW.length>1&&bD.exec(bV)){if(bW.length===2&&bE.relative[bW[0]]){b3=bM(bW[0]+bW[1],e,bZ)}else{b3=bE.relative[bW[0]]?[e]:by(bW.shift(),e);while(bW.length){bV=bW.shift();if(bE.relative[bV]){bV+=bW.shift()}b3=bM(bV,b3,bZ)}}}else{if(!bZ&&bW.length>1&&e.nodeType===9&&!bT&&bE.match.ID.test(bW[0])&&!bE.match.ID.test(bW[bW.length-1])){b2=by.find(bW.shift(),e,bT);e=b2.expr?by.filter(b2.expr,b2.set)[0]:b2.set[0]}if(e){b2=bZ?{expr:bW.pop(),set:bF(bZ)}:by.find(bW.pop(),bW.length===1&&(bW[0]==="~"||bW[0]==="+")&&e.parentNode?e.parentNode:e,bT);b3=b2.expr?by.filter(b2.expr,b2.set):b2.set;if(bW.length>0){b6=bF(b3)}else{bU=false}while(bW.length){b5=bW.pop();b4=b5;if(!bE.relative[b5]){b5=""}else{b4=bW.pop()}if(b4==null){b4=e}bE.relative[b5](b6,b4,bT)}}else{b6=bW=[]}}if(!b6){b6=b3}if(!b6){by.error(b5||bV)}if(bL.call(b6)==="[object Array]"){if(!bU){bY.push.apply(bY,b6)}else{if(e&&e.nodeType===1){for(bX=0;b6[bX]!=null;bX++){if(b6[bX]&&(b6[bX]===true||b6[bX].nodeType===1&&by.contains(e,b6[bX]))){bY.push(b3[bX])}}}else{for(bX=0;b6[bX]!=null;bX++){if(b6[bX]&&b6[bX].nodeType===1){bY.push(b3[bX])}}}}}else{bF(b6,bY)}if(bR){by(bR,b1,bY,bZ);by.uniqueSort(bY)}return bY};by.uniqueSort=function(bR){if(bJ){bB=bA;bR.sort(bJ);if(bB){for(var e=1;e0};by.find=function(bX,e,bY){var bW,bS,bU,bT,bV,bR;if(!bX){return[]}for(bS=0,bU=bE.order.length;bS":function(bW,bR){var bV,bU=typeof bR==="string",bS=0,e=bW.length;if(bU&&!bQ.test(bR)){bR=bR.toLowerCase();for(;bS=0)){if(!bS){e.push(bV)}}else{if(bS){bR[bU]=false}}}}return false},ID:function(e){return e[1].replace(bK,"")},TAG:function(bR,e){return bR[1].replace(bK,"").toLowerCase()},CHILD:function(e){if(e[1]==="nth"){if(!e[2]){by.error(e[0])}e[2]=e[2].replace(/^\+|\s*/g,"");var bR=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(e[2]==="even"&&"2n"||e[2]==="odd"&&"2n+1"||!/\D/.test(e[2])&&"0n+"+e[2]||e[2]);e[2]=(bR[1]+(bR[2]||1))-0;e[3]=bR[3]-0}else{if(e[2]){by.error(e[0])}}e[0]=bI++;return e},ATTR:function(bU,bR,bS,e,bV,bW){var bT=bU[1]=bU[1].replace(bK,"");if(!bW&&bE.attrMap[bT]){bU[1]=bE.attrMap[bT]}bU[4]=(bU[4]||bU[5]||"").replace(bK,"");if(bU[2]==="~="){bU[4]=" "+bU[4]+" "}return bU},PSEUDO:function(bU,bR,bS,e,bV){if(bU[1]==="not"){if((bH.exec(bU[3])||"").length>1||/^\w/.test(bU[3])){bU[3]=by(bU[3],null,null,bR)}else{var bT=by.filter(bU[3],bR,bS,true^bV);if(!bS){e.push.apply(e,bT)}return false}}else{if(bE.match.POS.test(bU[0])||bE.match.CHILD.test(bU[0])){return true}}return bU},POS:function(e){e.unshift(true);return e}},filters:{enabled:function(e){return e.disabled===false&&e.type!=="hidden"},disabled:function(e){return e.disabled===true},checked:function(e){return e.checked===true},selected:function(e){if(e.parentNode){e.parentNode.selectedIndex}return e.selected===true},parent:function(e){return !!e.firstChild},empty:function(e){return !e.firstChild},has:function(bS,bR,e){return !!by(e[3],bS).length},header:function(e){return(/h\d/i).test(e.nodeName)},text:function(bS){var e=bS.getAttribute("type"),bR=bS.type;return bS.nodeName.toLowerCase()==="input"&&"text"===bR&&(e===bR||e===null)},radio:function(e){return e.nodeName.toLowerCase()==="input"&&"radio"===e.type},checkbox:function(e){return e.nodeName.toLowerCase()==="input"&&"checkbox"===e.type},file:function(e){return e.nodeName.toLowerCase()==="input"&&"file"===e.type},password:function(e){return e.nodeName.toLowerCase()==="input"&&"password"===e.type},submit:function(bR){var e=bR.nodeName.toLowerCase();return(e==="input"||e==="button")&&"submit"===bR.type},image:function(e){return e.nodeName.toLowerCase()==="input"&&"image"===e.type},reset:function(bR){var e=bR.nodeName.toLowerCase();return(e==="input"||e==="button")&&"reset"===bR.type},button:function(bR){var e=bR.nodeName.toLowerCase();return e==="input"&&"button"===bR.type||e==="button"},input:function(e){return(/input|select|textarea|button/i).test(e.nodeName)},focus:function(e){return e===e.ownerDocument.activeElement}},setFilters:{first:function(bR,e){return e===0},last:function(bS,bR,e,bT){return bR===bT.length-1},even:function(bR,e){return e%2===0},odd:function(bR,e){return e%2===1},lt:function(bS,bR,e){return bRe[3]-0},nth:function(bS,bR,e){return e[3]-0===bR},eq:function(bS,bR,e){return e[3]-0===bR}},filter:{PSEUDO:function(bS,bX,bW,bY){var e=bX[1],bR=bE.filters[e];if(bR){return bR(bS,bW,bX,bY)}else{if(e==="contains"){return(bS.textContent||bS.innerText||bw([bS])||"").indexOf(bX[3])>=0}else{if(e==="not"){var bT=bX[3];for(var bV=0,bU=bT.length;bV=0)}}},ID:function(bR,e){return bR.nodeType===1&&bR.getAttribute("id")===e},TAG:function(bR,e){return(e==="*"&&bR.nodeType===1)||!!bR.nodeName&&bR.nodeName.toLowerCase()===e},CLASS:function(bR,e){return(" "+(bR.className||bR.getAttribute("class"))+" ").indexOf(e)>-1},ATTR:function(bV,bT){var bS=bT[1],e=by.attr?by.attr(bV,bS):bE.attrHandle[bS]?bE.attrHandle[bS](bV):bV[bS]!=null?bV[bS]:bV.getAttribute(bS),bW=e+"",bU=bT[2],bR=bT[4];return e==null?bU==="!=":!bU&&by.attr?e!=null:bU==="="?bW===bR:bU==="*="?bW.indexOf(bR)>=0:bU==="~="?(" "+bW+" ").indexOf(bR)>=0:!bR?bW&&e!==false:bU==="!="?bW!==bR:bU==="^="?bW.indexOf(bR)===0:bU==="$="?bW.substr(bW.length-bR.length)===bR:bU==="|="?bW===bR||bW.substr(0,bR.length+1)===bR+"-":false},POS:function(bU,bR,bS,bV){var e=bR[2],bT=bE.setFilters[e];if(bT){return bT(bU,bS,bR,bV)}}}};var bD=bE.match.POS,bx=function(bR,e){return"\\"+(e-0+1)};for(var bz in bE.match){bE.match[bz]=new RegExp(bE.match[bz].source+(/(?![^\[]*\])(?![^\(]*\))/.source));bE.leftMatch[bz]=new RegExp(/(^(?:.|\r|\n)*?)/.source+bE.match[bz].source.replace(/\\(\d+)/g,bx))}var bF=function(bR,e){bR=Array.prototype.slice.call(bR,0);if(e){e.push.apply(e,bR);return e}return bR};try{Array.prototype.slice.call(av.documentElement.childNodes,0)[0].nodeType}catch(bP){bF=function(bU,bT){var bS=0,bR=bT||[];if(bL.call(bU)==="[object Array]"){Array.prototype.push.apply(bR,bU)}else{if(typeof bU.length==="number"){for(var e=bU.length;bS";e.insertBefore(bR,e.firstChild);if(av.getElementById(bS)){bE.find.ID=function(bU,bV,bW){if(typeof bV.getElementById!=="undefined"&&!bW){var bT=bV.getElementById(bU[1]);return bT?bT.id===bU[1]||typeof bT.getAttributeNode!=="undefined"&&bT.getAttributeNode("id").nodeValue===bU[1]?[bT]:L:[]}};bE.filter.ID=function(bV,bT){var bU=typeof bV.getAttributeNode!=="undefined"&&bV.getAttributeNode("id");return bV.nodeType===1&&bU&&bU.nodeValue===bT}}e.removeChild(bR);e=bR=null})();(function(){var e=av.createElement("div");e.appendChild(av.createComment(""));if(e.getElementsByTagName("*").length>0){bE.find.TAG=function(bR,bV){var bU=bV.getElementsByTagName(bR[1]);if(bR[1]==="*"){var bT=[];for(var bS=0;bU[bS];bS++){if(bU[bS].nodeType===1){bT.push(bU[bS])}}bU=bT}return bU}}e.innerHTML="";if(e.firstChild&&typeof e.firstChild.getAttribute!=="undefined"&&e.firstChild.getAttribute("href")!=="#"){bE.attrHandle.href=function(bR){return bR.getAttribute("href",2)}}e=null})();if(av.querySelectorAll){(function(){var e=by,bT=av.createElement("div"),bS="__sizzle__";bT.innerHTML="

    ";if(bT.querySelectorAll&&bT.querySelectorAll(".TEST").length===0){return}by=function(b4,bV,bZ,b3){bV=bV||av;if(!b3&&!by.isXML(bV)){var b2=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b4);if(b2&&(bV.nodeType===1||bV.nodeType===9)){if(b2[1]){return bF(bV.getElementsByTagName(b4),bZ)}else{if(b2[2]&&bE.find.CLASS&&bV.getElementsByClassName){return bF(bV.getElementsByClassName(b2[2]),bZ)}}}if(bV.nodeType===9){if(b4==="body"&&bV.body){return bF([bV.body],bZ)}else{if(b2&&b2[3]){var bY=bV.getElementById(b2[3]);if(bY&&bY.parentNode){if(bY.id===b2[3]){return bF([bY],bZ)}}else{return bF([],bZ)}}}try{return bF(bV.querySelectorAll(b4),bZ)}catch(b0){}}else{if(bV.nodeType===1&&bV.nodeName.toLowerCase()!=="object"){var bW=bV,bX=bV.getAttribute("id"),bU=bX||bS,b6=bV.parentNode,b5=/^\s*[+~]/.test(b4);if(!bX){bV.setAttribute("id",bU)}else{bU=bU.replace(/'/g,"\\$&")}if(b5&&b6){bV=bV.parentNode}try{if(!b5||b6){return bF(bV.querySelectorAll("[id='"+bU+"'] "+b4),bZ)}}catch(b1){}finally{if(!bX){bW.removeAttribute("id")}}}}}return e(b4,bV,bZ,b3)};for(var bR in e){by[bR]=e[bR]}bT=null})()}(function(){var e=av.documentElement,bS=e.matchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||e.msMatchesSelector;if(bS){var bU=!bS.call(av.createElement("div"),"div"),bR=false;try{bS.call(av.documentElement,"[test!='']:sizzle")}catch(bT){bR=true}by.matchesSelector=function(bW,bY){bY=bY.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!by.isXML(bW)){try{if(bR||!bE.match.PSEUDO.test(bY)&&!/!=/.test(bY)){var bV=bS.call(bW,bY);if(bV||!bU||bW.document&&bW.document.nodeType!==11){return bV}}}catch(bX){}}return by(bY,null,null,[bW]).length>0}}})();(function(){var e=av.createElement("div");e.innerHTML="
    ";if(!e.getElementsByClassName||e.getElementsByClassName("e").length===0){return}e.lastChild.className="e";if(e.getElementsByClassName("e").length===1){return}bE.order.splice(1,0,"CLASS");bE.find.CLASS=function(bR,bS,bT){if(typeof bS.getElementsByClassName!=="undefined"&&!bT){return bS.getElementsByClassName(bR[1])}};e=null})();function bv(bR,bW,bV,bZ,bX,bY){for(var bT=0,bS=bZ.length;bT0){bU=e;break}}}e=e[bR]}bZ[bT]=bU}}}if(av.documentElement.contains){by.contains=function(bR,e){return bR!==e&&(bR.contains?bR.contains(e):true)}}else{if(av.documentElement.compareDocumentPosition){by.contains=function(bR,e){return !!(bR.compareDocumentPosition(e)&16)}}else{by.contains=function(){return false}}}by.isXML=function(e){var bR=(e?e.ownerDocument||e:0).documentElement;return bR?bR.nodeName!=="HTML":false};var bM=function(bS,e,bW){var bV,bX=[],bU="",bY=e.nodeType?[e]:e;while((bV=bE.match.PSEUDO.exec(bS))){bU+=bV[0];bS=bS.replace(bE.match.PSEUDO,"")}bS=bE.relative[bS]?bS+"*":bS;for(var bT=0,bR=bY.length;bT0){for(bB=bA;bB=0:b.filter(e,this).length>0:this.filter(e).length>0)},closest:function(by,bx){var bv=[],bw,e,bz=this[0];if(b.isArray(by)){var bB=1;while(bz&&bz.ownerDocument&&bz!==bx){for(bw=0;bw-1:b.find.matchesSelector(bz,by)){bv.push(bz);break}else{bz=bz.parentNode;if(!bz||!bz.ownerDocument||bz===bx||bz.nodeType===11){break}}}}bv=bv.length>1?b.unique(bv):bv;return this.pushStack(bv,"closest",by)},index:function(e){if(!e){return(this[0]&&this[0].parentNode)?this.prevAll().length:-1}if(typeof e==="string"){return b.inArray(this[0],b(e))}return b.inArray(e.jquery?e[0]:e,this)},add:function(e,bv){var bx=typeof e==="string"?b(e,bv):b.makeArray(e&&e.nodeType?[e]:e),bw=b.merge(this.get(),bx);return this.pushStack(C(bx[0])||C(bw[0])?bw:b.unique(bw))},andSelf:function(){return this.add(this.prevObject)}});function C(e){return !e||!e.parentNode||e.parentNode.nodeType===11}b.each({parent:function(bv){var e=bv.parentNode;return e&&e.nodeType!==11?e:null},parents:function(e){return b.dir(e,"parentNode")},parentsUntil:function(bv,e,bw){return b.dir(bv,"parentNode",bw)},next:function(e){return b.nth(e,2,"nextSibling")},prev:function(e){return b.nth(e,2,"previousSibling")},nextAll:function(e){return b.dir(e,"nextSibling")},prevAll:function(e){return b.dir(e,"previousSibling")},nextUntil:function(bv,e,bw){return b.dir(bv,"nextSibling",bw)},prevUntil:function(bv,e,bw){return b.dir(bv,"previousSibling",bw)},siblings:function(e){return b.sibling(e.parentNode.firstChild,e)},children:function(e){return b.sibling(e.firstChild)},contents:function(e){return b.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:b.makeArray(e.childNodes)}},function(e,bv){b.fn[e]=function(by,bw){var bx=b.map(this,bv,by);if(!ab.test(e)){bw=by}if(bw&&typeof bw==="string"){bx=b.filter(bw,bx)}bx=this.length>1&&!ay[e]?b.unique(bx):bx;if((this.length>1||a9.test(bw))&&aq.test(e)){bx=bx.reverse()}return this.pushStack(bx,e,P.call(arguments).join(","))}});b.extend({filter:function(bw,e,bv){if(bv){bw=":not("+bw+")"}return e.length===1?b.find.matchesSelector(e[0],bw)?[e[0]]:[]:b.find.matches(bw,e)},dir:function(bw,bv,by){var e=[],bx=bw[bv];while(bx&&bx.nodeType!==9&&(by===L||bx.nodeType!==1||!b(bx).is(by))){if(bx.nodeType===1){e.push(bx)}bx=bx[bv]}return e},nth:function(by,e,bw,bx){e=e||1;var bv=0;for(;by;by=by[bw]){if(by.nodeType===1&&++bv===e){break}}return by},sibling:function(bw,bv){var e=[];for(;bw;bw=bw.nextSibling){if(bw.nodeType===1&&bw!==bv){e.push(bw)}}return e}});function aG(bx,bw,e){bw=bw||0;if(b.isFunction(bw)){return b.grep(bx,function(bz,by){var bA=!!bw.call(bz,by,bz);return bA===e})}else{if(bw.nodeType){return b.grep(bx,function(bz,by){return(bz===bw)===e})}else{if(typeof bw==="string"){var bv=b.grep(bx,function(by){return by.nodeType===1});if(bp.test(bw)){return b.filter(bw,bv,!e)}else{bw=b.filter(bw,bv)}}}}return b.grep(bx,function(bz,by){return(b.inArray(bz,bw)>=0)===e})}function a(e){var bw=aR.split("|"),bv=e.createDocumentFragment();if(bv.createElement){while(bw.length){bv.createElement(bw.pop())}}return bv}var aR="abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",ag=/ jQuery\d+="(?:\d+|null)"/g,ar=/^\s+/,R=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,d=/<([\w:]+)/,w=/",""],legend:[1,"
    ","
    "],thead:[1,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],col:[2,"","
    "],area:[1,"",""],_default:[0,"",""]},ac=a(av);ax.optgroup=ax.option;ax.tbody=ax.tfoot=ax.colgroup=ax.caption=ax.thead;ax.th=ax.td;if(!b.support.htmlSerialize){ax._default=[1,"div
    ","
    "]}b.fn.extend({text:function(e){if(b.isFunction(e)){return this.each(function(bw){var bv=b(this);bv.text(e.call(this,bw,bv.text()))})}if(typeof e!=="object"&&e!==L){return this.empty().append((this[0]&&this[0].ownerDocument||av).createTextNode(e))}return b.text(this)},wrapAll:function(e){if(b.isFunction(e)){return this.each(function(bw){b(this).wrapAll(e.call(this,bw))})}if(this[0]){var bv=b(e,this[0].ownerDocument).eq(0).clone(true);if(this[0].parentNode){bv.insertBefore(this[0])}bv.map(function(){var bw=this;while(bw.firstChild&&bw.firstChild.nodeType===1){bw=bw.firstChild}return bw}).append(this)}return this},wrapInner:function(e){if(b.isFunction(e)){return this.each(function(bv){b(this).wrapInner(e.call(this,bv))})}return this.each(function(){var bv=b(this),bw=bv.contents();if(bw.length){bw.wrapAll(e)}else{bv.append(e)}})},wrap:function(e){var bv=b.isFunction(e);return this.each(function(bw){b(this).wrapAll(bv?e.call(this,bw):e)})},unwrap:function(){return this.parent().each(function(){if(!b.nodeName(this,"body")){b(this).replaceWith(this.childNodes)}}).end()},append:function(){return this.domManip(arguments,true,function(e){if(this.nodeType===1){this.appendChild(e)}})},prepend:function(){return this.domManip(arguments,true,function(e){if(this.nodeType===1){this.insertBefore(e,this.firstChild)}})},before:function(){if(this[0]&&this[0].parentNode){return this.domManip(arguments,false,function(bv){this.parentNode.insertBefore(bv,this)})}else{if(arguments.length){var e=b.clean(arguments);e.push.apply(e,this.toArray());return this.pushStack(e,"before",arguments)}}},after:function(){if(this[0]&&this[0].parentNode){return this.domManip(arguments,false,function(bv){this.parentNode.insertBefore(bv,this.nextSibling)})}else{if(arguments.length){var e=this.pushStack(this,"after",arguments);e.push.apply(e,b.clean(arguments));return e}}},remove:function(e,bx){for(var bv=0,bw;(bw=this[bv])!=null;bv++){if(!e||b.filter(e,[bw]).length){if(!bx&&bw.nodeType===1){b.cleanData(bw.getElementsByTagName("*"));b.cleanData([bw])}if(bw.parentNode){bw.parentNode.removeChild(bw)}}}return this},empty:function(){for(var e=0,bv;(bv=this[e])!=null;e++){if(bv.nodeType===1){b.cleanData(bv.getElementsByTagName("*"))}while(bv.firstChild){bv.removeChild(bv.firstChild)}}return this},clone:function(bv,e){bv=bv==null?false:bv;e=e==null?bv:e;return this.map(function(){return b.clone(this,bv,e)})},html:function(bx){if(bx===L){return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(ag,""):null}else{if(typeof bx==="string"&&!ae.test(bx)&&(b.support.leadingWhitespace||!ar.test(bx))&&!ax[(d.exec(bx)||["",""])[1].toLowerCase()]){bx=bx.replace(R,"<$1>");try{for(var bw=0,bv=this.length;bw1&&bw0?this.clone(true):this).get();b(bC[bA])[bv](by);bz=bz.concat(by)}return this.pushStack(bz,e,bC.selector)}}});function bg(e){if(typeof e.getElementsByTagName!=="undefined"){return e.getElementsByTagName("*")}else{if(typeof e.querySelectorAll!=="undefined"){return e.querySelectorAll("*")}else{return[]}}}function az(e){if(e.type==="checkbox"||e.type==="radio"){e.defaultChecked=e.checked}}function E(e){var bv=(e.nodeName||"").toLowerCase();if(bv==="input"){az(e)}else{if(bv!=="script"&&typeof e.getElementsByTagName!=="undefined"){b.grep(e.getElementsByTagName("input"),az)}}}function al(e){var bv=av.createElement("div");ac.appendChild(bv);bv.innerHTML=e.outerHTML;return bv.firstChild}b.extend({clone:function(by,bA,bw){var e,bv,bx,bz=b.support.html5Clone||!ah.test("<"+by.nodeName)?by.cloneNode(true):al(by);if((!b.support.noCloneEvent||!b.support.noCloneChecked)&&(by.nodeType===1||by.nodeType===11)&&!b.isXMLDoc(by)){ai(by,bz);e=bg(by);bv=bg(bz);for(bx=0;e[bx];++bx){if(bv[bx]){ai(e[bx],bv[bx])}}}if(bA){t(by,bz);if(bw){e=bg(by);bv=bg(bz);for(bx=0;e[bx];++bx){t(e[bx],bv[bx])}}}e=bv=null;return bz},clean:function(bw,by,bH,bA){var bF;by=by||av;if(typeof by.createElement==="undefined"){by=by.ownerDocument||by[0]&&by[0].ownerDocument||av}var bI=[],bB;for(var bE=0,bz;(bz=bw[bE])!=null;bE++){if(typeof bz==="number"){bz+=""}if(!bz){continue}if(typeof bz==="string"){if(!W.test(bz)){bz=by.createTextNode(bz)}else{bz=bz.replace(R,"<$1>");var bK=(d.exec(bz)||["",""])[1].toLowerCase(),bx=ax[bK]||ax._default,bD=bx[0],bv=by.createElement("div");if(by===av){ac.appendChild(bv)}else{a(by).appendChild(bv)}bv.innerHTML=bx[1]+bz+bx[2];while(bD--){bv=bv.lastChild}if(!b.support.tbody){var e=w.test(bz),bC=bK==="table"&&!e?bv.firstChild&&bv.firstChild.childNodes:bx[1]===""&&!e?bv.childNodes:[];for(bB=bC.length-1;bB>=0;--bB){if(b.nodeName(bC[bB],"tbody")&&!bC[bB].childNodes.length){bC[bB].parentNode.removeChild(bC[bB])}}}if(!b.support.leadingWhitespace&&ar.test(bz)){bv.insertBefore(by.createTextNode(ar.exec(bz)[0]),bv.firstChild)}bz=bv.childNodes}}var bG;if(!b.support.appendChecked){if(bz[0]&&typeof(bG=bz.length)==="number"){for(bB=0;bB=0){return bx+"px"}}else{return bx}}}});if(!b.support.opacity){b.cssHooks.opacity={get:function(bv,e){return au.test((e&&bv.currentStyle?bv.currentStyle.filter:bv.style.filter)||"")?(parseFloat(RegExp.$1)/100)+"":e?"1":""},set:function(by,bz){var bx=by.style,bv=by.currentStyle,e=b.isNumeric(bz)?"alpha(opacity="+bz*100+")":"",bw=bv&&bv.filter||bx.filter||"";bx.zoom=1;if(bz>=1&&b.trim(bw.replace(ak,""))===""){bx.removeAttribute("filter");if(bv&&!bv.filter){return}}bx.filter=ak.test(bw)?bw.replace(ak,e):bw+" "+e}}}b(function(){if(!b.support.reliableMarginRight){b.cssHooks.marginRight={get:function(bw,bv){var e;b.swap(bw,{display:"inline-block"},function(){if(bv){e=Z(bw,"margin-right","marginRight")}else{e=bw.style.marginRight}});return e}}}});if(av.defaultView&&av.defaultView.getComputedStyle){aI=function(by,bw){var bv,bx,e;bw=bw.replace(z,"-$1").toLowerCase();if((bx=by.ownerDocument.defaultView)&&(e=bx.getComputedStyle(by,null))){bv=e.getPropertyValue(bw);if(bv===""&&!b.contains(by.ownerDocument.documentElement,by)){bv=b.style(by,bw)}}return bv}}if(av.documentElement.currentStyle){aX=function(bz,bw){var bA,e,by,bv=bz.currentStyle&&bz.currentStyle[bw],bx=bz.style;if(bv===null&&bx&&(by=bx[bw])){bv=by}if(!bc.test(bv)&&bn.test(bv)){bA=bx.left;e=bz.runtimeStyle&&bz.runtimeStyle.left;if(e){bz.runtimeStyle.left=bz.currentStyle.left}bx.left=bw==="fontSize"?"1em":(bv||0);bv=bx.pixelLeft+"px";bx.left=bA;if(e){bz.runtimeStyle.left=e}}return bv===""?"auto":bv}}Z=aI||aX;function p(by,bw,bv){var bA=bw==="width"?by.offsetWidth:by.offsetHeight,bz=bw==="width"?an:a1,bx=0,e=bz.length;if(bA>0){if(bv!=="border"){for(;bx)<[^<]*)*<\/script>/gi,q=/^(?:select|textarea)/i,h=/\s+/,br=/([?&])_=[^&]*/,K=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,A=b.fn.load,aa={},r={},aE,s,aV=["*/"]+["*"];try{aE=bl.href}catch(aw){aE=av.createElement("a");aE.href="";aE=aE.href}s=K.exec(aE.toLowerCase())||[];function f(e){return function(by,bA){if(typeof by!=="string"){bA=by;by="*"}if(b.isFunction(bA)){var bx=by.toLowerCase().split(h),bw=0,bz=bx.length,bv,bB,bC;for(;bw=0){var e=bw.slice(by,bw.length);bw=bw.slice(0,by)}var bx="GET";if(bz){if(b.isFunction(bz)){bA=bz;bz=L}else{if(typeof bz==="object"){bz=b.param(bz,b.ajaxSettings.traditional);bx="POST"}}}var bv=this;b.ajax({url:bw,type:bx,dataType:"html",data:bz,complete:function(bC,bB,bD){bD=bC.responseText;if(bC.isResolved()){bC.done(function(bE){bD=bE});bv.html(e?b("
    ").append(bD.replace(a6,"")).find(e):bD)}if(bA){bv.each(bA,[bD,bB,bC])}}});return this},serialize:function(){return b.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?b.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||q.test(this.nodeName)||aZ.test(this.type))}).map(function(e,bv){var bw=b(this).val();return bw==null?null:b.isArray(bw)?b.map(bw,function(by,bx){return{name:bv.name,value:by.replace(bs,"\r\n")}}):{name:bv.name,value:bw.replace(bs,"\r\n")}}).get()}});b.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(e,bv){b.fn[bv]=function(bw){return this.on(bv,bw)}});b.each(["get","post"],function(e,bv){b[bv]=function(bw,by,bz,bx){if(b.isFunction(by)){bx=bx||bz;bz=by;by=L}return b.ajax({type:bv,url:bw,data:by,success:bz,dataType:bx})}});b.extend({getScript:function(e,bv){return b.get(e,L,bv,"script")},getJSON:function(e,bv,bw){return b.get(e,bv,bw,"json")},ajaxSetup:function(bv,e){if(e){am(bv,b.ajaxSettings)}else{e=bv;bv=b.ajaxSettings}am(bv,e);return bv},ajaxSettings:{url:aE,isLocal:aM.test(s[1]),global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":aV},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":bb.String,"text html":true,"text json":b.parseJSON,"text xml":b.parseXML},flatOptions:{context:true,url:true}},ajaxPrefilter:f(aa),ajaxTransport:f(r),ajax:function(bz,bx){if(typeof bz==="object"){bx=bz;bz=L}bx=bx||{};var bD=b.ajaxSetup({},bx),bS=bD.context||bD,bG=bS!==bD&&(bS.nodeType||bS instanceof b)?b(bS):b.event,bR=b.Deferred(),bN=b.Callbacks("once memory"),bB=bD.statusCode||{},bC,bH={},bO={},bQ,by,bL,bE,bI,bA=0,bw,bK,bJ={readyState:0,setRequestHeader:function(bT,bU){if(!bA){var e=bT.toLowerCase();bT=bO[e]=bO[e]||bT;bH[bT]=bU}return this},getAllResponseHeaders:function(){return bA===2?bQ:null},getResponseHeader:function(bT){var e;if(bA===2){if(!by){by={};while((e=aD.exec(bQ))){by[e[1].toLowerCase()]=e[2]}}e=by[bT.toLowerCase()]}return e===L?null:e},overrideMimeType:function(e){if(!bA){bD.mimeType=e}return this},abort:function(e){e=e||"abort";if(bL){bL.abort(e)}bF(0,e);return this}};function bF(bZ,bU,b0,bW){if(bA===2){return}bA=2;if(bE){clearTimeout(bE)}bL=L;bQ=bW||"";bJ.readyState=bZ>0?4:0;var bT,b4,b3,bX=bU,bY=b0?bj(bD,bJ,b0):L,bV,b2;if(bZ>=200&&bZ<300||bZ===304){if(bD.ifModified){if((bV=bJ.getResponseHeader("Last-Modified"))){b.lastModified[bC]=bV}if((b2=bJ.getResponseHeader("Etag"))){b.etag[bC]=b2}}if(bZ===304){bX="notmodified";bT=true}else{try{b4=G(bD,bY);bX="success";bT=true}catch(b1){bX="parsererror";b3=b1}}}else{b3=bX;if(!bX||bZ){bX="error";if(bZ<0){bZ=0}}}bJ.status=bZ;bJ.statusText=""+(bU||bX);if(bT){bR.resolveWith(bS,[b4,bX,bJ])}else{bR.rejectWith(bS,[bJ,bX,b3])}bJ.statusCode(bB);bB=L;if(bw){bG.trigger("ajax"+(bT?"Success":"Error"),[bJ,bD,bT?b4:b3])}bN.fireWith(bS,[bJ,bX]);if(bw){bG.trigger("ajaxComplete",[bJ,bD]);if(!(--b.active)){b.event.trigger("ajaxStop")}}}bR.promise(bJ);bJ.success=bJ.done;bJ.error=bJ.fail;bJ.complete=bN.add;bJ.statusCode=function(bT){if(bT){var e;if(bA<2){for(e in bT){bB[e]=[bB[e],bT[e]]}}else{e=bT[bJ.status];bJ.then(e,e)}}return this};bD.url=((bz||bD.url)+"").replace(bq,"").replace(c,s[1]+"//");bD.dataTypes=b.trim(bD.dataType||"*").toLowerCase().split(h);if(bD.crossDomain==null){bI=K.exec(bD.url.toLowerCase());bD.crossDomain=!!(bI&&(bI[1]!=s[1]||bI[2]!=s[2]||(bI[3]||(bI[1]==="http:"?80:443))!=(s[3]||(s[1]==="http:"?80:443))))}if(bD.data&&bD.processData&&typeof bD.data!=="string"){bD.data=b.param(bD.data,bD.traditional)}aW(aa,bD,bx,bJ);if(bA===2){return false}bw=bD.global;bD.type=bD.type.toUpperCase();bD.hasContent=!aQ.test(bD.type);if(bw&&b.active++===0){b.event.trigger("ajaxStart")}if(!bD.hasContent){if(bD.data){bD.url+=(M.test(bD.url)?"&":"?")+bD.data;delete bD.data}bC=bD.url;if(bD.cache===false){var bv=b.now(),bP=bD.url.replace(br,"$1_="+bv);bD.url=bP+((bP===bD.url)?(M.test(bD.url)?"&":"?")+"_="+bv:"")}}if(bD.data&&bD.hasContent&&bD.contentType!==false||bx.contentType){bJ.setRequestHeader("Content-Type",bD.contentType)}if(bD.ifModified){bC=bC||bD.url;if(b.lastModified[bC]){bJ.setRequestHeader("If-Modified-Since",b.lastModified[bC])}if(b.etag[bC]){bJ.setRequestHeader("If-None-Match",b.etag[bC])}}bJ.setRequestHeader("Accept",bD.dataTypes[0]&&bD.accepts[bD.dataTypes[0]]?bD.accepts[bD.dataTypes[0]]+(bD.dataTypes[0]!=="*"?", "+aV+"; q=0.01":""):bD.accepts["*"]);for(bK in bD.headers){bJ.setRequestHeader(bK,bD.headers[bK])}if(bD.beforeSend&&(bD.beforeSend.call(bS,bJ,bD)===false||bA===2)){bJ.abort();return false}for(bK in {success:1,error:1,complete:1}){bJ[bK](bD[bK])}bL=aW(r,bD,bx,bJ);if(!bL){bF(-1,"No Transport")}else{bJ.readyState=1;if(bw){bG.trigger("ajaxSend",[bJ,bD])}if(bD.async&&bD.timeout>0){bE=setTimeout(function(){bJ.abort("timeout")},bD.timeout)}try{bA=1;bL.send(bH,bF)}catch(bM){if(bA<2){bF(-1,bM)}else{throw bM}}}return bJ},param:function(e,bw){var bv=[],by=function(bz,bA){bA=b.isFunction(bA)?bA():bA;bv[bv.length]=encodeURIComponent(bz)+"="+encodeURIComponent(bA)};if(bw===L){bw=b.ajaxSettings.traditional}if(b.isArray(e)||(e.jquery&&!b.isPlainObject(e))){b.each(e,function(){by(this.name,this.value)})}else{for(var bx in e){v(bx,e[bx],bw,by)}}return bv.join("&").replace(k,"+")}});function v(bw,by,bv,bx){if(b.isArray(by)){b.each(by,function(bA,bz){if(bv||ap.test(bw)){bx(bw,bz)}else{v(bw+"["+(typeof bz==="object"||b.isArray(bz)?bA:"")+"]",bz,bv,bx)}})}else{if(!bv&&by!=null&&typeof by==="object"){for(var e in by){v(bw+"["+e+"]",by[e],bv,bx)}}else{bx(bw,by)}}}b.extend({active:0,lastModified:{},etag:{}});function bj(bD,bC,bz){var bv=bD.contents,bB=bD.dataTypes,bw=bD.responseFields,by,bA,bx,e;for(bA in bw){if(bA in bz){bC[bw[bA]]=bz[bA]}}while(bB[0]==="*"){bB.shift();if(by===L){by=bD.mimeType||bC.getResponseHeader("content-type")}}if(by){for(bA in bv){if(bv[bA]&&bv[bA].test(by)){bB.unshift(bA);break}}}if(bB[0] in bz){bx=bB[0]}else{for(bA in bz){if(!bB[0]||bD.converters[bA+" "+bB[0]]){bx=bA;break}if(!e){e=bA}}bx=bx||e}if(bx){if(bx!==bB[0]){bB.unshift(bx)}return bz[bx]}}function G(bH,bz){if(bH.dataFilter){bz=bH.dataFilter(bz,bH.dataType)}var bD=bH.dataTypes,bG={},bA,bE,bw=bD.length,bB,bC=bD[0],bx,by,bF,bv,e;for(bA=1;bA=bw.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();bw.animatedProperties[this.prop]=true;for(bA in bw.animatedProperties){if(bw.animatedProperties[bA]!==true){e=false}}if(e){if(bw.overflow!=null&&!b.support.shrinkWrapBlocks){b.each(["","X","Y"],function(bC,bD){bz.style["overflow"+bD]=bw.overflow[bC]})}if(bw.hide){b(bz).hide()}if(bw.hide||bw.show){for(bA in bw.animatedProperties){b.style(bz,bA,bw.orig[bA]);b.removeData(bz,"fxshow"+bA,true);b.removeData(bz,"toggle"+bA,true)}}bv=bw.complete;if(bv){bw.complete=false;bv.call(bz)}}return false}else{if(bw.duration==Infinity){this.now=bx}else{bB=bx-this.startTime;this.state=bB/bw.duration;this.pos=b.easing[bw.animatedProperties[this.prop]](this.state,bB,0,1,bw.duration);this.now=this.start+((this.end-this.start)*this.pos)}this.update()}return true}};b.extend(b.fx,{tick:function(){var bw,bv=b.timers,e=0;for(;e").appendTo(e),bw=bv.css("display");bv.remove();if(bw==="none"||bw===""){if(!a8){a8=av.createElement("iframe");a8.frameBorder=a8.width=a8.height=0}e.appendChild(a8);if(!m||!a8.createElement){m=(a8.contentWindow||a8.contentDocument).document;m.write((av.compatMode==="CSS1Compat"?"":"")+"");m.close()}bv=m.createElement(bx);m.body.appendChild(bv);bw=b.css(bv,"display");e.removeChild(a8)}Q[bx]=bw}return Q[bx]}var V=/^t(?:able|d|h)$/i,ad=/^(?:body|html)$/i;if("getBoundingClientRect" in av.documentElement){b.fn.offset=function(bI){var by=this[0],bB;if(bI){return this.each(function(e){b.offset.setOffset(this,bI,e)})}if(!by||!by.ownerDocument){return null}if(by===by.ownerDocument.body){return b.offset.bodyOffset(by)}try{bB=by.getBoundingClientRect()}catch(bF){}var bH=by.ownerDocument,bw=bH.documentElement;if(!bB||!b.contains(bw,by)){return bB?{top:bB.top,left:bB.left}:{top:0,left:0}}var bC=bH.body,bD=aK(bH),bA=bw.clientTop||bC.clientTop||0,bE=bw.clientLeft||bC.clientLeft||0,bv=bD.pageYOffset||b.support.boxModel&&bw.scrollTop||bC.scrollTop,bz=bD.pageXOffset||b.support.boxModel&&bw.scrollLeft||bC.scrollLeft,bG=bB.top+bv-bA,bx=bB.left+bz-bE;return{top:bG,left:bx}}}else{b.fn.offset=function(bF){var bz=this[0];if(bF){return this.each(function(bG){b.offset.setOffset(this,bF,bG)})}if(!bz||!bz.ownerDocument){return null}if(bz===bz.ownerDocument.body){return b.offset.bodyOffset(bz)}var bC,bw=bz.offsetParent,bv=bz,bE=bz.ownerDocument,bx=bE.documentElement,bA=bE.body,bB=bE.defaultView,e=bB?bB.getComputedStyle(bz,null):bz.currentStyle,bD=bz.offsetTop,by=bz.offsetLeft;while((bz=bz.parentNode)&&bz!==bA&&bz!==bx){if(b.support.fixedPosition&&e.position==="fixed"){break}bC=bB?bB.getComputedStyle(bz,null):bz.currentStyle;bD-=bz.scrollTop;by-=bz.scrollLeft;if(bz===bw){bD+=bz.offsetTop;by+=bz.offsetLeft;if(b.support.doesNotAddBorder&&!(b.support.doesAddBorderForTableAndCells&&V.test(bz.nodeName))){bD+=parseFloat(bC.borderTopWidth)||0;by+=parseFloat(bC.borderLeftWidth)||0}bv=bw;bw=bz.offsetParent}if(b.support.subtractsBorderForOverflowNotVisible&&bC.overflow!=="visible"){bD+=parseFloat(bC.borderTopWidth)||0;by+=parseFloat(bC.borderLeftWidth)||0}e=bC}if(e.position==="relative"||e.position==="static"){bD+=bA.offsetTop;by+=bA.offsetLeft}if(b.support.fixedPosition&&e.position==="fixed"){bD+=Math.max(bx.scrollTop,bA.scrollTop);by+=Math.max(bx.scrollLeft,bA.scrollLeft)}return{top:bD,left:by}}}b.offset={bodyOffset:function(e){var bw=e.offsetTop,bv=e.offsetLeft;if(b.support.doesNotIncludeMarginInBodyOffset){bw+=parseFloat(b.css(e,"marginTop"))||0;bv+=parseFloat(b.css(e,"marginLeft"))||0}return{top:bw,left:bv}},setOffset:function(bx,bG,bA){var bB=b.css(bx,"position");if(bB==="static"){bx.style.position="relative"}var bz=b(bx),bv=bz.offset(),e=b.css(bx,"top"),bE=b.css(bx,"left"),bF=(bB==="absolute"||bB==="fixed")&&b.inArray("auto",[e,bE])>-1,bD={},bC={},bw,by;if(bF){bC=bz.position();bw=bC.top;by=bC.left}else{bw=parseFloat(e)||0;by=parseFloat(bE)||0}if(b.isFunction(bG)){bG=bG.call(bx,bA,bv)}if(bG.top!=null){bD.top=(bG.top-bv.top)+bw}if(bG.left!=null){bD.left=(bG.left-bv.left)+by}if("using" in bG){bG.using.call(bx,bD)}else{bz.css(bD)}}};b.fn.extend({position:function(){if(!this[0]){return null}var bw=this[0],bv=this.offsetParent(),bx=this.offset(),e=ad.test(bv[0].nodeName)?{top:0,left:0}:bv.offset();bx.top-=parseFloat(b.css(bw,"marginTop"))||0;bx.left-=parseFloat(b.css(bw,"marginLeft"))||0;e.top+=parseFloat(b.css(bv[0],"borderTopWidth"))||0;e.left+=parseFloat(b.css(bv[0],"borderLeftWidth"))||0;return{top:bx.top-e.top,left:bx.left-e.left}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||av.body;while(e&&(!ad.test(e.nodeName)&&b.css(e,"position")==="static")){e=e.offsetParent}return e})}});b.each(["Left","Top"],function(bv,e){var bw="scroll"+e;b.fn[bw]=function(bz){var bx,by;if(bz===L){bx=this[0];if(!bx){return null}by=aK(bx);return by?("pageXOffset" in by)?by[bv?"pageYOffset":"pageXOffset"]:b.support.boxModel&&by.document.documentElement[bw]||by.document.body[bw]:bx[bw]}return this.each(function(){by=aK(this);if(by){by.scrollTo(!bv?bz:b(by).scrollLeft(),bv?bz:b(by).scrollTop())}else{this[bw]=bz}})}});function aK(e){return b.isWindow(e)?e:e.nodeType===9?e.defaultView||e.parentWindow:false}b.each(["Height","Width"],function(bv,e){var bw=e.toLowerCase();b.fn["inner"+e]=function(){var bx=this[0];return bx?bx.style?parseFloat(b.css(bx,bw,"padding")):this[bw]():null};b.fn["outer"+e]=function(by){var bx=this[0];return bx?bx.style?parseFloat(b.css(bx,bw,by?"margin":"border")):this[bw]():null};b.fn[bw]=function(bz){var bA=this[0];if(!bA){return bz==null?null:this}if(b.isFunction(bz)){return this.each(function(bE){var bD=b(this);bD[bw](bz.call(this,bE,bD[bw]()))})}if(b.isWindow(bA)){var bB=bA.document.documentElement["client"+e],bx=bA.document.body;return bA.document.compatMode==="CSS1Compat"&&bB||bx&&bx["client"+e]||bB}else{if(bA.nodeType===9){return Math.max(bA.documentElement["client"+e],bA.body["scroll"+e],bA.documentElement["scroll"+e],bA.body["offset"+e],bA.documentElement["offset"+e])}else{if(bz===L){var bC=b.css(bA,bw),by=parseFloat(bC);return b.isNumeric(by)?by:bC}else{return this.css(bw,typeof bz==="string"?bz:bz+"px")}}}}});bb.jQuery=bb.$=b;if(typeof define==="function"&&define.amd&&define.amd.jQuery){define("jquery",[],function(){return b})}})(window);/*! * jQuery UI 1.8.18 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI */ (function(a,d){a.ui=a.ui||{};if(a.ui.version){return}a.extend(a.ui,{version:"1.8.18",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});a.fn.extend({propAttr:a.fn.prop||a.fn.attr,_focus:a.fn.focus,focus:function(e,f){return typeof e==="number"?this.each(function(){var g=this;setTimeout(function(){a(g).focus();if(f){f.call(g)}},e)}):this._focus.apply(this,arguments)},scrollParent:function(){var e;if((a.browser.msie&&(/(static|relative)/).test(this.css("position")))||(/absolute/).test(this.css("position"))){e=this.parents().filter(function(){return(/(relative|absolute|fixed)/).test(a.curCSS(this,"position",1))&&(/(auto|scroll)/).test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"overflow-x",1))}).eq(0)}else{e=this.parents().filter(function(){return(/(auto|scroll)/).test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"overflow-x",1))}).eq(0)}return(/fixed/).test(this.css("position"))||!e.length?a(document):e},zIndex:function(h){if(h!==d){return this.css("zIndex",h)}if(this.length){var f=a(this[0]),e,g;while(f.length&&f[0]!==document){e=f.css("position");if(e==="absolute"||e==="relative"||e==="fixed"){g=parseInt(f.css("zIndex"),10);if(!isNaN(g)&&g!==0){return g}}f=f.parent()}}return 0},disableSelection:function(){return this.bind((a.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(e){e.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});a.each(["Width","Height"],function(g,e){var f=e==="Width"?["Left","Right"]:["Top","Bottom"],h=e.toLowerCase(),k={innerWidth:a.fn.innerWidth,innerHeight:a.fn.innerHeight,outerWidth:a.fn.outerWidth,outerHeight:a.fn.outerHeight};function j(m,l,i,n){a.each(f,function(){l-=parseFloat(a.curCSS(m,"padding"+this,true))||0;if(i){l-=parseFloat(a.curCSS(m,"border"+this+"Width",true))||0}if(n){l-=parseFloat(a.curCSS(m,"margin"+this,true))||0}});return l}a.fn["inner"+e]=function(i){if(i===d){return k["inner"+e].call(this)}return this.each(function(){a(this).css(h,j(this,i)+"px")})};a.fn["outer"+e]=function(i,l){if(typeof i!=="number"){return k["outer"+e].call(this,i)}return this.each(function(){a(this).css(h,j(this,i,true,l)+"px")})}});function c(g,e){var j=g.nodeName.toLowerCase();if("area"===j){var i=g.parentNode,h=i.name,f;if(!g.href||!h||i.nodeName.toLowerCase()!=="map"){return false}f=a("img[usemap=#"+h+"]")[0];return !!f&&b(f)}return(/input|select|textarea|button|object/.test(j)?!g.disabled:"a"==j?g.href||e:e)&&b(g)}function b(e){return !a(e).parents().andSelf().filter(function(){return a.curCSS(this,"visibility")==="hidden"||a.expr.filters.hidden(this)}).length}a.extend(a.expr[":"],{data:function(g,f,e){return !!a.data(g,e[3])},focusable:function(e){return c(e,!isNaN(a.attr(e,"tabindex")))},tabbable:function(g){var e=a.attr(g,"tabindex"),f=isNaN(e);return(f||e>=0)&&c(g,!f)}});a(function(){var e=document.body,f=e.appendChild(f=document.createElement("div"));f.offsetHeight;a.extend(f.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});a.support.minHeight=f.offsetHeight===100;a.support.selectstart="onselectstart" in f;e.removeChild(f).style.display="none"});a.extend(a.ui,{plugin:{add:function(f,g,j){var h=a.ui[f].prototype;for(var e in j){h.plugins[e]=h.plugins[e]||[];h.plugins[e].push([g,j[e]])}},call:function(e,g,f){var j=e.plugins[g];if(!j||!e.element[0].parentNode){return}for(var h=0;h0){return true}h[e]=1;g=(h[e]>0);h[e]=0;return g},isOverAxis:function(f,e,g){return(f>e)&&(f<(e+g))},isOver:function(j,f,i,h,e,g){return a.ui.isOverAxis(j,i,e)&&a.ui.isOverAxis(f,h,g)}})})(jQuery);/*! * jQuery UI Widget 1.8.18 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Widget */ (function(b,d){if(b.cleanData){var c=b.cleanData;b.cleanData=function(f){for(var g=0,h;(h=f[g])!=null;g++){try{b(h).triggerHandler("remove")}catch(j){}}c(f)}}else{var a=b.fn.remove;b.fn.remove=function(e,f){return this.each(function(){if(!f){if(!e||b.filter(e,[this]).length){b("*",this).add([this]).each(function(){try{b(this).triggerHandler("remove")}catch(g){}})}}return a.call(b(this),e,f)})}}b.widget=function(f,h,e){var g=f.split(".")[0],j;f=f.split(".")[1];j=g+"-"+f;if(!e){e=h;h=b.Widget}b.expr[":"][j]=function(k){return !!b.data(k,f)};b[g]=b[g]||{};b[g][f]=function(k,l){if(arguments.length){this._createWidget(k,l)}};var i=new h();i.options=b.extend(true,{},i.options);b[g][f].prototype=b.extend(true,i,{namespace:g,widgetName:f,widgetEventPrefix:b[g][f].prototype.widgetEventPrefix||f,widgetBaseClass:j},e);b.widget.bridge(f,b[g][f])};b.widget.bridge=function(f,e){b.fn[f]=function(i){var g=typeof i==="string",h=Array.prototype.slice.call(arguments,1),j=this;i=!g&&h.length?b.extend.apply(null,[true,i].concat(h)):i;if(g&&i.charAt(0)==="_"){return j}if(g){this.each(function(){var k=b.data(this,f),l=k&&b.isFunction(k[i])?k[i].apply(k,h):k;if(l!==k&&l!==d){j=l;return false}})}else{this.each(function(){var k=b.data(this,f);if(k){k.option(i||{})._init()}else{b.data(this,f,new e(i,this))}})}return j}};b.Widget=function(e,f){if(arguments.length){this._createWidget(e,f)}};b.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:false},_createWidget:function(f,g){b.data(g,this.widgetName,this);this.element=b(g);this.options=b.extend(true,{},this.options,this._getCreateOptions(),f);var e=this;this.element.bind("remove."+this.widgetName,function(){e.destroy()});this._create();this._trigger("create");this._init()},_getCreateOptions:function(){return b.metadata&&b.metadata.get(this.element[0])[this.widgetName]},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled ui-state-disabled")},widget:function(){return this.element},option:function(f,g){var e=f;if(arguments.length===0){return b.extend({},this.options)}if(typeof f==="string"){if(g===d){return this.options[f]}e={};e[f]=g}this._setOptions(e);return this},_setOptions:function(f){var e=this;b.each(f,function(g,h){e._setOption(g,h)});return this},_setOption:function(e,f){this.options[e]=f;if(e==="disabled"){this.widget()[f?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",f)}return this},enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(e,f,g){var j,i,h=this.options[e];g=g||{};f=b.Event(f);f.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase();f.target=this.element[0];i=f.originalEvent;if(i){for(j in i){if(!(j in f)){f[j]=i[j]}}}this.element.trigger(f,g);return !(b.isFunction(h)&&h.call(this.element[0],f,g)===false||f.isDefaultPrevented())}}})(jQuery);/*! * jQuery UI Mouse 1.8.18 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Mouse * * Depends: * jquery.ui.widget.js */ (function(b,c){var a=false;b(document).mouseup(function(d){a=false});b.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var d=this;this.element.bind("mousedown."+this.widgetName,function(e){return d._mouseDown(e)}).bind("click."+this.widgetName,function(e){if(true===b.data(e.target,d.widgetName+".preventClickEvent")){b.removeData(e.target,d.widgetName+".preventClickEvent");e.stopImmediatePropagation();return false}});this.started=false},_mouseDestroy:function(){this.element.unbind("."+this.widgetName)},_mouseDown:function(f){if(a){return}(this._mouseStarted&&this._mouseUp(f));this._mouseDownEvent=f;var e=this,g=(f.which==1),d=(typeof this.options.cancel=="string"&&f.target.nodeName?b(f.target).closest(this.options.cancel).length:false);if(!g||d||!this._mouseCapture(f)){return true}this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet){this._mouseDelayTimer=setTimeout(function(){e.mouseDelayMet=true},this.options.delay)}if(this._mouseDistanceMet(f)&&this._mouseDelayMet(f)){this._mouseStarted=(this._mouseStart(f)!==false);if(!this._mouseStarted){f.preventDefault();return true}}if(true===b.data(f.target,this.widgetName+".preventClickEvent")){b.removeData(f.target,this.widgetName+".preventClickEvent")}this._mouseMoveDelegate=function(h){return e._mouseMove(h)};this._mouseUpDelegate=function(h){return e._mouseUp(h)};b(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);f.preventDefault();a=true;return true},_mouseMove:function(d){if(b.browser.msie&&!(document.documentMode>=9)&&!d.button){return this._mouseUp(d)}if(this._mouseStarted){this._mouseDrag(d);return d.preventDefault()}if(this._mouseDistanceMet(d)&&this._mouseDelayMet(d)){this._mouseStarted=(this._mouseStart(this._mouseDownEvent,d)!==false);(this._mouseStarted?this._mouseDrag(d):this._mouseUp(d))}return !this._mouseStarted},_mouseUp:function(d){b(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;if(d.target==this._mouseDownEvent.target){b.data(d.target,this.widgetName+".preventClickEvent",true)}this._mouseStop(d)}return false},_mouseDistanceMet:function(d){return(Math.max(Math.abs(this._mouseDownEvent.pageX-d.pageX),Math.abs(this._mouseDownEvent.pageY-d.pageY))>=this.options.distance)},_mouseDelayMet:function(d){return this.mouseDelayMet},_mouseStart:function(d){},_mouseDrag:function(d){},_mouseStop:function(d){},_mouseCapture:function(d){return true}})})(jQuery);(function(c,d){c.widget("ui.resizable",c.ui.mouse,{widgetEventPrefix:"resize",options:{alsoResize:false,animate:false,animateDuration:"slow",animateEasing:"swing",aspectRatio:false,autoHide:false,containment:false,ghost:false,grid:false,handles:"e,s,se",helper:false,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:1000},_create:function(){var f=this,k=this.options;this.element.addClass("ui-resizable");c.extend(this,{_aspectRatio:!!(k.aspectRatio),aspectRatio:k.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:k.helper||k.ghost||k.animate?k.helper||"ui-resizable-helper":null});if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)){this.element.wrap(c('
    ').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")}));this.element=this.element.parent().data("resizable",this.element.data("resizable"));this.elementIsWrapper=true;this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")});this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});this.originalResizeStyle=this.originalElement.css("resize");this.originalElement.css("resize","none");this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"}));this.originalElement.css({margin:this.originalElement.css("margin")});this._proportionallyResize()}this.handles=k.handles||(!c(".ui-resizable-handle",this.element).length?"e,s,se":{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"});if(this.handles.constructor==String){if(this.handles=="all"){this.handles="n,e,s,w,se,sw,ne,nw"}var l=this.handles.split(",");this.handles={};for(var g=0;g
    ');if(/sw|se|ne|nw/.test(j)){h.css({zIndex:++k.zIndex})}if("se"==j){h.addClass("ui-icon ui-icon-gripsmall-diagonal-se")}this.handles[j]=".ui-resizable-"+j;this.element.append(h)}}this._renderAxis=function(q){q=q||this.element;for(var n in this.handles){if(this.handles[n].constructor==String){this.handles[n]=c(this.handles[n],this.element).show()}if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var o=c(this.handles[n],this.element),p=0;p=/sw|ne|nw|se|n|s/.test(n)?o.outerHeight():o.outerWidth();var m=["padding",/ne|nw|n/.test(n)?"Top":/se|sw|s/.test(n)?"Bottom":/^e$/.test(n)?"Right":"Left"].join("");q.css(m,p);this._proportionallyResize()}if(!c(this.handles[n]).length){continue}}};this._renderAxis(this.element);this._handles=c(".ui-resizable-handle",this.element).disableSelection();this._handles.mouseover(function(){if(!f.resizing){if(this.className){var i=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)}f.axis=i&&i[1]?i[1]:"se"}});if(k.autoHide){this._handles.hide();c(this.element).addClass("ui-resizable-autohide").hover(function(){if(k.disabled){return}c(this).removeClass("ui-resizable-autohide");f._handles.show()},function(){if(k.disabled){return}if(!f.resizing){c(this).addClass("ui-resizable-autohide");f._handles.hide()}})}this._mouseInit()},destroy:function(){this._mouseDestroy();var e=function(g){c(g).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};if(this.elementIsWrapper){e(this.element);var f=this.element;f.after(this.originalElement.css({position:f.css("position"),width:f.outerWidth(),height:f.outerHeight(),top:f.css("top"),left:f.css("left")})).remove()}this.originalElement.css("resize",this.originalResizeStyle);e(this.originalElement);return this},_mouseCapture:function(f){var g=false;for(var e in this.handles){if(c(this.handles[e])[0]==f.target){g=true}}return !this.options.disabled&&g},_mouseStart:function(g){var j=this.options,f=this.element.position(),e=this.element;this.resizing=true;this.documentScroll={top:c(document).scrollTop(),left:c(document).scrollLeft()};if(e.is(".ui-draggable")||(/absolute/).test(e.css("position"))){e.css({position:"absolute",top:f.top,left:f.left})}this._renderProxy();var k=b(this.helper.css("left")),h=b(this.helper.css("top"));if(j.containment){k+=c(j.containment).scrollLeft()||0;h+=c(j.containment).scrollTop()||0}this.offset=this.helper.offset();this.position={left:k,top:h};this.size=this._helper?{width:e.outerWidth(),height:e.outerHeight()}:{width:e.width(),height:e.height()};this.originalSize=this._helper?{width:e.outerWidth(),height:e.outerHeight()}:{width:e.width(),height:e.height()};this.originalPosition={left:k,top:h};this.sizeDiff={width:e.outerWidth()-e.width(),height:e.outerHeight()-e.height()};this.originalMousePosition={left:g.pageX,top:g.pageY};this.aspectRatio=(typeof j.aspectRatio=="number")?j.aspectRatio:((this.originalSize.width/this.originalSize.height)||1);var i=c(".ui-resizable-"+this.axis).css("cursor");c("body").css("cursor",i=="auto"?this.axis+"-resize":i);e.addClass("ui-resizable-resizing");this._propagate("start",g);return true},_mouseDrag:function(e){var h=this.helper,g=this.options,m={},q=this,j=this.originalMousePosition,n=this.axis;var r=(e.pageX-j.left)||0,p=(e.pageY-j.top)||0;var i=this._change[n];if(!i){return false}var l=i.apply(this,[e,r,p]),k=c.browser.msie&&c.browser.version<7,f=this.sizeDiff;this._updateVirtualBoundaries(e.shiftKey);if(this._aspectRatio||e.shiftKey){l=this._updateRatio(l,e)}l=this._respectSize(l,e);this._propagate("resize",e);h.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"});if(!this._helper&&this._proportionallyResizeElements.length){this._proportionallyResize()}this._updateCache(l);this._trigger("resize",e,this.ui());return false},_mouseStop:function(h){this.resizing=false;var i=this.options,m=this;if(this._helper){var g=this._proportionallyResizeElements,e=g.length&&(/textarea/i).test(g[0].nodeName),f=e&&c.ui.hasScroll(g[0],"left")?0:m.sizeDiff.height,k=e?0:m.sizeDiff.width;var n={width:(m.helper.width()-k),height:(m.helper.height()-f)},j=(parseInt(m.element.css("left"),10)+(m.position.left-m.originalPosition.left))||null,l=(parseInt(m.element.css("top"),10)+(m.position.top-m.originalPosition.top))||null;if(!i.animate){this.element.css(c.extend(n,{top:l,left:j}))}m.helper.height(m.size.height);m.helper.width(m.size.width);if(this._helper&&!i.animate){this._proportionallyResize()}}c("body").css("cursor","auto");this.element.removeClass("ui-resizable-resizing");this._propagate("stop",h);if(this._helper){this.helper.remove()}return false},_updateVirtualBoundaries:function(g){var j=this.options,i,h,f,k,e;e={minWidth:a(j.minWidth)?j.minWidth:0,maxWidth:a(j.maxWidth)?j.maxWidth:Infinity,minHeight:a(j.minHeight)?j.minHeight:0,maxHeight:a(j.maxHeight)?j.maxHeight:Infinity};if(this._aspectRatio||g){i=e.minHeight*this.aspectRatio;f=e.minWidth/this.aspectRatio;h=e.maxHeight*this.aspectRatio;k=e.maxWidth/this.aspectRatio;if(i>e.minWidth){e.minWidth=i}if(f>e.minHeight){e.minHeight=f}if(hl.width),s=a(l.height)&&i.minHeight&&(i.minHeight>l.height);if(h){l.width=i.minWidth}if(s){l.height=i.minHeight}if(t){l.width=i.maxWidth}if(m){l.height=i.maxHeight}var f=this.originalPosition.left+this.originalSize.width,p=this.position.top+this.size.height;var k=/sw|nw|w/.test(q),e=/nw|ne|n/.test(q);if(h&&k){l.left=f-i.minWidth}if(t&&k){l.left=f-i.maxWidth}if(s&&e){l.top=p-i.minHeight}if(m&&e){l.top=p-i.maxHeight}var n=!l.width&&!l.height;if(n&&!l.left&&l.top){l.top=null}else{if(n&&!l.top&&l.left){l.left=null}}return l},_proportionallyResize:function(){var k=this.options;if(!this._proportionallyResizeElements.length){return}var g=this.helper||this.element;for(var f=0;f');var e=c.browser.msie&&c.browser.version<7,g=(e?1:0),h=(e?2:-1);this.helper.addClass(this._helper).css({width:this.element.outerWidth()+h,height:this.element.outerHeight()+h,position:"absolute",left:this.elementOffset.left-g+"px",top:this.elementOffset.top-g+"px",zIndex:++i.zIndex});this.helper.appendTo("body").disableSelection()}else{this.helper=this.element}},_change:{e:function(g,f,e){return{width:this.originalSize.width+f}},w:function(h,f,e){var j=this.options,g=this.originalSize,i=this.originalPosition;return{left:i.left+f,width:g.width-f}},n:function(h,f,e){var j=this.options,g=this.originalSize,i=this.originalPosition;return{top:i.top+e,height:g.height-e}},s:function(g,f,e){return{height:this.originalSize.height+e}},se:function(g,f,e){return c.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[g,f,e]))},sw:function(g,f,e){return c.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[g,f,e]))},ne:function(g,f,e){return c.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[g,f,e]))},nw:function(g,f,e){return c.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[g,f,e]))}},_propagate:function(f,e){c.ui.plugin.call(this,f,[e,this.ui()]);(f!="resize"&&this._trigger(f,e,this.ui()))},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}});c.extend(c.ui.resizable,{version:"1.8.18"});c.ui.plugin.add("resizable","alsoResize",{start:function(f,g){var e=c(this).data("resizable"),i=e.options;var h=function(j){c(j).each(function(){var k=c(this);k.data("resizable-alsoresize",{width:parseInt(k.width(),10),height:parseInt(k.height(),10),left:parseInt(k.css("left"),10),top:parseInt(k.css("top"),10)})})};if(typeof(i.alsoResize)=="object"&&!i.alsoResize.parentNode){if(i.alsoResize.length){i.alsoResize=i.alsoResize[0];h(i.alsoResize)}else{c.each(i.alsoResize,function(j){h(j)})}}else{h(i.alsoResize)}},resize:function(g,i){var f=c(this).data("resizable"),j=f.options,h=f.originalSize,l=f.originalPosition;var k={height:(f.size.height-h.height)||0,width:(f.size.width-h.width)||0,top:(f.position.top-l.top)||0,left:(f.position.left-l.left)||0},e=function(m,n){c(m).each(function(){var q=c(this),r=c(this).data("resizable-alsoresize"),p={},o=n&&n.length?n:q.parents(i.originalElement[0]).length?["width","height"]:["width","height","top","left"];c.each(o,function(s,u){var t=(r[u]||0)+(k[u]||0);if(t&&t>=0){p[u]=t||null}});q.css(p)})};if(typeof(j.alsoResize)=="object"&&!j.alsoResize.nodeType){c.each(j.alsoResize,function(m,n){e(m,n)})}else{e(j.alsoResize)}},stop:function(e,f){c(this).removeData("resizable-alsoresize")}});c.ui.plugin.add("resizable","animate",{stop:function(i,n){var p=c(this).data("resizable"),j=p.options;var h=p._proportionallyResizeElements,e=h.length&&(/textarea/i).test(h[0].nodeName),f=e&&c.ui.hasScroll(h[0],"left")?0:p.sizeDiff.height,l=e?0:p.sizeDiff.width;var g={width:(p.size.width-l),height:(p.size.height-f)},k=(parseInt(p.element.css("left"),10)+(p.position.left-p.originalPosition.left))||null,m=(parseInt(p.element.css("top"),10)+(p.position.top-p.originalPosition.top))||null;p.element.animate(c.extend(g,m&&k?{top:m,left:k}:{}),{duration:j.animateDuration,easing:j.animateEasing,step:function(){var o={width:parseInt(p.element.css("width"),10),height:parseInt(p.element.css("height"),10),top:parseInt(p.element.css("top"),10),left:parseInt(p.element.css("left"),10)};if(h&&h.length){c(h[0]).css({width:o.width,height:o.height})}p._updateCache(o);p._propagate("resize",i)}})}});c.ui.plugin.add("resizable","containment",{start:function(f,r){var t=c(this).data("resizable"),j=t.options,l=t.element;var g=j.containment,k=(g instanceof c)?g.get(0):(/parent/.test(g))?l.parent().get(0):g;if(!k){return}t.containerElement=c(k);if(/document/.test(g)||g==document){t.containerOffset={left:0,top:0};t.containerPosition={left:0,top:0};t.parentData={element:c(document),left:0,top:0,width:c(document).width(),height:c(document).height()||document.body.parentNode.scrollHeight}}else{var n=c(k),i=[];c(["Top","Right","Left","Bottom"]).each(function(p,o){i[p]=b(n.css("padding"+o))});t.containerOffset=n.offset();t.containerPosition=n.position();t.containerSize={height:(n.innerHeight()-i[3]),width:(n.innerWidth()-i[1])};var q=t.containerOffset,e=t.containerSize.height,m=t.containerSize.width,h=(c.ui.hasScroll(k,"left")?k.scrollWidth:m),s=(c.ui.hasScroll(k)?k.scrollHeight:e);t.parentData={element:k,left:q.left,top:q.top,width:h,height:s}}},resize:function(g,q){var t=c(this).data("resizable"),i=t.options,f=t.containerSize,p=t.containerOffset,m=t.size,n=t.position,r=t._aspectRatio||g.shiftKey,e={top:0,left:0},h=t.containerElement;if(h[0]!=document&&(/static/).test(h.css("position"))){e=p}if(n.left<(t._helper?p.left:0)){t.size.width=t.size.width+(t._helper?(t.position.left-p.left):(t.position.left-e.left));if(r){t.size.height=t.size.width/i.aspectRatio}t.position.left=i.helper?p.left:0}if(n.top<(t._helper?p.top:0)){t.size.height=t.size.height+(t._helper?(t.position.top-p.top):t.position.top);if(r){t.size.width=t.size.height*i.aspectRatio}t.position.top=t._helper?p.top:0}t.offset.left=t.parentData.left+t.position.left;t.offset.top=t.parentData.top+t.position.top;var l=Math.abs((t._helper?t.offset.left-e.left:(t.offset.left-e.left))+t.sizeDiff.width),s=Math.abs((t._helper?t.offset.top-e.top:(t.offset.top-p.top))+t.sizeDiff.height);var k=t.containerElement.get(0)==t.element.parent().get(0),j=/relative|absolute/.test(t.containerElement.css("position"));if(k&&j){l-=t.parentData.left}if(l+t.size.width>=t.parentData.width){t.size.width=t.parentData.width-l;if(r){t.size.height=t.size.width/t.aspectRatio}}if(s+t.size.height>=t.parentData.height){t.size.height=t.parentData.height-s;if(r){t.size.width=t.size.height*t.aspectRatio}}},stop:function(f,n){var q=c(this).data("resizable"),g=q.options,l=q.position,m=q.containerOffset,e=q.containerPosition,i=q.containerElement;var j=c(q.helper),r=j.offset(),p=j.outerWidth()-q.sizeDiff.width,k=j.outerHeight()-q.sizeDiff.height;if(q._helper&&!g.animate&&(/relative/).test(i.css("position"))){c(this).css({left:r.left-e.left-m.left,width:p,height:k})}if(q._helper&&!g.animate&&(/static/).test(i.css("position"))){c(this).css({left:r.left-e.left-m.left,width:p,height:k})}}});c.ui.plugin.add("resizable","ghost",{start:function(g,h){var e=c(this).data("resizable"),i=e.options,f=e.size;e.ghost=e.originalElement.clone();e.ghost.css({opacity:0.25,display:"block",position:"relative",height:f.height,width:f.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof i.ghost=="string"?i.ghost:"");e.ghost.appendTo(e.helper)},resize:function(f,g){var e=c(this).data("resizable"),h=e.options;if(e.ghost){e.ghost.css({position:"relative",height:e.size.height,width:e.size.width})}},stop:function(f,g){var e=c(this).data("resizable"),h=e.options;if(e.ghost&&e.helper){e.helper.get(0).removeChild(e.ghost.get(0))}}});c.ui.plugin.add("resizable","grid",{resize:function(e,m){var p=c(this).data("resizable"),h=p.options,k=p.size,i=p.originalSize,j=p.originalPosition,n=p.axis,l=h._aspectRatio||e.shiftKey;h.grid=typeof h.grid=="number"?[h.grid,h.grid]:h.grid;var g=Math.round((k.width-i.width)/(h.grid[0]||1))*(h.grid[0]||1),f=Math.round((k.height-i.height)/(h.grid[1]||1))*(h.grid[1]||1);if(/^(se|s|e)$/.test(n)){p.size.width=i.width+g;p.size.height=i.height+f}else{if(/^(ne)$/.test(n)){p.size.width=i.width+g;p.size.height=i.height+f;p.position.top=j.top-f}else{if(/^(sw)$/.test(n)){p.size.width=i.width+g;p.size.height=i.height+f;p.position.left=j.left-g}else{p.size.width=i.width+g;p.size.height=i.height+f;p.position.top=j.top-f;p.position.left=j.left-g}}}}});var b=function(e){return parseInt(e,10)||0};var a=function(e){return !isNaN(parseInt(e,10))}})(jQuery);/*! * jQuery hashchange event - v1.3 - 7/21/2010 * http://benalman.com/projects/jquery-hashchange-plugin/ * * Copyright (c) 2010 "Cowboy" Ben Alman * Dual licensed under the MIT and GPL licenses. * http://benalman.com/about/license/ */ (function($,e,b){var c="hashchange",h=document,f,g=$.event.special,i=h.documentMode,d="on"+c in e&&(i===b||i>7);function a(j){j=j||location.href;return"#"+j.replace(/^[^#]*#?(.*)$/,"$1")}$.fn[c]=function(j){return j?this.bind(c,j):this.trigger(c)};$.fn[c].delay=50;g[c]=$.extend(g[c],{setup:function(){if(d){return false}$(f.start)},teardown:function(){if(d){return false}$(f.stop)}});f=(function(){var j={},p,m=a(),k=function(q){return q},l=k,o=k;j.start=function(){p||n()};j.stop=function(){p&&clearTimeout(p);p=b};function n(){var r=a(),q=o(m);if(r!==m){l(m=r,q);$(e).trigger(c)}else{if(q!==m){location.href=location.href.replace(/#.*/,"")+q}}p=setTimeout(n,$.fn[c].delay)}$.browser.msie&&!d&&(function(){var q,r;j.start=function(){if(!q){r=$.fn[c].src;r=r&&r+a();q=$('