Skip to content
Snippets Groups Projects
Unverified Commit 55a6d945 authored by Seamus Lee's avatar Seamus Lee Committed by GitHub
Browse files

Merge pull request #15438 from demeritcowboy/emailprocessor-still-hungry-alt

#1270 - fix email processor dropping attachments - ALTERNATE
parents b548bf80 8913e915
No related branches found
No related tags found
No related merge requests found
......@@ -38,6 +38,7 @@ class CRM_Admin_Form_Setting_Miscellaneous extends CRM_Admin_Form_Setting {
protected $_settings = [
'max_attachments' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
'max_attachments_backend' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
'contact_undelete' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
'empoweredBy' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
'logging' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
......@@ -73,6 +74,7 @@ class CRM_Admin_Form_Setting_Miscellaneous extends CRM_Admin_Form_Setting {
$this->assign('pure_config_settings', [
'empoweredBy',
'max_attachments',
'max_attachments_backend',
'maxFileSize',
'secondDegRelPermissions',
'recentItemsMaxCount',
......
......@@ -250,6 +250,10 @@ trait CRM_Admin_Form_SettingTrait {
//temp hack @todo fix to get from metadata
$this->addRule('max_attachments', ts('Value should be a positive number'), 'positiveInteger');
}
if ($setting == 'max_attachments_backend') {
//temp hack @todo fix to get from metadata
$this->addRule('max_attachments_backend', ts('Value should be a positive number'), 'positiveInteger');
}
if ($setting == 'maxFileSize') {
//temp hack
$this->addRule('maxFileSize', ts('Value should be a positive number'), 'positiveInteger');
......
......@@ -40,6 +40,13 @@ class CRM_Core_BAO_File extends CRM_Core_DAO_File {
public static $_signableFields = ['entityTable', 'entityID', 'fileID'];
/**
* If there is no setting configured on the admin screens, maximum number
* of attachments to try to process when given a list of attachments to
* process.
*/
const DEFAULT_MAX_ATTACHMENTS_BACKEND = 100;
/**
* Takes an associative array and creates a File object.
*
......@@ -596,24 +603,37 @@ AND CEF.entity_id = %2";
* @param int $entityID
*/
public static function processAttachment(&$params, $entityTable, $entityID) {
$numAttachments = Civi::settings()->get('max_attachments');
$numAttachments = Civi::settings()->get('max_attachments_backend') ?? self::DEFAULT_MAX_ATTACHMENTS_BACKEND;
for ($i = 1; $i <= $numAttachments; $i++) {
if (
isset($params["attachFile_$i"]) &&
is_array($params["attachFile_$i"])
) {
self::filePostProcess(
$params["attachFile_$i"]['location'],
NULL,
$entityTable,
$entityID,
NULL,
TRUE,
$params["attachFile_$i"],
"attachFile_$i",
$params["attachFile_$i"]['type']
);
if (isset($params["attachFile_$i"])) {
/**
* Moved the second condition into its own if block to avoid changing
* how it works if there happens to be an entry that is not an array,
* since we now might exit loop early via newly added break below.
*/
if (is_array($params["attachFile_$i"])) {
self::filePostProcess(
$params["attachFile_$i"]['location'],
NULL,
$entityTable,
$entityID,
NULL,
TRUE,
$params["attachFile_$i"],
"attachFile_$i",
$params["attachFile_$i"]['type']
);
}
}
else {
/**
* No point looping 100 times if there aren't any more.
* This assumes the array is continuous and doesn't skip array keys,
* but (a) where would it be doing that, and (b) it would have caused
* problems before anyway if there were skipped keys.
*/
break;
}
}
}
......
......@@ -168,6 +168,7 @@ class CRM_Core_Config_MagicMerge {
'maxFileSize' => ['setting'],
// renamed.
'maxAttachments' => ['setting', 'max_attachments'],
'maxAttachmentsBackend' => ['setting', 'max_attachments_backend'],
'monetaryDecimalPoint' => ['setting'],
'monetaryThousandSeparator' => ['setting'],
'moneyformat' => ['setting'],
......
......@@ -879,7 +879,8 @@ function _civicrm_api3_deprecated_activity_buildmailparams($result, $activityTyp
$params['activity_date_time'] = $result['date'];
$params['details'] = $result['body'];
for ($i = 1; $i <= 5; $i++) {
$numAttachments = Civi::settings()->get('max_attachments_backend') ?? CRM_Core_BAO_File::DEFAULT_MAX_ATTACHMENTS_BACKEND;
for ($i = 1; $i <= $numAttachments; $i++) {
if (isset($result["attachFile_$i"])) {
$params["attachFile_$i"] = $result["attachFile_$i"];
}
......
......@@ -32,6 +32,7 @@ mailerJobSize
mailerJobsMax
maxFileSize
max_attachments
max_attachments_backend
replyTo
secondDegRelPermissions
securityAlert
......
......@@ -387,7 +387,27 @@ return [
'title' => ts('Maximum Attachments'),
'is_domain' => 1,
'is_contact' => 0,
'description' => ts('Maximum number of files (documents, images, etc.) which can be attached to emails or activities.'),
'description' => ts('Maximum number of files (documents, images, etc.) which can be attached to emails or activities. This setting applies to UI forms and limits the number of fields available on the form.'),
'help_text' => NULL,
],
'max_attachments_backend' => [
'group_name' => 'CiviCRM Preferences',
'group' => 'core',
'name' => 'max_attachments_backend',
'legacy_key' => 'maxAttachmentsBackend',
'type' => 'Integer',
'quick_form_type' => 'Element',
'html_type' => 'text',
'html_attributes' => [
'size' => 2,
'maxlength' => 8,
],
'default' => CRM_Core_BAO_File::DEFAULT_MAX_ATTACHMENTS_BACKEND,
'add' => '5.20',
'title' => ts('Maximum Attachments For Backend Processes'),
'is_domain' => 1,
'is_contact' => 0,
'description' => ts('Maximum number of files (documents, images, etc.) which can be processed during backend processing such as automated inbound email processing. This should be a big number higher than the other Maximum Attachments setting above. This setting here merely provides an upper limit to prevent attacks that might overload the server.'),
'help_text' => NULL,
],
'maxFileSize' => [
......
<?php
/**
* Class CRM_Utils_Mail_EmailProcessorInboundTest
* @group headless
*/
class CRM_Utils_Mail_EmailProcessorInboundTest extends CiviUnitTestCase {
/**
* MailSettings record id.
*
* @var int
*/
protected $mailSettingsId;
public function setUp() {
parent::setUp();
CRM_Utils_File::cleanDir(__DIR__ . '/data/mail');
mkdir(__DIR__ . '/data/mail');
// Note this is configured for Inbound Email Processing (not bounces)
// but otherwise is the same as bounces.
$this->mailSettingsId = $this->callAPISuccess('MailSettings', 'create', [
'name' => 'local',
'protocol' => 'Localdir',
'source' => __DIR__ . '/data/mail',
'domain' => 'example.com',
// a little weird - is_default=0 means for inbound email processing
'is_default' => '0',
'domain_id' => 1,
])['id'];
}
public function tearDown() {
CRM_Utils_File::cleanDir(__DIR__ . '/data/mail');
$this->callAPISuccess('MailSettings', 'delete', [
'id' => $this->mailSettingsId,
]);
parent::tearDown();
}
/**
* Fetch activities with many attachments
*
* In particular the default limit for the UI is 3, which is how this came up
* because it was also being used as a limit for backend processes. So we
* test 4, which is bigger than 3 (unless running on a 2-bit CPU).
*/
public function testFetchActivitiesWithManyAttachments() {
$mail = 'test_message_many_attachments.eml';
// paranoid check that settings are the standard defaults
$currentUIMax = Civi::settings()->get('max_attachments');
$currentBackendMax = Civi::settings()->get('max_attachments_backend');
if ($currentUIMax > 3) {
Civi::settings()->set('max_attachments', 3);
}
if ($currentBackendMax < CRM_Core_BAO_File::DEFAULT_MAX_ATTACHMENTS_BACKEND) {
Civi::settings()->set('max_attachments_backend', CRM_Core_BAO_File::DEFAULT_MAX_ATTACHMENTS_BACKEND);
}
// create some contacts
$senderContactId = $this->individualCreate([], 1);
$senderContact = $this->callAPISuccess('Contact', 'getsingle', [
'id' => $senderContactId,
]);
$recipientContactId = $this->individualCreate([], 2);
$recipientContact = $this->callAPISuccess('Contact', 'getsingle', [
'id' => $recipientContactId,
]);
$templateFillData = [
'the_date' => date('r'),
'from_name' => $senderContact['display_name'],
'from_email' => $senderContact['email'],
'to_email' => $recipientContact['email'],
];
// Retrieve the template and insert our data like current dates
$file_contents = file_get_contents(__DIR__ . '/data/inbound/' . $mail);
foreach ($templateFillData as $field => $value) {
$file_contents = str_replace("%%{$field}%%", $value, $file_contents);
}
// put it in the mail dir
file_put_contents(__DIR__ . '/data/mail/' . $mail, $file_contents);
// run the job
$this->callAPISuccess('job', 'fetch_activities', []);
// check that file was removed from mail dir
$this->assertFalse(file_exists(__DIR__ . '/data/mail/' . $mail));
// get the filed activity, by sender contact id
$activities = $this->callAPISuccess('Activity', 'get', [
'source_contact_id' => $senderContact['id'],
]);
$this->assertEquals(1, $activities['count']);
// check subject
$activity = $activities['values'][$activities['id']];
$this->assertEquals('Testing 4 attachments', $activity['subject']);
// Check target is our recipient
$targets = $this->callAPISuccess('ActivityContact', 'get', [
'activity_id' => $activity['id'],
'record_type_id' => 'Activity Targets',
]);
$this->assertEquals($recipientContact['id'], $targets['values'][$targets['id']]['contact_id']);
// Check we have 4 attachments
$attachments = $this->callAPISuccess('Attachment', 'get', [
'entity_id' => $activity['id'],
'entity_table' => 'civicrm_activity',
]);
$this->assertEquals(4, $attachments['count']);
// reset in case it was different from defaults
Civi::settings()->set('max_attachments', $currentUIMax);
Civi::settings()->set('max_attachments_backend', $currentBackendMax);
}
}
MIME-Version: 1.0
Date: %%the_date%%
Message-ID: <CAL+tpUmRn2J7zcjUAv4WXMTJZcEV67V2_Ktu77Ha_MGmth0CmQ@mail.gmail.com>
Subject: Testing 4 attachments
From: %%from_name%% <%%from_email%%>
To: %%to_email%%
Content-Type: multipart/mixed; boundary="000000000000a2ad5f059325db35"
--000000000000a2ad5f059325db35
Content-Type: multipart/alternative; boundary="000000000000a2ad5a059325db33"
--000000000000a2ad5a059325db33
Content-Type: text/plain; charset="UTF-8"
test
--000000000000a2ad5a059325db33
Content-Type: text/html; charset="UTF-8"
<div dir="ltr">test<br></div>
--000000000000a2ad5a059325db33--
--000000000000a2ad5f059325db35
Content-Type: image/gif; name="c.gif"
Content-Disposition: attachment; filename="c.gif"
Content-Transfer-Encoding: base64
X-Attachment-Id: f_k0v4qdju2
Content-ID: <f_k0v4qdju2>
R0lGODlhPwA+APcAAAAAAAAAMwAAZgAAmQAAzAAA/wArAAArMwArZgArmQArzAAr/wBVAABVMwBV
ZgBVmQBVzABV/wCAAACAMwCAZgCAmQCAzACA/wCqAACqMwCqZgCqmQCqzACq/wDVAADVMwDVZgDV
mQDVzADV/wD/AAD/MwD/ZgD/mQD/zAD//zMAADMAMzMAZjMAmTMAzDMA/zMrADMrMzMrZjMrmTMr
zDMr/zNVADNVMzNVZjNVmTNVzDNV/zOAADOAMzOAZjOAmTOAzDOA/zOqADOqMzOqZjOqmTOqzDOq
/zPVADPVMzPVZjPVmTPVzDPV/zP/ADP/MzP/ZjP/mTP/zDP//2YAAGYAM2YAZmYAmWYAzGYA/2Yr
AGYrM2YrZmYrmWYrzGYr/2ZVAGZVM2ZVZmZVmWZVzGZV/2aAAGaAM2aAZmaAmWaAzGaA/2aqAGaq
M2aqZmaqmWaqzGaq/2bVAGbVM2bVZmbVmWbVzGbV/2b/AGb/M2b/Zmb/mWb/zGb//5kAAJkAM5kA
ZpkAmZkAzJkA/5krAJkrM5krZpkrmZkrzJkr/5lVAJlVM5lVZplVmZlVzJlV/5mAAJmAM5mAZpmA
mZmAzJmA/5mqAJmqM5mqZpmqmZmqzJmq/5nVAJnVM5nVZpnVmZnVzJnV/5n/AJn/M5n/Zpn/mZn/
zJn//8wAAMwAM8wAZswAmcwAzMwA/8wrAMwrM8wrZswrmcwrzMwr/8xVAMxVM8xVZsxVmcxVzMxV
/8yAAMyAM8yAZsyAmcyAzMyA/8yqAMyqM8yqZsyqmcyqzMyq/8zVAMzVM8zVZszVmczVzMzV/8z/
AMz/M8z/Zsz/mcz/zMz///8AAP8AM/8AZv8Amf8AzP8A//8rAP8rM/8rZv8rmf8rzP8r//9VAP9V
M/9VZv9Vmf9VzP9V//+AAP+AM/+AZv+Amf+AzP+A//+qAP+qM/+qZv+qmf+qzP+q///VAP/VM//V
Zv/Vmf/VzP/V////AP//M///Zv//mf//zP///wAAAAAAAAAAAAAAACH5BAEAAPwALAAAAAA/AD4A
AAjCAPcJHEiwoMGDCBMqXMiwocOHECNKnEixosWLGDNq3Mixo8ePIEOKHEmypMmTKFOqXMmypcuX
IgHInAnT4MybOGvi3JmzJc+fNFcCHQpAJdCCP1HyVLjU5E6GT0tGhdpzZNWGV0NmXTgVZFesN62G
jbi1Y1muY7WmfXh2Y9uEbzPGPTgXY92BXz/eFbjXYl6Ef/XODSz4bVOnh/EmRkx08cnGhKVClqmT
aE2CSS9r3sy5s+fPoEOLHk26tOnTqFPXDAgAOw==
--000000000000a2ad5f059325db35
Content-Type: image/gif; name="b.gif"
Content-Disposition: attachment; filename="b.gif"
Content-Transfer-Encoding: base64
X-Attachment-Id: f_k0v4qdjj1
Content-ID: <f_k0v4qdjj1>
R0lGODlhPwA+APcAAAAAAAAAMwAAZgAAmQAAzAAA/wArAAArMwArZgArmQArzAAr/wBVAABVMwBV
ZgBVmQBVzABV/wCAAACAMwCAZgCAmQCAzACA/wCqAACqMwCqZgCqmQCqzACq/wDVAADVMwDVZgDV
mQDVzADV/wD/AAD/MwD/ZgD/mQD/zAD//zMAADMAMzMAZjMAmTMAzDMA/zMrADMrMzMrZjMrmTMr
zDMr/zNVADNVMzNVZjNVmTNVzDNV/zOAADOAMzOAZjOAmTOAzDOA/zOqADOqMzOqZjOqmTOqzDOq
/zPVADPVMzPVZjPVmTPVzDPV/zP/ADP/MzP/ZjP/mTP/zDP//2YAAGYAM2YAZmYAmWYAzGYA/2Yr
AGYrM2YrZmYrmWYrzGYr/2ZVAGZVM2ZVZmZVmWZVzGZV/2aAAGaAM2aAZmaAmWaAzGaA/2aqAGaq
M2aqZmaqmWaqzGaq/2bVAGbVM2bVZmbVmWbVzGbV/2b/AGb/M2b/Zmb/mWb/zGb//5kAAJkAM5kA
ZpkAmZkAzJkA/5krAJkrM5krZpkrmZkrzJkr/5lVAJlVM5lVZplVmZlVzJlV/5mAAJmAM5mAZpmA
mZmAzJmA/5mqAJmqM5mqZpmqmZmqzJmq/5nVAJnVM5nVZpnVmZnVzJnV/5n/AJn/M5n/Zpn/mZn/
zJn//8wAAMwAM8wAZswAmcwAzMwA/8wrAMwrM8wrZswrmcwrzMwr/8xVAMxVM8xVZsxVmcxVzMxV
/8yAAMyAM8yAZsyAmcyAzMyA/8yqAMyqM8yqZsyqmcyqzMyq/8zVAMzVM8zVZszVmczVzMzV/8z/
AMz/M8z/Zsz/mcz/zMz///8AAP8AM/8AZv8Amf8AzP8A//8rAP8rM/8rZv8rmf8rzP8r//9VAP9V
M/9VZv9Vmf9VzP9V//+AAP+AM/+AZv+Amf+AzP+A//+qAP+qM/+qZv+qmf+qzP+q///VAP/VM//V
Zv/Vmf/VzP/V////AP//M///Zv//mf//zP///wAAAAAAAAAAAAAAACH5BAEAAPwALAAAAAA/AD4A
AAjCAPcJHEiwoMGDCBMqXMiwocOHECNKnEixosWLGDNq3Mixo8ePIEOKHEmypMmTKFOqXMmypcuX
IgHInAnT4MybOGvi3JmzJc+fNFcCHQpAJdCCP1HyVLjU5E6GT0tGhdpzZNWGV0NmXTgVZFesN62G
jbi1Y1muY7WmfXh2Y9uEbzPGPTgXY92BXz/eFbjXYl6Ef/XODSz4bVOnh/EmRkx08cnGhKVClqmT
aE2CSS9r3sy5s+fPoEOLHk26tOnTqFPXDAgAOw==
--000000000000a2ad5f059325db35
Content-Type: image/gif; name="d.gif"
Content-Disposition: attachment; filename="d.gif"
Content-Transfer-Encoding: base64
X-Attachment-Id: f_k0v4qdk73
Content-ID: <f_k0v4qdk73>
R0lGODlhPwA+APcAAAAAAAAAMwAAZgAAmQAAzAAA/wArAAArMwArZgArmQArzAAr/wBVAABVMwBV
ZgBVmQBVzABV/wCAAACAMwCAZgCAmQCAzACA/wCqAACqMwCqZgCqmQCqzACq/wDVAADVMwDVZgDV
mQDVzADV/wD/AAD/MwD/ZgD/mQD/zAD//zMAADMAMzMAZjMAmTMAzDMA/zMrADMrMzMrZjMrmTMr
zDMr/zNVADNVMzNVZjNVmTNVzDNV/zOAADOAMzOAZjOAmTOAzDOA/zOqADOqMzOqZjOqmTOqzDOq
/zPVADPVMzPVZjPVmTPVzDPV/zP/ADP/MzP/ZjP/mTP/zDP//2YAAGYAM2YAZmYAmWYAzGYA/2Yr
AGYrM2YrZmYrmWYrzGYr/2ZVAGZVM2ZVZmZVmWZVzGZV/2aAAGaAM2aAZmaAmWaAzGaA/2aqAGaq
M2aqZmaqmWaqzGaq/2bVAGbVM2bVZmbVmWbVzGbV/2b/AGb/M2b/Zmb/mWb/zGb//5kAAJkAM5kA
ZpkAmZkAzJkA/5krAJkrM5krZpkrmZkrzJkr/5lVAJlVM5lVZplVmZlVzJlV/5mAAJmAM5mAZpmA
mZmAzJmA/5mqAJmqM5mqZpmqmZmqzJmq/5nVAJnVM5nVZpnVmZnVzJnV/5n/AJn/M5n/Zpn/mZn/
zJn//8wAAMwAM8wAZswAmcwAzMwA/8wrAMwrM8wrZswrmcwrzMwr/8xVAMxVM8xVZsxVmcxVzMxV
/8yAAMyAM8yAZsyAmcyAzMyA/8yqAMyqM8yqZsyqmcyqzMyq/8zVAMzVM8zVZszVmczVzMzV/8z/
AMz/M8z/Zsz/mcz/zMz///8AAP8AM/8AZv8Amf8AzP8A//8rAP8rM/8rZv8rmf8rzP8r//9VAP9V
M/9VZv9Vmf9VzP9V//+AAP+AM/+AZv+Amf+AzP+A//+qAP+qM/+qZv+qmf+qzP+q///VAP/VM//V
Zv/Vmf/VzP/V////AP//M///Zv//mf//zP///wAAAAAAAAAAAAAAACH5BAEAAPwALAAAAAA/AD4A
AAjCAPcJHEiwoMGDCBMqXMiwocOHECNKnEixosWLGDNq3Mixo8ePIEOKHEmypMmTKFOqXMmypcuX
IgHInAnT4MybOGvi3JmzJc+fNFcCHQpAJdCCP1HyVLjU5E6GT0tGhdpzZNWGV0NmXTgVZFesN62G
jbi1Y1muY7WmfXh2Y9uEbzPGPTgXY92BXz/eFbjXYl6Ef/XODSz4bVOnh/EmRkx08cnGhKVClqmT
aE2CSS9r3sy5s+fPoEOLHk26tOnTqFPXDAgAOw==
--000000000000a2ad5f059325db35
Content-Type: image/gif; name="a.gif"
Content-Disposition: attachment; filename="a.gif"
Content-Transfer-Encoding: base64
X-Attachment-Id: f_k0v4qdiv0
Content-ID: <f_k0v4qdiv0>
R0lGODlhPwA+APcAAAAAAAAAMwAAZgAAmQAAzAAA/wArAAArMwArZgArmQArzAAr/wBVAABVMwBV
ZgBVmQBVzABV/wCAAACAMwCAZgCAmQCAzACA/wCqAACqMwCqZgCqmQCqzACq/wDVAADVMwDVZgDV
mQDVzADV/wD/AAD/MwD/ZgD/mQD/zAD//zMAADMAMzMAZjMAmTMAzDMA/zMrADMrMzMrZjMrmTMr
zDMr/zNVADNVMzNVZjNVmTNVzDNV/zOAADOAMzOAZjOAmTOAzDOA/zOqADOqMzOqZjOqmTOqzDOq
/zPVADPVMzPVZjPVmTPVzDPV/zP/ADP/MzP/ZjP/mTP/zDP//2YAAGYAM2YAZmYAmWYAzGYA/2Yr
AGYrM2YrZmYrmWYrzGYr/2ZVAGZVM2ZVZmZVmWZVzGZV/2aAAGaAM2aAZmaAmWaAzGaA/2aqAGaq
M2aqZmaqmWaqzGaq/2bVAGbVM2bVZmbVmWbVzGbV/2b/AGb/M2b/Zmb/mWb/zGb//5kAAJkAM5kA
ZpkAmZkAzJkA/5krAJkrM5krZpkrmZkrzJkr/5lVAJlVM5lVZplVmZlVzJlV/5mAAJmAM5mAZpmA
mZmAzJmA/5mqAJmqM5mqZpmqmZmqzJmq/5nVAJnVM5nVZpnVmZnVzJnV/5n/AJn/M5n/Zpn/mZn/
zJn//8wAAMwAM8wAZswAmcwAzMwA/8wrAMwrM8wrZswrmcwrzMwr/8xVAMxVM8xVZsxVmcxVzMxV
/8yAAMyAM8yAZsyAmcyAzMyA/8yqAMyqM8yqZsyqmcyqzMyq/8zVAMzVM8zVZszVmczVzMzV/8z/
AMz/M8z/Zsz/mcz/zMz///8AAP8AM/8AZv8Amf8AzP8A//8rAP8rM/8rZv8rmf8rzP8r//9VAP9V
M/9VZv9Vmf9VzP9V//+AAP+AM/+AZv+Amf+AzP+A//+qAP+qM/+qZv+qmf+qzP+q///VAP/VM//V
Zv/Vmf/VzP/V////AP//M///Zv//mf//zP///wAAAAAAAAAAAAAAACH5BAEAAPwALAAAAAA/AD4A
AAjCAPcJHEiwoMGDCBMqXMiwocOHECNKnEixosWLGDNq3Mixo8ePIEOKHEmypMmTKFOqXMmypcuX
IgHInAnT4MybOGvi3JmzJc+fNFcCHQpAJdCCP1HyVLjU5E6GT0tGhdpzZNWGV0NmXTgVZFesN62G
jbi1Y1muY7WmfXh2Y9uEbzPGPTgXY92BXz/eFbjXYl6Ef/XODSz4bVOnh/EmRkx08cnGhKVClqmT
aE2CSS9r3sy5s+fPoEOLHk26tOnTqFPXDAgAOw==
--000000000000a2ad5f059325db35--
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment