Commits (58)
name: LimeSurvey - CI pipeline for 3.x LTS
# Triggers the workflow on push or pull request events on all branches
on:
push:
pull_request:
branches:
- '3.x-LTS'
jobs:
CI-pipeline:
runs-on: ubuntu-18.04 # ubuntu runner hosted by Github
strategy:
matrix:
# Specify what versions of php you want to test
php-versions: ['5.6' , '7.4']
# Run all jobs, even if some fail
fail-fast: false
# Env vars for this job
env:
DBENGINE: INNODB
name: PHP ${{ matrix.php-versions }} # Check each version of php specified in matrix
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
# This will change the php version for every version specified in matrix https://github.com/marketplace/actions/setup-php-action
- name: Install specified PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
# Start the MySQL service - https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu1804-README.md#mysql
- name: Start the MySQL service
run: |
sudo systemctl start mysql.service
- name: Initilize and check all dependencies
run: |
mysql --version
sudo service mysql status
touch enabletests
# NB: PHPUnit 6.5.* is installed with composer.
composer install -vvv
# Update on PHP 5.x to get a compatible version
composer update
./third_party/bin/phpunit --version
- name: Set up Apache+PHP
run: |
# Set up the Apache and PHP
sudo apt-get update > /dev/null
sudo apt install php libapache2-mod-php -y
sudo cp -f ./tests/CI-pipeline/github-actions-apache /etc/apache2/sites-available/000-default.conf
sudo sed -e "s?%CI_BUILD_DIR%?$(pwd)?g" --in-place /etc/apache2/sites-available/000-default.conf
sudo service apache2 restart
# Give permision to access files for Apache
setfacl -dR -m u:www-data:rwX -m u:$(whoami):rwx ./tmp
setfacl -dR -m u:www-data:rwX -m u:$(whoami):rwx ./upload
setfacl -dR -m u:www-data:rwX -m u:$(whoami):rwx ./themes
setfacl -dR -m u:www-data:rwX -m u:$(whoami):rwx ./tests/tmp
setfacl -dR -m u:www-data:rwX -m u:$(whoami):rwx ./application/config
chmod -R 777 ./tmp
sudo chown -R www-data:docker ./tmp
chmod -R 777 ./upload
chmod -R 777 ./themes # Need 777 so both console and web server can cd into the folder.
chmod -R 777 ./tests/tmp
chmod -R 777 ./application/config
chmod +x ./tests/bin/lint-*
- name: Load custom console and start the Application
run: |
php application/commands/console.php install admin password TravisLS no@email.com verbose
cp application/config/config-sample-mysql.php application/config/config.php
- name: Check MySQL service
run: |
# InnoDB needs large_file_prefix & Barracuda file format
# https://discuss.gogs.io/t/solved-mysql-error-1064-while-running-first-install/1604
# InnoDB variables ARE already set to desired values in Github runner (ubuntu-18.04)
sudo service mysql status
mysql -uroot -proot -e "Show variables like '%large%';"
mysql -uroot -proot -e "Show variables like '%innodb_file%';"
mysql -uroot -proot -e "Show variables like '%innodb_default%';"
# Enable debug=2 in config file. OBS: This assumes debug is on line 61.
# TODO: Disable, a lines was added to config file and some tests started to fail.
# NB: EmCache is always disabled when debug => 2
# NB: There can be a difference in assets used when debug = 0 or 2 (minified version or not)
# sed -i '61s/.*/ "debug"=>2,/' application/config/config.php
# cat application/config/config.php
- name: Test the server
run: |
wget localhost
cat index.html
# Chromedriver setup.
# Note: Keep getting timeout problems on Travis with chromedriver.
# wget https://chromedriver.storage.googleapis.com/2.33/chromedriver_linux64.zip
# unzip chromedriver_linux64.zip
- name: Set up Selenium with firefox
run: |
which firefox
firefox -v
# Setup Selenium with Firefox headless mode, Gecko driver already installed
wget "https://selenium-release.storage.googleapis.com/3.7/selenium-server-standalone-3.7.1.jar"
export MOZ_HEADLESS=1
java -jar selenium-server-standalone-3.7.1.jar -enablePassThrough false > /dev/null 2> /dev/null &
- name: Check for syntax errors
run: find application/ -type f -name '*.php' -print0 | xargs -I {} -0 -P 0 php -l {}
- name: Run the test script
run: |
DOMAIN=localhost ./third_party/bin/phpunit --stop-on-failure
\ No newline at end of file
#
# Note: If you ever need to debug Travis locally, see this
# documentation: https://docs.travis-ci.com/user/common-build-problems/#Running-a-Container-Based-Docker-Image-Locally
# dummy
language: php
php:
- 5.6
- 7.0
- 7.1
- 7.2
- 7.3
- 7.4
services:
- mysql
notifications:
email:
recipients:
- secure: "1x0CmJbr8GWdS7OJ5PRchf0lDxNk4quRfbZ4Slu4GnEMh599Bo1jhsRZnc5ghOf9Yuwn4FzB4Oyfr6tIXP63FbUE2F4lhCwluxw7BWRmjvwMncctWqgDu7Jn3HRXlTdoVNhPlspBsbsNw7waeER6+jv6YFyMKH6gYXtUqloCuyrrhkLWetpj5qvGXmEV22Rhh0kLl9q/Khu0hI8eS022wLs1hQFurc4xbCvpSpcSzmxaD6gevKCuRiLJbG3QsT7lTAwm6U1QqSEMQP5QKhvQ01klttVD3KG2/F6Gs5pKhiNSKYmzeI5OqU7BggQQKCTBi1G0FNsqtFi+InwBoY09RrHpbGDzCOfeRqr/DQAiZKxt2p4d7OEwPcbojvHSG4gG3hIfScCyJGNVa9hZAfR7qyg1wcQdAUfZB+DFNE9FaYdXHFsjKtq/iHROqnS6FNFJF2hRR8diltDTdra5A81LuZcsA82WAj1PPT07kzzKfcbSHHfHaT2H8bzZ3LLd7rjUqW1hd5pK/TfhtPOLCiSLmcktbppcM5V64e9ZoPmywVsFD3GJ+kxBqQ6hx40JcyKkFmiU6WGoYurt/OhXMvRtkWVbeF52M7Mzpx9Iyh48mf3Xm55Q5GnYv0gKKgwYAllPOFXuNCk+DVcs4inpTvwQsCt/7W2RGwMIxlDhJ0+QySk="
on_success: change
on_failure: always
before_script:
# Test
#- sudo apt-get update
#- sudo apt-get upgrade
# disable xdebug to speed up tests
- phpenv config-rm xdebug.ini
# Check for syntax errors.
- find application/ -type f -name '*.php' -print0 | xargs -I {} -0 -P 0 php -l {}
# Install LimeSurvey.
- mysql --version
- sudo service mysql status
- touch enabletests
# NB: PHPUnit 6.5.* is installed with composer.
- composer install
- ./third_party/bin/phpunit --version
- chmod -R 777 tmp
- chmod -R 777 upload
- chmod -R 777 themes # Need 777 so both console and web server can cd into the folder.
- chmod -R 777 tests/tmp
- chmod +x tests/bin/lint-*
- php application/commands/console.php install admin password TravisLS no@email.com verbose
- cp application/config/config-sample-mysql.php application/config/config.php
# Enable debug=2 in config file. OBS: This assumes debug is on line 61.
# TODO: Disable, a lines was added to config file and some tests started to fail.
#- sed -i '61s/.*/ "debug"=>2,/' application/config/config.php
# Install Apache.
# Code fetched from https://docs.travis-ci.com/user/languages/php/#Apache-%2B-PHP
- sudo apt-get update > /dev/null
- sudo apt-get -y --force-yes install apache2 libapache2-mod-fastcgi nodejs firefox
- sudo cp /usr/bin/firefox /usr/local/bin/firefox
- sudo cp /usr/bin/firefox /usr/local/bin/firefox-bin
- which firefox
- firefox -v
- sudo cp ~/.phpenv/versions/$(phpenv version-name)/etc/php-fpm.conf.default ~/.phpenv/versions/$(phpenv version-name)/etc/php-fpm.conf
- if [[ ${TRAVIS_PHP_VERSION:0:3} != "5.6" ]]; then sudo cp ~/.phpenv/versions/$(phpenv version-name)/etc/php-fpm.d/www.conf.default ~/.phpenv/versions/$(phpenv version-name)/etc/php-fpm.d/www.conf; fi
- sudo a2enmod rewrite actions fastcgi alias
- echo "cgi.fix_pathinfo = 1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
- sudo sed -i -e "s,www-data,travis,g" /etc/apache2/envvars
- sudo chown -R travis:travis /var/lib/apache2/fastcgi
- ~/.phpenv/versions/$(phpenv version-name)/sbin/php-fpm
- sudo cp -f tests/travis/travis-ci-apache /etc/apache2/sites-available/000-default.conf
- sudo sed -e "s?%TRAVIS_BUILD_DIR%?$(pwd)?g" --in-place /etc/apache2/sites-available/000-default.conf
- sudo service apache2 restart
# Test server.
- wget localhost
- cat index.html
# Chromedriver setup.
# Note: Keep getting timeout problems on Travis with chromedriver.
#- wget https://chromedriver.storage.googleapis.com/2.33/chromedriver_linux64.zip
#- unzip chromedriver_linux64.zip
# Setup Selenium with Firefox headless mode.
- wget "https://selenium-release.storage.googleapis.com/3.7/selenium-server-standalone-3.7.1.jar"
- wget "https://github.com/mozilla/geckodriver/releases/download/v0.27.0/geckodriver-v0.27.0-linux64.tar.gz"
- tar xvzf geckodriver-v0.27.0-linux64.tar.gz
- export MOZ_HEADLESS=1
- java -jar selenium-server-standalone-3.7.1.jar -enablePassThrough false > /dev/null 2> /dev/null &
script:
# Run tests.
- DOMAIN=localhost ./third_party/bin/phpunit --stop-on-failure
......@@ -29,7 +29,7 @@ return array(
'connectionString' => 'mysql:host=localhost;port=3306;dbname=limesurvey;',
'emulatePrepare' => true,
'username' => 'root',
'password' => '',
'password' => 'root',
'charset' => 'utf8mb4',
'tablePrefix' => 'lime_',
),
......
......@@ -11,10 +11,10 @@
* See COPYRIGHT.php for copyright notices and details.
*/
$config['versionnumber'] = '3.27.1.0-Tika-1.0.2';
$config['versionnumber'] = '3.27.9.1-Tika-1.0.2';
$config['dbversionnumber'] = 365;
$config['buildnumber'] = '';
$config['updatable'] = false;
$config['templateapiversion'] = 3;
$config['assetsversionnumber'] = '30192';
$config['assetsversionnumber'] = '30200';
return $config;
......@@ -870,7 +870,13 @@ class InstallerController extends CController
// gd library check
if (function_exists('gd_info')) {
$aData['gdPresent'] = $this->check_HTML_image(array_key_exists('FreeType Support', gd_info()));
$gdInfo = gd_info();
$gdHasJpegSupport = !empty($gdInfo['JPEG Support']);
if ($gdHasJpegSupport) {
$aData['gdPresent'] = $this->check_HTML_image(true);
} else {
$aData['gdPresent'] = $this->check_HTML_image(false) . '<br/>' . gT("The GD extension found doesn't support JPEG");
}
} else {
$aData['gdPresent'] = $this->check_HTML_image(false);
}
......
......@@ -20,6 +20,9 @@ class UploaderController extends SurveyController
{
global $surveyid; // needed for getHeader below!
$surveyid = Yii::app()->session['LEMsid'];
if(empty($surveyid)) {
throw new CHttpException(401, gT("We are sorry but your session has expired."));
}
$oSurvey = Survey::model()->findByPk($surveyid);
if (!$oSurvey) {
throw new CHttpException(400);
......
......@@ -1280,7 +1280,7 @@ class dataentry extends Survey_Common_Action
$surveyid = sanitize_int($surveyid);
$survey = Survey::model()->findByPk($surveyid);
$id = $_REQUEST['id'];
$id = (int) $_REQUEST['id'];
$aData = array(
'surveyid' => $surveyid,
......@@ -1292,7 +1292,7 @@ class dataentry extends Survey_Common_Action
$surveytable = $survey->responsesTableName;
$aData['thissurvey'] = getSurveyInfo($surveyid);
$delquery = "DELETE FROM $surveytable WHERE id=$id";
$delquery = "DELETE FROM $surveytable WHERE id= " . (int) $id;
Yii::app()->loadHelper('database');
$beforeDataEntryDelete = new PluginEvent('beforeDataEntryDelete');
......@@ -1660,7 +1660,7 @@ class dataentry extends Survey_Common_Action
App()->getPluginManager()->dispatchEvent($beforeDataEntryCreate);
$new_response->save();
$last_db_id = $new_response->getPrimaryKey();
$last_db_id = (int) $new_response->getPrimaryKey();
if (isset($_POST['closerecord']) && isset($_POST['token']) && $_POST['token'] != '') {
// submittoken
// get submit date
......@@ -1695,7 +1695,7 @@ class dataentry extends Survey_Common_Action
dbExecuteAssoc($utquery); //Yii::app()->db->Execute($utquery) or safeDie ("Couldn't update tokens table!<br />\n$utquery<br />\n".Yii::app()->db->ErrorMsg());
// save submitdate into survey table
$sdquery = "UPDATE {{survey_$surveyid}} SET submitdate='".$submitdate."' WHERE id={$last_db_id}\n";
$sdquery = "UPDATE {{survey_$surveyid}} SET submitdate=" . App()->db->quoteValue($submitdate) . " WHERE id={$last_db_id}\n";
dbExecuteAssoc($sdquery) or safeDie("Couldn't set submitdate response in survey table!<br />\n$sdquery<br />\n");
}
if (isset($_POST['save']) && $_POST['save'] == "on") {
......
......@@ -1911,9 +1911,10 @@ $url .= "_view"; });
public function editShareInfo()
{
$operation = Yii::app()->request->getPost('oper');
// NB: Comma-separated list.
$shareIds = Yii::app()->request->getPost('id');
if ($operation == 'del') {
// If operation is delete , it will delete, otherwise edit it
// If operation is delete , it will delete, otherwise edit it
ParticipantShare::model()->deleteRow($shareIds);
} else {
$aData = array(
......@@ -2145,10 +2146,10 @@ $url .= "_view"; });
foreach ($participantIds as $id) {
$time = time();
$aData = array(
'participant_id' => (int) $id,
'participant_id' => $id,
'share_uid' => $iShareUserId,
'date_added' => date('Y-m-d H:i:s', $time),
'can_edit' => $bCanEdit
'can_edit' => ($bCanEdit === false ? 0 : 1)
);
ParticipantShare::model()->storeParticipantShare($aData, $permissions);
$i++;
......
......@@ -635,8 +635,13 @@ class responses extends Survey_Common_Action
$aQuestionFiles = $oResponse->getFiles($iQID);
if (isset($aQuestionFiles[$iIndex])) {
$aFile = $aQuestionFiles[$iIndex];
$sFileRealName = Yii::app()->getConfig('uploaddir')."/surveys/".$iSurveyId."/files/".$aFile['filename'];
if (file_exists($sFileRealName)) {
// Real path check from here: https://stackoverflow.com/questions/4205141/preventing-directory-traversal-in-php-but-allowing-paths
$sDir = Yii::app()->getConfig('uploaddir') . "/surveys/" . $iSurveyId . "/files/";
$sFileRealName = $sDir . $aFile['filename'];
$sRealUserPath = realpath($sFileRealName);
if ($sRealUserPath === false || strpos($sRealUserPath, $sDir) !== 0) {
throw new CHttpException(403, "Disable for security reasons.");
} else {
$mimeType = CFileHelper::getMimeType($sFileRealName, null, false);
if (is_null($mimeType)) {
$mimeType = "application/octet-stream";
......
......@@ -218,7 +218,6 @@ class SurveyRuntimeHelper
}
if ($this->sSurveyMode != 'survey' && isset($this->aSurveyInfo['showprogress']) && $this->aSurveyInfo['showprogress'] == 'Y') {
if ($this->bShowEmptyGroup) {
$this->aSurveyInfo['progress']['currentstep'] = $_SESSION[$this->LEMsessid]['totalsteps'] + 1;
$this->aSurveyInfo['progress']['total'] = $_SESSION[$this->LEMsessid]['totalsteps'];
......@@ -226,6 +225,8 @@ class SurveyRuntimeHelper
$this->aSurveyInfo['progress']['currentstep'] = $_SESSION[$this->LEMsessid]['step'];
$this->aSurveyInfo['progress']['total'] = isset($_SESSION[$this->LEMsessid]['totalsteps']) ? $_SESSION[$this->LEMsessid]['totalsteps'] : 1;
}
/* String used in vanilla/views/subviews/header/progress_bar.twig : for autotranslation */
$this->aSurveyInfo['progress']['string'] = gT('You have completed %s%% of this survey');
}
$this->aSurveyInfo['yiiflashmessages'] = Yii::app()->user->getFlashes();
......
......@@ -6671,11 +6671,19 @@
}
break;
case 'R':
if (count($unansweredSQs) > 0)
{
$qattr = isset($LEM->qattr[$qid]) ? $LEM->qattr[$qid] : array();
// If min_answers or max_answers is set, we check that at least one answer is ranked.
// But, if no limit is set, then all answers must be ranked.
if (!empty($qattr['min_answers']) || !empty($qattr['max_answers'])) {
$maxUnrankedAnswers = count($relevantSQs) - 1;
$sMandatoryText = $LEM->gT('Please rank the items.');
} else {
$maxUnrankedAnswers = 0;
$sMandatoryText = $LEM->gT('Please rank all items.');
}
if (count($unansweredSQs) > $maxUnrankedAnswers) {
$qmandViolation = true; // TODO - what about 'other'?
}
$sMandatoryText = $LEM->gT('Please rank all items.');
$mandatoryTip .= Yii::app()->getController()->renderPartial('//survey/questions/question_help/mandatory_tip', array(
'sMandatoryText'=>$sMandatoryText,
), true);
......
......@@ -31,19 +31,15 @@ function loadanswers()
$sLoadName = Yii::app()->request->getParam('loadname');
$sLoadPass = Yii::app()->request->getParam('loadpass');
$oCriteria = new CDbCriteria();
$oCriteria->select = 'sc.*';
$oCriteria->join = "LEFT JOIN {{saved_control}} as sc ON t.id=sc.srid";
$oCriteria->condition = "sc.sid=:sid";
$aParams = [':sid' => $surveyid];
if (isset($scid)) {
//Would only come from email : we don't need it ....
$oCriteria->addCondition("sc.scid=:scid");
$oCriteria->addCondition("saved_control.scid=:scid");
$aParams[':scid'] = $scid;
}
$oCriteria->addCondition("sc.identifier=:identifier");
$oCriteria->addCondition("saved_control.identifier=:identifier");
$aParams[':identifier'] = $sLoadName;
} elseif (isset($_SESSION['survey_' . $surveyid]['srid'])) {
$oCriteria = new CDbCriteria;
$oCriteria = new CDbCriteria();
$oCriteria->condition = "id=:id";
$aParams = [':id' => $_SESSION['survey_' . $surveyid]['srid']];
} else {
......@@ -75,7 +71,10 @@ function loadanswers()
$_SESSION['survey_' . $surveyid]['srid'] = $saved_control->srid; // Seems OK without
$_SESSION['survey_' . $surveyid]['refurl'] = $saved_control->refurl;
}
} else {
return false;
}
}
// Get if survey is been answered
$submitdate = $oResponses->submitdate;
......@@ -186,7 +185,7 @@ function getLanguageChangerDatas($sSelectedLanguage = "")
$aListLang = array();
foreach ($aSurveyLangs as $sLangCode => $aSurveyLang) {
$aListLang[$sLangCode] = html_entity_decode($aSurveyLang['nativedescription'], ENT_COMPAT, 'UTF-8');
$aListLang[$sLangCode] = html_entity_decode($aSurveyLang['nativedescription'], ENT_COMPAT, 'UTF-8') . ' - ' . $aSurveyLang['description'];
}
$sSelected = ($sSelectedLanguage) ? $sSelectedLanguage : App()->language;
......
......@@ -2749,6 +2749,47 @@ class remotecontrol_handle
}
}
/**
* Delete a response in a given survey using its id
*
* RPC Routine to delete responses of particular id in a survey.
* Returns array
*
* @access public
* @param string $sSessionKey Auth credentials
* @param int $iSurveyID Id of the survey that participants belong
* @param int $iResponseID Id of the response to delete
* @return array Result of the change action
*/
public function delete_response($sSessionKey, $iSurveyID, $iResponseID)
{
// check sessionKey is valid or not
if ($this->_checkSessionKey($sSessionKey)) {
$oSurvey = Survey::model()->findByPk($iSurveyID);
if (!isset($oSurvey)) {
return array('status' => 'Error: Invalid survey ID');
}
if (Permission::model()->hasSurveyPermission($iSurveyID, 'responses', 'delete')) {
// get response id from response table using ID
$Response = Response::model($iSurveyID)->findByPk($iResponseID);
if ($Response) {
// delete the files and timings and row
if ($Response->delete()) {
return array($iResponseID => 'deleted');
}
return array('status' => 'Response not deleted for unknow reason');
} else {
return array('status' => 'Response Id not found');
}
} else {
return array('status' => 'No permission');
}
} else {
return array('status' => 'Invalid Session Key');
}
}
/**
* Uploads one file to be used later.
*
......
......@@ -726,6 +726,14 @@ function getLanguageData($bOrderByNative = false, $sLanguageCode = 'en')
$supportedLanguages['ru']['radixpoint'] = 1;
$supportedLanguages['ru']['momentjs'] = 'ru';
// Sami
$supportedLanguages['smi']['description'] = gT('Sami');
$supportedLanguages['smi']['nativedescription'] = 'Sámi';
$supportedLanguages['smi']['rtl'] = false;
$supportedLanguages['smi']['dateformat'] = 4;
$supportedLanguages['smi']['radixpoint'] = 1;
$supportedLanguages['smi']['momentjs'] = 'sme';
// Serbian
$supportedLanguages['sr']['description'] = gT('Serbian (Cyrillic)');
$supportedLanguages['sr']['nativedescription'] = '&#1057;&#1088;&#1087;&#1089;&#1082;&#1080;';
......@@ -936,7 +944,7 @@ function getLanguageData($bOrderByNative = false, $sLanguageCode = 'en')
$supportedLanguages['cy']['radixpoint'] = 0;
$supportedLanguages['cy']['momentjs'] = 'cy';
// Azerbaijani
// Yakut
$supportedLanguages['sah']['description'] = gT('Yakut');
$supportedLanguages['sah']['nativedescription'] = '&#x421;&#x430;&#x445;&#x430; &#x442;&#x44B;&#x43B;&#x430;';
$supportedLanguages['sah']['rtl'] = false;
......
......@@ -58,7 +58,7 @@ class FailedLoginAttempt extends LSActiveRecord
*/
public function deleteAttempts()
{
$ip = substr(App()->getRequest()->getUserHostAddress(), 0, 40);
$ip = substr(getIPAddress(), 0, 40);
$this->deleteAllByAttributes(array('ip' => $ip));
}
......@@ -72,7 +72,7 @@ class FailedLoginAttempt extends LSActiveRecord
public function isLockedOut($attemptType = '')
{
$isLockedOut = false;
$ip = substr(App()->getRequest()->getUserHostAddress(), 0, 40);
$ip = substr(getIPAddress(), 0, 40);
// Return false if IP is whitelisted
if (!empty($attemptType) && $this->isWhitelisted($ip, $attemptType)) {
......@@ -117,7 +117,7 @@ class FailedLoginAttempt extends LSActiveRecord
{
if (!$this->isLockedOut()) {
$timestamp = date("Y-m-d H:i:s");
$ip = substr(App()->getRequest()->getUserHostAddress(), 0, 40);
$ip = substr(getIPAddress(), 0, 40);
$row = $this->findByAttributes(array('ip' => $ip));
if ($row !== null) {
......
......@@ -233,7 +233,7 @@ class ParticipantShare extends LSActiveRecord
"name" => 'share_uid',
"value" => '$data->sharedBy',
"type" => 'raw',
"header" => gT("Shared by"),
"header" => gT("Shared with"),
"filter" => $this->getSharedByList($this->share_uid)
),
array(
......@@ -382,7 +382,14 @@ class ParticipantShare extends LSActiveRecord
list($participantId, $uId) = explode("--", $row);
Yii::app()->db
->createCommand()
->delete('{{participant_shares}}', "participant_id = '$participantId' AND share_uid = $uId");
->delete(
'{{participant_shares}}',
sprintf(
"participant_id = '%d' AND share_uid = %d",
(int) $participantId,
(int) $uId
)
);
}
}
......
......@@ -10,6 +10,7 @@
{
if (parent::beforeDelete()) {
$this->deleteFiles();
$this->deleteTimings();
return true;
}
return false;
......@@ -132,6 +133,16 @@
return $errors;
}
/**
* Delete timings if savetimings is set.
*/
public function deleteTimings()
{
if (Survey::model()->findByPk($this->dynamicId)->isSaveTimings) {
SurveyTimingDynamic::model($this->dynamicId)->deleteByPk($this->id);
}
}
/**
* Delete uploaded files for this response AND modify
* response data to reflect all changes.
......
This diff is collapsed.
# Security notices relating to PHPMailer
Please disclose any vulnerabilities found responsibly - report any security problems found to the maintainers privately.
Please disclose any security issues or vulnerabilities found through [Tidelift's coordinated disclosure system](https://tidelift.com/security) or to the maintainers privately.
PHPMailer 6.4.1 and earlier contain a vulnerability that can result in untrusted code being called (if such code is injected into the host project's scope by other means). If the `$patternselect` parameter to `validateAddress()` is set to `'php'` (the default, defined by `PHPMailer::$validator`), and the global namespace contains a function called `php`, it will be called in preference to the built-in validator of the same name. Mitigated in PHPMailer 6.5.0 by denying the use of simple strings as validator function names. Recorded as [CVE-2021-3603](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2021-3603). Reported by [Vikrant Singh Chauhan](mailto:vi@hackberry.xyz) via [huntr.dev](https://www.huntr.dev/).
PHPMailer versions 6.4.1 and earlier contain a possible remote code execution vulnerability through the `$lang_path` parameter of the `setLanguage()` method. If the `$lang_path` parameter is passed unfiltered from user input, it can be set to [a UNC path](https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats#unc-paths), and if an attacker is also able to persuade the server to load a file from that UNC path, a script file under their control may be executed. This vulnerability only applies to systems that resolve UNC paths, typically only Microsoft Windows.
PHPMailer 6.5.0 mitigates this by no longer treating translation files as PHP code, but by parsing their text content directly. This approach avoids the possibility of executing unknown code while retaining backward compatibility. This isn't ideal, so the current translation format is deprecated and will be replaced in the next major release. Recorded as [CVE-2021-34551](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2021-34551). Reported by [Jilin Diting Information Technology Co., Ltd](https://listensec.com) via Tidelift.
PHPMailer versions between 6.1.8 and 6.4.0 contain a regression of the earlier CVE-2018-19296 object injection vulnerability as a result of [a fix for Windows UNC paths in 6.1.8](https://github.com/PHPMailer/PHPMailer/commit/e2e07a355ee8ff36aba21d0242c5950c56e4c6f9). Recorded as [CVE-2020-36326](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2020-36326). Reported by Fariskhi Vidyan via Tidelift. 6.4.1 fixes this issue, and also enforces stricter checks for URL schemes in local path contexts.
PHPMailer versions 6.1.5 and earlier contain an output escaping bug that occurs in `Content-Type` and `Content-Disposition` when filenames passed into `addAttachment` and other methods that accept attachment names contain double quote characters, in contravention of RFC822 3.4.1. No specific vulnerability has been found relating to this, but it could allow file attachments to bypass attachment filters that are based on matching filename extensions. Recorded as [CVE-2020-13625](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2020-13625). Reported by Elar Lang of Clarified Security.
PHPMailer versions prior to 6.0.6 and 5.2.27 are vulnerable to an object injection attack by passing `phar://` paths into `addAttachment()` and other functions that may receive unfiltered local paths, possibly leading to RCE. Recorded as [CVE-2018-19296](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2018-19296). See [this article](https://knasmueller.net/5-answers-about-php-phar-exploitation) for more info on this type of vulnerability. Mitigated by blocking the use of paths containing URL-protocol style prefixes such as `phar://`. Reported by Sehun Oh of cyberone.kr.
......
# Upgrading from PHPMailer 5.2 to 6.0
PHPMailer 6.0 is a major update, breaking backward compatibility.
If you're in doubt about how you should be using PHPMailer 6, take a look at the examples as they have all been updated to work in a PHPMailer 6.0 style.
## PHP Version
PHPMailer 6.0 requires PHP 5.5 or later, and is fully compatible with PHP 7.0. PHPMailer 5.2 supported PHP 5.0 and upwards, so if you need to run on a legacy PHP version, see the [PHPMailer 5.2-stable branch on Github](https://github.com/PHPMailer/PHPMailer/tree/5.2-stable).
## Loading PHPMailer
The single biggest change will be in the way that you load PHPMailer. In earlier versions you may have done this:
```php
require 'PHPMailerAutoload.php';
```
or
```php
require 'class.phpmailer.php';
require 'class.smtp.php';
```
We recommend that you load PHPMailer via composer, using its standard autoloader, which you probably won't need to load if you're using it already, but in case you're not, you will need to do this instead:
```php
require 'vendor/autoload.php';
```
If you're not using composer, you can still load the classes manually, depending on what you're using:
```php
require 'src/PHPMailer.php';
require 'src/SMTP.php';
require 'src/Exception.php';
```
## Namespace
PHPMailer 6 uses a [namespace](http://php.net/manual/en/language.namespaces.rationale.php) of `PHPMailer\PHPMailer`, because it's the PHPMailer project within the PHPMailer organisation. You **must** import (with a `use` statement) classes you're using explicitly into your own namespace, or reference them absolutely in the global namespace - all the examples do this. This means the fully-qualified name of the main PHPMailer class is `PHPMailer\PHPMailer\PHPMailer`, which is a bit of a mouthful, but there's no harm in it! If you are using other PHPMailer classes explicitly (such as `SMTP` or `Exception`), you will need to import them into your namespace too.
For example you might create an instance like this:
```php
<?php
namespace MyProject;
use PHPMailer\PHPMailer\PHPMailer;
require 'vendor/autoload.php';
$mail = new PHPMailer;
...
```
or alternatively, using a fully qualified name:
```php
<?php
namespace MyProject;
require 'vendor/autoload.php';
$mail = new PHPMailer\PHPMailer\PHPMailer;
...
```
Note that `use` statements apply *only* to the file they appear in (they are local aliases), so if an included file contains `use` statements, it will not import the namespaced classes into the file you're including from.
## Namespaced exceptions
PHPMailer now uses its own namespaced `Exception` class, so if you were previously catching exceptions of type `phpmailerException` (or subclasses of that), you will need to update them to use the PHPMailer namespace, and make any existing `Exception` references use the global namespace, i.e. `\Exception`. If your original code was:
```php
try {
...
} catch (phpmailerException $e) {
echo $e->errorMessage();
} catch (Exception $e) {
echo $e->getMessage();
}
```
Convert it to:
```php
use PHPMailer\PHPMailer\Exception;
...
try {
...
} catch (Exception $e) {
echo $e->errorMessage();
} catch (\Exception $e) {
echo $e->getMessage();
}
```
## OAuth2 Support
The OAuth2 implementation has been completely redesigned using the [OAuth2 packages](http://oauth2-client.thephpleague.com) from the [League of extraordinary packages](http://thephpleague.com), providing support for many more OAuth services, and you'll need to update your code if you were using OAuth in 5.2. See [the examples](https://github.com/PHPMailer/PHPMailer/tree/master/examples) and documentation in the [PHPMailer wiki](https://github.com/PHPMailer/PHPMailer/wiki).
## Extras
Additional classes previously bundled in the `Extras` folder (such as htmlfilter and EasyPeasyICS) have been removed - use equivalent packages from [packagist.org](https://packagist.org) instead.
## Other upgrade changes
See the changelog for full details.
* File structure simplified, classes live in the `src/` folder
* Most statically called functions now use the `static` keyword instead of `self`, so it's possible to override static internal functions in subclasses, for example `validateAddress()`
* Complete RFC standardisation on CRLF (`\r\n`) line breaks by default:
* `PHPMailer::$LE` still exists, but all uses of it are changed to `static::$LE` for easier overriding. It may be changed to `\n` automatically when sending via `mail()` on UNIX-like OSs
* `PHPMailer::CRLF` line ending constant removed
* The length of the line break is no longer used in line length calculations
* Similar changes to line break handling in SMTP and POP3 classes
* All elements previously marked as deprecated have been removed:
* `PHPMailer->Version`
* `PHPMailer->ReturnPath`
* `PHPMailer->PluginDir`
* `PHPMailer->encodeQPphp()`
* `SMTP->CRLF`
* `SMTP->Version`
* `SMTP->SMTP_PORT`
* `POP3->CRLF`
* `POP3->Version`
* NTLM authentication has been removed - it never worked anyway!
* `PHPMailer->Workstation`
* `PHPMailer->Realm`
* `SMTP::authenticate` method signature changed
* `parseAddresses()` is now static
* `validateAddress()` is now called statically from `parseAddresses()`
* `idnSupported()` is now static and is called statically from `punyencodeAddress()`
* `PHPMailer->SingleToArray` is now protected