diff --git a/tests/acceptance/expected-failures-API-on-OCIS-storage.md b/tests/acceptance/expected-failures-API-on-OCIS-storage.md index 1187904668..8c2d499891 100644 --- a/tests/acceptance/expected-failures-API-on-OCIS-storage.md +++ b/tests/acceptance/expected-failures-API-on-OCIS-storage.md @@ -805,32 +805,11 @@ cannot share a folder with create permission - [apiWebdavPreviews/previews.feature:17](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L17) - [apiWebdavPreviews/previews.feature:18](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L18) - [apiWebdavPreviews/previews.feature:19](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L19) -- [apiWebdavPreviews/previews.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L30) -- [apiWebdavPreviews/previews.feature:31](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L31) -- [apiWebdavPreviews/previews.feature:32](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L32) -- [apiWebdavPreviews/previews.feature:33](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L33) -- [apiWebdavPreviews/previews.feature:34](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L34) -- [apiWebdavPreviews/previews.feature:35](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L35) -- [apiWebdavPreviews/previews.feature:36](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L36) -- [apiWebdavPreviews/previews.feature:47](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L47) -- [apiWebdavPreviews/previews.feature:48](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L48) -- [apiWebdavPreviews/previews.feature:49](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L49) -- [apiWebdavPreviews/previews.feature:50](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L50) -- [apiWebdavPreviews/previews.feature:51](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L51) -- [apiWebdavPreviews/previews.feature:52](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L52) -- [apiWebdavPreviews/previews.feature:53](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L53) - [apiWebdavPreviews/previews.feature:56](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L56) -- [apiWebdavPreviews/previews.feature:71](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L71) -- [apiWebdavPreviews/previews.feature:72](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L72) -- [apiWebdavPreviews/previews.feature:73](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L73) - [apiWebdavPreviews/previews.feature:87](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L87) - [apiWebdavPreviews/previews.feature:95](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L95) - [apiWebdavPreviews/previews.feature:104](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L104) - [apiWebdavPreviews/previews.feature:113](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L113) -- [apiWebdavPreviews/previews.feature:120](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L120) -- [apiWebdavPreviews/previews.feature:127](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L127) -- [apiWebdavPreviews/previews.feature:135](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L135) -- [apiWebdavPreviews/previews.feature:144](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L144) - [apiWebdavPreviews/previews.feature:163](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L163) - [apiWebdavPreviews/previews.feature:164](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L164) - [apiWebdavPreviews/previews.feature:165](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L165) diff --git a/tests/acceptance/expected-failures-API-on-OWNCLOUD-storage.md b/tests/acceptance/expected-failures-API-on-OWNCLOUD-storage.md index b12b4b6b61..b3d4b38e26 100644 --- a/tests/acceptance/expected-failures-API-on-OWNCLOUD-storage.md +++ b/tests/acceptance/expected-failures-API-on-OWNCLOUD-storage.md @@ -829,32 +829,11 @@ cannot share a folder with create permission - [apiWebdavPreviews/previews.feature:17](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L17) - [apiWebdavPreviews/previews.feature:18](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L18) - [apiWebdavPreviews/previews.feature:19](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L19) -- [apiWebdavPreviews/previews.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L30) -- [apiWebdavPreviews/previews.feature:31](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L31) -- [apiWebdavPreviews/previews.feature:32](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L32) -- [apiWebdavPreviews/previews.feature:33](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L33) -- [apiWebdavPreviews/previews.feature:34](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L34) -- [apiWebdavPreviews/previews.feature:35](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L35) -- [apiWebdavPreviews/previews.feature:36](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L36) -- [apiWebdavPreviews/previews.feature:47](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L47) -- [apiWebdavPreviews/previews.feature:48](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L48) -- [apiWebdavPreviews/previews.feature:49](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L49) -- [apiWebdavPreviews/previews.feature:50](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L50) -- [apiWebdavPreviews/previews.feature:51](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L51) -- [apiWebdavPreviews/previews.feature:52](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L52) -- [apiWebdavPreviews/previews.feature:53](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L53) - [apiWebdavPreviews/previews.feature:56](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L56) -- [apiWebdavPreviews/previews.feature:71](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L71) -- [apiWebdavPreviews/previews.feature:72](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L72) -- [apiWebdavPreviews/previews.feature:73](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L73) - [apiWebdavPreviews/previews.feature:87](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L87) - [apiWebdavPreviews/previews.feature:95](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L95) - [apiWebdavPreviews/previews.feature:104](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L104) - [apiWebdavPreviews/previews.feature:113](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L113) -- [apiWebdavPreviews/previews.feature:120](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L120) -- [apiWebdavPreviews/previews.feature:127](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L127) -- [apiWebdavPreviews/previews.feature:135](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L135) -- [apiWebdavPreviews/previews.feature:144](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L144) - [apiWebdavPreviews/previews.feature:163](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L163) - [apiWebdavPreviews/previews.feature:164](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L164) - [apiWebdavPreviews/previews.feature:165](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavPreviews/previews.feature#L165) diff --git a/tests/acceptance/features/apiBugDemonstration/apiWebdavPreviews-previews.feature b/tests/acceptance/features/apiBugDemonstration/apiWebdavPreviews-previews.feature index 7b1679d572..1740e5395a 100644 --- a/tests/acceptance/features/apiBugDemonstration/apiWebdavPreviews-previews.feature +++ b/tests/acceptance/features/apiBugDemonstration/apiWebdavPreviews-previews.feature @@ -36,37 +36,13 @@ Feature: previews of files downloaded through the webdav API | A | | %2F | - @issue-ocis-189 - # after fixing all issues delete this Scenario and use the one from oC10 core - Scenario Outline: download previews of file types that don't support preview - Given user "Alice" has uploaded file "filesForUpload/" to "/" - When user "Alice" downloads the preview of "/" with width "32" and height "32" using the WebDAV API - Then the HTTP status code should be "400" - Examples: - | filename | newfilename | - | simple.pdf | test.pdf | - | simple.odt | test.odt | - | new-data.zip | test.zip | - - @issue-ocis-187 - # after fixing all issues delete this Scenario and use the one from oC10 core - Scenario Outline: download previews of different image file types - Given user "Alice" has uploaded file "filesForUpload/" to "/" - When user "Alice" downloads the preview of "/" with width "32" and height "32" using the WebDAV API - Then the HTTP status code should be "400" - # And the downloaded image should be "1240" pixels wide and "648" pixels high - Examples: - | imageName | newImageName | - | testavatar.jpg | testimage.jpg | - | testavatar.png | testimage.png | - @issue-ocis-187 # after fixing all issues delete this Scenario and use the one from oC10 core Scenario: download previews of image after renaming it Given user "Alice" has uploaded file "filesForUpload/testavatar.jpg" to "/testimage.jpg" When user "Alice" moves file "/testimage.jpg" to "/testimage.txt" using the WebDAV API And user "Alice" downloads the preview of "/testimage.txt" with width "32" and height "32" using the WebDAV API - Then the HTTP status code should be "400" + Then the HTTP status code should be "404" # And the downloaded image should be "1240" pixels wide and "648" pixels high @issue-ocis-thumbnails-191 @skipOnOcis-EOS-Storage @issue-ocis-reva-308 @@ -75,7 +51,7 @@ Feature: previews of files downloaded through the webdav API Given user "Brian" has been created with default attributes and without skeleton files And user "Alice" has uploaded file "filesForUpload/lorem.txt" to "/parent.txt" When user "Brian" downloads the preview of "/parent.txt" of "Alice" with width "32" and height "32" using the WebDAV API - Then the HTTP status code should be "400" + Then the HTTP status code should be "404" @issue-ocis-190 # after fixing all issues delete this Scenario and use the one from oC10 core @@ -90,7 +66,7 @@ Feature: previews of files downloaded through the webdav API Given the administrator has updated system config key "enable_previews" with value "false" and type "boolean" And user "Alice" has uploaded file "filesForUpload/lorem.txt" to "/parent.txt" When user "Alice" downloads the preview of "/parent.txt" with width "32" and height "32" using the WebDAV API - Then the HTTP status code should be "400" + Then the HTTP status code should be "404" @issue-ocis-193 # after fixing all issues delete this Scenario and use the one from oC10 core @@ -99,7 +75,7 @@ Feature: previews of files downloaded through the webdav API And the administrator has updated system config key "preview_max_x" with value "null" And the administrator has updated system config key "preview_max_y" with value "null" When user "Alice" downloads the preview of "/parent.txt" with width "32" and height "32" using the WebDAV API - Then the HTTP status code should be "400" + Then the HTTP status code should be "404" @issue-ocis-193 # after fixing all issues delete this Scenario and use the one from oC10 core diff --git a/thumbnails/pkg/proto/v0/thumbnails.pb.go b/thumbnails/pkg/proto/v0/thumbnails.pb.go index c60b8e655a..25d0697e02 100644 --- a/thumbnails/pkg/proto/v0/thumbnails.pb.go +++ b/thumbnails/pkg/proto/v0/thumbnails.pb.go @@ -22,49 +22,49 @@ const ( ) // The file types to which the thumbnail can get encoded to. -type GetThumbnailRequest_FileType int32 +type GetThumbnailRequest_ThumbnailType int32 const ( - GetThumbnailRequest_PNG GetThumbnailRequest_FileType = 0 // Represents PNG type - GetThumbnailRequest_JPG GetThumbnailRequest_FileType = 1 // Represents JPG type + GetThumbnailRequest_PNG GetThumbnailRequest_ThumbnailType = 0 // Represents PNG type + GetThumbnailRequest_JPG GetThumbnailRequest_ThumbnailType = 1 // Represents JPG type ) -// Enum value maps for GetThumbnailRequest_FileType. +// Enum value maps for GetThumbnailRequest_ThumbnailType. var ( - GetThumbnailRequest_FileType_name = map[int32]string{ + GetThumbnailRequest_ThumbnailType_name = map[int32]string{ 0: "PNG", 1: "JPG", } - GetThumbnailRequest_FileType_value = map[string]int32{ + GetThumbnailRequest_ThumbnailType_value = map[string]int32{ "PNG": 0, "JPG": 1, } ) -func (x GetThumbnailRequest_FileType) Enum() *GetThumbnailRequest_FileType { - p := new(GetThumbnailRequest_FileType) +func (x GetThumbnailRequest_ThumbnailType) Enum() *GetThumbnailRequest_ThumbnailType { + p := new(GetThumbnailRequest_ThumbnailType) *p = x return p } -func (x GetThumbnailRequest_FileType) String() string { +func (x GetThumbnailRequest_ThumbnailType) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } -func (GetThumbnailRequest_FileType) Descriptor() protoreflect.EnumDescriptor { +func (GetThumbnailRequest_ThumbnailType) Descriptor() protoreflect.EnumDescriptor { return file_thumbnails_proto_enumTypes[0].Descriptor() } -func (GetThumbnailRequest_FileType) Type() protoreflect.EnumType { +func (GetThumbnailRequest_ThumbnailType) Type() protoreflect.EnumType { return &file_thumbnails_proto_enumTypes[0] } -func (x GetThumbnailRequest_FileType) Number() protoreflect.EnumNumber { +func (x GetThumbnailRequest_ThumbnailType) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } -// Deprecated: Use GetThumbnailRequest_FileType.Descriptor instead. -func (GetThumbnailRequest_FileType) EnumDescriptor() ([]byte, []int) { +// Deprecated: Use GetThumbnailRequest_ThumbnailType.Descriptor instead. +func (GetThumbnailRequest_ThumbnailType) EnumDescriptor() ([]byte, []int) { return file_thumbnails_proto_rawDescGZIP(), []int{0, 0} } @@ -77,7 +77,7 @@ type GetThumbnailRequest struct { // The path to the source image Filepath string `protobuf:"bytes,1,opt,name=filepath,proto3" json:"filepath,omitempty"` // The type to which the thumbnail should get encoded to. - ThumbnailType GetThumbnailRequest_FileType `protobuf:"varint,2,opt,name=thumbnail_type,json=thumbnailType,proto3,enum=com.owncloud.ocis.thumbnails.v0.GetThumbnailRequest_FileType" json:"thumbnail_type,omitempty"` + ThumbnailType GetThumbnailRequest_ThumbnailType `protobuf:"varint,2,opt,name=thumbnail_type,json=thumbnailType,proto3,enum=com.owncloud.ocis.thumbnails.v0.GetThumbnailRequest_ThumbnailType" json:"thumbnail_type,omitempty"` // The width of the thumbnail Width int32 `protobuf:"varint,3,opt,name=width,proto3" json:"width,omitempty"` // The height of the thumbnail @@ -127,7 +127,7 @@ func (x *GetThumbnailRequest) GetFilepath() string { return "" } -func (x *GetThumbnailRequest) GetThumbnailType() GetThumbnailRequest_FileType { +func (x *GetThumbnailRequest) GetThumbnailType() GetThumbnailRequest_ThumbnailType { if x != nil { return x.ThumbnailType } @@ -391,87 +391,87 @@ var file_thumbnails_proto_rawDesc = []byte{ 0x2e, 0x76, 0x30, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x22, 0x90, 0x03, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x54, 0x68, 0x75, 0x6d, 0x62, + 0x6f, 0x74, 0x6f, 0x22, 0x9a, 0x03, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, - 0x69, 0x6c, 0x65, 0x70, 0x61, 0x74, 0x68, 0x12, 0x64, 0x0a, 0x0e, 0x74, 0x68, 0x75, 0x6d, 0x62, + 0x69, 0x6c, 0x65, 0x70, 0x61, 0x74, 0x68, 0x12, 0x69, 0x0a, 0x0e, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x3d, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6f, + 0x42, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x76, 0x30, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0d, - 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, - 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x77, 0x69, - 0x64, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x54, 0x0a, 0x0d, 0x77, - 0x65, 0x62, 0x64, 0x61, 0x76, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, - 0x64, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, - 0x73, 0x2e, 0x76, 0x30, 0x2e, 0x57, 0x65, 0x62, 0x64, 0x61, 0x76, 0x53, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x48, 0x00, 0x52, 0x0c, 0x77, 0x65, 0x62, 0x64, 0x61, 0x76, 0x53, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x12, 0x4b, 0x0a, 0x0a, 0x63, 0x73, 0x33, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x6f, 0x77, 0x6e, 0x63, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x54, + 0x79, 0x70, 0x65, 0x52, 0x0d, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x12, 0x54, 0x0a, 0x0d, 0x77, 0x65, 0x62, 0x64, 0x61, 0x76, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x6f, 0x77, + 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x74, 0x68, 0x75, 0x6d, + 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x76, 0x30, 0x2e, 0x57, 0x65, 0x62, 0x64, 0x61, 0x76, + 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x48, 0x00, 0x52, 0x0c, 0x77, 0x65, 0x62, 0x64, 0x61, 0x76, + 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x4b, 0x0a, 0x0a, 0x63, 0x73, 0x33, 0x5f, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6d, + 0x2e, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x74, + 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x76, 0x30, 0x2e, 0x43, 0x53, 0x33, + 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x48, 0x00, 0x52, 0x09, 0x63, 0x73, 0x33, 0x53, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x22, 0x21, 0x0a, 0x0d, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x50, 0x4e, 0x47, 0x10, 0x00, 0x12, 0x07, 0x0a, + 0x03, 0x4a, 0x50, 0x47, 0x10, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x22, 0xd4, 0x01, 0x0a, 0x0c, 0x57, 0x65, 0x62, 0x64, 0x61, 0x76, 0x53, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x75, 0x72, 0x6c, 0x12, 0x24, 0x0a, 0x0e, 0x69, 0x73, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x73, 0x50, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x31, 0x0a, 0x14, 0x77, 0x65, 0x62, + 0x64, 0x61, 0x76, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x77, 0x65, 0x62, 0x64, 0x61, 0x76, 0x41, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x12, + 0x72, 0x65, 0x76, 0x61, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x72, 0x65, 0x76, 0x61, 0x41, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x11, 0x70, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4c, 0x69, + 0x6e, 0x6b, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x45, 0x0a, 0x09, 0x43, 0x53, 0x33, 0x53, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x24, 0x0a, 0x0d, 0x61, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0d, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x50, + 0x0a, 0x14, 0x47, 0x65, 0x74, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, + 0x61, 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x74, 0x68, 0x75, 0x6d, 0x62, + 0x6e, 0x61, 0x69, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x74, 0x79, 0x70, 0x65, + 0x32, 0x8f, 0x01, 0x0a, 0x10, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x7b, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x54, 0x68, 0x75, 0x6d, + 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x12, 0x34, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, - 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x76, 0x30, 0x2e, 0x43, 0x53, 0x33, 0x53, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x48, 0x00, 0x52, 0x09, 0x63, 0x73, 0x33, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x1c, - 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x50, 0x4e, - 0x47, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4a, 0x50, 0x47, 0x10, 0x01, 0x42, 0x08, 0x0a, 0x06, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0xd4, 0x01, 0x0a, 0x0c, 0x57, 0x65, 0x62, 0x64, 0x61, - 0x76, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x24, 0x0a, 0x0e, 0x69, 0x73, 0x5f, - 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0c, 0x69, 0x73, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4c, 0x69, 0x6e, 0x6b, 0x12, - 0x31, 0x0a, 0x14, 0x77, 0x65, 0x62, 0x64, 0x61, 0x76, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, - 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x77, - 0x65, 0x62, 0x64, 0x61, 0x76, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x72, 0x65, 0x76, 0x61, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, - 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, - 0x72, 0x65, 0x76, 0x61, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6c, 0x69, 0x6e, 0x6b, - 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x75, - 0x62, 0x6c, 0x69, 0x63, 0x4c, 0x69, 0x6e, 0x6b, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x45, 0x0a, - 0x09, 0x43, 0x53, 0x33, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, - 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x24, - 0x0a, 0x0d, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x50, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x54, 0x68, 0x75, 0x6d, 0x62, - 0x6e, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, - 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x09, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x69, - 0x6d, 0x65, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x69, - 0x6d, 0x65, 0x74, 0x79, 0x70, 0x65, 0x32, 0x8f, 0x01, 0x0a, 0x10, 0x54, 0x68, 0x75, 0x6d, 0x62, - 0x6e, 0x61, 0x69, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x7b, 0x0a, 0x0c, 0x47, - 0x65, 0x74, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x12, 0x34, 0x2e, 0x63, 0x6f, + 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x76, 0x30, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x68, 0x75, 0x6d, 0x62, + 0x6e, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x76, 0x30, 0x2e, 0x47, 0x65, - 0x74, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, - 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, - 0x2e, 0x76, 0x30, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0xe0, 0x02, 0x5a, 0x36, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, - 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, - 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x30, 0x3b, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x92, 0x41, 0xa4, 0x02, 0x12, 0xb8, 0x01, 0x0a, 0x22, 0x6f, 0x77, 0x6e, 0x43, - 0x6c, 0x6f, 0x75, 0x64, 0x20, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x65, 0x20, 0x53, 0x63, - 0x61, 0x6c, 0x65, 0x20, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x47, - 0x0a, 0x0d, 0x6f, 0x77, 0x6e, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x20, 0x47, 0x6d, 0x62, 0x48, 0x12, - 0x20, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x74, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x42, 0xe0, 0x02, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x2f, + 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x30, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x92, 0x41, 0xa4, + 0x02, 0x12, 0xb8, 0x01, 0x0a, 0x22, 0x6f, 0x77, 0x6e, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x20, 0x49, + 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x65, 0x20, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x20, 0x74, 0x68, + 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x47, 0x0a, 0x0d, 0x6f, 0x77, 0x6e, 0x43, + 0x6c, 0x6f, 0x75, 0x64, 0x20, 0x47, 0x6d, 0x62, 0x48, 0x12, 0x20, 0x68, 0x74, 0x74, 0x70, 0x73, + 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x77, + 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x1a, 0x14, 0x73, 0x75, 0x70, + 0x70, 0x6f, 0x72, 0x74, 0x40, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x63, 0x6f, + 0x6d, 0x2a, 0x42, 0x0a, 0x0a, 0x41, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2d, 0x32, 0x2e, 0x30, 0x12, + 0x34, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6f, 0x63, 0x69, - 0x73, 0x1a, 0x14, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x40, 0x6f, 0x77, 0x6e, 0x63, 0x6c, - 0x6f, 0x75, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x2a, 0x42, 0x0a, 0x0a, 0x41, 0x70, 0x61, 0x63, 0x68, - 0x65, 0x2d, 0x32, 0x2e, 0x30, 0x12, 0x34, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, - 0x75, 0x64, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x62, 0x6c, 0x6f, 0x62, 0x2f, 0x6d, 0x61, 0x73, - 0x74, 0x65, 0x72, 0x2f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x32, 0x05, 0x31, 0x2e, 0x30, - 0x2e, 0x30, 0x2a, 0x02, 0x01, 0x02, 0x32, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x72, 0x3f, 0x0a, 0x10, 0x44, 0x65, - 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x72, 0x20, 0x4d, 0x61, 0x6e, 0x75, 0x61, 0x6c, 0x12, 0x2b, - 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, - 0x2e, 0x64, 0x65, 0x76, 0x2f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2f, - 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x2f, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x73, 0x2f, 0x62, 0x6c, 0x6f, 0x62, 0x2f, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x2f, 0x4c, 0x49, + 0x43, 0x45, 0x4e, 0x53, 0x45, 0x32, 0x05, 0x31, 0x2e, 0x30, 0x2e, 0x30, 0x2a, 0x02, 0x01, 0x02, + 0x32, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, + 0x6f, 0x6e, 0x3a, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, + 0x6a, 0x73, 0x6f, 0x6e, 0x72, 0x3f, 0x0a, 0x10, 0x44, 0x65, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, + 0x72, 0x20, 0x4d, 0x61, 0x6e, 0x75, 0x61, 0x6c, 0x12, 0x2b, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, + 0x2f, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x64, 0x65, 0x76, 0x2f, 0x65, + 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, + 0x61, 0x69, 0x6c, 0x73, 0x2f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -489,14 +489,14 @@ func file_thumbnails_proto_rawDescGZIP() []byte { var file_thumbnails_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_thumbnails_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_thumbnails_proto_goTypes = []interface{}{ - (GetThumbnailRequest_FileType)(0), // 0: com.owncloud.ocis.thumbnails.v0.GetThumbnailRequest.FileType - (*GetThumbnailRequest)(nil), // 1: com.owncloud.ocis.thumbnails.v0.GetThumbnailRequest - (*WebdavSource)(nil), // 2: com.owncloud.ocis.thumbnails.v0.WebdavSource - (*CS3Source)(nil), // 3: com.owncloud.ocis.thumbnails.v0.CS3Source - (*GetThumbnailResponse)(nil), // 4: com.owncloud.ocis.thumbnails.v0.GetThumbnailResponse + (GetThumbnailRequest_ThumbnailType)(0), // 0: com.owncloud.ocis.thumbnails.v0.GetThumbnailRequest.ThumbnailType + (*GetThumbnailRequest)(nil), // 1: com.owncloud.ocis.thumbnails.v0.GetThumbnailRequest + (*WebdavSource)(nil), // 2: com.owncloud.ocis.thumbnails.v0.WebdavSource + (*CS3Source)(nil), // 3: com.owncloud.ocis.thumbnails.v0.CS3Source + (*GetThumbnailResponse)(nil), // 4: com.owncloud.ocis.thumbnails.v0.GetThumbnailResponse } var file_thumbnails_proto_depIdxs = []int32{ - 0, // 0: com.owncloud.ocis.thumbnails.v0.GetThumbnailRequest.thumbnail_type:type_name -> com.owncloud.ocis.thumbnails.v0.GetThumbnailRequest.FileType + 0, // 0: com.owncloud.ocis.thumbnails.v0.GetThumbnailRequest.thumbnail_type:type_name -> com.owncloud.ocis.thumbnails.v0.GetThumbnailRequest.ThumbnailType 2, // 1: com.owncloud.ocis.thumbnails.v0.GetThumbnailRequest.webdav_source:type_name -> com.owncloud.ocis.thumbnails.v0.WebdavSource 3, // 2: com.owncloud.ocis.thumbnails.v0.GetThumbnailRequest.cs3_source:type_name -> com.owncloud.ocis.thumbnails.v0.CS3Source 1, // 3: com.owncloud.ocis.thumbnails.v0.ThumbnailService.GetThumbnail:input_type -> com.owncloud.ocis.thumbnails.v0.GetThumbnailRequest diff --git a/thumbnails/pkg/proto/v0/thumbnails.pb_test.go b/thumbnails/pkg/proto/v0/thumbnails.pb_test.go index 2ecabf04e1..e09610f711 100644 --- a/thumbnails/pkg/proto/v0/thumbnails.pb_test.go +++ b/thumbnails/pkg/proto/v0/thumbnails.pb_test.go @@ -10,7 +10,7 @@ import ( type TestRequest struct { testDataName string filepath string - filetype proto.GetThumbnailRequest_FileType + filetype proto.GetThumbnailRequest_ThumbnailType Checksum string width int32 height int32 diff --git a/thumbnails/pkg/proto/v0/thumbnails.proto b/thumbnails/pkg/proto/v0/thumbnails.proto index 3faea799d9..2c5d956daf 100644 --- a/thumbnails/pkg/proto/v0/thumbnails.proto +++ b/thumbnails/pkg/proto/v0/thumbnails.proto @@ -41,12 +41,12 @@ message GetThumbnailRequest { // The path to the source image string filepath = 1; // The file types to which the thumbnail can get encoded to. - enum FileType { + enum ThumbnailType { PNG = 0; // Represents PNG type JPG = 1; // Represents JPG type } // The type to which the thumbnail should get encoded to. - FileType thumbnail_type = 2; + ThumbnailType thumbnail_type = 2; // The width of the thumbnail int32 width = 3; // The height of the thumbnail diff --git a/thumbnails/pkg/service/v0/service.go b/thumbnails/pkg/service/v0/service.go index a6ea2ef340..45adfa9231 100644 --- a/thumbnails/pkg/service/v0/service.go +++ b/thumbnails/pkg/service/v0/service.go @@ -55,7 +55,7 @@ type Thumbnail struct { // GetThumbnail retrieves a thumbnail for an image func (g Thumbnail) GetThumbnail(ctx context.Context, req *v0proto.GetThumbnailRequest, rsp *v0proto.GetThumbnailResponse) error { - _, ok := v0proto.GetThumbnailRequest_FileType_value[req.ThumbnailType.String()] + _, ok := v0proto.GetThumbnailRequest_ThumbnailType_value[req.ThumbnailType.String()] if !ok { g.logger.Debug().Str("thumbnail_type", req.ThumbnailType.String()).Msg("unsupported thumbnail type") return nil @@ -205,14 +205,26 @@ func (g Thumbnail) stat(path, auth string) (*provider.StatResponse, error) { } if rsp.Status.Code != rpc.Code_CODE_OK { - g.logger.Error().Str("status_message", rsp.Status.Message).Str("path", path).Msg("could not stat file") - return nil, merrors.InternalServerError(g.serviceID, "could not stat file: %s", rsp.Status.Message) + switch rsp.Status.Code { + case rpc.Code_CODE_NOT_FOUND: + return nil, merrors.NotFound(g.serviceID, "could not stat file: %s", rsp.Status.Message) + default: + g.logger.Error().Str("status_message", rsp.Status.Message).Str("path", path).Msg("could not stat file") + return nil, merrors.InternalServerError(g.serviceID, "could not stat file: %s", rsp.Status.Message) + } + } + if rsp.Info.Type != provider.ResourceType_RESOURCE_TYPE_FILE { + return nil, merrors.BadRequest(g.serviceID, "Unsupported file type") } - if rsp.Info.GetChecksum().GetSum() == "" { g.logger.Error().Msg("resource info is missing checksum") - return nil, merrors.InternalServerError(g.serviceID, "resource info is missing a checksum") + return nil, merrors.NotFound(g.serviceID, "resource info is missing a checksum") } + if !thumbnail.IsMimeTypeSupported(rsp.Info.MimeType) { + return nil, merrors.NotFound(g.serviceID, "Unsupported file type") + } + + return rsp, nil } diff --git a/thumbnails/pkg/thumbnail/imgsource/cs3.go b/thumbnails/pkg/thumbnail/imgsource/cs3.go index 1c5c0bed34..11689952f7 100644 --- a/thumbnails/pkg/thumbnail/imgsource/cs3.go +++ b/thumbnails/pkg/thumbnail/imgsource/cs3.go @@ -16,7 +16,7 @@ import ( ) type CS3 struct { - client gateway.GatewayAPIClient + client gateway.GatewayAPIClient } func NewCS3Source(c gateway.GatewayAPIClient) CS3 { @@ -26,14 +26,17 @@ func NewCS3Source(c gateway.GatewayAPIClient) CS3 { } func (s CS3) Get(ctx context.Context, path string) (image.Image, error) { - auth, _ := ContextGetAuthorization(ctx) + auth, ok := ContextGetAuthorization(ctx) + if !ok { + return nil, errors.New("cs3source: authorization missing") + } ctx = metadata.AppendToOutgoingContext(context.Background(), token.TokenHeader, auth) rsp, err := s.client.InitiateFileDownload(ctx, &provider.InitiateFileDownloadRequest{ Ref: &provider.Reference{ Spec: &provider.Reference_Path{ Path: path, }, - } , + }, }) if err != nil { diff --git a/thumbnails/pkg/thumbnail/storage/storage.go b/thumbnails/pkg/thumbnail/storage/storage.go index 7d9e8b0880..bf7f3c5454 100644 --- a/thumbnails/pkg/thumbnail/storage/storage.go +++ b/thumbnails/pkg/thumbnail/storage/storage.go @@ -6,8 +6,14 @@ import ( // Request combines different attributes needed for storage operations. type Request struct { + // The checksum of the source file + // Will be used to determine if a thumbnail exists Checksum string + // Types provided by the encoder. + // Contains the mimetypes of the thumbnail. + // In case of jpg/jpeg it will contain both. Types []string + // The resolution of the thumbnail Resolution image.Rectangle } diff --git a/thumbnails/pkg/thumbnail/thumbnail.go b/thumbnails/pkg/thumbnail/thumbnail.go index 7954a8624d..e976c62a44 100644 --- a/thumbnails/pkg/thumbnail/thumbnail.go +++ b/thumbnails/pkg/thumbnail/thumbnail.go @@ -6,6 +6,16 @@ import ( "github.com/owncloud/ocis/ocis-pkg/log" "github.com/owncloud/ocis/thumbnails/pkg/thumbnail/storage" "image" + "strings" +) + +var ( + SupportedMimeTypes = [...]string{ + "image/png", + "image/jpg", + "image/jpeg", + "image/gif", + } ) // Request bundles information needed to generate a thumbnail for afile @@ -40,7 +50,8 @@ type SimpleManager struct { resolutions Resolutions } -// Generate implements the Get Method of Manager +// Generate creates a thumbnail and stores it. +// The created thumbnail is also being returned. func (s SimpleManager) Generate(r Request, img image.Image) ([]byte, error) { match := s.resolutions.ClosestMatch(r.Resolution, img.Bounds()) thumbnail := s.generate(match, img) @@ -77,3 +88,12 @@ func mapToStorageRequest(r Request) storage.Request { Types: r.Encoder.Types(), } } + +func IsMimeTypeSupported(m string) bool { + for _, mt := range SupportedMimeTypes { + if strings.EqualFold(mt, m) { + return true + } + } + return false +} diff --git a/webdav/go.mod b/webdav/go.mod index a4b067027a..25c5520fdc 100644 --- a/webdav/go.mod +++ b/webdav/go.mod @@ -8,6 +8,7 @@ require ( contrib.go.opencensus.io/exporter/zipkin v0.1.2 github.com/asim/go-micro/v3 v3.5.1-0.20210217182006-0f0ace1a44a9 github.com/go-chi/chi v4.1.2+incompatible + github.com/go-chi/render v1.0.1 github.com/micro/cli/v2 v2.1.2 github.com/oklog/run v1.1.0 github.com/olekukonko/tablewriter v0.0.5 diff --git a/webdav/go.sum b/webdav/go.sum index 5881aef353..20a82edbb2 100644 --- a/webdav/go.sum +++ b/webdav/go.sum @@ -380,6 +380,7 @@ github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkPro github.com/go-bindata/go-bindata v3.1.1+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo= github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= +github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8= github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns= github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= diff --git a/webdav/pkg/dav/requests/thumbnail.go b/webdav/pkg/dav/requests/thumbnail.go index 7e2ffae508..3159c116ef 100644 --- a/webdav/pkg/dav/requests/thumbnail.go +++ b/webdav/pkg/dav/requests/thumbnail.go @@ -2,6 +2,7 @@ package requests import ( "errors" + "fmt" "net/http" "net/url" "path/filepath" @@ -18,34 +19,43 @@ const ( DefaultHeight = 32 ) -// Request combines all parameters provided when requesting a thumbnail +// ThumbnailRequest combines all parameters provided when requesting a thumbnail type ThumbnailRequest struct { + // The file path of the source file Filepath string + // The file name of the source file including the extension + Filename string + // The file extension Extension string + // The requested width of the thumbnail Width int32 + // The requested height of the thumbnail Height int32 + // In case of a public share the public link token. PublicLinkToken string } -// NewRequest extracts all required parameters from a http request. -func ParseThumbnailRequest(r *http.Request) (ThumbnailRequest, error) { - fp := extractFilePath(r) +// ParseThumbnailRequest extracts all required parameters from a http request. +func ParseThumbnailRequest(r *http.Request) (*ThumbnailRequest, error) { + fp, err := extractFilePath(r) + if err != nil { + return nil, err + } q := r.URL.Query() width, height, err := parseDimensions(q) if err != nil { - return ThumbnailRequest{}, err + return nil, err } - tr := ThumbnailRequest{ + return &ThumbnailRequest{ Filepath: fp, + Filename: filepath.Base(fp), Extension: filepath.Ext(fp), Width: int32(width), Height: int32(height), PublicLinkToken: chi.URLParam(r, "token"), - } - - return tr, nil + }, nil } // the url looks as followed @@ -54,43 +64,40 @@ func ParseThumbnailRequest(r *http.Request) (ThumbnailRequest, error) { // // User and filepath are dynamic and filepath can contain slashes // So using the URLParam function is not possible. -func extractFilePath(r *http.Request) string { +func extractFilePath(r *http.Request) (string, error) { user := chi.URLParam(r, "user") if user != "" { parts := strings.SplitN(r.URL.Path, user, 2) - return parts[1] + return parts[1], nil } token := chi.URLParam(r, "token") if token != "" { parts := strings.SplitN(r.URL.Path, token, 2) - return parts[1] + return parts[1], nil } - return "" + return "", errors.New("could not extract file path") } func parseDimensions(q url.Values) (int64, int64, error) { - width, err := parseDimension(q.Get("x"), DefaultWidth) + width, err := parseDimension(q.Get("x"), "width", DefaultWidth) if err != nil { return 0, 0, err } - height, err := parseDimension(q.Get("y"), DefaultHeight) + height, err := parseDimension(q.Get("y"), "height", DefaultHeight) if err != nil { return 0, 0, err } return width, height, nil } -func parseDimension(d string, defaultValue int64) (int64, error) { +func parseDimension(d, name string, defaultValue int64) (int64, error) { if d == "" { return defaultValue, nil } result, err := strconv.ParseInt(d, 10, 32) - if err != nil { - return 0, err + if err != nil || result < 1 { + // The error message doesn't fit but for OC10 API compatibility reasons we have to set this. + return 0, fmt.Errorf("Cannot set %s of 0 or smaller!", name) //nolint:golint } - if result < 1 { - return 0, errors.New("invalid dimension") - } - return result, nil } diff --git a/webdav/pkg/flagset/flagset.go b/webdav/pkg/flagset/flagset.go index a525943a84..d60bed265e 100644 --- a/webdav/pkg/flagset/flagset.go +++ b/webdav/pkg/flagset/flagset.go @@ -145,7 +145,7 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag { Name: "ocis-public-url", Value: flags.OverrideDefaultString(cfg.OcisPublicURL, "https://127.0.0.1:9200"), Usage: "The domain under which oCIS is reachable", - EnvVars: []string{"WEBDAV_OCIS_PUBLIC_URL", "OCIS_URL"}, + EnvVars: []string{"OCIS_PUBLIC_URL", "OCIS_URL"}, Destination: &cfg.OcisPublicURL, }, } diff --git a/webdav/pkg/service/v0/service.go b/webdav/pkg/service/v0/service.go index 89c7e385b7..ee253cf42d 100644 --- a/webdav/pkg/service/v0/service.go +++ b/webdav/pkg/service/v0/service.go @@ -1,7 +1,9 @@ package svc import ( - "io" + "encoding/xml" + merrors "github.com/asim/go-micro/v3/errors" + "github.com/go-chi/render" "net/http" "path" "strings" @@ -19,6 +21,15 @@ const ( TokenHeader = "X-Access-Token" ) +var ( + codesEnum = map[int]string{ + http.StatusBadRequest: "Sabre\\DAV\\Exception\\BadRequest", + http.StatusUnauthorized: "Sabre\\DAV\\Exception\\NotAuthenticated", + http.StatusNotFound: "Sabre\\DAV\\Exception\\NotFound", + http.StatusMethodNotAllowed: "Sabre\\DAV\\Exception\\MethodNotAllowed", + } +) + // Service defines the extension handlers. type Service interface { ServeHTTP(http.ResponseWriter, *http.Request) @@ -36,6 +47,7 @@ func NewService(opts ...Option) Service { config: options.Config, log: options.Logger, mux: m, + thumbnailsClient: thumbnails.NewThumbnailService("com.owncloud.api.thumbnails", grpc.DefaultClient), } m.Route(options.Config.HTTP.Root, func(r chi.Router) { @@ -52,6 +64,7 @@ type Webdav struct { config *config.Config log log.Logger mux *chi.Mux + thumbnailsClient thumbnails.ThumbnailService } // ServeHTTP implements the Service interface. @@ -64,16 +77,14 @@ func (g Webdav) Thumbnail(w http.ResponseWriter, r *http.Request) { tr, err := requests.ParseThumbnailRequest(r) if err != nil { g.log.Error().Err(err).Msg("could not create Request") - w.WriteHeader(http.StatusBadRequest) - mustWrite(g.log, w, []byte(err.Error())) + renderError(w, r, errBadRequest(err.Error())) return } - c := thumbnails.NewThumbnailService("com.owncloud.api.thumbnails", grpc.DefaultClient) - t := r.Header.Get("X-Access-Token") - rsp, err := c.GetThumbnail(r.Context(), &thumbnails.GetThumbnailRequest{ + t := r.Header.Get(TokenHeader) + rsp, err := g.thumbnailsClient.GetThumbnail(r.Context(), &thumbnails.GetThumbnailRequest{ Filepath: strings.TrimLeft(tr.Filepath, "/"), - ThumbnailType: extensionToFiletype(strings.TrimLeft(tr.Extension, ".")), + ThumbnailType: extensionToThumbnailType(strings.TrimLeft(tr.Extension, ".")), Width: tr.Width, Height: tr.Height, Source: &thumbnails.GetThumbnailRequest_Cs3Source{ @@ -85,34 +96,37 @@ func (g Webdav) Thumbnail(w http.ResponseWriter, r *http.Request) { }) if err != nil { g.log.Error().Err(err).Msg("could not get thumbnail") - w.WriteHeader(http.StatusBadRequest) - mustWrite(g.log, w, []byte(err.Error())) + e := merrors.Parse(err.Error()) + switch e.Code { + case http.StatusNotFound: + renderError(w, r, errNotFound(notFoundMsg(tr.Filename))) + case http.StatusBadRequest: + renderError(w, r, errBadRequest(err.Error())) + default: + renderError(w, r, errInternalError(err.Error())) + } return } if len(rsp.Thumbnail) == 0 { - w.WriteHeader(http.StatusNotFound) + renderError(w, r, errNotFound("")) return } - w.Header().Set("Content-Type", rsp.GetMimetype()) - w.WriteHeader(http.StatusOK) - mustWrite(g.log, w, rsp.Thumbnail) + g.mustRender(w, r, newThumbnailResponse(rsp)) } func (g Webdav) PublicThumbnail(w http.ResponseWriter, r *http.Request) { tr, err := requests.ParseThumbnailRequest(r) if err != nil { g.log.Error().Err(err).Msg("could not create Request") - w.WriteHeader(http.StatusBadRequest) - mustWrite(g.log, w, []byte(err.Error())) + renderError(w, r, errBadRequest(err.Error())) return } - c := thumbnails.NewThumbnailService("com.owncloud.api.thumbnails", grpc.DefaultClient) - rsp, err := c.GetThumbnail(r.Context(), &thumbnails.GetThumbnailRequest{ + rsp, err := g.thumbnailsClient.GetThumbnail(r.Context(), &thumbnails.GetThumbnailRequest{ Filepath: strings.TrimLeft(tr.Filepath, "/"), - ThumbnailType: extensionToFiletype(strings.TrimLeft(tr.Extension, ".")), + ThumbnailType: extensionToThumbnailType(strings.TrimLeft(tr.Extension, ".")), Width: tr.Width, Height: tr.Height, Source: &thumbnails.GetThumbnailRequest_WebdavSource{ @@ -125,33 +139,37 @@ func (g Webdav) PublicThumbnail(w http.ResponseWriter, r *http.Request) { }) if err != nil { g.log.Error().Err(err).Msg("could not get thumbnail") - w.WriteHeader(http.StatusBadRequest) - mustWrite(g.log, w, []byte(err.Error())) + e := merrors.Parse(err.Error()) + switch e.Code { + case http.StatusNotFound: + renderError(w, r, errNotFound(notFoundMsg(tr.Filename))) + case http.StatusBadRequest: + renderError(w, r, errBadRequest(err.Error())) + default: + renderError(w, r, errInternalError(err.Error())) + } return } if len(rsp.Thumbnail) == 0 { - w.WriteHeader(http.StatusNotFound) + renderError(w, r, errNotFound("")) return } - w.Header().Set("Content-Type", rsp.GetMimetype()) - w.WriteHeader(http.StatusOK) - mustWrite(g.log, w, rsp.Thumbnail) + g.mustRender(w, r, newThumbnailResponse(rsp)) } func (g Webdav) PublicThumbnailHead(w http.ResponseWriter, r *http.Request) { tr, err := requests.ParseThumbnailRequest(r) if err != nil { g.log.Error().Err(err).Msg("could not create Request") - w.WriteHeader(http.StatusBadRequest) + renderError(w, r, errBadRequest(err.Error())) return } - c := thumbnails.NewThumbnailService("com.owncloud.api.thumbnails", grpc.DefaultClient) - rsp, err := c.GetThumbnail(r.Context(), &thumbnails.GetThumbnailRequest{ + rsp, err := g.thumbnailsClient.GetThumbnail(r.Context(), &thumbnails.GetThumbnailRequest{ Filepath: strings.TrimLeft(tr.Filepath, "/"), - ThumbnailType: extensionToFiletype(strings.TrimLeft(tr.Extension, ".")), + ThumbnailType: extensionToThumbnailType(strings.TrimLeft(tr.Extension, ".")), Width: tr.Width, Height: tr.Height, Source: &thumbnails.GetThumbnailRequest_WebdavSource{ @@ -163,33 +181,103 @@ func (g Webdav) PublicThumbnailHead(w http.ResponseWriter, r *http.Request) { }, }) if err != nil { - g.log.Error().Err(err).Msg("could not get thumbnail") - w.WriteHeader(http.StatusBadRequest) + e := merrors.Parse(err.Error()) + switch e.Code { + case http.StatusNotFound: + renderError(w, r, errNotFound(notFoundMsg(tr.Filename))) + case http.StatusBadRequest: + g.log.Error().Err(err).Msg("could not get thumbnail") + renderError(w, r, errBadRequest(err.Error())) + default: + g.log.Error().Err(err).Msg("could not get thumbnail") + renderError(w, r, errInternalError(err.Error())) + } return } if len(rsp.Thumbnail) == 0 { - w.WriteHeader(http.StatusNotFound) + renderError(w, r, errNotFound("")) return } - w.Header().Set("Content-Type", rsp.GetMimetype()) w.WriteHeader(http.StatusOK) } -func extensionToFiletype(ext string) thumbnails.GetThumbnailRequest_FileType { +func extensionToThumbnailType(ext string) thumbnails.GetThumbnailRequest_ThumbnailType { switch strings.ToUpper(ext) { case "GIF", "PNG": return thumbnails.GetThumbnailRequest_PNG - case "JPEG", "JPG": - return thumbnails.GetThumbnailRequest_JPG default: - return thumbnails.GetThumbnailRequest_FileType(-1) + return thumbnails.GetThumbnailRequest_JPG } } -func mustWrite(logger log.Logger, w io.Writer, val []byte) { - if _, err := w.Write(val); err != nil { - logger.Error().Err(err).Msg("could not write response") +func (g Webdav) mustRender(w http.ResponseWriter, r *http.Request, renderer render.Renderer) { + if err := render.Render(w, r, renderer); err != nil { + g.log.Err(err).Msg("failed to write response") } } + +// http://www.webdav.org/specs/rfc4918.html#ELEMENT_error +type errResponse struct { + HTTPStatusCode int `json:"-" xml:"-"` + XMLName xml.Name `xml:"d:error"` + Xmlnsd string `xml:"xmlns:d,attr"` + Xmlnss string `xml:"xmlns:s,attr"` + Exception string `xml:"s:exception"` + Message string `xml:"s:message"` + InnerXML []byte `xml:",innerxml"` +} + +func newErrResponse(statusCode int, msg string) *errResponse { + rsp := &errResponse{ + HTTPStatusCode: statusCode, + Xmlnsd: "DAV", + Xmlnss: "http://sabredav.org/ns", + Exception: codesEnum[statusCode], + } + if msg != "" { + rsp.Message = msg + } + return rsp +} + +func errInternalError(msg string) *errResponse { + return newErrResponse(http.StatusInternalServerError, msg) +} + +func errBadRequest(msg string) *errResponse { + return newErrResponse(http.StatusBadRequest, msg) +} + +func errNotFound(msg string) *errResponse { + return newErrResponse(http.StatusNotFound, msg) +} + +type thumbnailResponse struct { + contentType string + thumbnail []byte +} + +func (t *thumbnailResponse) Render(w http.ResponseWriter, _ *http.Request) error { + w.WriteHeader(http.StatusOK) + w.Header().Set("Content-Type", t.contentType) + _, err := w.Write(t.thumbnail) + return err +} + +func newThumbnailResponse(rsp *thumbnails.GetThumbnailResponse) *thumbnailResponse { + return &thumbnailResponse{ + contentType: rsp.Mimetype, + thumbnail: rsp.Thumbnail, + } +} + +func renderError(w http.ResponseWriter, r *http.Request, err *errResponse) { + render.Status(r, err.HTTPStatusCode) + render.XML(w, r, err) +} + +func notFoundMsg(name string) string { + return "File with name " + name + " could not be located" +}