Skip to content

Commit 9caac24

Browse files
authored
Merge branch 'master' into fix/theming-config-image-key-read
2 parents c1f9bf9 + 413f60d commit 9caac24

10 files changed

Lines changed: 135 additions & 9 deletions

File tree

apps/dav/lib/CalDAV/TipBroker.php

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ protected function parseEventForOrganizer(VCalendar $calendar, array $eventInfo,
305305
if (count($eventInfo['instances']) === 0 && count($oldEventInfo['instances']) > 0) {
306306
foreach ($oldEventInfo['attendees'] as $attendee) {
307307
$messages[] = $this->generateMessage(
308-
$oldEventInfo['instances'], $organizerHref, $organizerName, $attendee, $objectId, $objectType, $objectSequence, 'CANCEL', $template
308+
$oldEventInfo['instances'], $organizerHref, $organizerName, $attendee, $objectId, $objectType, $objectSequence, 'CANCEL', $template, true
309309
);
310310
}
311311
return $messages;
@@ -314,7 +314,7 @@ protected function parseEventForOrganizer(VCalendar $calendar, array $eventInfo,
314314
if ($eventInfo['instances']['master']?->STATUS?->getValue() === 'CANCELLED' && $oldEventInfo['instances']['master']?->STATUS?->getValue() !== 'CANCELLED') {
315315
foreach ($eventInfo['attendees'] as $attendee) {
316316
$messages[] = $this->generateMessage(
317-
$eventInfo['instances'], $organizerHref, $organizerName, $attendee, $objectId, $objectType, $objectSequence, 'CANCEL', $template
317+
$eventInfo['instances'], $organizerHref, $organizerName, $attendee, $objectId, $objectType, $objectSequence, 'CANCEL', $template, true
318318
);
319319
}
320320
return $messages;
@@ -328,7 +328,7 @@ protected function parseEventForOrganizer(VCalendar $calendar, array $eventInfo,
328328
$cancelledNewInstances[] = $id;
329329
foreach ($eventInfo['attendees'] as $attendee) {
330330
$messages[] = $this->generateMessage(
331-
[$id => $instance], $organizerHref, $organizerName, $attendee, $objectId, $objectType, $objectSequence, 'CANCEL', $template
331+
[$id => $instance], $organizerHref, $organizerName, $attendee, $objectId, $objectType, $objectSequence, 'CANCEL', $template, true
332332
);
333333
}
334334
}
@@ -359,7 +359,7 @@ protected function parseEventForOrganizer(VCalendar $calendar, array $eventInfo,
359359
//get all instances of the attendee was removed from.
360360
$instances = array_intersect_key($oldEventInfo['instances'], array_flip(array_keys($oldEventInfo['attendees'][$attendee]['instances'])));
361361
$messages[] = $this->generateMessage(
362-
$instances, $organizerHref, $organizerName, $oldEventInfo['attendees'][$attendee], $objectId, $objectType, $objectSequence, 'CANCEL', $template
362+
$instances, $organizerHref, $organizerName, $oldEventInfo['attendees'][$attendee], $objectId, $objectType, $objectSequence, 'CANCEL', $template, true
363363
);
364364
continue;
365365
}
@@ -398,8 +398,19 @@ protected function parseEventForOrganizer(VCalendar $calendar, array $eventInfo,
398398
}
399399
}
400400

401+
// determine if this attendee needs to be notified of the change, based on whether
402+
// a significant property changed on the event, or the set of instances they are part of changed
403+
$oldAttendeeInstances = isset($oldEventInfo['attendees'][$attendee]) ? array_keys($oldEventInfo['attendees'][$attendee]['instances']) : [];
404+
$newAttendeeInstances = array_keys($eventInfo['attendees'][$attendee]['instances']);
405+
406+
$significantChange
407+
= $eventInfo['attendees'][$attendee]['forceSend'] === 'REQUEST'
408+
|| count($oldAttendeeInstances) !== count($newAttendeeInstances)
409+
|| count(array_diff($oldAttendeeInstances, $newAttendeeInstances)) > 0
410+
|| $oldEventInfo['significantChangeHash'] !== $eventInfo['significantChangeHash'];
411+
401412
$messages[] = $this->generateMessage(
402-
$instances, $organizerHref, $organizerName, $eventInfo['attendees'][$attendee], $objectId, $objectType, $objectSequence, 'REQUEST', $template
413+
$instances, $organizerHref, $organizerName, $eventInfo['attendees'][$attendee], $objectId, $objectType, $objectSequence, 'REQUEST', $template, $significantChange
403414
);
404415
}
405416

@@ -424,6 +435,8 @@ protected function parseEventForOrganizer(VCalendar $calendar, array $eventInfo,
424435
* @param int $objectSequence The sequence number of the event
425436
* @param string $method The iTip method ('REQUEST', 'CANCEL', 'REPLY', etc.)
426437
* @param VCalendar $template The template calendar object (without event components)
438+
* @param bool $significantChange Whether a property listed in $significantChangeProperties changed,
439+
* or the attendee's set of instances changed
427440
* @return Message The generated iTip message ready to be sent
428441
*/
429442
protected function generateMessage(
@@ -436,6 +449,7 @@ protected function generateMessage(
436449
int $objectSequence,
437450
string $method,
438451
VCalendar $template,
452+
bool $significantChange,
439453
): Message {
440454

441455
$recipientAddress = $attendee['href'] ?? '';
@@ -460,7 +474,7 @@ protected function generateMessage(
460474
$message->senderName = $organizerName;
461475
$message->recipient = $recipientAddress;
462476
$message->recipientName = $recipientName;
463-
$message->significantChange = true;
477+
$message->significantChange = $significantChange;
464478
$message->message = $vObject;
465479

466480
return $message;

apps/dav/tests/unit/CalDAV/TipBrokerTest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,28 @@ public function testParseEventForOrganizerModified(): void {
120120
$this->assertEquals('REQUEST', $messages[0]->method);
121121
$this->assertEquals($mutatedCalendar->VEVENT->ORGANIZER->getValue(), $messages[0]->sender);
122122
$this->assertEquals($mutatedCalendar->VEVENT->ATTENDEE[0]->getValue(), $messages[0]->recipient);
123+
$this->assertTrue($messages[0]->significantChange);
124+
}
125+
126+
/**
127+
* Tests user modifying a property that is not part of $significantChangeProperties (e.g. COLOR)
128+
*/
129+
public function testParseEventForOrganizerModifiedNonSignificantProperty(): void {
130+
// construct calendar and generate event info for modified event with one attendee
131+
$originalCalendar = clone $this->vCalendar1a;
132+
$originalEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$originalCalendar]);
133+
$mutatedCalendar = clone $this->vCalendar1a;
134+
$mutatedCalendar->VEVENT->{'LAST-MODIFIED'}->setValue('20240701T020000Z');
135+
$mutatedCalendar->VEVENT->SEQUENCE->setValue(2);
136+
$mutatedCalendar->VEVENT->add('COLOR', 'khaki');
137+
$mutatedEventInfo = $this->invokePrivate($this->broker, 'parseEventInfo', [$mutatedCalendar]);
138+
// test iTip generation
139+
$messages = $this->invokePrivate($this->broker, 'parseEventForOrganizer', [$mutatedCalendar, $mutatedEventInfo, $originalEventInfo]);
140+
$this->assertCount(1, $messages);
141+
$this->assertEquals('REQUEST', $messages[0]->method);
142+
$this->assertEquals($mutatedCalendar->VEVENT->ORGANIZER->getValue(), $messages[0]->sender);
143+
$this->assertEquals($mutatedCalendar->VEVENT->ATTENDEE[0]->getValue(), $messages[0]->recipient);
144+
$this->assertFalse($messages[0]->significantChange);
123145
}
124146

125147
/**

lib/private/DB/ConnectionFactory.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ public function createConnectionParams(string $configPrefix = '', array $additio
210210
//additional driver options, eg. for mysql ssl
211211
$driverOptions = $this->config->getValue($configPrefix . 'dbdriveroptions', $this->config->getValue('dbdriveroptions', null));
212212
if ($driverOptions) {
213-
$connectionParams['driverOptions'] = array_merge($connectionParams['driverOptions'], $driverOptions);
213+
$connectionParams['driverOptions'] = $driverOptions + ($connectionParams['driverOptions'] ?? []);
214214
}
215215

216216
// set default table creation options

lib/private/Repair/RepairMimeTypes.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,18 @@ private function introduceTomlAndOvpnType(): IResult|int|null {
374374
return $this->updateMimetypes($updatedMimetypes);
375375
}
376376

377+
/**
378+
* @throws Exception
379+
* @since 36.0.0
380+
*/
381+
private function introduceAvifType(): IResult|int|null {
382+
$updatedMimetypes = [
383+
'avif' => 'image/avif',
384+
];
385+
386+
return $this->updateMimetypes($updatedMimetypes);
387+
}
388+
377389
/**
378390
* Check if there are any migrations available
379391
*
@@ -497,6 +509,10 @@ public function run(IOutput $output): void {
497509
$output->info('Fixed toml and ovpn mime type');
498510
}
499511

512+
if (version_compare($mimeTypeVersion, '36.0.0.0', '<') && $this->introduceAvifType()) {
513+
$output->info('Fixed avif mime type');
514+
}
515+
500516
if (!$this->dryRun) {
501517
$this->appConfig->setValueString('files', 'mimetype_version', $serverVersion);
502518
}

resources/config/mimetypemapping.dist.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"arw": ["image/x-dcraw"],
1818
"asciidoc": ["text/asciidoc", "text/plain"],
1919
"avi": ["video/x-msvideo"],
20+
"avif": ["image/avif"],
2021
"bash": ["text/x-shellscript"],
2122
"bat": ["application/x-msdos-program"],
2223
"bin": ["application/x-bin"],

resources/config/mimetypenames.dist.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@
128128
"audio/wav": "RIFF/WAVe standard Audio",
129129
"audio/webm": "WebM audio",
130130
"audio/x-scpls": "MP3 ShoutCast playlist",
131+
"image/avif": "AVIF image",
131132
"image/bmp": "Windows BMP image",
132133
"image/bpg": "Better Portable Graphics image",
133134
"image/emf": "EMF image",

tests/data/integritycheck/mimetypeListModified/core/js/mimetypelist.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ OC.MimeTypeList={
249249
'audio/wav': t('core', "RIFF\/WAVe standard Audio"),
250250
'audio/webm': t('core', "WebM audio"),
251251
'audio/x-scpls': t('core', "MP3 ShoutCast playlist"),
252+
'image/avif': t('core', "AVIF image"),
252253
'image/bmp': t('core', "Windows BMP image"),
253254
'image/bpg': t('core', "Better Portable Graphics image"),
254255
'image/emf': t('core', "EMF image"),
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"hashes": {
3-
"core\/js\/mimetypelist.js": "25635feead9858d82354005d59b36ae965323bc651b012910c0178bef8e90c9809abb2a6aa2425268d0f08a545567d2f2a10f00818435dc04ded44b7d9e61fad"
3+
"core\/js\/mimetypelist.js": "c08ff8fbaa204fb1fff9a1d9f58f78dc02d8f0b5dac7a0266e6cb8a0ea5a38facc1fb765cf5c63fb2c69230f7a972e6fb534efebec664392fb48a1b1b2cf952b"
44
},
5-
"signature": "lrvTud+92i2m7XmQXbpT2LFSuFUk351DPjCRJ8hvPngKLB1yoSLQqUNOFTRN97fUKlkR2k2O8x\/HWAOG9pmB9H2m5bYrJ8\/mUbhbYSoVV5yC1rWtWH7A1uCvdj\/wDjuTR9NRKQXbIQa+v0DuwxyeRO6GiY9YRl+K5UYmP2jZscolECHCWKnHfLBoesrkt6VsJIqJTRk\/6l8\/MzMxmzcq8mWH5AdEvwieyEIXqmJevRi\/lVQG8h4l3yZukjtDHdFnbKdyOjezRQFK\/joJlxV3pNUVIsBl6ClGghS75c\/kO9AD+70BX6u6aRr9TPbkh84EmCyzceFaU+FzCd\/NC+fwPjDrJQlVh6EfwOEzkpNQP5KA5r8FVQZw51U9o1kJ3mdK04w0X2Eap0ZOz3RP2W2Vo2KJH7OitXA9TCNh9VoVY5jBJRYNeFi3NQewC6qZ2IXhXIEi8nyLjrsh71WfimvBl37s8LkDHODzKIQTCO14a7mDiiPZHry2BRHwF8G7znaEf6CCbygUZi4PK3VZT5Sv5OBzLfgkpTxRXIN6NxKljBRF8B2bz027J4L7A3s6ttpmBOXtj98MQekp27eWC4mb+ihUaxvpe6M3dsK7hCfUDl4VvPlVYo+vnCP8WNa1I2IcB38HN5Wb6E5JDhaXgCz6i3CcNXwghcrs1j7wXiDw8Rc=",
5+
"signature": "MyLGk+nY8BLIBkr1PvLq4ePKe693xkq8xPzuyDQ4Sg7OlLkoDdlr4FqYTx7nJCyaaIO7BiaYrHHvX1BB1CSFG+Ta0bdq2A0yh+OlgQcpgFgxxv1QOS+PKrJCdNcM+UQDiyr2R6iKuhi2TfkwU7iyNHJLbTyvlJze5+mUNeLpaFIUirsiOw0O78adcJaIk6GI8eLgxI079PrZNAel7DAxzESsKPRy6td\/fenhuxSaCIx2Hmy0isjdm7a78yZ7P4+1DZmb0UGTrMOrNkSclAmvkqpKcKYwmeLQ711swmRr5SbXjjJZZrVmsWZSFFKZzmFfsNvQ83FaLCZ\/rQRbzBnBiGNQrofDkDcNb6SNiElUSQhP4rc1iu6fFgwoWt+CUmbgK3vtaxmw6VZ835\/8lciQ1xCStEQYQeImxPDeaASzTSrUMybutKToYdBLHWVXbisOtOf09xJeh0vA3cnsaCwaWqcwAKEoZN7sNHwBDlw0E361y9dNkPwuU9X8Swt3V4XWGpQ1UTMpz60n1Uf77r8u1mR0gUMYd++cv7g8tLvzUNgNyLGL7CNwj2W5MYHP+WSY8\/ywHG0aL\/1YM2xGG88gi3UOug6tY6eymFURutU8qC6i3bA4nYKqvTqqSY58tkuQo0G54tC9J\/E\/f3rGlIaVDWvdGTFxFC5ZvZEOGiEAKjo=",
66
"certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
77
}

tests/lib/DB/ConnectionFactoryTest.php

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,64 @@ public function testSplitHostFromPortAndSocket($host, array $expected): void {
4141
$this->assertEquals($expected, self::invokePrivate($factory, 'splitHostFromPortAndSocket', [$host]));
4242
}
4343

44+
/**
45+
* The numeric value of a PDO MySQL attribute, e.g. `SSL_CA`.
46+
*
47+
* The values are not stable across PHP versions, so they must never be hardcoded.
48+
* Since PHP 8.5 the `PDO::MYSQL_ATTR_*` constants are deprecated in favor of
49+
* `Pdo\Mysql::ATTR_*`, and either only exists with the MySQL driver installed.
50+
*/
51+
private function mysqlAttribute(string $name): int {
52+
if (!extension_loaded('pdo_mysql')) {
53+
$this->markTestSkipped('The pdo_mysql extension is required to resolve the PDO attribute values');
54+
}
55+
if (PHP_VERSION_ID >= 80500 && class_exists(\Pdo\Mysql::class)) {
56+
return (int)constant('Pdo\Mysql::ATTR_' . $name);
57+
}
58+
return (int)constant('PDO::MYSQL_ATTR_' . $name);
59+
}
60+
61+
public function testMysqlSslConnection(): void {
62+
/** @var SystemConfig|\PHPUnit\Framework\MockObject\MockObject $config */
63+
$config = $this->createMock(SystemConfig::class);
64+
$config->method('getValue')
65+
->willReturnCallback(function ($key, $default) {
66+
return match ($key) {
67+
'dbdriveroptions' => [
68+
$this->mysqlAttribute('SSL_CA') => 'rootCA.crt',
69+
$this->mysqlAttribute('SSL_CERT') => 'client.crt',
70+
$this->mysqlAttribute('SSL_KEY') => 'client.key',
71+
$this->mysqlAttribute('SSL_VERIFY_SERVER_CERT') => true,
72+
],
73+
'dbtype' => 'mysql',
74+
default => $default,
75+
};
76+
});
77+
$factory = new ConnectionFactory($config);
78+
79+
$params = $factory->createConnectionParams();
80+
81+
$this->assertEquals('pdo_mysql', $params['driver']);
82+
$this->assertEquals([
83+
$this->mysqlAttribute('FOUND_ROWS') => true,
84+
$this->mysqlAttribute('SSL_CA') => 'rootCA.crt',
85+
$this->mysqlAttribute('SSL_CERT') => 'client.crt',
86+
$this->mysqlAttribute('SSL_KEY') => 'client.key',
87+
$this->mysqlAttribute('SSL_VERIFY_SERVER_CERT') => true,
88+
], $params['driverOptions']);
89+
}
90+
4491
public function testPgsqlSslConnection(): void {
4592
/** @var SystemConfig|\PHPUnit\Framework\MockObject\MockObject $config */
4693
$config = $this->createMock(SystemConfig::class);
4794
$config->method('getValue')
4895
->willReturnCallback(function ($key, $default) {
4996
return match ($key) {
5097
'dbtype' => 'pgsql',
98+
'dbdriveroptions' => [
99+
1 => 'foo',
100+
3 => 'bar',
101+
],
51102
'pgsql_ssl' => [
52103
'mode' => 'verify-full',
53104
'cert' => 'client.crt',
@@ -68,5 +119,9 @@ public function testPgsqlSslConnection(): void {
68119
$this->assertEquals('client.crt', $params['sslcert']);
69120
$this->assertEquals('client.key', $params['sslkey']);
70121
$this->assertEquals('client.crl', $params['sslcrl']);
122+
$this->assertEquals([
123+
1 => 'foo',
124+
3 => 'bar',
125+
], $params['driverOptions']);
71126
}
72127
}

tests/lib/Repair/RepairMimeTypesTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,22 @@ public function testRenameImageTypes(): void {
139139
$this->renameMimeTypes($currentMimeTypes, $fixedMimeTypes);
140140
}
141141

142+
/**
143+
* Test renaming AVIF images, which had no mapping and so were stored
144+
* as whatever the content sniffer made of them, or as nothing at all
145+
*/
146+
public function testRenameAvifType(): void {
147+
$currentMimeTypes = [
148+
['test.avif', 'application/octet-stream'],
149+
];
150+
151+
$fixedMimeTypes = [
152+
['test.avif', 'image/avif'],
153+
];
154+
155+
$this->renameMimeTypes($currentMimeTypes, $fixedMimeTypes);
156+
}
157+
142158
/**
143159
* Test renaming the richdocuments additional office mime types
144160
*/

0 commit comments

Comments
 (0)