cloudlog/application/models/Cq.php
Peter Goodhall d6fe6e8110 Sanitize and escape query inputs in models
Sanitize location lists and escape query parameters across multiple models to prevent SQL injection and fix query formatting. Changes include: converting location arrays to comma-separated integer lists (array_map('intval')) instead of quoted implode, adding escape_str for band/mode/sat, adding a sanitize_location_list helper and early-return checks in CQ, escaping band lists, and updating Adif_data date where clauses to use $this->db->escape(..., NULL, FALSE). Files modified: Accumulate_model, Activated_gridmap_model, Adif_data, Cq, and Gridmap_model.
2026-06-24 22:19:35 +01:00

278 lines
10 KiB
PHP

<?php
class CQ extends CI_Model{
private function sanitize_location_list($location_list) {
$parts = explode(',', (string) $location_list);
$ids = array();
foreach ($parts as $part) {
$trimmed = trim($part, " \t\n\r\0\x0B'\"");
if (is_numeric($trimmed)) {
$ids[] = (int) $trimmed;
}
}
return implode(',', array_values(array_unique($ids)));
}
function get_cq_array($bands, $postdata, $location_list) {
$cqZ = array(); // Used for keeping track of which states that are not worked
for ($i = 1; $i <= 40; $i++) {
$cqZ[$i]['count'] = 0; // Inits each cq zone's count
}
$qsl = "";
if ($postdata['confirmed'] != NULL) {
if ($postdata['qsl'] != NULL ) {
$qsl .= "Q";
}
if ($postdata['lotw'] != NULL ) {
$qsl .= "L";
}
if ($postdata['eqsl'] != NULL ) {
$qsl .= "E";
}
}
foreach ($bands as $band) {
for ($i = 1; $i <= 40; $i++) {
$bandCq[$i][$band] = '-'; // Sets all to dash to indicate no result
}
if ($postdata['worked'] != NULL) {
$cqBand = $this->getCQWorked($location_list, $band, $postdata);
foreach ($cqBand as $line) {
$bandCq[$line->col_cqz][$band] = '<div class="bg-danger awardsBgDanger"><a href=\'javascript:displayContacts("' . str_replace("&", "%26", $line->col_cqz) . '","' . $band . '","'. $postdata['mode'] . '","CQZone","")\'>W</a></div>';
$cqZ[$line->col_cqz]['count']++;
}
}
if ($postdata['confirmed'] != NULL) {
$cqBand = $this->getCQConfirmed($location_list, $band, $postdata);
foreach ($cqBand as $line) {
$bandCq[$line->col_cqz][$band] = '<div class="bg-success awardsBgSuccess"><a href=\'javascript:displayContacts("' . str_replace("&", "%26", $line->col_cqz) . '","' . $band . '","'. $postdata['mode'] . '","CQZone","'.$qsl.'")\'>C</a></div>';
$cqZ[$line->col_cqz]['count']++;
}
}
}
// We want to remove the worked zones in the list, since we do not want to display them
if ($postdata['worked'] == NULL) {
$cqBand = $this->getCQWorked($location_list, $postdata['band'], $postdata);
foreach ($cqBand as $line) {
unset($bandCq[$line->col_cqz]);
}
}
// We want to remove the confirmed zones in the list, since we do not want to display them
if ($postdata['confirmed'] == NULL) {
$cqBand = $this->getCQConfirmed($location_list, $postdata['band'], $postdata);
foreach ($cqBand as $line) {
unset($bandCq[$line->col_cqz]);
}
}
if ($postdata['notworked'] == NULL) {
for ($i = 1; $i <= 40; $i++) {
if ($cqZ[$i]['count'] == 0) {
unset($bandCq[$i]);
};
}
}
if (isset($bandCq)) {
return $bandCq;
} else {
return 0;
}
}
/*
* Function returns all worked, but not confirmed states
* $postdata contains data from the form, in this case Lotw or QSL are used
*/
function getCQWorked($location_list, $band, $postdata) {
$location_list = $this->sanitize_location_list($location_list);
if ($location_list === '') {
return array();
}
$mode = $this->db->escape_str($postdata['mode']);
$sql = "SELECT distinct col_cqz FROM " . $this->config->item('table_name') . " thcv
where station_id in (" . $location_list . ") and col_cqz <= 40 and col_cqz <> ''";
if ($postdata['mode'] != 'All') {
$sql .= " and (col_mode = '" . $mode . "' or col_submode = '" . $mode . "')";
}
$sql .= $this->addBandToQuery($band);
$sql .= " and not exists (select 1 from " . $this->config->item('table_name') .
" where station_id in (" . $location_list .
") and col_cqz = thcv.col_cqz and col_cqz <> '' ";
if ($postdata['mode'] != 'All') {
$sql .= " and (col_mode = '" . $mode . "' or col_submode = '" . $mode . "')";
}
$sql .= $this->addBandToQuery($band);
$sql .= $this->addQslToQuery($postdata);
$sql .= ")";
$query = $this->db->query($sql);
return $query->result();
}
/*
* Function returns all confirmed states on given band and on LoTW or QSL
* $postdata contains data from the form, in this case Lotw or QSL are used
*/
function getCQConfirmed($location_list, $band, $postdata) {
$location_list = $this->sanitize_location_list($location_list);
if ($location_list === '') {
return array();
}
$mode = $this->db->escape_str($postdata['mode']);
$sql = "SELECT distinct col_cqz FROM " . $this->config->item('table_name') . " thcv
where station_id in (" . $location_list . ") and col_cqz <= 40 and col_cqz <> ''";
if ($postdata['mode'] != 'All') {
$sql .= " and (col_mode = '" . $mode . "' or col_submode = '" . $mode . "')";
}
$sql .= $this->addBandToQuery($band);
$sql .= $this->addQslToQuery($postdata);
$query = $this->db->query($sql);
return $query->result();
}
function addQslToQuery($postdata) {
$sql = '';
$qsl = array();
if ($postdata['lotw'] != NULL || $postdata['qsl'] != NULL || $postdata['eqsl'] != NULL) {
$sql .= ' and (';
if ($postdata['qsl'] != NULL) {
array_push($qsl, "col_qsl_rcvd = 'Y'");
}
if ($postdata['lotw'] != NULL) {
array_push($qsl, "col_lotw_qsl_rcvd = 'Y'");
}
if ($postdata['eqsl'] != NULL) {
array_push($qsl, "col_eqsl_qsl_rcvd = 'Y'");
}
$sql .= implode(' or ', $qsl);
$sql .= ')';
}
return $sql;
}
function addBandToQuery($band) {
$band = $this->db->escape_str($band);
$sql = '';
if ($band != 'All') {
if ($band == 'SAT') {
$sql .= " and col_prop_mode ='" . $band . "'";
} else {
$sql .= " and col_prop_mode !='SAT'";
$sql .= " and col_band ='" . $band . "'";
}
}
return $sql;
}
/*
* Function gets worked and confirmed summary on each band on the active stationprofile
*/
function get_cq_summary($bands, $postdata, $location_list) {
foreach ($bands as $band) {
$worked = $this->getSummaryByBand($band, $postdata, $location_list);
$confirmed = $this->getSummaryByBandConfirmed($band, $postdata, $location_list);
$cqSummary['worked'][$band] = $worked[0]->count;
$cqSummary['confirmed'][$band] = $confirmed[0]->count;
}
$workedTotal = $this->getSummaryByBand($postdata['band'], $postdata, $location_list);
$confirmedTotal = $this->getSummaryByBandConfirmed($postdata['band'], $postdata, $location_list);
$cqSummary['worked']['Total'] = $workedTotal[0]->count;
$cqSummary['confirmed']['Total'] = $confirmedTotal[0]->count;
return $cqSummary;
}
function getSummaryByBand($band, $postdata, $location_list) {
$location_list = $this->sanitize_location_list($location_list);
if ($location_list === '') {
return array((object) array('count' => 0));
}
$mode = $this->db->escape_str($postdata['mode']);
$sql = "SELECT count(distinct thcv.col_cqz) as count FROM " . $this->config->item('table_name') . " thcv";
$sql .= " where station_id in (" . $location_list . ') and col_cqz <= 40 and col_cqz > 0';
if ($band == 'SAT') {
$sql .= " and thcv.col_prop_mode ='" . $band . "'";
} else if ($band == 'All') {
$this->load->model('bands');
$bandslots = $this->bands->get_worked_bands('cq');
$bandslots_sanitized = array_map(array($this->db, 'escape_str'), $bandslots);
$bandslots_list = "'".implode("','",$bandslots_sanitized)."'";
$sql .= " and thcv.col_band in (" . $bandslots_list . ")" .
" and thcv.col_prop_mode !='SAT'";
} else {
$sql .= " and thcv.col_prop_mode !='SAT'";
$sql .= " and thcv.col_band ='" . $band . "'";
}
if ($postdata['mode'] != 'All') {
$sql .= " and (col_mode = '" . $mode . "' or col_submode = '" . $mode . "')";
}
$query = $this->db->query($sql);
return $query->result();
}
function getSummaryByBandConfirmed($band, $postdata, $location_list){
$location_list = $this->sanitize_location_list($location_list);
if ($location_list === '') {
return array((object) array('count' => 0));
}
$mode = $this->db->escape_str($postdata['mode']);
$sql = "SELECT count(distinct thcv.col_cqz) as count FROM " . $this->config->item('table_name') . " thcv";
$sql .= " where station_id in (" . $location_list . ') and col_cqz <= 40 and col_cqz > 0';
if ($band == 'SAT') {
$sql .= " and thcv.col_prop_mode ='" . $band . "'";
} else if ($band == 'All') {
$this->load->model('bands');
$bandslots = $this->bands->get_worked_bands('cq');
$bandslots_sanitized = array_map(array($this->db, 'escape_str'), $bandslots);
$bandslots_list = "'".implode("','",$bandslots_sanitized)."'";
$sql .= " and thcv.col_band in (" . $bandslots_list . ")" .
" and thcv.col_prop_mode !='SAT'";
} else {
$sql .= " and thcv.col_prop_mode !='SAT'";
$sql .= " and thcv.col_band ='" . $band . "'";
}
if ($postdata['mode'] != 'All') {
$sql .= " and (col_mode = '" . $mode . "' or col_submode = '" . $mode . "')";
}
$sql .= $this->addQslToQuery($postdata);
$query = $this->db->query($sql);
return $query->result();
}
}