kopia lustrzana https://github.com/jamescoxon/dl-fldigi
1208 wiersze
29 KiB
C++
1208 wiersze
29 KiB
C++
// ----------------------------------------------------------------------------
|
|
// lookupcall.cxx -- a part of fldigi
|
|
//
|
|
// Copyright (C) 2006-2009
|
|
// Dave Freese, W1HKJ
|
|
// Copyright (C) 2006-2007
|
|
// Leigh Klotz, WA5ZNU
|
|
// Copyright (C) 2008-2009
|
|
// Stelios Bounanos, M0GLD
|
|
//
|
|
// This file is part of fldigi.
|
|
//
|
|
// Fldigi is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Fldigi is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with fldigi. If not, see <http://www.gnu.org/licenses/>.
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#include <config.h>
|
|
|
|
#ifdef __MINGW32__
|
|
# include "compat.h"
|
|
#endif
|
|
|
|
#include <sys/time.h>
|
|
#include "signal.h"
|
|
#include <string>
|
|
#include <iostream>
|
|
#include <cstring>
|
|
#include <cmath>
|
|
#include <cctype>
|
|
|
|
#include "threads.h"
|
|
|
|
#include "misc.h"
|
|
#include "configuration.h"
|
|
|
|
#include "lookupcall.h"
|
|
#include "logsupport.h"
|
|
#include "main.h"
|
|
#include "confdialog.h"
|
|
#include "fl_digi.h"
|
|
#include "qrzlib.h"
|
|
#include "trx.h"
|
|
|
|
#include "xmlreader.h"
|
|
|
|
#include "qrunner.h"
|
|
#include "debug.h"
|
|
#include "network.h"
|
|
#include "locator.h"
|
|
|
|
using namespace std;
|
|
|
|
|
|
string qrzhost = "xml.qrz.com"; //"online.qrz.com";
|
|
string qrzSessionKey;
|
|
string qrzalert;
|
|
string qrzerror;
|
|
|
|
string callsign;
|
|
|
|
string lookup_name;
|
|
string lookup_addr1;
|
|
string lookup_addr2;
|
|
string lookup_state;
|
|
string lookup_province;
|
|
string lookup_zip;
|
|
string lookup_country;
|
|
string lookup_born;
|
|
string lookup_fname;
|
|
string lookup_qth;
|
|
string lookup_grid;
|
|
string lookup_latd;
|
|
string lookup_lond;
|
|
string lookup_notes;
|
|
|
|
qrz_query_t DB_query = QRZNONE;
|
|
|
|
enum TAG {
|
|
QRZ_IGNORE, QRZ_KEY, QRZ_ALERT, QRZ_ERROR, QRZ_CALL,
|
|
QRZ_FNAME, QRZ_NAME, QRZ_ADDR1, QRZ_ADDR2, QRZ_STATE,
|
|
QRZ_ZIP, QRZ_COUNTRY, QRZ_LATD, QRZ_LOND, QRZ_GRID,
|
|
QRZ_DOB
|
|
};
|
|
|
|
pthread_t* QRZ_thread = 0;
|
|
pthread_mutex_t qrz_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
pthread_cond_t qrz_cond = PTHREAD_COND_INITIALIZER;
|
|
|
|
static void *LOOKUP_loop(void *args);
|
|
|
|
bool parseSessionKey();
|
|
bool parse_xml();
|
|
|
|
bool getSessionKey(string& sessionpage);
|
|
bool QRZGetXML(string& xmlpage);
|
|
int bearing(const char *, const char *);
|
|
void qra(const char *, double &, double &);
|
|
void QRZ_disp_result();
|
|
void QRZ_CD_query();
|
|
void Lookup_init(void);
|
|
void QRZclose(void);
|
|
void qthappend(string &qth, string &datum);
|
|
void QRZAlert();
|
|
bool QRZLogin(string& sessionpage);
|
|
void QRZquery();
|
|
void parse_html(const string& htmlpage);
|
|
bool HAMCALLget(string& htmlpage);
|
|
void HAMCALLquery();
|
|
|
|
void QRZ_DETAILS_query();
|
|
|
|
QRZ *qCall = 0;
|
|
|
|
void clear_Lookup()
|
|
{
|
|
lookup_name.clear();
|
|
lookup_addr1.clear();
|
|
lookup_addr2.clear();
|
|
lookup_state.clear();
|
|
lookup_province.clear();
|
|
lookup_zip.clear();
|
|
lookup_born.clear();
|
|
lookup_fname.clear();
|
|
lookup_qth.clear();
|
|
lookup_grid.clear();
|
|
lookup_latd.clear();
|
|
lookup_lond.clear();
|
|
lookup_notes.clear();
|
|
lookup_country.clear();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// QRZ subscription query
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool parseSessionKey(const string& sessionpage)
|
|
{
|
|
IrrXMLReader* xml = createIrrXMLReader(new IIrrXMLStringReader(sessionpage));
|
|
TAG tag=QRZ_IGNORE;
|
|
while(xml && xml->read()) {
|
|
switch(xml->getNodeType())
|
|
{
|
|
case EXN_TEXT:
|
|
case EXN_CDATA:
|
|
switch (tag)
|
|
{
|
|
default:
|
|
break;
|
|
case QRZ_KEY:
|
|
qrzSessionKey = xml->getNodeData();
|
|
break;
|
|
case QRZ_ALERT:
|
|
qrzalert = xml->getNodeData();
|
|
break;
|
|
case QRZ_ERROR:
|
|
qrzerror = xml->getNodeData();
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case EXN_ELEMENT_END:
|
|
tag=QRZ_IGNORE;
|
|
break;
|
|
|
|
case EXN_ELEMENT:
|
|
{
|
|
const char *nodeName = xml->getNodeName();
|
|
if (!strcmp("Key", nodeName)) tag=QRZ_KEY;
|
|
else if (!strcmp("Alert", nodeName)) tag=QRZ_ALERT;
|
|
else if (!strcmp("Error", nodeName)) tag=QRZ_ERROR;
|
|
else tag=QRZ_IGNORE;
|
|
break;
|
|
}
|
|
|
|
case EXN_NONE:
|
|
case EXN_COMMENT:
|
|
case EXN_UNKNOWN:
|
|
break;
|
|
}
|
|
}
|
|
delete xml;
|
|
return true;
|
|
}
|
|
|
|
|
|
bool parse_xml(const string& xmlpage)
|
|
{
|
|
//printf("%s\n", xmlpage.c_str());
|
|
IrrXMLReader* xml = createIrrXMLReader(new IIrrXMLStringReader(xmlpage));
|
|
|
|
// If we got any result back, clear the session key so that it will be
|
|
// refreshed by this response, or if not present, will be removed and we'll
|
|
// know to log in next time.
|
|
if (xml) {
|
|
qrzSessionKey.clear();
|
|
qrzalert.clear();
|
|
qrzerror.clear();
|
|
clear_Lookup();
|
|
}
|
|
|
|
TAG tag = QRZ_IGNORE;
|
|
|
|
// parse the file until end reached
|
|
while(xml && xml->read()) {
|
|
switch(xml->getNodeType()) {
|
|
case EXN_TEXT:
|
|
case EXN_CDATA:
|
|
switch (tag) {
|
|
default:
|
|
case QRZ_IGNORE:
|
|
break;
|
|
case QRZ_CALL:
|
|
break;
|
|
case QRZ_FNAME:
|
|
lookup_fname = xml->getNodeData();
|
|
break;
|
|
case QRZ_NAME:
|
|
lookup_name = xml->getNodeData();
|
|
break;
|
|
case QRZ_ADDR1:
|
|
{
|
|
lookup_addr1 = xml->getNodeData();
|
|
size_t apt = lookup_addr1.find("#");
|
|
if (apt != string::npos)
|
|
lookup_addr1.erase(apt, lookup_addr1.length() - apt);
|
|
break;
|
|
}
|
|
case QRZ_ADDR2:
|
|
lookup_addr2 = xml->getNodeData();
|
|
break;
|
|
case QRZ_STATE:
|
|
lookup_state = xml->getNodeData();
|
|
break;
|
|
case QRZ_ZIP:
|
|
lookup_zip = xml->getNodeData();
|
|
break;
|
|
case QRZ_COUNTRY:
|
|
lookup_country = xml->getNodeData();
|
|
break;
|
|
case QRZ_LATD:
|
|
lookup_latd = xml->getNodeData();
|
|
break;
|
|
case QRZ_LOND:
|
|
lookup_lond = xml->getNodeData();
|
|
break;
|
|
case QRZ_GRID:
|
|
lookup_grid = xml->getNodeData();
|
|
break;
|
|
case QRZ_DOB:
|
|
lookup_notes = "DOB: ";
|
|
lookup_notes += xml->getNodeData();
|
|
break;
|
|
case QRZ_ALERT:
|
|
qrzalert = xml->getNodeData();
|
|
break;
|
|
case QRZ_ERROR:
|
|
qrzerror = xml->getNodeData();
|
|
break;
|
|
case QRZ_KEY:
|
|
qrzSessionKey = xml->getNodeData();
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case EXN_ELEMENT_END:
|
|
tag=QRZ_IGNORE;
|
|
break;
|
|
|
|
case EXN_ELEMENT:
|
|
{
|
|
const char *nodeName = xml->getNodeName();
|
|
if (!strcmp("call", nodeName)) tag = QRZ_CALL;
|
|
else if (!strcmp("fname", nodeName)) tag = QRZ_FNAME;
|
|
else if (!strcmp("name", nodeName)) tag = QRZ_NAME;
|
|
else if (!strcmp("addr1", nodeName)) tag = QRZ_ADDR1;
|
|
else if (!strcmp("addr2", nodeName)) tag = QRZ_ADDR2;
|
|
else if (!strcmp("state", nodeName)) tag = QRZ_STATE;
|
|
else if (!strcmp("zip", nodeName)) tag = QRZ_ZIP;
|
|
else if (!strcmp("country", nodeName)) tag = QRZ_COUNTRY;
|
|
else if (!strcmp("lat", nodeName)) tag = QRZ_LATD;
|
|
else if (!strcmp("lon", nodeName)) tag = QRZ_LOND;
|
|
else if (!strcmp("grid", nodeName)) tag = QRZ_GRID;
|
|
else if (!strcmp("dob", nodeName)) tag = QRZ_DOB;
|
|
else if (!strcmp("Alert", nodeName)) tag = QRZ_ALERT;
|
|
else if (!strcmp("Error", nodeName)) tag = QRZ_ERROR;
|
|
else if (!strcmp("Key", nodeName)) tag = QRZ_KEY;
|
|
else tag = QRZ_IGNORE;
|
|
}
|
|
break;
|
|
|
|
case EXN_NONE:
|
|
case EXN_COMMENT:
|
|
case EXN_UNKNOWN:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// delete the xml parser after usage
|
|
delete xml;
|
|
return true;
|
|
}
|
|
|
|
bool getSessionKey(string& sessionpage)
|
|
{
|
|
|
|
string detail;
|
|
detail = "GET /bin/xml?username=";
|
|
detail += progdefaults.QRZusername;
|
|
detail += ";password=";
|
|
detail += progdefaults.QRZuserpassword;
|
|
detail += ";version=";
|
|
detail += PACKAGE_NAME;
|
|
detail += "/";
|
|
detail += PACKAGE_VERSION;
|
|
detail += " HTTP/1.0\n";
|
|
detail += "Host: ";
|
|
detail += qrzhost;
|
|
detail += "\n";
|
|
detail += "Connection: close\n";
|
|
detail += "\n";
|
|
|
|
return request_reply(qrzhost, "http", detail, sessionpage, 5.0);
|
|
}
|
|
|
|
bool QRZGetXML(string& xmlpage)
|
|
{
|
|
string detail;
|
|
detail = "GET /bin/xml?s=";
|
|
detail += qrzSessionKey;
|
|
detail += ";callsign=";
|
|
detail += callsign;
|
|
detail += " HTTP/1.0\n";
|
|
detail += "Host: ";
|
|
detail += qrzhost;
|
|
detail += "\n";
|
|
detail += "Connection: close\n";
|
|
detail += "\n";
|
|
|
|
// return request_reply(qrzhost, "http", detail, xmlpage, 5.0);
|
|
bool res = request_reply(qrzhost, "http", detail, xmlpage, 5.0);
|
|
LOG_DEBUG("result = %d", res);
|
|
return res;
|
|
}
|
|
|
|
void camel_case(string &s)
|
|
{
|
|
bool first_letter = true;
|
|
for (size_t n = 0; n < s.length(); n++) {
|
|
if (s[n] == ' ') first_letter = true;
|
|
else if (first_letter) {
|
|
s[n] = toupper(s[n]);
|
|
first_letter = false;
|
|
} else s[n] = tolower(s[n]);
|
|
}
|
|
}
|
|
|
|
void QRZ_disp_result()
|
|
{
|
|
ENSURE_THREAD(FLMAIN_TID);
|
|
|
|
if (lookup_fname.length() > 0) {
|
|
camel_case(lookup_fname);
|
|
string::size_type spacePos = lookup_fname.find(" ");
|
|
// if fname is "ABC" then display "ABC"
|
|
// or if fname is "A BCD" then display "A BCD"
|
|
if (spacePos == string::npos || (spacePos == 1)) {
|
|
inpName->value(lookup_fname.c_str());
|
|
}
|
|
// if fname is "ABC Y" then display "ABC"
|
|
else if (spacePos > 2) {
|
|
string fname;
|
|
fname.assign(lookup_fname, 0, spacePos);
|
|
inpName->value(fname.c_str());
|
|
}
|
|
// fname must be "ABC DEF" so display "ABC DEF"
|
|
else {
|
|
inpName->value(lookup_fname.c_str());
|
|
}
|
|
} else if (lookup_name.length() > 0) {
|
|
// only name is set; don't know first/last, so just show all
|
|
inpName->value(lookup_name.c_str());
|
|
}
|
|
|
|
inpQth->value(lookup_qth.c_str());
|
|
|
|
inpState->value(lookup_state.c_str());
|
|
|
|
inpVEprov->value(lookup_province.c_str());
|
|
|
|
inpLoc->value(lookup_grid.c_str());
|
|
|
|
if (!lookup_country.empty())
|
|
inpCountry->value(lookup_country.c_str());
|
|
|
|
if (!progdefaults.myLocator.empty() && !lookup_grid.empty()) {
|
|
char buf[10];
|
|
buf[0] = '\0';
|
|
double distance, azimuth, lon[2], lat[2];
|
|
if (locator2longlat(&lon[0], &lat[0], progdefaults.myLocator.c_str()) == RIG_OK &&
|
|
locator2longlat(&lon[1], &lat[1], lookup_grid.c_str()) == RIG_OK &&
|
|
qrb(lon[0], lat[0], lon[1], lat[1], &distance, &azimuth) == RIG_OK)
|
|
snprintf(buf, sizeof(buf), "%03.0f", round(azimuth));
|
|
inpAZ->value(buf);
|
|
}
|
|
inpNotes->value(lookup_notes.c_str());
|
|
}
|
|
|
|
void QRZ_CD_query()
|
|
{
|
|
ENSURE_THREAD(QRZ_TID);
|
|
|
|
char srch[20];
|
|
size_t snip;
|
|
|
|
memset( srch, 0, sizeof(srch) );
|
|
strncpy( srch, callsign.c_str(), 6 );
|
|
for (size_t i = 0; i < strlen(srch); i ++ )
|
|
srch[i] = toupper(srch[i]);
|
|
|
|
if( qCall->FindRecord( srch ) == 1) {
|
|
lookup_fname = qCall->GetFname();
|
|
camel_case(lookup_fname);
|
|
snip = lookup_fname.find(' ');
|
|
if (snip != string::npos)
|
|
lookup_fname.erase(snip, lookup_fname.length() - snip);
|
|
lookup_qth = qCall->GetCity();
|
|
lookup_state = qCall->GetState();
|
|
lookup_grid.clear();
|
|
lookup_notes.clear();
|
|
} else {
|
|
lookup_fname.clear();
|
|
lookup_qth.clear();
|
|
lookup_grid.clear();
|
|
lookup_born.clear();
|
|
lookup_notes = "Not found in CD database";
|
|
}
|
|
REQ(QRZ_disp_result);
|
|
}
|
|
|
|
void Lookup_init(void)
|
|
{
|
|
ENSURE_THREAD(FLMAIN_TID);
|
|
|
|
if (QRZ_thread)
|
|
return;
|
|
QRZ_thread = new pthread_t;
|
|
if (pthread_create(QRZ_thread, NULL, LOOKUP_loop, NULL) != 0) {
|
|
LOG_PERROR("pthread_create");
|
|
return;
|
|
}
|
|
MilliSleep(10);
|
|
}
|
|
|
|
void QRZclose(void)
|
|
{
|
|
ENSURE_THREAD(FLMAIN_TID);
|
|
|
|
if (!QRZ_thread)
|
|
return;
|
|
|
|
CANCEL_THREAD(*QRZ_thread);
|
|
|
|
DB_query = QRZ_EXIT;
|
|
pthread_mutex_lock(&qrz_mutex);
|
|
pthread_cond_signal(&qrz_cond);
|
|
pthread_mutex_unlock(&qrz_mutex);
|
|
|
|
pthread_join(*QRZ_thread, NULL);
|
|
delete QRZ_thread;
|
|
QRZ_thread = 0;
|
|
}
|
|
|
|
void qthappend(string &qth, string &datum) {
|
|
if (datum.empty()) return;
|
|
if (!qth.empty()) qth += ", ";
|
|
qth += datum;
|
|
}
|
|
|
|
void QRZAlert()
|
|
{
|
|
ENSURE_THREAD(FLMAIN_TID);
|
|
|
|
string qrznote;
|
|
if (!qrzalert.empty()) {
|
|
qrznote.append("QRZ alert notice:\n");
|
|
qrznote.append(qrzalert);
|
|
qrznote.append("\n");
|
|
qrzalert.clear();
|
|
}
|
|
if (!qrzerror.empty()) {
|
|
qrznote.append("QRZ error notice:\n");
|
|
qrznote.append(qrzalert);
|
|
qrzerror.clear();
|
|
}
|
|
inpNotes->value(qrznote.c_str());
|
|
}
|
|
|
|
bool QRZLogin(string& sessionpage)
|
|
{
|
|
bool ok = true;
|
|
if (qrzSessionKey.empty()) {
|
|
ok = getSessionKey(sessionpage);
|
|
if (ok) ok = parseSessionKey(sessionpage);
|
|
}
|
|
if (!ok) {
|
|
LOG_DEBUG("failed");
|
|
REQ(QRZAlert);
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
void QRZquery()
|
|
{
|
|
ENSURE_THREAD(QRZ_TID);
|
|
|
|
bool ok = true;
|
|
|
|
string qrzpage;
|
|
|
|
if (qrzSessionKey.empty())
|
|
ok = QRZLogin(qrzpage);
|
|
if (ok)
|
|
ok = QRZGetXML(qrzpage);
|
|
if (!ok) { // change to negative for MS not getting on first try
|
|
if (qrzSessionKey.empty())
|
|
ok = QRZLogin(qrzpage);
|
|
if (ok)
|
|
ok = QRZGetXML(qrzpage);
|
|
}
|
|
if (ok) {
|
|
parse_xml(qrzpage);
|
|
if (!qrzalert.empty() || !qrzerror.empty())
|
|
REQ(QRZAlert);
|
|
else {
|
|
lookup_qth = lookup_addr2;
|
|
string isCAN = "vV";
|
|
if (isCAN.find(callsign[0]) != string::npos) { // Can callsign
|
|
size_t pos = lookup_qth.find(',');
|
|
if (pos != string::npos) {
|
|
lookup_province = lookup_qth.substr(pos);
|
|
lookup_qth = lookup_qth.substr(0, pos);
|
|
pos = lookup_province.find_first_not_of(", ");
|
|
if (pos != string::npos)
|
|
lookup_province = lookup_province.substr(pos);
|
|
pos = lookup_province.find(' ');
|
|
if (pos != string::npos)
|
|
lookup_province = lookup_province.substr(0,pos);
|
|
}
|
|
}
|
|
REQ(QRZ_disp_result);
|
|
}
|
|
}
|
|
else {
|
|
qrzerror = qrzpage;
|
|
REQ(QRZAlert);
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------
|
|
// HTTP:://callook.info queries
|
|
// ---------------------------------------------------------------------
|
|
|
|
string node_data(const string &xmlpage, const string nodename)
|
|
{
|
|
size_t pos1, pos2;
|
|
pos1 = xmlpage.find(string("<").append(nodename).append(">"));
|
|
if (pos1 == string::npos) return "";
|
|
pos2 = xmlpage.find(string("</").append(nodename).append(">"));
|
|
if (pos2 == string::npos) return "";
|
|
pos1 += (string("<").append(nodename).append(">")).length();
|
|
return xmlpage.substr(pos1, pos2 - pos1);
|
|
}
|
|
|
|
void parse_callook(string& xmlpage)
|
|
{
|
|
string nodestr;
|
|
nodestr = node_data(xmlpage, "current");
|
|
if (nodestr.empty()) {
|
|
lookup_notes = "no data from callook.info";
|
|
return;
|
|
}
|
|
xmlpage = xmlpage.substr(xmlpage.find("</trustee>"));
|
|
lookup_fname = node_data(xmlpage, "name");
|
|
camel_case(lookup_fname);
|
|
nodestr = node_data(xmlpage, "address");
|
|
if (!nodestr.empty()) {
|
|
lookup_addr1 = node_data(nodestr, "line1");
|
|
lookup_addr2 = node_data(nodestr, "line2");
|
|
}
|
|
nodestr = node_data(xmlpage, "location");
|
|
if (!nodestr.empty()) {
|
|
lookup_lond = node_data(nodestr, "longitude");
|
|
lookup_latd = node_data(nodestr, "latitude");
|
|
lookup_grid = node_data(nodestr, "gridsquare");
|
|
}
|
|
size_t p;
|
|
p = lookup_addr2.find(",");
|
|
if (p != string::npos) {
|
|
lookup_qth = lookup_addr2.substr(0, p);
|
|
lookup_addr2.erase(0, p+2);
|
|
p = lookup_addr2.find(" ");
|
|
if (p != string::npos)
|
|
lookup_state = lookup_addr2.substr(0, p);
|
|
}
|
|
}
|
|
|
|
bool CALLOOKGetXML(string& xmlpage)
|
|
{
|
|
string url = string("http://callook.info/").append(callsign).append("/xml");
|
|
bool res = fetch_http(url, xmlpage, 5.0);
|
|
LOG_DEBUG("result = %d", res);
|
|
return res;
|
|
}
|
|
|
|
void CALLOOKquery()
|
|
{
|
|
ENSURE_THREAD(QRZ_TID);
|
|
|
|
bool ok = true;
|
|
|
|
string CALLOOKpage;
|
|
|
|
clear_Lookup();
|
|
ok = CALLOOKGetXML(CALLOOKpage);
|
|
if (!ok) // change to negative for MS not getting on first try
|
|
ok = CALLOOKGetXML(CALLOOKpage);
|
|
if (ok)
|
|
parse_callook(CALLOOKpage);
|
|
REQ(QRZ_disp_result);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Hamcall specific functions
|
|
// ---------------------------------------------------------------------
|
|
|
|
#define HAMCALL_CALL 181
|
|
#define HAMCALL_FIRST 184
|
|
#define HAMCALL_CITY 191
|
|
#define HAMCALL_STATE 192
|
|
#define HAMCALL_GRID 202
|
|
#define HAMCALL_DOB 194
|
|
|
|
void parse_html(const string& htmlpage)
|
|
{
|
|
size_t p;
|
|
|
|
clear_Lookup();
|
|
|
|
if ((p = htmlpage.find(HAMCALL_FIRST)) != string::npos) {
|
|
p++;
|
|
while ((uchar)htmlpage[p] < 128 && p < htmlpage.length() )
|
|
lookup_fname += htmlpage[p++];
|
|
camel_case(lookup_fname);
|
|
}
|
|
if ((p = htmlpage.find(HAMCALL_CITY)) != string::npos) {
|
|
p++;
|
|
while ((uchar)htmlpage[p] < 128 && p < htmlpage.length())
|
|
lookup_qth += htmlpage[p++];
|
|
}
|
|
if ((p = htmlpage.find(HAMCALL_STATE)) != string::npos) {
|
|
p++;
|
|
while ((uchar)htmlpage[p] < 128 && p < htmlpage.length())
|
|
lookup_state += htmlpage[p++];
|
|
}
|
|
if ((p = htmlpage.find(HAMCALL_GRID)) != string::npos) {
|
|
p++;
|
|
while ((uchar)htmlpage[p] < 128 && p < htmlpage.length())
|
|
lookup_grid += htmlpage[p++];
|
|
}
|
|
if ((p = htmlpage.find(HAMCALL_DOB)) != string::npos) {
|
|
p++;
|
|
lookup_notes = "DOB: ";
|
|
while ((uchar)htmlpage[p] < 128 && p < htmlpage.length())
|
|
lookup_notes += htmlpage[p++];
|
|
}
|
|
}
|
|
|
|
bool HAMCALLget(string& htmlpage)
|
|
{
|
|
string url_detail;
|
|
url_detail = "GET /call?username=";
|
|
url_detail += progdefaults.QRZusername;
|
|
url_detail += "&password=";
|
|
url_detail += progdefaults.QRZuserpassword;
|
|
url_detail += "&rawlookup=1&callsign=";
|
|
url_detail += callsign;
|
|
url_detail += "&program=fldigi-";
|
|
url_detail += VERSION;
|
|
url_detail += "\r\n";
|
|
|
|
return request_reply("www.hamcall.net", "http", url_detail, htmlpage, 5.0);
|
|
}
|
|
|
|
void HAMCALLquery()
|
|
{
|
|
ENSURE_THREAD(QRZ_TID);
|
|
|
|
string htmlpage;
|
|
|
|
if (HAMCALLget(htmlpage))
|
|
parse_html(htmlpage);
|
|
else
|
|
lookup_notes = htmlpage;
|
|
REQ(QRZ_disp_result);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Hamcall specific functions
|
|
// ---------------------------------------------------------------------
|
|
|
|
static string HAMQTH_session_id = "";
|
|
static string HAMQTH_reply = "";
|
|
|
|
#define HAMQTH_DEBUG 1
|
|
#undef HAMQTH_DEBUG
|
|
|
|
bool HAMQTH_get_session_id()
|
|
{
|
|
string url = "";
|
|
string retstr = "";
|
|
size_t p1 = string::npos;
|
|
size_t p2 = string::npos;
|
|
|
|
url.append("http://www.hamqth.com/xml.php?u=").append(progdefaults.QRZusername);
|
|
url.append("&p=").append(progdefaults.QRZuserpassword);
|
|
|
|
HAMQTH_session_id.clear();
|
|
if (!fetch_http(url, retstr, 5.0)) {
|
|
return false;
|
|
}
|
|
if ((p1 = retstr.find("<error>")) != string::npos) {
|
|
p2 = retstr.find("</error>");
|
|
lookup_notes = retstr.substr(p1 + 7, p2 - p1 - 7);
|
|
return false;
|
|
}
|
|
if ((p1 = retstr.find("<session_id>")) == string::npos) {
|
|
lookup_notes = "HamQTH not available";
|
|
return false;
|
|
}
|
|
p2 = retstr.find("</session_id>");
|
|
HAMQTH_session_id = retstr.substr(p1 + 12, p2 - p1 - 12);
|
|
//#ifdef HAMQTH_DEBUG
|
|
// printf("session id = %s\n", HAMQTH_session_id.c_str());
|
|
//#endif
|
|
return true;
|
|
}
|
|
|
|
void parse_HAMQTH_html(const string& htmlpage)
|
|
{
|
|
size_t p = string::npos;
|
|
size_t p1 = string::npos;
|
|
|
|
clear_Lookup();
|
|
|
|
lookup_fname.clear();
|
|
lookup_qth.clear();
|
|
lookup_state.clear();
|
|
lookup_grid.clear();
|
|
lookup_notes.clear();
|
|
lookup_country.clear();
|
|
|
|
if ((p = htmlpage.find("<error>")) != string::npos) {
|
|
p += 7;
|
|
p1 = htmlpage.find("</error>");
|
|
if (p1 != string::npos)
|
|
lookup_notes.append(htmlpage.substr(p, p1 - p));
|
|
return;
|
|
}
|
|
if ((p = htmlpage.find("<nick>")) != string::npos) {
|
|
p += 6;
|
|
p1 = htmlpage.find("</nick>", p);
|
|
if (p1 != string::npos) {
|
|
lookup_fname = htmlpage.substr(p, p1 - p);
|
|
camel_case(lookup_fname);
|
|
}
|
|
}
|
|
if ((p = htmlpage.find("<qth>")) != string::npos) {
|
|
p += 5;
|
|
p1 = htmlpage.find("</qth>", p);
|
|
if (p1 != string::npos)
|
|
lookup_qth = htmlpage.substr(p, p1 - p);
|
|
}
|
|
if ((p = htmlpage.find("<country>")) != string::npos) {
|
|
p += 9;
|
|
p1 = htmlpage.find("</country>", p);
|
|
if (p1 != string::npos)
|
|
lookup_country = htmlpage.substr(p, p1 - p);
|
|
}
|
|
if ((p = htmlpage.find("<us_state>")) != string::npos) {
|
|
p += 10;
|
|
p1 = htmlpage.find("</us_state>");
|
|
if (p1 != string::npos)
|
|
lookup_state = htmlpage.substr(p, p1 - p);
|
|
}
|
|
if ((p = htmlpage.find("<grid>")) != string::npos) {
|
|
p += 6;
|
|
p1 = htmlpage.find("</grid>");
|
|
if (p1 != string::npos)
|
|
lookup_grid = htmlpage.substr(p, p1 - p);
|
|
}
|
|
if ((p = htmlpage.find("<qsl_via>")) != string::npos) {
|
|
p += 9;
|
|
p1 = htmlpage.find("</qsl_via>");
|
|
if (p1 != string::npos)
|
|
lookup_notes.append("QSL via: ").append(htmlpage.substr(p, p1 - p)).append("\n");
|
|
}
|
|
if ((p = htmlpage.find("<adr_name>")) != string::npos) {
|
|
p += 10;
|
|
p1 = htmlpage.find("</adr_name>");
|
|
if (p1 != string::npos)
|
|
lookup_notes.append(htmlpage.substr(p, p1 - p)).append("\n");
|
|
}
|
|
if ((p = htmlpage.find("<adr_street1>")) != string::npos) {
|
|
p += 13;
|
|
p1 = htmlpage.find("</adr_street1>");
|
|
if (p1 != string::npos)
|
|
lookup_notes.append(htmlpage.substr(p, p1 - p)).append("\n");
|
|
}
|
|
if ((p = htmlpage.find("<adr_city>")) != string::npos) {
|
|
p += 10;
|
|
p1 = htmlpage.find("</adr_city>");
|
|
if (p1 != string::npos)
|
|
lookup_notes.append(htmlpage.substr(p, p1 - p)).append(", ").append(lookup_state);
|
|
}
|
|
if ((p = htmlpage.find("<adr_zip>")) != string::npos) {
|
|
p += 9;
|
|
p1 = htmlpage.find("</adr_zip>");
|
|
if (p1 != string::npos)
|
|
lookup_notes.append(" ").append(htmlpage.substr(p, p1 - p));
|
|
}
|
|
if ((p = htmlpage.find("<adr_country>")) != string::npos) {
|
|
p += 13;
|
|
p1 = htmlpage.find("</adr_country>");
|
|
if (p1 != string::npos)
|
|
lookup_notes.append(" ").append(htmlpage.substr(p, p1 - p));
|
|
}
|
|
}
|
|
|
|
bool HAMQTHget(string& htmlpage)
|
|
{
|
|
string url = "";
|
|
bool ret;
|
|
if (HAMQTH_session_id.empty()) {
|
|
if (!HAMQTH_get_session_id()) return false;
|
|
}
|
|
url.append("http://www.hamqth.com/xml.php?id=").append(HAMQTH_session_id);
|
|
url.append("&callsign=").append(callsign);
|
|
url.append("&prg=fldigi-").append(VERSION);
|
|
|
|
ret = fetch_http(url, htmlpage, 5.0);
|
|
if (htmlpage.find("<error>") != string::npos) {
|
|
htmlpage.clear();
|
|
if (!HAMQTH_get_session_id()) {
|
|
lookup_notes = "Get session id failed!\n";
|
|
return false;
|
|
}
|
|
ret = fetch_http(url, htmlpage, 5.0);
|
|
}
|
|
#ifdef HAMQTH_DEBUG
|
|
FILE *fetchit = fopen("fetchit.txt", "a");
|
|
fprintf(fetchit, "%s\n", htmlpage.c_str());
|
|
fclose(fetchit);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
void HAMQTHquery()
|
|
{
|
|
ENSURE_THREAD(QRZ_TID);
|
|
|
|
string htmlpage;
|
|
|
|
if (!HAMQTHget(htmlpage)) return;
|
|
|
|
parse_HAMQTH_html(htmlpage);
|
|
REQ(QRZ_disp_result);
|
|
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void QRZ_DETAILS_query()
|
|
{
|
|
string qrzurl = "http://www.qrz.com/db/";
|
|
qrzurl.append(callsign);
|
|
|
|
cb_mnuVisitURL(0, (void*)qrzurl.c_str());
|
|
}
|
|
|
|
void HAMCALL_DETAILS_query()
|
|
{
|
|
string hamcallurl = "http://www.hamcall.net/call?callsign=";
|
|
hamcallurl.append(callsign);
|
|
|
|
cb_mnuVisitURL(0, (void*)hamcallurl.c_str());
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
static void *LOOKUP_loop(void *args)
|
|
{
|
|
SET_THREAD_ID(QRZ_TID);
|
|
|
|
SET_THREAD_CANCEL();
|
|
|
|
for (;;) {
|
|
TEST_THREAD_CANCEL();
|
|
pthread_mutex_lock(&qrz_mutex);
|
|
pthread_cond_wait(&qrz_cond, &qrz_mutex);
|
|
pthread_mutex_unlock(&qrz_mutex);
|
|
|
|
switch (DB_query) {
|
|
case QRZCD :
|
|
QRZ_CD_query();
|
|
break;
|
|
case QRZNET :
|
|
QRZquery();
|
|
break;
|
|
case HAMCALLNET :
|
|
HAMCALLquery();
|
|
break;
|
|
case QRZHTML :
|
|
QRZ_DETAILS_query();
|
|
break;
|
|
case HAMCALLHTML :
|
|
HAMCALL_DETAILS_query();
|
|
break;
|
|
case CALLOOK:
|
|
CALLOOKquery();
|
|
break;
|
|
case HAMQTH:
|
|
HAMQTHquery();
|
|
break;
|
|
case QRZ_EXIT:
|
|
return NULL;
|
|
default:
|
|
LOG_ERROR("Bad query type %d", DB_query);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void CALLSIGNquery()
|
|
{
|
|
ENSURE_THREAD(FLMAIN_TID);
|
|
|
|
if (!QRZ_thread)
|
|
Lookup_init();
|
|
|
|
// Filter callsign for nonsense characters (remove all but [A-Za-z0-9/])
|
|
callsign.clear();
|
|
for (const char* p = inpCall->value(); *p; p++)
|
|
if (isalnum(*p) || *p == '/')
|
|
callsign += *p;
|
|
if (callsign.empty())
|
|
return;
|
|
if (callsign != inpCall->value())
|
|
inpCall->value(callsign.c_str());
|
|
|
|
switch (DB_query = static_cast<qrz_query_t>(progdefaults.QRZ)) {
|
|
case QRZNET:
|
|
inpNotes->value("Request sent to\nqrz.com...");
|
|
break;
|
|
case QRZHTML: case HAMCALLHTML:
|
|
break;
|
|
case HAMCALLNET:
|
|
inpNotes->value("Request sent to\nwww.hamcall.net...");
|
|
break;
|
|
case QRZCD:
|
|
if (!qCall)
|
|
qCall = new QRZ( "callbkc" );
|
|
if (progdefaults.QRZchanged) {
|
|
qCall->NewDBpath("callbkc");
|
|
progdefaults.QRZchanged = false;
|
|
}
|
|
if (!qCall->getQRZvalid()) {
|
|
inpNotes->value("QRZ DB error");
|
|
DB_query = QRZNONE;
|
|
return;
|
|
}
|
|
break;
|
|
case CALLOOK:
|
|
inpNotes->value("Request sent to\nhttp://callook.info...");
|
|
break;
|
|
case HAMQTH:
|
|
inpNotes->value("Request sent to \nhttp://hamqth.com...");
|
|
break;
|
|
default:
|
|
LOG_ERROR("Bad query type %d", DB_query);
|
|
return;
|
|
}
|
|
|
|
pthread_mutex_lock(&qrz_mutex);
|
|
pthread_cond_signal(&qrz_cond);
|
|
pthread_mutex_unlock(&qrz_mutex);
|
|
}
|
|
|
|
//======================================================================
|
|
// thread to support sending log entry to eQSL
|
|
//======================================================================
|
|
|
|
pthread_t* EQSLthread = 0;
|
|
pthread_mutex_t EQSLmutex = PTHREAD_MUTEX_INITIALIZER;
|
|
pthread_cond_t EQSLcond = PTHREAD_COND_INITIALIZER;
|
|
|
|
static void *EQSL_loop(void *args);
|
|
static void EQSL_init(void);
|
|
|
|
void EQSL_close(void);
|
|
void EQSL_send();
|
|
|
|
static std::string EQSL_url = "";
|
|
static std::string EQSL_xmlpage = "";
|
|
|
|
static bool EQSLEXIT = false;
|
|
|
|
static void *EQSL_loop(void *args)
|
|
{
|
|
SET_THREAD_ID(EQSL_TID);
|
|
|
|
SET_THREAD_CANCEL();
|
|
|
|
for (;;) {
|
|
TEST_THREAD_CANCEL();
|
|
pthread_mutex_lock(&EQSLmutex);
|
|
pthread_cond_wait(&EQSLcond, &EQSLmutex);
|
|
pthread_mutex_unlock(&EQSLmutex);
|
|
|
|
if (EQSLEXIT)
|
|
return NULL;
|
|
|
|
size_t p;
|
|
if (fetch_http(EQSL_url, EQSL_xmlpage, 5.0) == -1)
|
|
LOG_ERROR("%s", "eQSL not available");
|
|
|
|
else if ((p = EQSL_xmlpage.find("Error:")) != std::string::npos) {
|
|
size_t p2 = EQSL_xmlpage.find('\n', p);
|
|
LOG_ERROR("%s\n%s", EQSL_xmlpage.substr(p, p2 - p - 1).c_str(), EQSL_url.c_str());
|
|
} else
|
|
LOG_INFO("eQSL logged %s", EQSL_url.c_str());
|
|
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void EQSL_close(void)
|
|
{
|
|
ENSURE_THREAD(FLMAIN_TID);
|
|
|
|
if (!EQSLthread)
|
|
return;
|
|
|
|
CANCEL_THREAD(*EQSLthread);
|
|
|
|
pthread_mutex_lock(&qrz_mutex);
|
|
EQSLEXIT = true;
|
|
pthread_cond_signal(&qrz_cond);
|
|
pthread_mutex_unlock(&qrz_mutex);
|
|
|
|
pthread_join(*QRZ_thread, NULL);
|
|
delete QRZ_thread;
|
|
QRZ_thread = 0;
|
|
}
|
|
|
|
static void EQSL_init(void)
|
|
{
|
|
ENSURE_THREAD(FLMAIN_TID);
|
|
|
|
if (EQSLthread)
|
|
return;
|
|
EQSLthread = new pthread_t;
|
|
EQSLEXIT = false;
|
|
if (pthread_create(EQSLthread, NULL, EQSL_loop, NULL) != 0) {
|
|
LOG_PERROR("pthread_create");
|
|
return;
|
|
}
|
|
MilliSleep(10);
|
|
}
|
|
|
|
void sendEQSL(const char *url)
|
|
{
|
|
ENSURE_THREAD(FLMAIN_TID);
|
|
|
|
if (!EQSLthread)
|
|
EQSL_init();
|
|
|
|
pthread_mutex_lock(&EQSLmutex);
|
|
EQSL_url = url;
|
|
pthread_cond_signal(&EQSLcond);
|
|
pthread_mutex_unlock(&EQSLmutex);
|
|
}
|
|
|
|
// this function may be called from several places including macro
|
|
// expansion and execution
|
|
|
|
void makeEQSL(const char *message)
|
|
{
|
|
char sztemp[100];
|
|
std::string tempstr;
|
|
std::string eQSL_url;
|
|
std::string msg;
|
|
size_t p = 0;
|
|
|
|
msg = message;
|
|
|
|
if (msg.empty()) msg = progdefaults.eqsl_default_message;
|
|
|
|
// replace message tags {CALL}, {NAME}, {MODE}
|
|
while ((p = msg.find("{CALL}")) != std::string::npos)
|
|
msg.replace(p, 6, inpCall->value());
|
|
while ((p = msg.find("{NAME}")) != std::string::npos)
|
|
msg.replace(p, 6, inpName->value());
|
|
while ((p = msg.find("{MODE}")) != std::string::npos)
|
|
msg.replace(p, 6, mode_info[active_modem->get_mode()].adif_name);
|
|
|
|
|
|
// eqsl url header
|
|
eQSL_url = "http://www.eqsl.cc/qslcard/importADIF.cfm?ADIFdata=upload <adIF_ver:5>2.1.9";
|
|
snprintf(sztemp, sizeof(sztemp),"<EQSL_USER:%d>%s<EQSL_PSWD:%d>%s",
|
|
progdefaults.eqsl_id.length(), progdefaults.eqsl_id.c_str(),
|
|
progdefaults.eqsl_pwd.length(), progdefaults.eqsl_pwd.c_str());
|
|
eQSL_url.append(sztemp);
|
|
// eqsl nickname
|
|
if (!progdefaults.eqsl_nick.empty()) {
|
|
snprintf(sztemp, sizeof(sztemp), "<APP_EQSL_QTH_NICKNAME:%d>%s",
|
|
progdefaults.eqsl_nick.length(), progdefaults.eqsl_nick.c_str());
|
|
eQSL_url.append(sztemp);
|
|
}
|
|
eQSL_url.append("<PROGRAMID:6>FLDIGI<EOH>");
|
|
|
|
// eqsl record
|
|
// band
|
|
tempstr = band_name(band(wf->rfcarrier()));
|
|
snprintf(sztemp, sizeof(sztemp), "<BAND:%d>%s", tempstr.length(), tempstr.c_str());
|
|
eQSL_url.append(sztemp);
|
|
// call
|
|
tempstr = inpCall->value();
|
|
snprintf(sztemp, sizeof(sztemp), "<CALL:%d>%s", tempstr.length(), tempstr.c_str());
|
|
eQSL_url.append(sztemp);
|
|
// mode
|
|
tempstr = mode_info[active_modem->get_mode()].adif_name;
|
|
// test for modes not supported by eQSL
|
|
if ((tempstr.find("MFSK4") != std::string::npos) ||
|
|
(tempstr.find("MFSK11") != std::string::npos) ||
|
|
(tempstr.find("MFSK22") != std::string::npos) ||
|
|
(tempstr.find("MFSK31") != std::string::npos) ||
|
|
(tempstr.find("MFSK32") != std::string::npos) ||
|
|
(tempstr.find("MFSK64") != std::string::npos) )
|
|
tempstr = "MFSK16";
|
|
if ((tempstr.find("PSK250") != std::string::npos) ||
|
|
(tempstr.find("PSK500") != std::string::npos) ||
|
|
(tempstr.find("PSK125R") != std::string::npos) ||
|
|
(tempstr.find("PSK250R") != std::string::npos) ||
|
|
(tempstr.find("PSK500R") != std::string::npos))
|
|
tempstr = "PSK125";
|
|
if ((tempstr.find("QPSK250") != std::string::npos) ||
|
|
(tempstr.find("QPSK500") != std::string::npos) ||
|
|
(tempstr.find("QPSK125R") != std::string::npos) ||
|
|
(tempstr.find("QPSK250R") != std::string::npos) ||
|
|
(tempstr.find("QPSK500R") != std::string::npos))
|
|
tempstr = "QPSK125";
|
|
|
|
snprintf(sztemp, sizeof(sztemp), "<MODE:%d>%s", tempstr.length(), tempstr.c_str());
|
|
eQSL_url.append(sztemp);
|
|
// qso date
|
|
snprintf(sztemp, sizeof(sztemp), "<QSO_DATE:%d>%s", sDate_on.length(), sDate_on.c_str());
|
|
eQSL_url.append(sztemp);
|
|
// qso time
|
|
tempstr = inpTimeOn->value();
|
|
snprintf(sztemp, sizeof(sztemp), "<TIME_ON:%d>%s", tempstr.length(), tempstr.c_str());
|
|
eQSL_url.append(sztemp);
|
|
// rst sent
|
|
tempstr = inpRstOut->value();
|
|
snprintf(sztemp, sizeof(sztemp), "<RST_SENT:%d>%s", tempstr.length(), tempstr.c_str());
|
|
eQSL_url.append(sztemp);
|
|
// message
|
|
if (!msg.empty()) {
|
|
snprintf(sztemp, sizeof(sztemp), "<QSLMSG:%d>%s", msg.length(), msg.c_str());
|
|
eQSL_url.append(sztemp);
|
|
}
|
|
eQSL_url.append("<EOR>");
|
|
|
|
tempstr.clear();
|
|
for (size_t n = 0; n < eQSL_url.length(); n++) {
|
|
if (eQSL_url[n] == ' ') tempstr.append("%20");
|
|
else if (eQSL_url[n] == '<') tempstr.append("%3c");
|
|
else if (eQSL_url[n] == '>') tempstr.append("%3e");
|
|
else tempstr += eQSL_url[n];
|
|
}
|
|
|
|
sendEQSL(tempstr.c_str());
|
|
|
|
}
|
|
|