Edit File by line
/home/barbar84/www/wp-conte.../plugins/updraftp.../includes
File: S3.php
);
[1500] Fix | Delete
if (!isset($aHeaders['x-amz-content-sha256'])) {
[1501] Fix | Delete
$resultHeaders['x-amz-content-sha256'] = $payloadHash;
[1502] Fix | Delete
}
[1503] Fix | Delete
[1504] Fix | Delete
return $resultHeaders;
[1505] Fix | Delete
}
[1506] Fix | Delete
[1507] Fix | Delete
}
[1508] Fix | Delete
[1509] Fix | Delete
final class UpdraftPlus_S3Request {
[1510] Fix | Delete
[1511] Fix | Delete
private $endpoint, $verb, $bucket, $uri, $resource = '', $parameters = array(),
[1512] Fix | Delete
$amzHeaders = array(), $headers = array(
[1513] Fix | Delete
'Host' => '', 'Date' => '', 'Content-MD5' => '', 'Content-Type' => ''
[1514] Fix | Delete
);
[1515] Fix | Delete
public $fp = false, $size = 0, $data = false, $response;
[1516] Fix | Delete
[1517] Fix | Delete
private $s3;
[1518] Fix | Delete
[1519] Fix | Delete
/**
[1520] Fix | Delete
* Constructor
[1521] Fix | Delete
*
[1522] Fix | Delete
* @param string $verb Verb
[1523] Fix | Delete
* @param string $bucket Bucket name
[1524] Fix | Delete
* @param string $uri Object URI
[1525] Fix | Delete
* @param string $endpoint Endpoint of storage
[1526] Fix | Delete
* @param boolean $use_dns_bucket_name
[1527] Fix | Delete
* @param object $s3 S3 Object that calls these requests
[1528] Fix | Delete
*
[1529] Fix | Delete
* @return mixed
[1530] Fix | Delete
*/
[1531] Fix | Delete
function __construct($verb, $bucket = '', $uri = '', $endpoint = 's3.amazonaws.com', $use_dns_bucket_name = false, $s3 = null) {
[1532] Fix | Delete
$this->endpoint = $endpoint;
[1533] Fix | Delete
$this->verb = $verb;
[1534] Fix | Delete
$this->bucket = $bucket;
[1535] Fix | Delete
$this->uri = $uri !== '' ? '/'.str_replace('%2F', '/', rawurlencode($uri)) : '/';
[1536] Fix | Delete
$this->s3 = $s3;
[1537] Fix | Delete
[1538] Fix | Delete
//if ($this->bucket !== '')
[1539] Fix | Delete
// $this->resource = '/'.$this->bucket.$this->uri;
[1540] Fix | Delete
//else
[1541] Fix | Delete
// $this->resource = $this->uri;
[1542] Fix | Delete
[1543] Fix | Delete
if ('' !== $this->bucket) {
[1544] Fix | Delete
if ($this->__dnsBucketName($this->bucket) || $use_dns_bucket_name) {
[1545] Fix | Delete
$this->headers['Host'] = $this->bucket.'.'.$this->endpoint;
[1546] Fix | Delete
$this->resource = '/'.$this->bucket.$this->uri;
[1547] Fix | Delete
} else {
[1548] Fix | Delete
$this->headers['Host'] = $this->endpoint;
[1549] Fix | Delete
$this->uri = $this->uri;
[1550] Fix | Delete
if ('' !== $this->bucket) $this->uri = '/'.$this->bucket.$this->uri;
[1551] Fix | Delete
$this->bucket = '';
[1552] Fix | Delete
$this->resource = $this->uri;
[1553] Fix | Delete
}
[1554] Fix | Delete
} else {
[1555] Fix | Delete
$this->headers['Host'] = $this->endpoint;
[1556] Fix | Delete
$this->resource = $this->uri;
[1557] Fix | Delete
}
[1558] Fix | Delete
[1559] Fix | Delete
$this->headers['Date'] = gmdate('D, d M Y H:i:s T');
[1560] Fix | Delete
$this->response = new STDClass;
[1561] Fix | Delete
$this->response->error = false;
[1562] Fix | Delete
$this->response->body = null;
[1563] Fix | Delete
}
[1564] Fix | Delete
[1565] Fix | Delete
/**
[1566] Fix | Delete
* Set request parameter
[1567] Fix | Delete
*
[1568] Fix | Delete
* @param string $key Key
[1569] Fix | Delete
* @param string $value Value
[1570] Fix | Delete
*
[1571] Fix | Delete
* @return void
[1572] Fix | Delete
*/
[1573] Fix | Delete
public function setParameter($key, $value) {
[1574] Fix | Delete
$this->parameters[$key] = $value;
[1575] Fix | Delete
}
[1576] Fix | Delete
[1577] Fix | Delete
[1578] Fix | Delete
/**
[1579] Fix | Delete
* Set request header
[1580] Fix | Delete
*
[1581] Fix | Delete
* @param string $key Key
[1582] Fix | Delete
* @param string $value Value
[1583] Fix | Delete
*
[1584] Fix | Delete
* @return void
[1585] Fix | Delete
*/
[1586] Fix | Delete
public function setHeader($key, $value) {
[1587] Fix | Delete
$this->headers[$key] = $value;
[1588] Fix | Delete
}
[1589] Fix | Delete
[1590] Fix | Delete
[1591] Fix | Delete
/**
[1592] Fix | Delete
* Set x-amz-meta-* header
[1593] Fix | Delete
*
[1594] Fix | Delete
* @param string $key Key
[1595] Fix | Delete
* @param string $value Value
[1596] Fix | Delete
*
[1597] Fix | Delete
* @return void
[1598] Fix | Delete
*/
[1599] Fix | Delete
public function setAmzHeader($key, $value) {
[1600] Fix | Delete
$this->amzHeaders[$key] = $value;
[1601] Fix | Delete
}
[1602] Fix | Delete
[1603] Fix | Delete
/**
[1604] Fix | Delete
* Get the S3 response
[1605] Fix | Delete
*
[1606] Fix | Delete
* @return object | false
[1607] Fix | Delete
*/
[1608] Fix | Delete
public function getResponse() {
[1609] Fix | Delete
$query = '';
[1610] Fix | Delete
if (sizeof($this->parameters) > 0) {
[1611] Fix | Delete
$query = ('?' !== substr($this->uri, -1)) ? '?' : '&';
[1612] Fix | Delete
foreach ($this->parameters as $var => $value)
[1613] Fix | Delete
if (null == $value || '' == $value) $query .= $var.'&';
[1614] Fix | Delete
else $query .= $var.'='.rawurlencode($value).'&';
[1615] Fix | Delete
$query = substr($query, 0, -1);
[1616] Fix | Delete
$this->uri .= $query;
[1617] Fix | Delete
[1618] Fix | Delete
if (array_key_exists('acl', $this->parameters) ||
[1619] Fix | Delete
array_key_exists('location', $this->parameters) ||
[1620] Fix | Delete
array_key_exists('torrent', $this->parameters) ||
[1621] Fix | Delete
array_key_exists('logging', $this->parameters) ||
[1622] Fix | Delete
array_key_exists('partNumber', $this->parameters) ||
[1623] Fix | Delete
array_key_exists('uploads', $this->parameters) ||
[1624] Fix | Delete
array_key_exists('uploadId', $this->parameters))
[1625] Fix | Delete
$this->resource .= $query;
[1626] Fix | Delete
}
[1627] Fix | Delete
$url = ($this->s3->useSSL ? 'https://' : 'http://') . ('' !== $this->headers['Host'] ? $this->headers['Host'] : $this->endpoint) . $this->uri;
[1628] Fix | Delete
[1629] Fix | Delete
//var_dump('bucket: ' . $this->bucket, 'uri: ' . $this->uri, 'resource: ' . $this->resource, 'url: ' . $url);
[1630] Fix | Delete
[1631] Fix | Delete
$curl = curl_init();
[1632] Fix | Delete
curl_setopt($curl, CURLOPT_USERAGENT, 'S3/php');
[1633] Fix | Delete
[1634] Fix | Delete
if ($this->s3->useSSL) {
[1635] Fix | Delete
// SSL Validation can now be optional for those with broken OpenSSL installations
[1636] Fix | Delete
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $this->s3->useSSLValidation ? 2 : 0);
[1637] Fix | Delete
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->s3->useSSLValidation ? 1 : 0);
[1638] Fix | Delete
[1639] Fix | Delete
if (null !== $this->s3->sslKey) curl_setopt($curl, CURLOPT_SSLKEY, $this->s3->sslKey);
[1640] Fix | Delete
if (null !== $this->s3->sslCert) curl_setopt($curl, CURLOPT_SSLCERT, $this->s3->sslCert);
[1641] Fix | Delete
if (null !== $this->s3->sslCACert && file_exists($this->s3->sslCACert)) curl_setopt($curl, CURLOPT_CAINFO, $this->s3->sslCACert);
[1642] Fix | Delete
}
[1643] Fix | Delete
[1644] Fix | Delete
curl_setopt($curl, CURLOPT_URL, $url);
[1645] Fix | Delete
[1646] Fix | Delete
$wp_proxy = new WP_HTTP_Proxy();
[1647] Fix | Delete
[1648] Fix | Delete
if (null != $this->s3->proxy && isset($this->s3->proxy['host']) && $wp_proxy->send_through_proxy($url)) {
[1649] Fix | Delete
curl_setopt($curl, CURLOPT_PROXY, $this->s3->proxy['host']);
[1650] Fix | Delete
curl_setopt($curl, CURLOPT_PROXYTYPE, $this->s3->proxy['type']);
[1651] Fix | Delete
if (!empty($this->s3->proxy['port'])) curl_setopt($curl,CURLOPT_PROXYPORT, $this->s3->proxy['port']);
[1652] Fix | Delete
if (isset($this->s3->proxy['user'], $this->s3->proxy['pass']) && null != $this->s3->proxy['user'] && null != $this->s3->proxy['pass']) {
[1653] Fix | Delete
curl_setopt($curl, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
[1654] Fix | Delete
curl_setopt($curl, CURLOPT_PROXYUSERPWD, sprintf('%s:%s', $this->s3->proxy['user'], $this->s3->proxy['pass']));
[1655] Fix | Delete
}
[1656] Fix | Delete
}
[1657] Fix | Delete
[1658] Fix | Delete
// Headers
[1659] Fix | Delete
$headers = array(); $amz = array();
[1660] Fix | Delete
foreach ($this->amzHeaders as $header => $value)
[1661] Fix | Delete
if (strlen($value) > 0) $headers[] = $header.': '.$value;
[1662] Fix | Delete
foreach ($this->headers as $header => $value)
[1663] Fix | Delete
if (strlen($value) > 0) $headers[] = $header.': '.$value;
[1664] Fix | Delete
[1665] Fix | Delete
// Collect AMZ headers for signature
[1666] Fix | Delete
foreach ($this->amzHeaders as $header => $value)
[1667] Fix | Delete
if (strlen($value) > 0) $amz[] = strtolower($header).':'.$value;
[1668] Fix | Delete
[1669] Fix | Delete
// AMZ headers must be sorted
[1670] Fix | Delete
if (sizeof($amz) > 0) {
[1671] Fix | Delete
//sort($amz);
[1672] Fix | Delete
usort($amz, array(&$this, '__sortMetaHeadersCmp'));
[1673] Fix | Delete
$amz = "\n".implode("\n", $amz);
[1674] Fix | Delete
} else {
[1675] Fix | Delete
$amz = '';
[1676] Fix | Delete
}
[1677] Fix | Delete
[1678] Fix | Delete
if ($this->s3->hasAuth()) {
[1679] Fix | Delete
// Authorization string (CloudFront stringToSign should only contain a date)
[1680] Fix | Delete
if ('cloudfront.amazonaws.com' == $this->headers['Host']) {
[1681] Fix | Delete
$headers[] = 'Authorization: ' . $this->s3->__getSignature($this->headers['Date']);
[1682] Fix | Delete
} else {
[1683] Fix | Delete
if ('v2' === $this->s3->signVer) {
[1684] Fix | Delete
$headers[] = 'Authorization: ' . $this->s3->__getSignature(
[1685] Fix | Delete
$this->verb."\n".
[1686] Fix | Delete
$this->headers['Content-MD5']."\n".
[1687] Fix | Delete
$this->headers['Content-Type']."\n".
[1688] Fix | Delete
$this->headers['Date'].$amz."\n".
[1689] Fix | Delete
$this->resource
[1690] Fix | Delete
);
[1691] Fix | Delete
} else {
[1692] Fix | Delete
$amzHeaders = $this->s3->__getSignatureV4(
[1693] Fix | Delete
$this->amzHeaders,
[1694] Fix | Delete
$this->headers,
[1695] Fix | Delete
$this->verb,
[1696] Fix | Delete
$this->uri,
[1697] Fix | Delete
$this->data
[1698] Fix | Delete
);
[1699] Fix | Delete
foreach ($amzHeaders as $k => $v) {
[1700] Fix | Delete
$headers[] = $k . ': ' . $v;
[1701] Fix | Delete
}
[1702] Fix | Delete
}
[1703] Fix | Delete
}
[1704] Fix | Delete
}
[1705] Fix | Delete
[1706] Fix | Delete
if (false !== $this->s3->port) curl_setopt($curl, CURLOPT_PORT, $this->s3->port);
[1707] Fix | Delete
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
[1708] Fix | Delete
curl_setopt($curl, CURLOPT_HEADER, false);
[1709] Fix | Delete
curl_setopt($curl, CURLOPT_RETURNTRANSFER, false);
[1710] Fix | Delete
curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$this, '__responseWriteCallback'));
[1711] Fix | Delete
curl_setopt($curl, CURLOPT_HEADERFUNCTION, array(&$this, '__responseHeaderCallback'));
[1712] Fix | Delete
@curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
[1713] Fix | Delete
[1714] Fix | Delete
// Request types
[1715] Fix | Delete
switch ($this->verb) {
[1716] Fix | Delete
case 'GET': break;
[1717] Fix | Delete
case 'PUT': case 'POST':
[1718] Fix | Delete
if (false !== $this->fp) {
[1719] Fix | Delete
curl_setopt($curl, CURLOPT_PUT, true);
[1720] Fix | Delete
curl_setopt($curl, CURLOPT_INFILE, $this->fp);
[1721] Fix | Delete
if ($this->size >= 0) {
[1722] Fix | Delete
curl_setopt($curl, CURLOPT_INFILESIZE, $this->size);
[1723] Fix | Delete
}
[1724] Fix | Delete
} elseif (false !== $this->data) {
[1725] Fix | Delete
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb);
[1726] Fix | Delete
curl_setopt($curl, CURLOPT_POSTFIELDS, $this->data);
[1727] Fix | Delete
curl_setopt($curl, CURLOPT_INFILESIZE, strlen($this->data));
[1728] Fix | Delete
} else {
[1729] Fix | Delete
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb);
[1730] Fix | Delete
}
[1731] Fix | Delete
break;
[1732] Fix | Delete
case 'HEAD':
[1733] Fix | Delete
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'HEAD');
[1734] Fix | Delete
curl_setopt($curl, CURLOPT_NOBODY, true);
[1735] Fix | Delete
break;
[1736] Fix | Delete
case 'DELETE':
[1737] Fix | Delete
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE');
[1738] Fix | Delete
break;
[1739] Fix | Delete
default: break;
[1740] Fix | Delete
}
[1741] Fix | Delete
[1742] Fix | Delete
// Execute, grab errors
[1743] Fix | Delete
if (curl_exec($curl))
[1744] Fix | Delete
$this->response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
[1745] Fix | Delete
else
[1746] Fix | Delete
$this->response->error = array(
[1747] Fix | Delete
'code' => curl_errno($curl),
[1748] Fix | Delete
'message' => curl_error($curl),
[1749] Fix | Delete
'resource' => $this->resource
[1750] Fix | Delete
);
[1751] Fix | Delete
[1752] Fix | Delete
@curl_close($curl);
[1753] Fix | Delete
[1754] Fix | Delete
// Parse body into XML
[1755] Fix | Delete
// The case in which there is not application/xml content-type header is to support a DreamObjects case seen, April 2018
[1756] Fix | Delete
if (false === $this->response->error && isset($this->response->body) && ((isset($this->response->headers['type']) && false !== strpos($this->response->headers['type'], 'application/xml')) || (!isset($this->response->headers['type']) && 0 === strpos($this->response->body, '<?xml')))) {
[1757] Fix | Delete
$this->response->body = simplexml_load_string($this->response->body);
[1758] Fix | Delete
[1759] Fix | Delete
// Grab S3 errors
[1760] Fix | Delete
if (!in_array($this->response->code, array(200, 204, 206)) &&
[1761] Fix | Delete
isset($this->response->body->Code)) {
[1762] Fix | Delete
$this->response->error = array(
[1763] Fix | Delete
'code' => (string)$this->response->body->Code,
[1764] Fix | Delete
);
[1765] Fix | Delete
$this->response->error['message'] = isset($this->response->body->Message) ? $this->response->body->Message : '';
[1766] Fix | Delete
if (isset($this->response->body->Resource))
[1767] Fix | Delete
$this->response->error['resource'] = (string)$this->response->body->Resource;
[1768] Fix | Delete
unset($this->response->body);
[1769] Fix | Delete
}
[1770] Fix | Delete
}
[1771] Fix | Delete
[1772] Fix | Delete
// Clean up file resources
[1773] Fix | Delete
if (false !== $this->fp && is_resource($this->fp)) fclose($this->fp);
[1774] Fix | Delete
[1775] Fix | Delete
return $this->response;
[1776] Fix | Delete
}
[1777] Fix | Delete
[1778] Fix | Delete
/**
[1779] Fix | Delete
* Sort compare for meta headers
[1780] Fix | Delete
*
[1781] Fix | Delete
* @internal Used to sort x-amz meta headers
[1782] Fix | Delete
*
[1783] Fix | Delete
* @param string $a String A
[1784] Fix | Delete
* @param string $b String B
[1785] Fix | Delete
*
[1786] Fix | Delete
* @return integer
[1787] Fix | Delete
*/
[1788] Fix | Delete
private function __sortMetaHeadersCmp($a, $b) {// phpcs:ignore PHPCompatibility.FunctionNameRestrictions.ReservedFunctionNames.MethodDoubleUnderscore -- Method name "UpdraftPlus_S3Request::__responseHeaderCallback" is discouraged; PHP has reserved all method names with a double underscore prefix for future use.
[1789] Fix | Delete
$lenA = strpos($a, ':');
[1790] Fix | Delete
$lenB = strpos($b, ':');
[1791] Fix | Delete
$minLen = min($lenA, $lenB);
[1792] Fix | Delete
$ncmp = strncmp($a, $b, $minLen);
[1793] Fix | Delete
if ($lenA == $lenB) return $ncmp;
[1794] Fix | Delete
if (0 == $ncmp) return $lenA < $lenB ? -1 : 1;
[1795] Fix | Delete
return $ncmp;
[1796] Fix | Delete
}
[1797] Fix | Delete
[1798] Fix | Delete
/**
[1799] Fix | Delete
* CURL write callback
[1800] Fix | Delete
*
[1801] Fix | Delete
* @param resource $curl CURL resource
[1802] Fix | Delete
* @param string $data Data
[1803] Fix | Delete
*
[1804] Fix | Delete
* @return integer
[1805] Fix | Delete
*/
[1806] Fix | Delete
private function __responseWriteCallback($curl, $data) {// phpcs:ignore PHPCompatibility.FunctionNameRestrictions.ReservedFunctionNames.MethodDoubleUnderscore -- Method name "UpdraftPlus_S3Request::__responseHeaderCallback" is discouraged; PHP has reserved all method names with a double underscore prefix for future use.
[1807] Fix | Delete
if (in_array($this->response->code, array(200, 206)) && false !== $this->fp)
[1808] Fix | Delete
return fwrite($this->fp, $data);
[1809] Fix | Delete
else
[1810] Fix | Delete
$this->response->body = (empty($this->response->body)) ? $data : $this->response->body.$data;
[1811] Fix | Delete
return strlen($data);
[1812] Fix | Delete
}
[1813] Fix | Delete
[1814] Fix | Delete
[1815] Fix | Delete
/**
[1816] Fix | Delete
* Check DNS conformity
[1817] Fix | Delete
*
[1818] Fix | Delete
* @param string $bucket Bucket name
[1819] Fix | Delete
*
[1820] Fix | Delete
* @return boolean
[1821] Fix | Delete
*/
[1822] Fix | Delete
private function __dnsBucketName($bucket) {// phpcs:ignore PHPCompatibility.FunctionNameRestrictions.ReservedFunctionNames.MethodDoubleUnderscore -- Method name "UpdraftPlus_S3Request::__responseHeaderCallback" is discouraged; PHP has reserved all method names with a double underscore prefix for future use.
[1823] Fix | Delete
# A DNS bucket name cannot have len>63
[1824] Fix | Delete
# A DNS bucket name must have a character in other than a-z, 0-9, . -
[1825] Fix | Delete
# The purpose of this second check is not clear - is it that there's some limitation somewhere on bucket names that match that pattern that means that the bucket must be accessed by hostname?
[1826] Fix | Delete
if (strlen($bucket) > 63 || !preg_match("/[^a-z0-9\.-]/", $bucket)) return false;
[1827] Fix | Delete
# A DNS bucket name cannot contain -.
[1828] Fix | Delete
if (false !== strstr($bucket, '-.')) return false;
[1829] Fix | Delete
# A DNS bucket name cannot contain ..
[1830] Fix | Delete
if (false !== strstr($bucket, '..')) return false;
[1831] Fix | Delete
# A DNS bucket name must begin with 0-9a-z
[1832] Fix | Delete
if (!preg_match("/^[0-9a-z]/", $bucket)) return false;
[1833] Fix | Delete
# A DNS bucket name must end with 0-9 a-z
[1834] Fix | Delete
if (!preg_match("/[0-9a-z]$/", $bucket)) return false;
[1835] Fix | Delete
return true;
[1836] Fix | Delete
}
[1837] Fix | Delete
[1838] Fix | Delete
/**
[1839] Fix | Delete
* CURL header callback
[1840] Fix | Delete
*
[1841] Fix | Delete
* @param resource $curl CURL resource
[1842] Fix | Delete
* @param string $data Data
[1843] Fix | Delete
* @return integer
[1844] Fix | Delete
*/
[1845] Fix | Delete
private function __responseHeaderCallback($curl, $data) {// phpcs:ignore PHPCompatibility.FunctionNameRestrictions.ReservedFunctionNames.MethodDoubleUnderscore -- Method name "UpdraftPlus_S3Request::__responseHeaderCallback" is discouraged; PHP has reserved all method names with a double underscore prefix for future use.
[1846] Fix | Delete
if (($strlen = strlen($data)) <= 2) return $strlen;
[1847] Fix | Delete
if ('HTTP' == substr($data, 0, 4)) {
[1848] Fix | Delete
$this->response->code = (int)substr($data, 9, 3);
[1849] Fix | Delete
} else {
[1850] Fix | Delete
$data = trim($data);
[1851] Fix | Delete
if (false === strpos($data, ': ')) return $strlen;
[1852] Fix | Delete
list($header, $value) = explode(': ', $data, 2);
[1853] Fix | Delete
if ('last-modified' == strtolower($header))
[1854] Fix | Delete
$this->response->headers['time'] = strtotime($value);
[1855] Fix | Delete
elseif ('content-length' == strtolower($header))
[1856] Fix | Delete
$this->response->headers['size'] = (int)$value;
[1857] Fix | Delete
elseif ('content-type' == strtolower($header))
[1858] Fix | Delete
$this->response->headers['type'] = $value;
[1859] Fix | Delete
elseif ('etag' == strtolower($header))
[1860] Fix | Delete
$this->response->headers['hash'] = '"' == $value[0] ? substr($value, 1, -1) : $value;
[1861] Fix | Delete
elseif (preg_match('/^x-amz-meta-.*$/i', $header))
[1862] Fix | Delete
$this->response->headers[strtolower($header)] = $value;
[1863] Fix | Delete
}
[1864] Fix | Delete
return $strlen;
[1865] Fix | Delete
}
[1866] Fix | Delete
[1867] Fix | Delete
}
[1868] Fix | Delete
[1869] Fix | Delete
It is recommended that you Edit text format, this type of Fix handles quite a lot in one request
Function