2024-05-15 22:24:39 +01:00
< ? php if ( ! defined ( 'BASEPATH' )) exit ( 'No direct script access allowed' );
2013-08-25 13:45:09 -05:00
2024-05-15 22:24:39 +01:00
class eqsl extends CI_Controller
{
2013-08-25 13:45:09 -05:00
/* Controls who can access the controller and its functions */
2024-05-15 22:24:39 +01:00
function __construct ()
{
2013-08-25 13:45:09 -05:00
parent :: __construct ();
$this -> load -> helper ( array ( 'form' , 'url' ));
2026-07-18 22:53:51 +01:00
$this -> load -> library ( 'encryption' );
2013-08-25 13:45:09 -05:00
}
2020-09-15 22:49:05 +01:00
2024-05-15 22:24:39 +01:00
// Default view when loading controller.
public function index ()
{
2023-05-10 16:03:30 +02:00
2024-05-15 22:24:39 +01:00
$this -> lang -> load ( 'qslcard' );
2024-04-02 17:01:17 -05:00
$this -> load -> helper ( 'storage' );
2024-05-15 22:24:39 +01:00
$folder_name = " images/eqsl_card_images " ;
$data [ 'storage_used' ] = sizeFormat ( folderSize ( $folder_name ));
2023-05-10 16:03:30 +02:00
2023-07-04 18:49:00 +00:00
2024-05-15 22:24:39 +01:00
// Render Page
$data [ 'page_title' ] = " eQSL Cards " ;
2023-05-10 16:03:30 +02:00
2024-05-15 22:24:39 +01:00
$this -> load -> model ( 'eqsl_images' );
$data [ 'qslarray' ] = $this -> eqsl_images -> eqsl_qso_list ();
2023-05-10 16:03:30 +02:00
2024-05-15 22:24:39 +01:00
$this -> load -> view ( 'interface_assets/header' , $data );
$this -> load -> view ( 'eqslcard/index' );
$this -> load -> view ( 'interface_assets/footer' );
}
public function import ()
{
2023-05-05 16:50:17 +02:00
$this -> load -> model ( 'user_model' );
2024-05-15 22:24:39 +01:00
if ( ! $this -> user_model -> authorize ( 2 )) {
$this -> session -> set_flashdata ( 'notice' , 'You\'re not allowed to do that!' );
redirect ( 'dashboard' );
}
2023-05-05 16:50:17 +02:00
2023-07-04 18:49:00 +00:00
$this -> load -> model ( 'stations' );
2026-07-18 22:01:33 +01:00
$this -> load -> model ( 'eqsl_mappings_model' );
2023-07-04 18:49:00 +00:00
$data [ 'station_profile' ] = $this -> stations -> all_of_user ();
2024-05-15 22:24:39 +01:00
$active_station_id = $this -> stations -> find_active ();
$station_profile = $this -> stations -> profile ( $active_station_id );
2023-07-04 18:49:00 +00:00
$data [ 'active_station_info' ] = $station_profile -> row ();
2023-05-06 08:17:23 +02:00
$this -> load -> model ( 'eqslmethods_model' );
2026-07-18 22:01:33 +01:00
$user_id = ( int ) $this -> session -> userdata ( 'user_id' );
$active_mappings = $this -> eqsl_mappings_model -> get_active_mappings_for_user ( $user_id );
$use_mapping_mode = ! empty ( $active_mappings );
$data [ 'mapped_station_ids' ] = array_map ( function ( $mapping ) {
return ( int ) $mapping [ 'station_id' ];
}, $active_mappings );
$data [ 'eqsl_mapping_mode' ] = $use_mapping_mode ;
$data [ 'eqsl_legacy_fallback' ] = ! $use_mapping_mode ;
$data [ 'eqsl_mappings_count' ] = count ( $active_mappings );
$eqsl_locations = null ;
if ( ! $use_mapping_mode ) {
$eqsl_locations = $this -> eqslmethods_model -> all_of_user_with_eqsl_nick_defined ();
if ( $eqsl_locations -> num_rows () == 0 ) {
$this -> session -> set_flashdata ( 'error' , 'eQSL Nicknames in Station Profiles aren\'t defined!' );
}
2022-08-24 17:29:44 +02:00
}
2020-09-15 22:49:05 +01:00
2019-05-22 17:17:26 +01:00
ini_set ( 'memory_limit' , '-1' );
set_time_limit ( 0 );
2013-08-25 13:45:09 -05:00
$config [ 'upload_path' ] = './uploads/' ;
$config [ 'allowed_types' ] = 'adi|ADI' ;
2022-08-24 17:29:44 +02:00
2013-08-25 13:45:09 -05:00
$this -> load -> library ( 'upload' , $config );
2022-08-24 17:29:44 +02:00
$eqsl_results = array ();
2024-05-15 22:24:39 +01:00
if ( $this -> input -> post ( 'eqslimport' ) == 'fetch' ) {
2022-08-24 17:29:44 +02:00
$this -> load -> library ( 'EqslImporter' );
2026-07-18 22:01:33 +01:00
$eqsl_force_from_date = ( ! $this -> input -> post ( 'eqsl_force_from_date' ) == " " ) ? $this -> input -> post ( 'eqsl_force_from_date' ) : " " ;
if ( $use_mapping_mode ) {
foreach ( $active_mappings as $mapping ) {
$this -> eqslimporter -> from_callsign_and_QTH (
$mapping [ 'station_callsign' ],
$mapping [ 'eqsl_qth_nickname' ],
$config [ 'upload_path' ],
$mapping [ 'station_id' ]
);
$eqsl_results [] = $this -> eqslimporter -> fetch ( $mapping [ 'eqsl_password' ], $eqsl_force_from_date );
}
} else {
// Get credentials for eQSL
$query = $this -> user_model -> get_by_id ( $this -> session -> userdata ( 'user_id' ));
$q = $query -> row ();
2026-07-18 22:53:51 +01:00
$eqsl_password = $this -> decode_eqsl_password ( $q -> user_eqsl_password );
2026-07-18 22:01:33 +01:00
// Validate that eQSL credentials are not empty
if ( $eqsl_password == '' ) {
$this -> session -> set_flashdata ( 'warning' , 'You have not defined your eQSL.cc credentials!' );
redirect ( 'eqsl/import' );
}
2019-10-18 15:20:53 +01:00
2026-07-18 22:01:33 +01:00
foreach ( $eqsl_locations -> result_array () as $eqsl_location ) {
$this -> eqslimporter -> from_callsign_and_QTH (
$eqsl_location [ 'station_callsign' ],
$eqsl_location [ 'eqslqthnickname' ],
$config [ 'upload_path' ],
$eqsl_location [ 'station_id' ]
);
2019-10-18 15:51:29 +01:00
2026-07-18 22:01:33 +01:00
$eqsl_results [] = $this -> eqslimporter -> fetch ( $eqsl_password , $eqsl_force_from_date );
}
2013-11-03 08:45:30 -06:00
}
2023-07-04 18:49:00 +00:00
} elseif ( $this -> input -> post ( 'eqslimport' ) == 'upload' ) {
2024-05-15 22:24:39 +01:00
$station_id4upload = $this -> input -> post ( 'station_profile' );
2023-07-04 18:49:00 +00:00
if ( $this -> stations -> check_station_is_accessible ( $station_id4upload )) {
2024-05-15 22:24:39 +01:00
$station_callsign = $this -> stations -> profile ( $station_id4upload ) -> row () -> station_callsign ;
if ( ! $this -> upload -> do_upload ()) {
2023-07-04 18:49:00 +00:00
$data [ 'page_title' ] = " eQSL Import " ;
$data [ 'error' ] = $this -> upload -> display_errors ();
$this -> load -> view ( 'interface_assets/header' , $data );
$this -> load -> view ( 'eqsl/import' );
$this -> load -> view ( 'interface_assets/footer' );
return ;
} else {
$data = array ( 'upload_data' => $this -> upload -> data ());
$this -> load -> library ( 'EqslImporter' );
2024-05-15 22:24:39 +01:00
$this -> eqslimporter -> from_file ( './uploads/' . $data [ 'upload_data' ][ 'file_name' ], $station_callsign , $station_id4upload );
2023-07-04 18:49:00 +00:00
$eqsl_results [] = $this -> eqslimporter -> import ();
}
} else {
2024-05-15 22:24:39 +01:00
log_message ( 'error' , $station_id4upload . " is not valid for user! " );
2013-08-25 13:45:09 -05:00
}
2023-07-04 18:49:00 +00:00
} else {
$data [ 'page_title' ] = " eQSL Import " ;
2022-08-24 17:29:44 +02:00
2023-07-04 18:49:00 +00:00
$this -> load -> view ( 'interface_assets/header' , $data );
$this -> load -> view ( 'eqsl/import' );
$this -> load -> view ( 'interface_assets/footer' );
2022-08-24 17:29:44 +02:00
2023-07-04 18:49:00 +00:00
return ;
2013-08-25 13:45:09 -05:00
}
2022-08-24 17:29:44 +02:00
$data [ 'eqsl_results' ] = $eqsl_results ;
$data [ 'page_title' ] = " eQSL Import Information " ;
$this -> load -> view ( 'interface_assets/header' , $data );
$this -> load -> view ( 'eqsl/analysis' );
$this -> load -> view ( 'interface_assets/footer' );
2013-08-25 13:45:09 -05:00
} // end function
2022-08-24 17:29:44 +02:00
2024-05-15 22:24:39 +01:00
public function export ()
{
2023-05-05 16:50:17 +02:00
$this -> load -> model ( 'user_model' );
2024-05-15 22:24:39 +01:00
if ( ! $this -> user_model -> authorize ( 2 )) {
$this -> session -> set_flashdata ( 'notice' , 'You\'re not allowed to do that!' );
redirect ( 'dashboard' );
}
2023-05-05 16:50:17 +02:00
2024-05-15 22:24:39 +01:00
$this -> load -> model ( 'stations' );
2026-07-18 22:01:33 +01:00
$this -> load -> model ( 'eqsl_mappings_model' );
$this -> load -> model ( 'logbooks_model' );
2020-09-15 22:49:05 +01:00
2019-05-22 17:17:26 +01:00
ini_set ( 'memory_limit' , '-1' );
set_time_limit ( 0 );
2023-05-05 09:34:32 +02:00
$this -> load -> model ( 'eqslmethods_model' );
2024-05-15 22:24:39 +01:00
2026-07-18 22:01:33 +01:00
$user_id = ( int ) $this -> session -> userdata ( 'user_id' );
$active_mappings = $this -> eqsl_mappings_model -> get_active_mappings_for_user ( $user_id );
$use_mapping_mode = ! empty ( $active_mappings );
$data [ 'eqsl_mapping_mode' ] = $use_mapping_mode ;
$data [ 'eqsl_legacy_fallback' ] = ! $use_mapping_mode ;
$data [ 'eqsl_mappings_count' ] = count ( $active_mappings );
if ( ! $use_mapping_mode && $this -> stations -> are_eqsl_nicks_defined () == 0 ) {
$this -> session -> set_flashdata ( 'error' , 'eQSL Nicknames in Station Profiles aren\'t defined!' );
}
2013-09-08 15:30:24 -05:00
$data [ 'page_title' ] = " eQSL QSO Upload " ;
2022-11-30 14:57:24 +01:00
$custom_date_format = $this -> session -> userdata ( 'user_date_format' );
2026-07-18 22:01:33 +01:00
$active_logbook_station_ids = $this -> logbooks_model -> list_logbook_relationships ( $this -> session -> userdata ( 'active_station_logbook' ));
if ( ! is_array ( $active_logbook_station_ids )) {
$active_logbook_station_ids = array ();
}
2024-05-15 22:24:39 +01:00
2023-05-05 09:34:32 +02:00
if ( $this -> input -> post ( 'eqslexport' ) == " export " ) {
2023-05-08 07:59:51 +02:00
$rows = '' ;
2026-02-11 11:32:25 +00:00
$successful_uploads = array (); // Collect successful QSO primary keys for batch update
2026-07-18 22:01:33 +01:00
$processed_qso_ids = array ();
2026-02-11 11:32:25 +00:00
2026-07-18 22:01:33 +01:00
if ( $use_mapping_mode ) {
foreach ( $active_mappings as $mapping ) {
2026-07-18 22:29:58 +01:00
// Mapping mode intentionally processes all active mappings.
// This avoids missing QSOs when station-logbook relationships are incomplete.
2026-07-18 22:01:33 +01:00
$credentials = array (
'user_eqsl_name' => $mapping [ 'eqsl_username' ],
'user_eqsl_password' => $mapping [ 'eqsl_password' ],
);
$qslsnotsent = $this -> eqslmethods_model -> eqsl_not_yet_sent_for_station ( $mapping [ 'station_id' ], $mapping [ 'eqsl_qth_nickname' ]);
foreach ( $qslsnotsent -> result_array () as $qsl ) {
$qso_id = ( int ) $qsl [ 'COL_PRIMARY_KEY' ];
if ( isset ( $processed_qso_ids [ $qso_id ])) {
continue ;
}
$processed_qso_ids [ $qso_id ] = true ;
$qsl [ 'eqslqthnickname' ] = $mapping [ 'eqsl_qth_nickname' ];
$rows .= " <tr> " ;
$adif = $this -> generateAdif ( $qsl , $credentials );
$status = $this -> uploadQso ( $adif , $qsl , $successful_uploads );
$timestamp = strtotime ( $qsl [ 'COL_TIME_ON' ]);
$rows .= " <td> " . date ( $custom_date_format , $timestamp ) . " </td> " ;
$rows .= " <td> " . date ( 'H:i' , $timestamp ) . " </td> " ;
$rows .= " <td> " . str_replace ( " 0 " , " Ø " , $qsl [ 'COL_CALL' ]) . " </td> " ;
$rows .= " <td> " . $qsl [ 'COL_MODE' ] . " </td> " ;
if ( isset ( $qsl [ 'COL_SUBMODE' ])) {
$rows .= " <td> " . $qsl [ 'COL_SUBMODE' ] . " </td> " ;
} else {
$rows .= " <td></td> " ;
}
$rows .= " <td> " . $qsl [ 'COL_BAND' ] . " </td> " ;
$rows .= " <td> " . $status . " </td> " ;
$rows .= " </tr> " ;
}
}
} else {
// Get credentials for eQSL
$query = $this -> user_model -> get_by_id ( $this -> session -> userdata ( 'user_id' ));
$q = $query -> row ();
$credentials [ 'user_eqsl_name' ] = $q -> user_eqsl_name ;
2026-07-18 22:53:51 +01:00
$credentials [ 'user_eqsl_password' ] = $this -> decode_eqsl_password ( $q -> user_eqsl_password );
2026-07-18 22:01:33 +01:00
// Validate that eQSL credentials are not empty
if ( $credentials [ 'user_eqsl_name' ] == '' || $credentials [ 'user_eqsl_password' ] == '' ) {
$this -> session -> set_flashdata ( 'warning' , 'You have not defined your eQSL.cc credentials!' );
redirect ( 'eqsl/import' );
}
$qslsnotsent = $this -> eqslmethods_model -> eqsl_not_yet_sent ();
foreach ( $qslsnotsent -> result_array () as $qsl ) {
$rows .= " <tr> " ;
$adif = $this -> generateAdif ( $qsl , $credentials );
$status = $this -> uploadQso ( $adif , $qsl , $successful_uploads );
$timestamp = strtotime ( $qsl [ 'COL_TIME_ON' ]);
$rows .= " <td> " . date ( $custom_date_format , $timestamp ) . " </td> " ;
$rows .= " <td> " . date ( 'H:i' , $timestamp ) . " </td> " ;
$rows .= " <td> " . str_replace ( " 0 " , " Ø " , $qsl [ 'COL_CALL' ]) . " </td> " ;
$rows .= " <td> " . $qsl [ 'COL_MODE' ] . " </td> " ;
if ( isset ( $qsl [ 'COL_SUBMODE' ])) {
$rows .= " <td> " . $qsl [ 'COL_SUBMODE' ] . " </td> " ;
} else {
$rows .= " <td></td> " ;
}
$rows .= " <td> " . $qsl [ 'COL_BAND' ] . " </td> " ;
$rows .= " <td> " . $status . " </td> " ;
$rows .= " </tr> " ;
2023-05-05 11:38:14 +02:00
}
2023-05-05 09:34:32 +02:00
}
2026-02-11 11:32:25 +00:00
// Batch update all successful uploads at the end for better performance
if ( ! empty ( $successful_uploads )) {
$affected_rows = $this -> eqslmethods_model -> eqsl_mark_sent_batch ( $successful_uploads );
log_message ( 'info' , 'eQSL export: Marked ' . $affected_rows . ' QSOs as sent to eQSL' );
}
2026-07-18 22:01:33 +01:00
if ( $rows != '' ) {
$data [ 'eqsl_table' ] = $this -> generateResultTable ( $custom_date_format , $rows );
}
2023-05-05 09:34:32 +02:00
} else {
2026-07-18 22:01:33 +01:00
if ( $use_mapping_mode ) {
$processed_qso_ids = array ();
$pending_qsos = array ();
foreach ( $active_mappings as $mapping ) {
$qslsnotsent = $this -> eqslmethods_model -> eqsl_not_yet_sent_for_station ( $mapping [ 'station_id' ], $mapping [ 'eqsl_qth_nickname' ]);
foreach ( $qslsnotsent -> result_array () as $qsl ) {
$qso_id = ( int ) $qsl [ 'COL_PRIMARY_KEY' ];
if ( isset ( $processed_qso_ids [ $qso_id ])) {
continue ;
}
$processed_qso_ids [ $qso_id ] = true ;
$qsl [ 'eqslqthnickname' ] = $mapping [ 'eqsl_qth_nickname' ];
$pending_qsos [] = $qsl ;
}
}
if ( ! empty ( $pending_qsos )) {
$data [ 'eqsl_table' ] = $this -> writeEqslNotSent ( $pending_qsos , $custom_date_format );
}
} else {
$qslsnotsent = $this -> eqslmethods_model -> eqsl_not_yet_sent ();
if ( $qslsnotsent -> num_rows () > 0 ) {
$data [ 'eqsl_table' ] = $this -> writeEqslNotSent ( $qslsnotsent -> result_array (), $custom_date_format );
}
2023-05-05 09:34:32 +02:00
}
}
2024-05-15 22:24:39 +01:00
2023-05-05 09:34:32 +02:00
$this -> load -> view ( 'interface_assets/header' , $data );
$this -> load -> view ( 'eqsl/export' );
$this -> load -> view ( 'interface_assets/footer' );
}
2016-01-26 15:43:35 +00:00
2026-02-11 11:32:25 +00:00
function uploadQso ( $adif , $qsl , & $successful_uploads = null )
2024-05-15 22:24:39 +01:00
{
2023-05-05 13:12:41 +02:00
$this -> load -> model ( 'eqslmethods_model' );
2023-05-05 09:34:32 +02:00
$status = " " ;
2024-05-15 22:24:39 +01:00
2023-05-05 09:34:32 +02:00
// begin script
2023-05-14 20:25:03 +02:00
$ch = curl_init ();
2017-04-21 07:18:07 +01:00
2023-05-05 09:34:32 +02:00
// basic curl options for all requests
2023-05-14 20:25:03 +02:00
curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , 1 );
2023-05-05 09:34:32 +02:00
curl_setopt ( $ch , CURLOPT_HEADER , 1 );
2024-05-15 22:24:39 +01:00
2023-05-05 09:34:32 +02:00
// use the URL we built
curl_setopt ( $ch , CURLOPT_URL , $adif );
2024-05-15 22:24:39 +01:00
2023-05-14 20:25:03 +02:00
$result = curl_exec ( $ch );
2023-05-05 09:34:32 +02:00
$chi = curl_getinfo ( $ch );
curl_close ( $ch );
/* Time for some error handling
Things we might get back
Result : 0 out of 0 records added -> eQSL didn ' t understand the format
Result : 1 out of 1 records added -> Fantastic
Error : No match on eQSL_User / eQSL_Pswd -> eQSL credentials probably wrong
Warning : Y = 2013 M = 08 D = 11 F6ARS 15 M JT65 Bad record : Duplicate
Result : 0 out of 1 records added -> Dupe , OM !
*/
2024-05-15 22:24:39 +01:00
2023-05-05 09:34:32 +02:00
if ( $chi [ 'http_code' ] == " 200 " ) {
if ( stristr ( $result , " Result: 1 out of 1 records added " )) {
$status = " Sent " ;
2026-02-11 11:32:25 +00:00
// If batch array is provided, add to it instead of immediate update
if ( $successful_uploads !== null ) {
$successful_uploads [] = $qsl [ 'COL_PRIMARY_KEY' ];
} else {
// Legacy: immediate update (for backward compatibility)
$this -> eqslmethods_model -> eqsl_mark_sent ( $qsl [ 'COL_PRIMARY_KEY' ]);
}
2023-05-05 09:34:32 +02:00
} else {
if ( stristr ( $result , " Error: No match on eQSL_User/eQSL_Pswd " )) {
2024-05-15 22:24:39 +01:00
$this -> session -> set_flashdata ( 'warning' , 'Your eQSL username and/or password is incorrect.' );
redirect ( 'eqsl/export' );
2023-05-05 09:34:32 +02:00
} else {
if ( stristr ( $result , " Result: 0 out of 0 records added " )) {
2024-05-15 22:24:39 +01:00
$this -> session -> set_flashdata ( 'warning' , 'Something went wrong with eQSL.cc!' );
redirect ( 'eqsl/export' );
2023-05-05 09:34:32 +02:00
} else {
if ( stristr ( $result , " Bad record: Duplicate " )) {
$status = " Duplicate " ;
2024-05-15 22:24:39 +01:00
2023-05-05 09:34:32 +02:00
# Mark the QSL as sent if this is a dupe.
2026-02-11 11:32:25 +00:00
if ( $successful_uploads !== null ) {
$successful_uploads [] = $qsl [ 'COL_PRIMARY_KEY' ];
} else {
$this -> eqslmethods_model -> eqsl_mark_sent ( $qsl [ 'COL_PRIMARY_KEY' ]);
}
2023-05-05 09:34:32 +02:00
}
}
2019-07-22 16:32:45 +01:00
}
2023-05-05 09:34:32 +02:00
}
} else {
if ( $chi [ 'http_code' ] == " 500 " ) {
2024-05-15 22:24:39 +01:00
$this -> session -> set_flashdata ( 'warning' , 'eQSL.cc is experiencing issues. Please try exporting QSOs later.' );
redirect ( 'eqsl/export' );
2023-05-05 09:34:32 +02:00
} else {
if ( $chi [ 'http_code' ] == " 400 " ) {
2024-05-15 22:24:39 +01:00
$this -> session -> set_flashdata ( 'warning' , 'There was an error in one of the QSOs. You might want to manually upload them.' );
redirect ( 'eqsl/export' );
2023-05-05 09:34:32 +02:00
$status = " Error " ;
} else {
if ( $chi [ 'http_code' ] == " 404 " ) {
2024-05-15 22:24:39 +01:00
$this -> session -> set_flashdata ( 'warning' , 'It seems that the eQSL site has changed. Please open up an issue on GitHub.' );
redirect ( 'eqsl/export' );
2023-05-05 09:34:32 +02:00
}
2019-07-22 16:32:45 +01:00
}
2023-05-05 09:34:32 +02:00
}
}
log_message ( 'debug' , $result );
return $status ;
}
2019-07-22 16:32:45 +01:00
2024-05-15 22:24:39 +01:00
function generateResultTable ( $custom_date_format , $rows )
{
2023-05-05 11:38:14 +02:00
$table = '<table = style="width:100%" class="table-sm table table-bordered table-hover table-striped table-condensed text-center">' ;
2024-05-15 22:24:39 +01:00
$table .= " <thead><tr class= \" titles \" > " ;
$table .= " <th>Date</th> " ;
$table .= " <th>Time</th> " ;
$table .= " <th>Call</th> " ;
$table .= " <th>Mode</th> " ;
$table .= " <th>Submode</th> " ;
$table .= " <th>Band</th> " ;
$table .= " <th>Status</th> " ;
$table .= " </tr></thead><tbody> " ;
2023-05-05 11:38:14 +02:00
$table .= $rows ;
$table .= " </tbody></table> " ;
return $table ;
}
2023-08-27 14:53:57 +02:00
// Build out the ADIF info string according to specs https://eqsl.cc/qslcard/ADIFContentSpecs.cfm
2024-05-15 22:24:39 +01:00
function generateAdif ( $qsl , $data )
{
$COL_QSO_DATE = date ( 'Ymd' , strtotime ( $qsl [ 'COL_TIME_ON' ]));
$COL_TIME_ON = date ( 'Hi' , strtotime ( $qsl [ 'COL_TIME_ON' ]));
2023-05-05 09:34:32 +02:00
# Set up the single record file
2023-08-27 14:53:57 +02:00
$adif = " https://www.eqsl.cc/qslcard/importADIF.cfm? " ;
2023-05-05 09:34:32 +02:00
$adif .= " ADIFData=CloudlogUpload%20 " ;
2024-05-15 22:24:39 +01:00
2023-05-05 09:34:32 +02:00
/* Handy reference of escaping chars
" < " = 3 C
" > " = 3 E
" : " = 3 A
" " = 20
" _ " = 5 F
" - " = 2 D
" . " = 2 E
2023-11-30 23:43:50 +01:00
" & " = 26
2023-05-05 09:34:32 +02:00
*/
2024-05-15 22:24:39 +01:00
2023-05-05 09:34:32 +02:00
$adif .= " %3C " ;
$adif .= " ADIF%5FVER " ;
$adif .= " %3A " ;
$adif .= " 4 " ;
$adif .= " %3E " ;
$adif .= " 1%2E00 " ;
$adif .= " %20 " ;
2024-05-15 22:24:39 +01:00
2023-05-05 09:34:32 +02:00
$adif .= " %3C " ;
$adif .= " EQSL%5FUSER " ;
$adif .= " %3A " ;
$adif .= strlen ( $data [ 'user_eqsl_name' ]);
$adif .= " %3E " ;
$adif .= $data [ 'user_eqsl_name' ];
$adif .= " %20 " ;
2024-05-15 22:24:39 +01:00
2023-05-05 09:34:32 +02:00
$adif .= " %3C " ;
$adif .= " EQSL%5FPSWD " ;
$adif .= " %3A " ;
$adif .= strlen ( $data [ 'user_eqsl_password' ]);
$adif .= " %3E " ;
$adif .= urlencode ( $data [ 'user_eqsl_password' ]);
$adif .= " %20 " ;
2024-05-15 22:24:39 +01:00
2023-05-05 09:34:32 +02:00
$adif .= " %3C " ;
$adif .= " EOH " ;
$adif .= " %3E " ;
2024-05-15 22:24:39 +01:00
2023-05-05 09:34:32 +02:00
# Lay out the required fields
$adif .= " %3C " ;
$adif .= " QSO%5FDATE " ;
$adif .= " %3A " ;
$adif .= " 8 " ;
$adif .= " %3E " ;
$adif .= $COL_QSO_DATE ;
$adif .= " %20 " ;
2024-05-15 22:24:39 +01:00
2023-05-05 09:34:32 +02:00
$adif .= " %3C " ;
$adif .= " TIME%5FON " ;
$adif .= " %3A " ;
$adif .= " 4 " ;
$adif .= " %3E " ;
$adif .= $COL_TIME_ON ;
$adif .= " %20 " ;
2024-05-15 22:24:39 +01:00
2023-05-05 09:34:32 +02:00
$adif .= " %3C " ;
$adif .= " CALL " ;
$adif .= " %3A " ;
$adif .= strlen ( $qsl [ 'COL_CALL' ]);
$adif .= " %3E " ;
$adif .= $qsl [ 'COL_CALL' ];
$adif .= " %20 " ;
2024-05-15 22:24:39 +01:00
2023-05-05 09:34:32 +02:00
$adif .= " %3C " ;
$adif .= " MODE " ;
$adif .= " %3A " ;
$adif .= strlen ( $qsl [ 'COL_MODE' ]);
$adif .= " %3E " ;
$adif .= $qsl [ 'COL_MODE' ];
$adif .= " %20 " ;
2024-05-15 22:24:39 +01:00
if ( isset ( $qsl [ 'COL_SUBMODE' ])) {
$adif .= " %3C " ;
$adif .= " SUBMODE " ;
$adif .= " %3A " ;
$adif .= strlen ( $qsl [ 'COL_SUBMODE' ]);
$adif .= " %3E " ;
$adif .= $qsl [ 'COL_SUBMODE' ];
$adif .= " %20 " ;
2023-05-05 09:34:32 +02:00
}
2022-03-16 20:22:40 +01:00
2023-05-05 09:34:32 +02:00
$adif .= " %3C " ;
$adif .= " BAND " ;
$adif .= " %3A " ;
$adif .= strlen ( $qsl [ 'COL_BAND' ]);
$adif .= " %3E " ;
$adif .= $qsl [ 'COL_BAND' ];
$adif .= " %20 " ;
2024-05-15 22:24:39 +01:00
2023-05-05 09:34:32 +02:00
# End all the required fields
2024-05-15 22:24:39 +01:00
2023-05-05 09:34:32 +02:00
// adding RST_Sent
$adif .= " %3C " ;
$adif .= " RST%5FSENT " ;
$adif .= " %3A " ;
$adif .= strlen ( $qsl [ 'COL_RST_SENT' ]);
$adif .= " %3E " ;
$adif .= $qsl [ 'COL_RST_SENT' ];
$adif .= " %20 " ;
// adding prop mode if it isn't blank
2024-05-15 22:24:39 +01:00
if ( $qsl [ 'COL_PROP_MODE' ]) {
2023-05-05 09:34:32 +02:00
$adif .= " %3C " ;
$adif .= " PROP%5FMODE " ;
$adif .= " %3A " ;
$adif .= strlen ( $qsl [ 'COL_PROP_MODE' ]);
$adif .= " %3E " ;
$adif .= $qsl [ 'COL_PROP_MODE' ];
$adif .= " %20 " ;
}
2019-10-04 11:32:29 +01:00
2023-05-05 09:34:32 +02:00
// adding sat name if it isn't blank
2024-05-15 22:24:39 +01:00
if ( $qsl [ 'COL_SAT_NAME' ] != '' ) {
2023-05-05 09:34:32 +02:00
$adif .= " %3C " ;
$adif .= " SAT%5FNAME " ;
$adif .= " %3A " ;
$adif .= strlen ( $qsl [ 'COL_SAT_NAME' ]);
$adif .= " %3E " ;
$adif .= str_replace ( '-' , '%2D' , $qsl [ 'COL_SAT_NAME' ]);
$adif .= " %20 " ;
}
2019-10-04 11:32:29 +01:00
2023-05-05 09:34:32 +02:00
// adding sat mode if it isn't blank
2024-05-15 22:24:39 +01:00
if ( $qsl [ 'COL_SAT_MODE' ] != '' ) {
2023-05-05 09:34:32 +02:00
$adif .= " %3C " ;
$adif .= " SAT%5FMODE " ;
$adif .= " %3A " ;
$adif .= strlen ( $qsl [ 'COL_SAT_MODE' ]);
$adif .= " %3E " ;
$adif .= $qsl [ 'COL_SAT_MODE' ];
$adif .= " %20 " ;
}
2019-07-22 16:32:45 +01:00
2023-05-05 09:34:32 +02:00
// adding qslmsg if it isn't blank
2024-05-15 22:24:39 +01:00
if ( $qsl [ 'COL_QSLMSG' ] != '' ) {
$qsl [ 'COL_QSLMSG' ] = str_replace ( array ( chr ( 10 ), chr ( 13 )), array ( ' ' , ' ' ), $qsl [ 'COL_QSLMSG' ]);
2023-05-05 09:34:32 +02:00
$adif .= " %3C " ;
$adif .= " QSLMSG " ;
$adif .= " %3A " ;
$adif .= strlen ( $qsl [ 'COL_QSLMSG' ]);
$adif .= " %3E " ;
2024-05-15 22:24:39 +01:00
$adif .= str_replace ( '&' , '%26' , $qsl [ 'COL_QSLMSG' ]);
2023-05-05 09:34:32 +02:00
$adif .= " %20 " ;
}
2013-11-02 10:29:20 -05:00
2024-05-15 22:24:39 +01:00
if ( $qsl [ 'eqslqthnickname' ] != '' ) {
2023-05-05 09:34:32 +02:00
$adif .= " %3C " ;
$adif .= " APP%5FEQSL%5FQTH%5FNICKNAME " ;
$adif .= " %3A " ;
$adif .= strlen ( $qsl [ 'eqslqthnickname' ]);
$adif .= " %3E " ;
$adif .= $qsl [ 'eqslqthnickname' ];
$adif .= " %20 " ;
}
2022-11-30 14:57:24 +01:00
2023-05-05 09:34:32 +02:00
// adding sat mode if it isn't blank
2024-05-15 22:24:39 +01:00
if ( $qsl [ 'station_gridsquare' ] != '' ) {
2023-05-05 09:34:32 +02:00
$adif .= " %3C " ;
$adif .= " MY%5FGRIDSQUARE " ;
$adif .= " %3A " ;
$adif .= strlen ( $qsl [ 'station_gridsquare' ]);
$adif .= " %3E " ;
$adif .= $qsl [ 'station_gridsquare' ];
$adif .= " %20 " ;
2013-08-25 13:45:09 -05:00
}
2023-05-05 09:34:32 +02:00
# Tie a bow on it!
$adif .= " %3C " ;
$adif .= " EOR " ;
$adif .= " %3E " ;
2024-05-15 22:24:39 +01:00
2023-05-05 09:34:32 +02:00
# Make sure we don't have any spaces
$adif = str_replace ( " " , '%20' , $adif );
return $adif ;
}
2024-05-15 22:24:39 +01:00
function writeEqslNotSent ( $qslsnotsent , $custom_date_format )
{
2023-08-15 22:23:11 +02:00
$table = '<table = style="width:100%" class="table-sm table qsotable table-bordered table-hover table-striped table-condensed text-center">' ;
2024-05-15 22:24:39 +01:00
$table .= " <thead><tr class= \" titles \" > " ;
$table .= " <th>Date</th> " ;
$table .= " <th>Time</th> " ;
$table .= " <th>Call</th> " ;
$table .= " <th>Mode</th> " ;
$table .= " <th>Submode</th> " ;
$table .= " <th>Band</th> " ;
$table .= " <th>eQSL QTH Nickname</th> " ;
$table .= " </tr></thead><tbody> " ;
foreach ( $qslsnotsent as $qsl ) {
2023-05-05 09:34:32 +02:00
$table .= " <tr> " ;
2024-05-15 22:24:39 +01:00
$timestamp = strtotime ( $qsl [ 'COL_TIME_ON' ]);
$table .= " <td> " . date ( $custom_date_format , $timestamp ) . " </td> " ;
$table .= " <td> " . date ( 'H:i' , $timestamp ) . " </td> " ;
$table .= " <td><a href= \" javascript:displayQso( " . $qsl [ 'COL_PRIMARY_KEY' ] . " ) \" > " . str_replace ( " 0 " , " Ø " , strtoupper ( $qsl [ 'COL_CALL' ])) . " </a></td> " ;
$table .= " <td> " . $qsl [ 'COL_MODE' ] . " </td> " ;
if ( isset ( $qsl [ 'COL_SUBMODE' ])) {
$table .= " <td> " . $qsl [ 'COL_SUBMODE' ] . " </td> " ;
} else {
$table .= " <td></td> " ;
}
$table .= " <td> " . $qsl [ 'COL_BAND' ] . " </td> " ;
$table .= " <td> " . $qsl [ 'eqslqthnickname' ] . " </td> " ;
2023-05-05 10:58:36 +02:00
$table .= " </tr> " ;
2013-09-08 15:30:24 -05:00
}
2023-05-05 10:58:36 +02:00
$table .= " </tbody></table> " ;
2023-05-05 09:34:32 +02:00
return $table ;
2013-08-25 13:45:09 -05:00
}
2020-03-01 16:59:03 +00:00
2024-05-15 22:24:39 +01:00
function image ( $id )
{
2020-03-01 19:18:10 +00:00
$this -> load -> library ( 'electronicqsl' );
2020-03-01 18:08:17 +00:00
$this -> load -> model ( 'Eqsl_images' );
2020-03-01 16:59:03 +00:00
2024-05-15 22:24:39 +01:00
if ( $this -> Eqsl_images -> get_image ( $id ) == " No Image " ) {
2021-09-13 15:27:00 +02:00
$this -> load -> model ( 'logbook_model' );
2023-05-07 18:03:50 +02:00
$this -> load -> model ( 'user_model' );
2021-09-13 15:27:00 +02:00
$qso_query = $this -> logbook_model -> get_qso ( $id );
$qso = $qso_query -> row ();
$qso_timestamp = strtotime ( $qso -> COL_TIME_ON );
$callsign = $qso -> COL_CALL ;
$band = $qso -> COL_BAND ;
$mode = $qso -> COL_MODE ;
$year = date ( 'Y' , $qso_timestamp );
$month = date ( 'm' , $qso_timestamp );
$day = date ( 'd' , $qso_timestamp );
$hour = date ( 'H' , $qso_timestamp );
$minute = date ( 'i' , $qso_timestamp );
2020-03-01 18:08:17 +00:00
$query = $this -> user_model -> get_by_id ( $this -> session -> userdata ( 'user_id' ));
$q = $query -> row ();
2023-06-23 23:08:01 +02:00
$username = $qso -> COL_STATION_CALLSIGN ;
2026-07-18 22:53:51 +01:00
$password = $this -> decode_eqsl_password ( $q -> user_eqsl_password );
2026-07-18 22:01:33 +01:00
$this -> load -> model ( 'eqsl_mappings_model' );
$station_mapping = $this -> eqsl_mappings_model -> get_preferred_mapping_for_station ( $this -> session -> userdata ( 'user_id' ), $qso -> station_id );
if ( $station_mapping != null ) {
$password = $station_mapping [ 'eqsl_password' ];
}
2020-03-01 16:59:03 +00:00
2020-05-26 21:38:36 +02:00
$image_url = $this -> electronicqsl -> card_image ( $username , urlencode ( $password ), $callsign , $band , $mode , $year , $month , $day , $hour , $minute );
2020-03-01 18:08:17 +00:00
$file = file_get_contents ( $image_url , true );
2020-03-01 16:59:03 +00:00
2023-05-14 20:25:03 +02:00
$dom = new domDocument ;
$dom -> loadHTML ( $file );
2020-03-01 18:08:17 +00:00
$dom -> preserveWhiteSpace = false ;
$images = $dom -> getElementsByTagName ( 'img' );
2020-03-01 16:59:03 +00:00
2024-05-15 22:24:39 +01:00
if ( ! isset ( $images ) || count ( $images ) == 0 ) {
2020-03-01 19:36:07 +00:00
echo " Rate Limited " ;
2020-03-01 18:45:50 +00:00
exit ;
}
2024-05-15 22:24:39 +01:00
foreach ( $images as $image ) {
2021-09-13 15:27:00 +02:00
header ( 'Content-Type: image/jpg' );
2024-05-15 22:24:39 +01:00
$content = file_get_contents ( " https://www.eqsl.cc " . $image -> getAttribute ( 'src' ));
2021-11-07 22:58:17 +01:00
if ( $content === false ) {
echo " No response " ;
exit ;
}
echo $content ;
2024-05-15 22:24:39 +01:00
$filename = uniqid () . '.jpg' ;
if ( file_put_contents ( 'images/eqsl_card_images/' . '/' . $filename , $content ) !== false ) {
2021-11-07 22:58:17 +01:00
$this -> Eqsl_images -> save_image ( $id , $filename );
}
2020-03-01 18:08:17 +00:00
}
} else {
header ( 'Content-Type: image/jpg' );
2024-05-15 22:24:39 +01:00
$image_url = base_url ( 'images/eqsl_card_images/' . $this -> Eqsl_images -> get_image ( $id ));
2021-11-07 22:58:17 +01:00
header ( 'Location: ' . $image_url );
2020-03-01 16:59:03 +00:00
}
}
2020-11-03 16:06:35 +00:00
2024-05-15 22:24:39 +01:00
function bulk_download_image ( $id )
{
2023-05-22 10:34:13 +02:00
$this -> load -> library ( 'electronicqsl' );
2023-05-14 20:25:03 +02:00
$this -> load -> model ( 'Eqsl_images' );
$this -> load -> model ( 'logbook_model' );
$this -> load -> model ( 'user_model' );
$qso_query = $this -> logbook_model -> get_qso ( $id );
$qso = $qso_query -> row ();
$qso_timestamp = strtotime ( $qso -> COL_TIME_ON );
$callsign = $qso -> COL_CALL ;
$band = $qso -> COL_BAND ;
$mode = $qso -> COL_MODE ;
$year = date ( 'Y' , $qso_timestamp );
$month = date ( 'm' , $qso_timestamp );
$day = date ( 'd' , $qso_timestamp );
$hour = date ( 'H' , $qso_timestamp );
$minute = date ( 'i' , $qso_timestamp );
$query = $this -> user_model -> get_by_id ( $this -> session -> userdata ( 'user_id' ));
$q = $query -> row ();
2023-06-23 23:08:01 +02:00
$username = $qso -> COL_STATION_CALLSIGN ;
2026-07-18 22:53:51 +01:00
$password = $this -> decode_eqsl_password ( $q -> user_eqsl_password );
2026-07-18 22:01:33 +01:00
$this -> load -> model ( 'eqsl_mappings_model' );
$station_mapping = $this -> eqsl_mappings_model -> get_preferred_mapping_for_station ( $this -> session -> userdata ( 'user_id' ), $qso -> station_id );
if ( $station_mapping != null ) {
$password = $station_mapping [ 'eqsl_password' ];
}
2023-05-18 21:25:12 +02:00
$error = '' ;
2023-05-14 20:25:03 +02:00
$image_url = $this -> electronicqsl -> card_image ( $username , urlencode ( $password ), $callsign , $band , $mode , $year , $month , $day , $hour , $minute );
$file = file_get_contents ( $image_url , true );
2023-05-22 12:58:03 +02:00
if ( strpos ( $file , 'Error' ) !== false ) {
2023-05-22 00:10:56 +02:00
$error = rtrim ( preg_replace ( '/^\s*Error: /' , '' , $file ));
return $error ;
2023-05-18 21:25:12 +02:00
}
2023-05-14 20:25:03 +02:00
$dom = new domDocument ;
$dom -> loadHTML ( $file );
$dom -> preserveWhiteSpace = false ;
$images = $dom -> getElementsByTagName ( 'img' );
2024-05-15 22:24:39 +01:00
if ( ! isset ( $images ) || count ( $images ) == 0 ) {
2023-05-15 11:32:05 +02:00
$error = " Rate Limited " ;
return $error ;
2023-05-14 20:25:03 +02:00
}
2024-05-15 22:24:39 +01:00
foreach ( $images as $image ) {
$content = file_get_contents ( " https://www.eqsl.cc " . $image -> getAttribute ( 'src' ));
2023-05-14 20:25:03 +02:00
if ( $content === false ) {
2023-05-15 11:32:05 +02:00
$error = " No response " ;
return $error ;
2023-05-14 20:25:03 +02:00
}
2024-05-15 22:24:39 +01:00
$filename = uniqid () . '.jpg' ;
if ( file_put_contents ( 'images/eqsl_card_images/' . '/' . $filename , $content ) !== false ) {
2023-05-14 20:25:03 +02:00
$this -> Eqsl_images -> save_image ( $id , $filename );
}
}
2023-05-15 11:32:05 +02:00
return $error ;
2023-05-14 20:25:03 +02:00
}
2024-05-15 22:24:39 +01:00
public function tools ()
{
2020-11-03 16:06:35 +00:00
// Check logged in
$this -> load -> model ( 'user_model' );
2024-05-15 22:24:39 +01:00
if ( ! $this -> user_model -> authorize ( 2 )) {
$this -> session -> set_flashdata ( 'notice' , 'You\'re not allowed to do that!' );
redirect ( 'dashboard' );
}
2020-11-03 16:06:35 +00:00
$data [ 'page_title' ] = " eQSL Tools " ;
// Load frontend
$this -> load -> view ( 'interface_assets/header' , $data );
$this -> load -> view ( 'eqsl/tools' );
$this -> load -> view ( 'interface_assets/footer' );
}
2026-07-18 22:01:33 +01:00
public function mappings ()
{
$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' );
}
$this -> load -> model ( 'eqsl_mappings_model' );
$this -> load -> model ( 'stations' );
$data [ 'page_title' ] = 'eQSL Mappings' ;
$data [ 'station_profile' ] = $this -> stations -> all_of_user ();
$data [ 'mappings' ] = $this -> eqsl_mappings_model -> get_user_mappings ( $this -> session -> userdata ( 'user_id' ));
$editing_mapping_id = ( int ) $this -> input -> get ( 'edit' );
$data [ 'editing_mapping' ] = null ;
if ( $editing_mapping_id > 0 ) {
$data [ 'editing_mapping' ] = $this -> eqsl_mappings_model -> get_mapping_by_id_for_user ( $editing_mapping_id , $this -> session -> userdata ( 'user_id' ));
}
$this -> load -> view ( 'interface_assets/header' , $data );
$this -> load -> view ( 'eqsl/mappings' );
$this -> load -> view ( 'interface_assets/footer' );
}
public function save_mapping ()
{
$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' );
}
$this -> load -> model ( 'eqsl_mappings_model' );
$this -> load -> model ( 'stations' );
$user_id = ( int ) $this -> session -> userdata ( 'user_id' );
$mapping_id = ( int ) $this -> input -> post ( 'mapping_id' );
$station_id = ( int ) $this -> input -> post ( 'station_id' );
$eqsl_username = $this -> security -> xss_clean (( string ) $this -> input -> post ( 'eqsl_username' , true ));
$eqsl_password = $this -> security -> xss_clean (( string ) $this -> input -> post ( 'eqsl_password' , true ));
$eqsl_qth_nickname = $this -> security -> xss_clean (( string ) $this -> input -> post ( 'eqsl_qth_nickname' , true ));
$enabled = $this -> input -> post ( 'enabled' ) ? 1 : 0 ;
$preferred_for_download = $this -> input -> post ( 'preferred_for_download' ) ? 1 : 0 ;
if ( ! $this -> stations -> check_station_is_accessible ( $station_id )) {
$this -> session -> set_flashdata ( 'error' , 'Selected station location is not accessible.' );
redirect ( 'eqsl/mappings' );
}
2026-07-18 22:13:27 +01:00
if ( trim ( $eqsl_username ) === '' || trim ( $eqsl_qth_nickname ) === '' ) {
$this -> session -> set_flashdata ( 'error' , 'Username and QTH nickname are required.' );
2026-07-18 22:01:33 +01:00
redirect ( 'eqsl/mappings' );
}
if ( $mapping_id > 0 ) {
$mapping = $this -> eqsl_mappings_model -> get_mapping_by_id_for_user ( $mapping_id , $user_id );
if ( $mapping == null ) {
$this -> session -> set_flashdata ( 'error' , 'Mapping not found.' );
redirect ( 'eqsl/mappings' );
}
2026-07-18 22:13:27 +01:00
if ( trim ( $eqsl_password ) === '' ) {
$eqsl_password = $mapping [ 'eqsl_password' ];
}
if ( trim ( $eqsl_password ) === '' ) {
$reused_password = $this -> eqsl_mappings_model -> get_password_for_user_and_username ( $user_id , $eqsl_username );
if ( $reused_password !== null ) {
$eqsl_password = $reused_password ;
}
}
if ( trim ( $eqsl_password ) === '' ) {
$this -> session -> set_flashdata ( 'error' , 'Password is required for a new eQSL username.' );
redirect ( 'eqsl/mappings' );
}
2026-07-18 22:53:51 +01:00
$updated = $this -> eqsl_mappings_model -> update_mapping ( $mapping_id , $user_id , $station_id , $eqsl_username , $eqsl_password , $eqsl_qth_nickname , $enabled , $preferred_for_download );
if ( $updated === false ) {
2026-07-18 23:10:06 +01:00
$reason = $this -> eqsl_mappings_model -> get_last_failure_reason ();
$db_error = $this -> eqsl_mappings_model -> get_last_db_error ();
if ( $reason === 'encryption_failed' ) {
$this -> session -> set_flashdata ( 'error' , 'Unable to secure the eQSL password. Please ask your administrator to check encryption configuration.' );
} elseif (( int ) ( $db_error [ 'code' ] ? ? 0 ) === 1406 ) {
$this -> session -> set_flashdata ( 'error' , 'Unable to save mapping because the database schema is out of date. Please run database migration 275 (encrypt eQSL passwords).' );
} else {
$this -> session -> set_flashdata ( 'error' , 'Unable to update mapping due to a database error. Please check logs and try again.' );
}
log_message ( 'error' , 'eQSL mapping update failed for user ' . ( int ) $user_id . ': reason=' . $reason . ', db_code=' . ( int ) ( $db_error [ 'code' ] ? ? 0 ) . ', db_message=' . ( $db_error [ 'message' ] ? ? '' ));
2026-07-18 22:53:51 +01:00
} else {
$this -> session -> set_flashdata ( 'success' , 'eQSL mapping updated.' );
}
2026-07-18 22:01:33 +01:00
} else {
2026-07-18 23:00:08 +01:00
$duplicate_mapping = $this -> eqsl_mappings_model -> find_duplicate_mapping (
$user_id ,
$station_id ,
$eqsl_username ,
$eqsl_qth_nickname
);
if ( $duplicate_mapping != null ) {
$updated = $this -> eqsl_mappings_model -> update_mapping (
$duplicate_mapping [ 'mapping_id' ],
$user_id ,
$station_id ,
$eqsl_username ,
$eqsl_password ,
$eqsl_qth_nickname ,
$enabled ,
$preferred_for_download
);
if ( $updated === false ) {
$this -> session -> set_flashdata ( 'error' , 'Mapping already exists, but updating it failed. Please verify encryption settings and try again.' );
} else {
$this -> session -> set_flashdata ( 'success' , 'Mapping already existed. Existing mapping was updated instead.' );
}
$this -> user_model -> update_session ( $user_id );
redirect ( 'eqsl/mappings' );
}
2026-07-18 22:13:27 +01:00
if ( trim ( $eqsl_password ) === '' ) {
$reused_password = $this -> eqsl_mappings_model -> get_password_for_user_and_username ( $user_id , $eqsl_username );
if ( $reused_password !== null ) {
$eqsl_password = $reused_password ;
}
}
if ( trim ( $eqsl_password ) === '' ) {
$this -> session -> set_flashdata ( 'error' , 'Password is required the first time you use an eQSL username.' );
redirect ( 'eqsl/mappings' );
}
2026-07-18 22:01:33 +01:00
$created = $this -> eqsl_mappings_model -> create_mapping ( $user_id , $station_id , $eqsl_username , $eqsl_password , $eqsl_qth_nickname , $enabled , $preferred_for_download );
if ( $created === false ) {
2026-07-18 23:06:37 +01:00
$fuzzy_duplicate_mapping = $this -> eqsl_mappings_model -> find_duplicate_mapping_fuzzy (
$user_id ,
$station_id ,
$eqsl_username ,
$eqsl_qth_nickname
);
if ( $fuzzy_duplicate_mapping != null ) {
$updated = $this -> eqsl_mappings_model -> update_mapping (
$fuzzy_duplicate_mapping [ 'mapping_id' ],
$user_id ,
$station_id ,
$eqsl_username ,
$eqsl_password ,
$eqsl_qth_nickname ,
$enabled ,
$preferred_for_download
);
if ( $updated === false ) {
2026-07-18 23:10:06 +01:00
$reason = $this -> eqsl_mappings_model -> get_last_failure_reason ();
$db_error = $this -> eqsl_mappings_model -> get_last_db_error ();
if ( $reason === 'encryption_failed' ) {
$this -> session -> set_flashdata ( 'error' , 'Mapping exists, but password encryption failed. Please ask your administrator to check encryption configuration.' );
} elseif (( int ) ( $db_error [ 'code' ] ? ? 0 ) === 1406 ) {
$this -> session -> set_flashdata ( 'error' , 'Mapping exists, but the database schema is out of date. Please run database migration 275 (encrypt eQSL passwords).' );
} else {
$this -> session -> set_flashdata ( 'error' , 'Mapping exists, but updating it failed due to a database error. Please check logs and try again.' );
}
log_message ( 'error' , 'eQSL duplicate mapping auto-update failed for user ' . ( int ) $user_id . ': reason=' . $reason . ', db_code=' . ( int ) ( $db_error [ 'code' ] ? ? 0 ) . ', db_message=' . ( $db_error [ 'message' ] ? ? '' ));
2026-07-18 23:06:37 +01:00
} else {
$this -> session -> set_flashdata ( 'success' , 'Mapping already existed. Existing mapping was updated instead.' );
}
} else {
2026-07-18 23:10:06 +01:00
$reason = $this -> eqsl_mappings_model -> get_last_failure_reason ();
$db_error = $this -> eqsl_mappings_model -> get_last_db_error ();
if ( $reason === 'encryption_failed' ) {
$this -> session -> set_flashdata ( 'error' , 'Unable to secure the eQSL password. Please ask your administrator to check encryption configuration.' );
} elseif (( int ) ( $db_error [ 'code' ] ? ? 0 ) === 1406 ) {
$this -> session -> set_flashdata ( 'error' , 'Unable to create mapping because the database schema is out of date. Please run database migration 275 (encrypt eQSL passwords).' );
} else {
$this -> session -> set_flashdata ( 'error' , 'Unable to create mapping due to a database error. Please check logs and try again.' );
}
log_message ( 'error' , 'eQSL mapping create failed for user ' . ( int ) $user_id . ': reason=' . $reason . ', db_code=' . ( int ) ( $db_error [ 'code' ] ? ? 0 ) . ', db_message=' . ( $db_error [ 'message' ] ? ? '' ));
2026-07-18 23:06:37 +01:00
}
2026-07-18 22:01:33 +01:00
} else {
$this -> session -> set_flashdata ( 'success' , 'eQSL mapping created.' );
}
}
$this -> user_model -> update_session ( $user_id );
redirect ( 'eqsl/mappings' );
}
public function delete_mapping ()
{
$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' );
}
$this -> load -> model ( 'eqsl_mappings_model' );
$user_id = ( int ) $this -> session -> userdata ( 'user_id' );
$mapping_id = ( int ) $this -> input -> post ( 'mapping_id' );
if ( $mapping_id <= 0 ) {
$this -> session -> set_flashdata ( 'error' , 'Invalid mapping selected.' );
redirect ( 'eqsl/mappings' );
}
if ( $this -> eqsl_mappings_model -> delete_mapping ( $mapping_id , $user_id )) {
$this -> session -> set_flashdata ( 'success' , 'eQSL mapping deleted.' );
} else {
$this -> session -> set_flashdata ( 'error' , 'Unable to delete mapping.' );
}
$this -> user_model -> update_session ( $user_id );
redirect ( 'eqsl/mappings' );
}
2024-05-15 22:24:39 +01:00
public function download ()
{
2023-05-10 21:36:46 +02:00
// Check logged in
$this -> load -> model ( 'user_model' );
2024-05-15 22:24:39 +01:00
if ( ! $this -> user_model -> authorize ( 2 )) {
$this -> session -> set_flashdata ( 'notice' , 'You\'re not allowed to do that!' );
redirect ( 'dashboard' );
}
$errors = 0 ;
2026-07-18 22:31:33 +01:00
$this -> load -> model ( 'eqsl_mappings_model' );
$user_id = ( int ) $this -> session -> userdata ( 'user_id' );
$active_mappings = $this -> eqsl_mappings_model -> get_active_mappings_for_user ( $user_id );
$use_mapping_mode = ! empty ( $active_mappings );
$mapped_station_ids = array_map ( function ( $mapping ) {
return ( int ) $mapping [ 'station_id' ];
}, $active_mappings );
$data [ 'eqsl_mapping_mode' ] = $use_mapping_mode ;
$data [ 'eqsl_legacy_fallback' ] = ! $use_mapping_mode ;
$data [ 'eqsl_mappings_count' ] = count ( $active_mappings );
2023-05-10 21:36:46 +02:00
2023-05-14 20:25:03 +02:00
if ( $this -> input -> post ( 'eqsldownload' ) == 'download' ) {
2023-05-22 00:10:56 +02:00
$i = 0 ;
2023-05-14 20:25:03 +02:00
$this -> load -> model ( 'eqslmethods_model' );
2026-07-18 22:31:33 +01:00
if ( $use_mapping_mode ) {
$qslsnotdownloaded = $this -> eqslmethods_model -> eqsl_not_yet_downloaded_for_station_ids ( $mapped_station_ids );
} else {
$qslsnotdownloaded = $this -> eqslmethods_model -> eqsl_not_yet_downloaded ();
}
2023-05-22 00:10:56 +02:00
$eqsl_results = array ();
2023-05-14 20:25:03 +02:00
foreach ( $qslsnotdownloaded -> result_array () as $qsl ) {
2023-05-22 00:10:56 +02:00
$result = $this -> bulk_download_image ( $qsl [ 'COL_PRIMARY_KEY' ]);
if ( $result != '' ) {
$errors ++ ;
if ( $result == 'Rate Limited' ) {
2023-05-18 21:25:12 +02:00
break ;
} else {
2023-05-22 00:10:56 +02:00
$eqsl_results [] = array (
'date' => $qsl [ 'COL_TIME_ON' ],
'call' => $qsl [ 'COL_CALL' ],
'mode' => $qsl [ 'COL_MODE' ],
'submode' => $qsl [ 'COL_SUBMODE' ],
'status' => $result ,
'qsoid' => $qsl [ 'COL_PRIMARY_KEY' ]
);
2023-05-18 21:25:12 +02:00
continue ;
}
2023-05-22 00:10:56 +02:00
} else {
$i ++ ;
2023-05-15 11:32:05 +02:00
}
2023-05-22 12:58:03 +02:00
if ( $i > 0 ) {
sleep ( 15 );
}
2023-05-14 20:25:03 +02:00
}
2023-05-22 00:10:56 +02:00
$data [ 'eqsl_results' ] = $eqsl_results ;
2024-05-15 22:24:39 +01:00
$data [ 'eqsl_stats' ] = " Successfully downloaded: " . $i . " / Errors: " . count ( $eqsl_results );
2023-05-22 00:10:56 +02:00
$data [ 'page_title' ] = " eQSL Download Information " ;
$this -> load -> view ( 'interface_assets/header' , $data );
$this -> load -> view ( 'eqsl/result' );
$this -> load -> view ( 'interface_assets/footer' );
2023-05-14 20:25:03 +02:00
} else {
2023-05-10 21:36:46 +02:00
2023-05-14 20:25:03 +02:00
$data [ 'page_title' ] = " eQSL Card Image Download " ;
$this -> load -> model ( 'eqslmethods_model' );
2023-05-10 21:36:46 +02:00
2023-05-14 20:25:03 +02:00
$data [ 'custom_date_format' ] = $this -> session -> userdata ( 'user_date_format' );
2026-07-18 22:31:33 +01:00
if ( $use_mapping_mode ) {
$data [ 'qslsnotdownloaded' ] = $this -> eqslmethods_model -> eqsl_not_yet_downloaded_for_station_ids ( $mapped_station_ids );
} else {
$data [ 'qslsnotdownloaded' ] = $this -> eqslmethods_model -> eqsl_not_yet_downloaded ();
}
2023-05-14 20:25:03 +02:00
$this -> load -> view ( 'interface_assets/header' , $data );
$this -> load -> view ( 'eqsl/download' );
$this -> load -> view ( 'interface_assets/footer' );
}
2023-05-10 21:36:46 +02:00
}
2024-05-15 22:24:39 +01:00
public function mark_all_sent ()
{
2020-11-03 16:06:35 +00:00
// Check logged in
$this -> load -> model ( 'user_model' );
2024-05-15 22:24:39 +01:00
if ( ! $this -> user_model -> authorize ( 2 )) {
$this -> session -> set_flashdata ( 'notice' , 'You\'re not allowed to do that!' );
redirect ( 'dashboard' );
}
2020-11-03 16:06:35 +00:00
// mark all eqsls as sent
$this -> load -> model ( 'eqslmethods_model' );
$this -> eqslmethods_model -> mark_all_as_sent ();
$this -> session -> set_flashdata ( 'success' , 'All eQSLs Marked as Uploaded' );
redirect ( 'eqsl/tools' );
}
2023-05-05 09:34:32 +02:00
2023-05-06 08:17:23 +02:00
/*
* Used for CRON job
*/
2024-05-15 22:24:39 +01:00
public function sync ()
{
2023-05-05 11:16:48 +02:00
ini_set ( 'memory_limit' , '-1' );
set_time_limit ( 0 );
2023-05-06 08:17:23 +02:00
$this -> load -> model ( 'eqslmethods_model' );
2026-07-18 22:01:33 +01:00
$this -> load -> model ( 'eqsl_mappings_model' );
$eqsl_mappings = $this -> eqsl_mappings_model -> get_active_mappings_for_sync ();
if ( ! empty ( $eqsl_mappings )) {
foreach ( $eqsl_mappings as $mapping ) {
$this -> uploadMapping ( $mapping );
if (( int ) $mapping [ 'preferred_for_download' ] === 1 ) {
$this -> downloadMapping ( $mapping );
}
}
return ;
}
2023-05-06 08:17:23 +02:00
$users = $this -> eqslmethods_model -> get_eqsl_users ();
foreach ( $users as $user ) {
2026-07-18 22:53:51 +01:00
$decoded_password = $this -> decode_eqsl_password ( $user -> user_eqsl_password );
$this -> uploadUser ( $user -> user_id , $user -> user_eqsl_name , $decoded_password );
$this -> downloadUser ( $user -> user_id , $user -> user_eqsl_name , $decoded_password );
2023-05-06 08:17:23 +02:00
}
}
2024-05-15 22:24:39 +01:00
public function downloadUser ( $userid , $username , $password )
{
2023-05-06 08:17:23 +02:00
$this -> load -> library ( 'EqslImporter' );
$this -> load -> model ( 'eqslmethods_model' );
$config [ 'upload_path' ] = './uploads/' ;
2023-05-07 09:31:45 +02:00
$eqsl_locations = $this -> eqslmethods_model -> all_of_user_with_eqsl_nick_defined ( $userid );
$eqsl_results = array ();
2023-05-06 08:17:23 +02:00
foreach ( $eqsl_locations -> result_array () as $eqsl_location ) {
$this -> eqslimporter -> from_callsign_and_QTH (
$eqsl_location [ 'station_callsign' ],
$eqsl_location [ 'eqslqthnickname' ],
2023-09-26 11:39:15 +02:00
$config [ 'upload_path' ],
$eqsl_location [ 'station_id' ]
2023-05-06 08:17:23 +02:00
);
2023-05-07 09:31:45 +02:00
$eqsl_results [] = $this -> eqslimporter -> fetch ( $password );
2023-05-06 08:17:23 +02:00
}
}
2026-07-18 22:01:33 +01:00
private function downloadMapping ( $mapping )
{
$this -> load -> library ( 'EqslImporter' );
$config [ 'upload_path' ] = './uploads/' ;
$this -> eqslimporter -> from_callsign_and_QTH (
$mapping [ 'station_callsign' ],
$mapping [ 'eqsl_qth_nickname' ],
$config [ 'upload_path' ],
$mapping [ 'station_id' ]
);
$this -> eqslimporter -> fetch ( $mapping [ 'eqsl_password' ]);
}
private function uploadMapping ( $mapping )
{
$this -> load -> model ( 'eqslmethods_model' );
$data [ 'user_eqsl_name' ] = $mapping [ 'eqsl_username' ];
$data [ 'user_eqsl_password' ] = $mapping [ 'eqsl_password' ];
$qslsnotsent = $this -> eqslmethods_model -> eqsl_not_yet_sent_for_station ( $mapping [ 'station_id' ], $mapping [ 'eqsl_qth_nickname' ]);
$successful_uploads = array ();
foreach ( $qslsnotsent -> result_array () as $qsl ) {
$qsl [ 'eqslqthnickname' ] = $mapping [ 'eqsl_qth_nickname' ];
$adif = $this -> generateAdif ( $qsl , $data );
$this -> uploadQso ( $adif , $qsl , $successful_uploads );
}
if ( ! empty ( $successful_uploads )) {
$this -> eqslmethods_model -> eqsl_mark_sent_batch ( $successful_uploads );
}
}
2024-05-15 22:24:39 +01:00
function uploadUser ( $userid , $username , $password )
{
2023-05-05 09:34:32 +02:00
$data [ 'user_eqsl_name' ] = $this -> security -> xss_clean ( $username );
$data [ 'user_eqsl_password' ] = $this -> security -> xss_clean ( $password );
$clean_userid = $this -> security -> xss_clean ( $userid );
2024-05-15 22:24:39 +01:00
2023-05-05 09:34:32 +02:00
$qslsnotsent = $this -> eqslmethods_model -> eqsl_not_yet_sent ( $clean_userid );
2023-05-05 14:29:45 +02:00
foreach ( $qslsnotsent -> result_array () as $qsl ) {
2023-05-05 09:34:32 +02:00
$adif = $this -> generateAdif ( $qsl , $data );
2024-05-15 22:24:39 +01:00
2023-05-05 13:26:10 +02:00
$status = $this -> uploadQso ( $adif , $qsl );
2023-05-05 09:34:32 +02:00
}
}
2026-07-18 22:53:51 +01:00
private function decode_eqsl_password ( $stored_password )
{
$stored_password = ( string ) $stored_password ;
if ( $stored_password === '' ) {
return '' ;
}
if ( strpos ( $stored_password , 'enc:' ) === 0 ) {
$cipher = substr ( $stored_password , 4 );
$decrypted = $this -> encryption -> decrypt ( $cipher );
if ( $decrypted !== false && $decrypted !== null ) {
return $decrypted ;
}
return '' ;
}
// Backward compatibility for existing plaintext credentials.
return $stored_password ;
}
2016-01-26 15:43:35 +00:00
} // end class