From d90facf293c31c7d8419d2170e25bc100fc7600d Mon Sep 17 00:00:00 2001 From: Corby Krick Date: Sun, 25 Aug 2013 13:45:09 -0500 Subject: [PATCH] Have the initial pull download working. More work to do on parsing the resulting .adi file and cleaning up the model. --- application/controllers/eqsl.php | 295 +++++++++++++++++++++++++++ application/models/logbook_model.php | 45 ++++ application/views/eqsl/import.php | 26 +++ application/views/layout/header.php | 2 + 4 files changed, 368 insertions(+) create mode 100644 application/controllers/eqsl.php create mode 100644 application/views/eqsl/import.php diff --git a/application/controllers/eqsl.php b/application/controllers/eqsl.php new file mode 100644 index 00000000..9fa768f7 --- /dev/null +++ b/application/controllers/eqsl.php @@ -0,0 +1,295 @@ +load->helper(array('form', 'url')); + + $this->load->model('user_model'); + if(!$this->user_model->authorize(2)) { $this->session->set_flashdata('notice', 'You\'re not allowed to do that!'); redirect('dashboard'); } + } + + private function loadFromFile($filepath) + { + // Figure out how we should be marking QSLs confirmed via eQSL + $query = $query = $this->db->query('SELECT eqsl_rcvd_mark FROM config'); + $q = $query->row(); + $config['eqsl_rcvd_mark'] = $q->eqsl_rcvd_mark; + + ini_set('memory_limit', '-1'); + set_time_limit(0); + + $this->load->library('adif_parser'); + + $this->adif_parser->load_from_file($filepath); + + $this->adif_parser->initialize(); + + $table = ""; + + while($record = $this->adif_parser->get_record()) + { + if(count($record) == 0) + { + break; + }; + + $time_on = date('Y-m-d', strtotime($record['qso_date'])) ." ".date('H:i', strtotime($record['time_on'])); + + $qsl_date = date('Y-m-d', strtotime($record['qslrdate'])) ." ".date('H:i', strtotime($record['qslrdate'])); + + if (isset($record['time_off'])) { + $time_off = date('Y-m-d', strtotime($record['qso_date'])) ." ".date('H:i', strtotime($record['time_off'])); + } else { + $time_off = date('Y-m-d', strtotime($record['qso_date'])) ." ".date('H:i', strtotime($record['time_on'])); + } + + // The report from eQSL should only contain entries that have been confirmed via eQSL + // If there's a match for the QSO from the report in our log, it's confirmed via eQSL. + + // If we have a positive match from LoTW, record it in the DB according to the user's preferences + if ($record['qsl_sent'] == "Y") + { + $record['qsl_sent'] = $config['lotw_rcvd_mark']; + } + + $status = $this->logbook_model->import_check($time_on, $record['call'], $record['band']); + ////////////////////////////////////////////// + $eqsl_status = $this->logbook_model->eqsl_update($time_on, $record['call'], $record['band'], $record['qsl_rcvd']); + + $table .= ""; + $table .= ""; + $table .= ""; + $table .= ""; + $table .= ""; + $table .= ""; + $table .= ""; + $table .= ""; + $table .= ""; + }; + + $table .= "
".$time_on."".$record['call']."".$record['mode']."".$record['qsl_rcvd']."".$qsl_date."QSO Record: ".$status."eQSL Record: ".$lotw_status."
"; + + unlink($filepath); + + $data['lotw_table'] = $table; + + $data['page_title'] = "eQSL ADIF Information"; + $this->load->view('layout/header', $data); + $this->load->view('eqsl/analysis'); + $this->load->view('layout/footer'); + } + + public function import() { + $data['page_title'] = "eQSL Import"; + + $config['upload_path'] = './uploads/'; + $config['allowed_types'] = 'adi|ADI'; + + $this->load->library('upload', $config); + + $this->load->model('logbook_model'); + + if ($this->input->post('eqslimport') == 'fetch') + { + $file = $config['upload_path'] . 'eqslreport_download.adi'; + + // Get credentials for LoTW + $query = $this->user_model->get_by_id($this->session->userdata('user_id')); + $q = $query->row(); + $data['user_eqsl_name'] = $q->user_eqsl_name; + $data['user_eqsl_password'] = $q->user_eqsl_password; + + // Get URL for downloading the eqsl.cc inbox + $query = $query = $this->db->query('SELECT eqsl_download_url FROM config'); + $q = $query->row(); + $eqsl_url = $q->eqsl_download_url; + + // Validate that LoTW credentials are not empty + if ($data['user_eqsl_name'] == '' || $data['user_eqsl_password'] == '') + { + $this->session->set_flashdata('warning', 'You have not defined your eQSL.cc credentials!'); redirect('eqsl/import'); + } + + // Query the logbook to determine when the last LoTW confirmation was + $eqsl_last_qsl_date = $this->logbook_model->eqsl_last_qsl_rcvd_date(); + + // Build URL for eQSL inbox file + $eqsl_url .= "?"; + $eqsl_url .= "UserName=" . $data['user_eqsl_name']; + $eqsl_url .= "&Password=" . $data['user_eqsl_password']; + + $eqsl_url .= "&RcvdSince=" . $eqsl_last_qsl_date; + + // Pull back only confirmations + $eqsl_url .= "&ConfirmedOnly=1"; + + // At this point, what we get isn't the ADI file we need, but rather + // an HTML page, which contains a link to the generated ADI file that we want. + // Adapted from Original PHP code by Chirp Internet: www.chirp.com.au + + $input = @file_get_contents($eqsl_url) or die("Could not access file: $eqsl_url"); + $regexp = "]*href=(\"??)([^\" >]*?)\\1[^>]*>(.*)<\/a>"; + if(preg_match_all("/$regexp/siU", $input, $matches)) { + foreach( $matches[2] as $match ) + { + // Look for the link that has the .adi file, and download it to $file + if (substr($match, -4, 4) == ".adi") + { + file_put_contents($file, file_get_contents("http://eqsl.cc/qslcard/" . $match)); + } + } + } + + + + ini_set('memory_limit', '-1'); + //$this->loadFromFile($file); + } + else + { + if ( ! $this->upload->do_upload()) + { + + $data['error'] = $this->upload->display_errors(); + + $this->load->view('layout/header', $data); + $this->load->view('eqsl/import'); + $this->load->view('layout/footer'); + } + else + { + $data = array('upload_data' => $this->upload->data()); + + $this->loadFromFile('./uploads/'.$data['upload_data']['file_name']); + } + } + } // end function + + public function export() { + $data['page_title'] = "eQSL Report Upload"; + + $config['upload_path'] = './uploads/'; + $config['allowed_types'] = 'tq8|TQ8'; + + $this->load->library('upload', $config); + + if ( ! $this->upload->do_upload()) + { + $data['error'] = $this->upload->display_errors(); + + $this->load->view('layout/header', $data); + $this->load->view('eqsl/export'); + $this->load->view('layout/footer'); + } + else + { + $data = array('upload_data' => $this->upload->data()); + + // Figure out how we should be marking QSLs confirmed via LoTW + $query = $query = $this->db->query('SELECT lotw_login_url FROM config'); + $q = $query->row(); + $config['lotw_login_url'] = $q->lotw_login_url; + + // Set some fields that we're going to need for ARRL login + $query = $this->user_model->get_by_id($this->session->userdata('user_id')); + $q = $query->row(); + $fields['login'] = $q->user_lotw_name; + $fields['password'] = $q->user_lotw_password; + $fields['acct_sel'] = ""; + + if ($fields['login'] == '' || $fields['password'] == '') + { + $this->session->set_flashdata('warning', 'You have not defined your ARRL LoTW credentials!'); redirect('lotw/status'); + } + + // Curl stuff goes here + + // First we need to get a cookie + + // options + $cookie_file_path = "./uploads/cookies.txt"; + $agent = "Mozilla/4.0 (compatible;)"; + + // begin script + $ch = curl_init(); + + // extra headers + $headers[] = "Accept: */*"; + $headers[] = "Connection: Keep-Alive"; + + // basic curl options for all requests + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + curl_setopt($ch, CURLOPT_HEADER, 0); + + // TODO: These SSL things should probably be set to true :) + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_USERAGENT, $agent); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); + curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file_path); + curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file_path); + + // Set login URL + curl_setopt($ch, CURLOPT_URL, $config['lotw_login_url']); + + // set postfields using what we extracted from the form + $POSTFIELDS = http_build_query($fields); + + // set post options + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $POSTFIELDS); + + // perform login + $result = curl_exec($ch); + if (stristr($result, "Username/password incorrect")) + { + $this->session->set_flashdata('warning', 'Your ARRL username and/or password is incorrect.'); redirect('lotw/status'); + } + + + // Now we need to use that cookie and upload the file + // change URL to upload destination URL + curl_setopt($ch, CURLOPT_URL, $config['lotw_login_url']); + + // Grab the file + $postfile = array( + "upfile"=>"@./uploads/".$data['upload_data']['file_name'], + ); + + //Upload it + curl_setopt($ch, CURLOPT_POSTFIELDS, $postfile); + $response = curl_exec($ch); + if (stristr($response, "accepted")) + { + $this->session->set_flashdata('lotw_status', 'accepted'); + $data['page_title'] = "eQSL Logs Sent"; + } + elseif (stristr($response, "rejected")) + { + $this->session->set_flashdata('lotw_status', 'rejected'); + $data['page_title'] = "LoTW .TQ8 Sent"; + } + else + { + // If we're here, we didn't find what we're looking for in the ARRL response + // and LoTW is probably down or broken. + $this->session->set_flashdata('warning', 'Did not receive proper response from LoTW. Try again later.'); + $data['page_title'] = "LoTW .TQ8 Not Sent"; + } + + // Now we need to clean up + unlink($cookie_file_path); + unlink('./uploads/'.$data['upload_data']['file_name']); + + $this->load->view('layout/header', $data); + $this->load->view('eqsl/status'); + $this->load->view('layout/footer'); + } + } + +} // end class \ No newline at end of file diff --git a/application/models/logbook_model.php b/application/models/logbook_model.php index 71610c04..43bc5be4 100644 --- a/application/models/logbook_model.php +++ b/application/models/logbook_model.php @@ -599,6 +599,51 @@ class Logbook_model extends CI_Model { return $row->COL_LOTW_QSLRDATE; } +////////////////////////////// + // Update a QSO with eQSL QSL info + // We could also probably use this use this: http://eqsl.cc/qslcard/VerifyQSO.txt + // http://www.eqsl.cc/qslcard/ImportADIF.txt + function eqsl_update($datetime, $callsign, $band, $qsl_status) { + $data = array( + 'COL_EQSL_QSLRDATE' => CURRENT_TIMESTAMP, // eQSL doesn't give us a date, so let's use current + 'COL_EQSL_QSL_RCVD' => $qsl_status, + ); + + $this->db->where('date_format(COL_TIME_ON, \'%Y-%m-%d %H:%i\') = "'.$datetime.'"'); + $this->db->where('COL_CALL', $callsign); + $this->db->where('COL_BAND', $band); + + $this->db->update($this->config->item('table_name'), $data); + + return "Updated"; + } + + // Get the last date we received an eQSL + function eqsl_last_qsl_rcvd_date() { + $this->db->select("DATE_FORMAT(COL_EQSL_QSLRDATE,'%Y%m%d') AS COL_EQSL_QSLRDATE", FALSE); + $this->db->where('COL_EQSL_QSLRDATE IS NOT NULL'); + $this->db->order_by("COL_EQSL_QSLRDATE", "desc"); + $this->db->limit(1); + + $query = $this->db->get($this->config->item('table_name')); + $row = $query->row(); + + return $row->COL_EQSL_QSLRDATE; + } + + // Determine if we've already received an eQSL for this QSO.. this needs writing. + function eqsl_dupe_check($datetime, $callsign, $band, $qsl_status) { + $this->db->select('COL_LOTW_QSLRDATE'); + $this->db->where('COL_LOTW_QSLRDATE IS NOT NULL'); + $this->db->order_by("COL_LOTW_QSLRDATE", "desc"); + $this->db->limit(1); + + $query = $this->db->get($this->config->item('table_name')); + $row = $query->row(); + + return $row->COL_LOTW_QSLRDATE; + } + function import($record) { // Join date+time //$datetime = date('Y-m-d') ." ". $this->input->post('start_time'); diff --git a/application/views/eqsl/import.php b/application/views/eqsl/import.php new file mode 100644 index 00000000..31c0ecfa --- /dev/null +++ b/application/views/eqsl/import.php @@ -0,0 +1,26 @@ +
+

+load->view('layout/messages'); ?> + + + + + + + + + + + +
Upload a file +

Upload the Exported ADIF file from eQSL from the Download Inbox page, to mark QSOs as confirmed on eQSL.

+

Important Log files must have the file type .adi

+ +
Pull eQSL data for me +

Cloudlog will use the eQSL username an password stored in your user profile to download confirmations from eQSL for you. We will only download confirmations received since your last eQSL confirmed QSO.

+
+ + + + +
diff --git a/application/views/layout/header.php b/application/views/layout/header.php index 6f19d877..9ac5df5a 100644 --- a/application/views/layout/header.php +++ b/application/views/layout/header.php @@ -87,9 +87,11 @@
  • Data Export
  • API
  • +
  • eQSL Import
  • LoTW Import
  • LoTW Export
  • +