diff --git a/apps/dav/lib/CardDAV/ImageExportPlugin.php b/apps/dav/lib/CardDAV/ImageExportPlugin.php index 31e8ed6a5e..e48410d26d 100644 --- a/apps/dav/lib/CardDAV/ImageExportPlugin.php +++ b/apps/dav/lib/CardDAV/ImageExportPlugin.php @@ -87,6 +87,7 @@ class ImageExportPlugin extends ServerPlugin { if ($result = $this->getPhoto($node)) { $response->setHeader('Content-Type', $result['Content-Type']); + $response->setHeader('Content-Disposition', 'attachment'); $response->setStatus(200); $response->setBody($result['body']); @@ -121,6 +122,17 @@ class ImageExportPlugin extends ServerPlugin { } $val = file_get_contents($val); } + + $allowedContentTypes = [ + 'image/png', + 'image/jpeg', + 'image/gif', + ]; + + if(!in_array($type, $allowedContentTypes, true)) { + $type = 'application/octet-stream'; + } + return [ 'Content-Type' => $type, 'body' => $val diff --git a/apps/dav/tests/unit/CardDAV/ImageExportPluginTest.php b/apps/dav/tests/unit/CardDAV/ImageExportPluginTest.php index 229fa7ec6f..e878cbd4f5 100644 --- a/apps/dav/tests/unit/CardDAV/ImageExportPluginTest.php +++ b/apps/dav/tests/unit/CardDAV/ImageExportPluginTest.php @@ -107,9 +107,20 @@ class ImageExportPluginTest extends TestCase { $this->plugin->expects($this->once())->method('getPhoto')->willReturn($getPhotoResult); if (!$expected) { - $this->response->expects($this->once())->method('setHeader'); - $this->response->expects($this->once())->method('setStatus'); - $this->response->expects($this->once())->method('setBody'); + $this->response + ->expects($this->at(0)) + ->method('setHeader') + ->with('Content-Type', $getPhotoResult['Content-Type']); + $this->response + ->expects($this->at(1)) + ->method('setHeader') + ->with('Content-Disposition', 'attachment'); + $this->response + ->expects($this->once()) + ->method('setStatus'); + $this->response + ->expects($this->once()) + ->method('setBody'); } $result = $this->plugin->httpGet($this->request, $this->response); @@ -142,12 +153,43 @@ class ImageExportPluginTest extends TestCase { public function providesPhotoData() { return [ - 'empty vcard' => [false, ''], - 'vcard without PHOTO' => [false, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nEND:VCARD\r\n"], - 'vcard 3 with PHOTO' => [['Content-Type' => 'image/jpeg', 'body' => '12345'], "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nPHOTO;ENCODING=b;TYPE=JPEG:MTIzNDU=\r\nEND:VCARD\r\n"], - 'vcard 3 with PHOTO URL' => [false, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nPHOTO;TYPE=JPEG;VALUE=URI:http://example.com/photo.jpg\r\nEND:VCARD\r\n"], - 'vcard 4 with PHOTO' => [['Content-Type' => 'image/jpeg', 'body' => '12345'], "BEGIN:VCARD\r\nVERSION:4.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nPHOTO:data:image/jpeg;base64,MTIzNDU=\r\nEND:VCARD\r\n"], - 'vcard 4 with PHOTO URL' => [false, "BEGIN:VCARD\r\nVERSION:4.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nPHOTO;MEDIATYPE=image/jpeg:http://example.org/photo.jpg\r\nEND:VCARD\r\n"], + 'empty vcard' => [ + false, + '' + ], + 'vcard without PHOTO' => [ + false, + "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nEND:VCARD\r\n" + ], + 'vcard 3 with PHOTO' => [ + [ + 'Content-Type' => 'image/jpeg', + 'body' => '12345' + ], + "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nPHOTO;ENCODING=b;TYPE=JPEG:MTIzNDU=\r\nEND:VCARD\r\n" + ], + 'vcard 3 with PHOTO URL' => [ + false, + "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nPHOTO;TYPE=JPEG;VALUE=URI:http://example.com/photo.jpg\r\nEND:VCARD\r\n" + ], + 'vcard 4 with PHOTO' => [ + [ + 'Content-Type' => 'image/jpeg', + 'body' => '12345' + ], + "BEGIN:VCARD\r\nVERSION:4.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nPHOTO:data:image/jpeg;base64,MTIzNDU=\r\nEND:VCARD\r\n" + ], + 'vcard 4 with PHOTO URL' => [ + false, + "BEGIN:VCARD\r\nVERSION:4.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nPHOTO;MEDIATYPE=image/jpeg:http://example.org/photo.jpg\r\nEND:VCARD\r\n" + ], + 'vcard 4 with PHOTO AND INVALID MIMEtYPE' => [ + [ + 'Content-Type' => 'application/octet-stream', + 'body' => '12345' + ], + "BEGIN:VCARD\r\nVERSION:4.0\r\nPRODID:-//Sabre//Sabre VObject 3.5.0//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nPHOTO:data:image/svg;base64,MTIzNDU=\r\nEND:VCARD\r\n" + ], ]; } }