diff --git a/Documentation/Installation.rst b/Documentation/Installation.rst index 7d68b5279757aac1f3691249184ff1e0706a7a8c..ce2db9bc54eed0ba10292d401f48f8d4f8639790 100644 --- a/Documentation/Installation.rst +++ b/Documentation/Installation.rst @@ -561,6 +561,7 @@ Extension Manager: QFQ Configuration +-----------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+ | formSubmitLogMode | all | | *all*: every form submission will be logged. | | | | | *none*: no logging. | +| | | | *modify*: prevent logging tables: FormSubmitLog and Dirty. | | | | | See :ref:`form-submit-log-page` for example QFQ code to display the log. | +-----------------------------------+-------------------------------------------------------+----------------------------------------------------------------------------+ | redirectAllMailTo | john@doe.com | If set, redirect all QFQ generated mails (Form, Report) to the specified. | diff --git a/Documentation/Store.rst b/Documentation/Store.rst index 10590852acb522de8f1eeca826dc24eb45a6bd56..8e1256da9687555cac517962550fe44733985953 100644 --- a/Documentation/Store.rst +++ b/Documentation/Store.rst @@ -286,11 +286,9 @@ QFQ values +-------------------+-------------+--------------------------------------------------------------------------------------------------------------------------------------------+ | Name | Scope | Explanation | +===================+=============+============================================================================================================================================+ -| random | Always | Random string with length of 32 alphanum chars (lower & upper case). This variable is always filled. Each access gives a new value. | -| | | Uniqueness is added via PHP function *uniqid()* which just prepends 13 chars at the beginning of the random string. *uniqid()* is only a | -| | | very precise timestamp, therefore these first 13 chars should *not* be taken as random! They should make uniqness more likely. | +| random | Always | Random string with length of 32 alphanum chars (lower & upper case). See :ref:`RANDOM`. | +-------------------+-------------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| slaveId | FE *any* | see :ref:`slave-id` | +| slaveId | FE *any* | See :ref:`slave-id` | +-------------------+-------------+--------------------------------------------------------------------------------------------------------------------------------------------+ | allRequiredGiven | Form | Form save - Set during check of all required FE. If every *required* FE is given: *1*. Else: *0*. If there is no required FE: *1*. | | | | Even with ``formModeGlobal = requiredOff | requiredOffButMark`` the variable will be set. | @@ -310,6 +308,25 @@ QFQ values | mimeType | FE *upload* | Mime type of the uploaded file. | +-------------------+-------------+--------------------------------------------------------------------------------------------------------------------------------------------+ +.. _RANDOM: + +{{random:V}} +^^^^^^^^^^^^ + +* The variable *{{random:V}}* is always filled. +* Hint: QFQ variables will be replaced **before** a SQL statement is fired. + + * Each access gives a new value. + * Execution of a SQL statement is **one** access - therefore ``SELECT '{{random:V}}' FROM Person`` will give the same + random value for all *Person*. + * Several ``SELECT ...{{random:V}}...`` will give several different random values. | + +* Uniqueness is added via PHP function *uniqid()* + + * This just prepends 13 chars at the beginning of the random string. + * *uniqid()* is only a very precise timestamp - timestamp means it becomes bigger and bigger and the first characters stays the same. + * These first 13 chars should *not* be taken as random! They will make *uniqness* for *{{random:V}}* more likely. + Custom values (via fillStoreVar) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/extension/Classes/Core/Constants.php b/extension/Classes/Core/Constants.php index 93508a2c5d68bb50a1265f4c97e03ca7642ea6eb..3f0256a83579cc55e99e47a365d70be40ffbee26 100644 --- a/extension/Classes/Core/Constants.php +++ b/extension/Classes/Core/Constants.php @@ -482,6 +482,7 @@ const CLIENT_SERVER_NAME = 'SERVER_NAME'; const CLIENT_SERVER_ADDRESS = 'SERVER_ADDR'; const CLIENT_SERVER_PORT = 'SERVER_PORT'; const CLIENT_REMOTE_ADDRESS = 'REMOTE_ADDR'; +const CLIENT_HTTP_X_REAL_IP = 'HTTP_X_REAL_IP'; const CLIENT_REQUEST_SCHEME = 'REQUEST_SCHEME'; const CLIENT_REQUEST_METHOD = 'REQUEST_METHOD'; const CLIENT_SCRIPT_FILENAME = 'SCRIPT_FILENAME'; @@ -569,6 +570,7 @@ const FORCE_RUN_PAGE_SLUG_MIGRATION_CHECK = 'FORCE_RUN_PAGE_SLUG_MIGRATION_CHECK const SYSTEM_FORM_SUBMIT_LOG_MODE = 'formSubmitLogMode'; const FORM_SUBMIT_LOG_MODE_ALL = 'all'; const FORM_SUBMIT_LOG_MODE_NONE = 'none'; +const FORM_SUBMIT_LOG_MODE_MODIFY = 'modify'; const SYSTEM_DATE_FORMAT = 'dateFormat'; const SYSTEM_DATE_TIME_PICKER_TYPE = 'dateTimePickerType'; @@ -791,6 +793,7 @@ const SYSTEM_PROTECTED_FOLDER_CHECK = 'protectedFolderCheck'; const EXTRA_ENABLE_SWITCH = 'enableSwitch'; const EXTRA_COLUMN_NAME_AlIAS_SLUG = 'columnNameAliasSlug'; +const EXTRA_FORM_SUBMIT_LOG_ID = 'formSubmitLogId'; const MODE_HTML = 'html'; const MODE_JSON = 'json'; diff --git a/extension/Classes/Core/Database/Database.php b/extension/Classes/Core/Database/Database.php index a9ff6be46db545fb5b63053eea08aec54b67c271..d22f9dcb31aa446a3b6dd5e5d0cce091a15a8c5e 100644 --- a/extension/Classes/Core/Database/Database.php +++ b/extension/Classes/Core/Database/Database.php @@ -614,6 +614,7 @@ class Database { ['tt', TYPO3_TT_CONTENT_UID, STORE_TYPO3], ['level', SYSTEM_REPORT_FULL_LEVEL, STORE_SYSTEM], ['form', SIP_FORM, STORE_SIP], + ['fslId', EXTRA_FORM_SUBMIT_LOG_ID, STORE_EXTRA], ]; $t3msg = ''; diff --git a/extension/Classes/Core/QuickFormQuery.php b/extension/Classes/Core/QuickFormQuery.php index 5696161e8c6e9c3c578836b4bbfe14c7e945f6fe..403184332e3f004e744ded75376c7acc1b5d360f 100644 --- a/extension/Classes/Core/QuickFormQuery.php +++ b/extension/Classes/Core/QuickFormQuery.php @@ -178,7 +178,7 @@ class QuickFormQuery { $this->dbIndexT3 = $this->dbIndexQfq; if (!defined('PHPUNIT_QFQ')) { - $t3DbConfig = T3Handler::getTypo3DbConfig($this->dbIndexData, $this->dbIndexQfq,$this->dbIndexT3); + $t3DbConfig = T3Handler::getTypo3DbConfig($this->dbIndexData, $this->dbIndexQfq, $this->dbIndexT3); } // Create Typo3 db object if config information exist. In case of api, it doesn't exist. if (count($t3DbConfig) > 1 && $this->dbIndexT3 !== 0) { @@ -681,15 +681,17 @@ class QuickFormQuery { break; case FORM_SAVE: - $this->logFormSubmitRequest(); - + $formSubmitLogId = $this->logFormSubmitRequest(); $recordId = $this->store->getVar(SIP_RECORD_ID, STORE_SIP . STORE_TYPO3); // SAVE $save = new Save($this->formSpec, $this->feSpecAction, $this->feSpecNative, $this->feSpecNativeRaw); - $rc = $save->process($recordId); + if ($recordId == 0 && $formSubmitLogId != 0) { + // Update recordId from formSubmitLog + $this->updateLogFormSubmitRequest($formSubmitLogId, $rc); + } if ($formMode == FORM_REST) { $data = $this->doRestPostPut($rc); $flagApiStructureReGroup = false; @@ -902,17 +904,28 @@ class QuickFormQuery { } /** + * Logs the form submit to table FormSubmitLog. + * * @throws \CodeException * @throws \DbException * @throws \UserFormException */ private function logFormSubmitRequest() { + $tableName = $this->formSpec[F_TABLE_NAME]; $formSubmitLogMode = $this->formSpec[F_FORM_SUBMIT_LOG_MODE] ?? $this->store->getVar(SYSTEM_FORM_SUBMIT_LOG_MODE, STORE_SYSTEM, SANITIZE_ALLOW_ALNUMX); if ($formSubmitLogMode === FORM_SUBMIT_LOG_MODE_NONE) { - return; + return 0; + } + + // Log is ignored in some special cases while mode LOG_MODIFY + if ($formSubmitLogMode === FORM_SUBMIT_LOG_MODE_MODIFY) { + // If table FormSubmitLog and given statement INSERT/UPDATE and table Dirty and given statement INSERT/UPDATE/DELETE + if ($tableName === 'FormSubmitLog' || $tableName === 'Dirty') { + return 0; + } } $formData = $_POST; @@ -943,9 +956,10 @@ class QuickFormQuery { } $formDataJson = json_encode($formData, JSON_UNESCAPED_UNICODE); - // FormSubmitLog.formData= TEXT = 65535 $currentLength = strlen($formDataJson); + // FormSubmitLog.formData (TEXT) = 65535 if ($currentLength > LOG_MAX_FORMDATA) { + // Oops, FormSubmitLog can only store LOG_MAX_FORMDATA. // JSON will be shrinked: remove elements as long as it is too big, return rest. $formDataJson = json_encode(OnString::limitSizeJsonEncode($formData, $currentLength, LOG_MAX_FORMDATA), JSON_UNESCAPED_UNICODE); } @@ -953,8 +967,8 @@ class QuickFormQuery { $sql = "INSERT INTO `FormSubmitLog` (`formData`, `sipData`, `clientIp`, `feUser`, `userAgent`, `formId`, `formName`, `recordId`, `pageId`, `sessionId`, `created`)" . "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())"; - $clientIp = $_SERVER[CLIENT_REMOTE_ADDRESS] ?? ''; - $userAgent = $_SERVER[CLIENT_HTTP_USER_AGENT] ?? ''; + $clientIp = $this->store->getVar(CLIENT_REMOTE_ADDRESS, STORE_CLIENT . STORE_EMPTY); + $userAgent = $this->store->getVar(CLIENT_HTTP_USER_AGENT, STORE_CLIENT . STORE_EMPTY); $sipData = json_encode($this->store->getStore(STORE_SIP), JSON_UNESCAPED_UNICODE); $formId = $this->formSpec[F_ID]; $formName = $this->formSpec[F_NAME]; @@ -964,6 +978,24 @@ class QuickFormQuery { $params = [$formDataJson, $sipData, $clientIp, $feUser, $userAgent, $formId, $formName, $recordId, $pageId, $sessionId]; + $this->dbArray[$this->dbIndexQfq]->sql($sql, ROW_REGULAR, $params); + $formSubmitLogId = $this->dbArray[$this->dbIndexQfq]->getLastInsertId(); + $this->store::setVar(EXTRA_FORM_SUBMIT_LOG_ID, $formSubmitLogId, STORE_EXTRA); + + return $formSubmitLogId; + } + + /** Update last FormSubmitLog record with new inserted recordId + * @param $formSubmitLogId + * @param $recordId + * @throws \CodeException + * @throws \DbException + * @throws \UserFormException + */ + private function updateLogFormSubmitRequest($formSubmitLogId, $recordId) { + $sql = "UPDATE `FormSubmitLog` SET `recordId` = ? WHERE `id` = ?"; + $params = [$recordId, $formSubmitLogId]; + $this->dbArray[$this->dbIndexQfq]->sql($sql, ROW_REGULAR, $params); } diff --git a/extension/Classes/Core/Store/Client.php b/extension/Classes/Core/Store/Client.php index 0a5199d333c4218025a44e5eed40a03d929115e6..a0e56341cff1f2bf5352dc67d040fab00ee5aabf 100644 --- a/extension/Classes/Core/Store/Client.php +++ b/extension/Classes/Core/Store/Client.php @@ -52,6 +52,11 @@ class Client { $cookie[CLIENT_COOKIE_QFQ] = $_COOKIE[SESSION_NAME]; } + // In case we're bedind a proxy, use the real ip as remote_address + if (isset($_SERVER[CLIENT_HTTP_X_REAL_IP])) { + $_SERVER[CLIENT_REMOTE_ADDRESS] = $_SERVER[CLIENT_HTTP_X_REAL_IP]; + } + if (isset($_SERVER)) { $server = Sanitize::htmlentitiesArr($_SERVER); // $_SERVER values might be compromised. } diff --git a/extension/Classes/Core/Store/Store.php b/extension/Classes/Core/Store/Store.php index 62a7ea83efa35d23903471fa416f32c54def0603..bcde3238da1791ff7d55c1dfb9c26372dfca6634 100644 --- a/extension/Classes/Core/Store/Store.php +++ b/extension/Classes/Core/Store/Store.php @@ -124,6 +124,7 @@ class Store { CLIENT_SERVER_ADDRESS => SANITIZE_ALLOW_ALNUMX, CLIENT_SERVER_PORT => SANITIZE_ALLOW_DIGIT, CLIENT_REMOTE_ADDRESS => SANITIZE_ALLOW_ALNUMX, + CLIENT_HTTP_X_REAL_IP => SANITIZE_ALLOW_ALNUMX, CLIENT_REQUEST_SCHEME => SANITIZE_ALLOW_ALNUMX, CLIENT_REQUEST_METHOD => SANITIZE_ALLOW_ALNUMX, CLIENT_SCRIPT_FILENAME => SANITIZE_ALLOW_ALNUMX,