github.com/opentelekomcloud/gophertelekomcloud@v0.9.3/openstack/obs/convert.go (about) 1 package obs 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "net/http" 9 "reflect" 10 "strings" 11 "time" 12 ) 13 14 func cleanHeaderPrefix(header http.Header) map[string][]string { 15 responseHeaders := make(map[string][]string) 16 for key, value := range header { 17 if len(value) > 0 { 18 key = strings.ToLower(key) 19 if strings.HasPrefix(key, HEADER_PREFIX) || strings.HasPrefix(key, HEADER_PREFIX_OBS) { 20 key = key[len(HEADER_PREFIX):] 21 } 22 responseHeaders[key] = value 23 } 24 } 25 return responseHeaders 26 } 27 28 // ParseStringToEventType converts string value to EventType value and returns it 29 func ParseStringToEventType(value string) (ret EventType) { 30 switch value { 31 case "ObjectCreated:*", "s3:ObjectCreated:*": 32 ret = ObjectCreatedAll 33 case "ObjectCreated:Put", "s3:ObjectCreated:Put": 34 ret = ObjectCreatedPut 35 case "ObjectCreated:Post", "s3:ObjectCreated:Post": 36 ret = ObjectCreatedPost 37 case "ObjectCreated:Copy", "s3:ObjectCreated:Copy": 38 ret = ObjectCreatedCopy 39 case "ObjectCreated:CompleteMultipartUpload", "s3:ObjectCreated:CompleteMultipartUpload": 40 ret = ObjectCreatedCompleteMultipartUpload 41 case "ObjectRemoved:*", "s3:ObjectRemoved:*": 42 ret = ObjectRemovedAll 43 case "ObjectRemoved:Delete", "s3:ObjectRemoved:Delete": 44 ret = ObjectRemovedDelete 45 case "ObjectRemoved:DeleteMarkerCreated", "s3:ObjectRemoved:DeleteMarkerCreated": 46 ret = ObjectRemovedDeleteMarkerCreated 47 default: 48 ret = "" 49 } 50 return 51 } 52 53 // ParseStringToStorageClassType converts string value to StorageClassType value and returns it 54 func ParseStringToStorageClassType(value string) (ret StorageClassType) { 55 switch value { 56 case "STANDARD": 57 ret = StorageClassStandard 58 case "STANDARD_IA", "WARM": 59 ret = StorageClassWarm 60 case "GLACIER", "COLD": 61 ret = StorageClassCold 62 default: 63 ret = "" 64 } 65 return 66 } 67 68 func prepareGrantURI(grant Grant) string { 69 if grant.Grantee.URI == GroupAllUsers || grant.Grantee.URI == GroupAuthenticatedUsers { 70 return fmt.Sprintf("<URI>%s%s</URI>", "http://acs.amazonaws.com/groups/global/", grant.Grantee.URI) 71 } 72 if grant.Grantee.URI == GroupLogDelivery { 73 return fmt.Sprintf("<URI>%s%s</URI>", "http://acs.amazonaws.com/groups/s3/", grant.Grantee.URI) 74 } 75 return fmt.Sprintf("<URI>%s</URI>", grant.Grantee.URI) 76 } 77 78 func convertGrantToXml(grant Grant, isObs bool, isBucket bool) string { 79 xml := make([]string, 0, 4) 80 if !isObs { 81 xml = append(xml, fmt.Sprintf("<Grant><Grantee xsi:type=\"%s\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">", grant.Grantee.Type)) 82 } 83 84 if grant.Grantee.Type == GranteeUser { 85 if isObs { 86 xml = append(xml, "<Grant><Grantee>") 87 } 88 if grant.Grantee.ID != "" { 89 granteeID := XmlTranscoding(grant.Grantee.ID) 90 xml = append(xml, fmt.Sprintf("<ID>%s</ID>", granteeID)) 91 } 92 if !isObs && grant.Grantee.DisplayName != "" { 93 granteeDisplayName := XmlTranscoding(grant.Grantee.DisplayName) 94 xml = append(xml, fmt.Sprintf("<DisplayName>%s</DisplayName>", granteeDisplayName)) 95 } 96 xml = append(xml, "</Grantee>") 97 } else { 98 if !isObs { 99 xml = append(xml, fmt.Sprintf("<Grant><Grantee xsi:type=\"%s\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">", grant.Grantee.Type)) 100 xml = append(xml, prepareGrantURI(grant)) 101 xml = append(xml, "</Grantee>") 102 } else if grant.Grantee.URI == GroupAllUsers { 103 xml = append(xml, "<Grant><Grantee>") 104 xml = append(xml, "<Canned>Everyone</Canned>") 105 xml = append(xml, "</Grantee>") 106 } else { 107 return strings.Join(xml, "") 108 } 109 } 110 111 xml = append(xml, fmt.Sprintf("<Permission>%s</Permission>", grant.Permission)) 112 if isObs && isBucket { 113 xml = append(xml, fmt.Sprintf("<Delivered>%t</Delivered>", grant.Delivered)) 114 } 115 xml = append(xml, "</Grant>") 116 return strings.Join(xml, "") 117 } 118 119 func ConvertLoggingStatusToXml(input BucketLoggingStatus, returnMd5 bool, isObs bool) (data string, md5 string) { 120 grantsLength := len(input.TargetGrants) 121 xml := make([]string, 0, 8+grantsLength) 122 123 xml = append(xml, "<BucketLoggingStatus>") 124 if isObs && input.Agency != "" { 125 agency := XmlTranscoding(input.Agency) 126 xml = append(xml, fmt.Sprintf("<Agency>%s</Agency>", agency)) 127 } 128 if input.TargetBucket != "" || input.TargetPrefix != "" || grantsLength > 0 { 129 xml = append(xml, "<LoggingEnabled>") 130 if input.TargetBucket != "" { 131 xml = append(xml, fmt.Sprintf("<TargetBucket>%s</TargetBucket>", input.TargetBucket)) 132 } 133 if input.TargetPrefix != "" { 134 targetPrefix := XmlTranscoding(input.TargetPrefix) 135 xml = append(xml, fmt.Sprintf("<TargetPrefix>%s</TargetPrefix>", targetPrefix)) 136 } 137 if grantsLength > 0 { 138 xml = append(xml, "<TargetGrants>") 139 for _, grant := range input.TargetGrants { 140 xml = append(xml, convertGrantToXml(grant, isObs, false)) 141 } 142 xml = append(xml, "</TargetGrants>") 143 } 144 145 xml = append(xml, "</LoggingEnabled>") 146 } 147 xml = append(xml, "</BucketLoggingStatus>") 148 data = strings.Join(xml, "") 149 if returnMd5 { 150 md5 = Base64Md5([]byte(data)) 151 } 152 return 153 } 154 155 // ConvertAclToXml converts AccessControlPolicy value to XML data and returns it 156 func ConvertAclToXml(input AccessControlPolicy, returnMd5 bool, isObs bool) (data string, md5 string) { 157 xml := make([]string, 0, 4+len(input.Grants)) 158 ownerID := XmlTranscoding(input.Owner.ID) 159 xml = append(xml, fmt.Sprintf("<AccessControlPolicy><Owner><ID>%s</ID>", ownerID)) 160 if !isObs && input.Owner.DisplayName != "" { 161 ownerDisplayName := XmlTranscoding(input.Owner.DisplayName) 162 xml = append(xml, fmt.Sprintf("<DisplayName>%s</DisplayName>", ownerDisplayName)) 163 } 164 if isObs && input.Delivered != "" { 165 objectDelivered := XmlTranscoding(input.Delivered) 166 xml = append(xml, fmt.Sprintf("</Owner><Delivered>%s</Delivered><AccessControlList>", objectDelivered)) 167 } else { 168 xml = append(xml, "</Owner><AccessControlList>") 169 } 170 for _, grant := range input.Grants { 171 xml = append(xml, convertGrantToXml(grant, isObs, false)) 172 } 173 xml = append(xml, "</AccessControlList></AccessControlPolicy>") 174 data = strings.Join(xml, "") 175 if returnMd5 { 176 md5 = Base64Md5([]byte(data)) 177 } 178 return 179 } 180 181 func convertBucketAclToXml(input AccessControlPolicy, returnMd5 bool, isObs bool) (data string, md5 string) { 182 xml := make([]string, 0, 4+len(input.Grants)) 183 ownerID := XmlTranscoding(input.Owner.ID) 184 xml = append(xml, fmt.Sprintf("<AccessControlPolicy><Owner><ID>%s</ID>", ownerID)) 185 if !isObs && input.Owner.DisplayName != "" { 186 ownerDisplayName := XmlTranscoding(input.Owner.DisplayName) 187 xml = append(xml, fmt.Sprintf("<DisplayName>%s</DisplayName>", ownerDisplayName)) 188 } 189 190 xml = append(xml, "</Owner><AccessControlList>") 191 192 for _, grant := range input.Grants { 193 xml = append(xml, convertGrantToXml(grant, isObs, true)) 194 } 195 xml = append(xml, "</AccessControlList></AccessControlPolicy>") 196 data = strings.Join(xml, "") 197 if returnMd5 { 198 md5 = Base64Md5([]byte(data)) 199 } 200 return 201 } 202 203 func convertConditionToXml(condition Condition) string { 204 xml := make([]string, 0, 2) 205 if condition.KeyPrefixEquals != "" { 206 keyPrefixEquals := XmlTranscoding(condition.KeyPrefixEquals) 207 xml = append(xml, fmt.Sprintf("<KeyPrefixEquals>%s</KeyPrefixEquals>", keyPrefixEquals)) 208 } 209 if condition.HttpErrorCodeReturnedEquals != "" { 210 xml = append(xml, fmt.Sprintf("<HttpErrorCodeReturnedEquals>%s</HttpErrorCodeReturnedEquals>", condition.HttpErrorCodeReturnedEquals)) 211 } 212 if len(xml) > 0 { 213 return fmt.Sprintf("<Condition>%s</Condition>", strings.Join(xml, "")) 214 } 215 return "" 216 } 217 218 func prepareRoutingRule(input BucketWebsiteConfiguration) string { 219 xml := make([]string, 0, len(input.RoutingRules)*10) 220 for _, routingRule := range input.RoutingRules { 221 xml = append(xml, "<RoutingRule>") 222 xml = append(xml, "<Redirect>") 223 if routingRule.Redirect.Protocol != "" { 224 xml = append(xml, fmt.Sprintf("<Protocol>%s</Protocol>", routingRule.Redirect.Protocol)) 225 } 226 if routingRule.Redirect.HostName != "" { 227 xml = append(xml, fmt.Sprintf("<HostName>%s</HostName>", routingRule.Redirect.HostName)) 228 } 229 if routingRule.Redirect.ReplaceKeyPrefixWith != "" { 230 replaceKeyPrefixWith := XmlTranscoding(routingRule.Redirect.ReplaceKeyPrefixWith) 231 xml = append(xml, fmt.Sprintf("<ReplaceKeyPrefixWith>%s</ReplaceKeyPrefixWith>", replaceKeyPrefixWith)) 232 } 233 234 if routingRule.Redirect.ReplaceKeyWith != "" { 235 replaceKeyWith := XmlTranscoding(routingRule.Redirect.ReplaceKeyWith) 236 xml = append(xml, fmt.Sprintf("<ReplaceKeyWith>%s</ReplaceKeyWith>", replaceKeyWith)) 237 } 238 if routingRule.Redirect.HttpRedirectCode != "" { 239 xml = append(xml, fmt.Sprintf("<HttpRedirectCode>%s</HttpRedirectCode>", routingRule.Redirect.HttpRedirectCode)) 240 } 241 xml = append(xml, "</Redirect>") 242 243 if ret := convertConditionToXml(routingRule.Condition); ret != "" { 244 xml = append(xml, ret) 245 } 246 xml = append(xml, "</RoutingRule>") 247 } 248 return strings.Join(xml, "") 249 } 250 251 // ConvertWebsiteConfigurationToXml converts BucketWebsiteConfiguration value to XML data and returns it 252 func ConvertWebsiteConfigurationToXml(input BucketWebsiteConfiguration, returnMd5 bool) (data string, md5 string) { 253 routingRuleLength := len(input.RoutingRules) 254 xml := make([]string, 0, 6+routingRuleLength*10) 255 xml = append(xml, "<WebsiteConfiguration>") 256 257 if input.RedirectAllRequestsTo.HostName != "" { 258 xml = append(xml, fmt.Sprintf("<RedirectAllRequestsTo><HostName>%s</HostName>", input.RedirectAllRequestsTo.HostName)) 259 if input.RedirectAllRequestsTo.Protocol != "" { 260 xml = append(xml, fmt.Sprintf("<Protocol>%s</Protocol>", input.RedirectAllRequestsTo.Protocol)) 261 } 262 xml = append(xml, "</RedirectAllRequestsTo>") 263 } else { 264 if input.IndexDocument.Suffix != "" { 265 indexDocumentSuffix := XmlTranscoding(input.IndexDocument.Suffix) 266 xml = append(xml, fmt.Sprintf("<IndexDocument><Suffix>%s</Suffix></IndexDocument>", indexDocumentSuffix)) 267 } 268 if input.ErrorDocument.Key != "" { 269 errorDocumentKey := XmlTranscoding(input.ErrorDocument.Key) 270 xml = append(xml, fmt.Sprintf("<ErrorDocument><Key>%s</Key></ErrorDocument>", errorDocumentKey)) 271 } 272 if routingRuleLength > 0 { 273 xml = append(xml, "<RoutingRules>") 274 xml = append(xml, prepareRoutingRule(input)) 275 xml = append(xml, "</RoutingRules>") 276 } 277 } 278 279 xml = append(xml, "</WebsiteConfiguration>") 280 data = strings.Join(xml, "") 281 if returnMd5 { 282 md5 = Base64Md5([]byte(data)) 283 } 284 return 285 } 286 287 func convertTransitionsToXml(transitions []Transition, isObs bool) string { 288 if length := len(transitions); length > 0 { 289 xml := make([]string, 0, length) 290 for _, transition := range transitions { 291 var temp string 292 if transition.Days > 0 { 293 temp = fmt.Sprintf("<Days>%d</Days>", transition.Days) 294 } else if !transition.Date.IsZero() { 295 temp = fmt.Sprintf("<Date>%s</Date>", transition.Date.UTC().Format(ISO8601_MIDNIGHT_DATE_FORMAT)) 296 } 297 if temp != "" { 298 if !isObs { 299 storageClass := string(transition.StorageClass) 300 if transition.StorageClass == "WARM" { 301 storageClass = "STANDARD_IA" 302 } else if transition.StorageClass == "COLD" { 303 storageClass = "GLACIER" 304 } 305 xml = append(xml, fmt.Sprintf("<Transition>%s<StorageClass>%s</StorageClass></Transition>", temp, storageClass)) 306 } else { 307 xml = append(xml, fmt.Sprintf("<Transition>%s<StorageClass>%s</StorageClass></Transition>", temp, transition.StorageClass)) 308 } 309 } 310 } 311 return strings.Join(xml, "") 312 } 313 return "" 314 } 315 316 func convertExpirationToXml(expiration Expiration) string { 317 if expiration.Days > 0 { 318 return fmt.Sprintf("<Expiration><Days>%d</Days></Expiration>", expiration.Days) 319 } else if !expiration.Date.IsZero() { 320 return fmt.Sprintf("<Expiration><Date>%s</Date></Expiration>", expiration.Date.UTC().Format(ISO8601_MIDNIGHT_DATE_FORMAT)) 321 } 322 return "" 323 } 324 325 func convertNoncurrentVersionTransitionsToXml(noncurrentVersionTransitions []NoncurrentVersionTransition, isObs bool) string { 326 if length := len(noncurrentVersionTransitions); length > 0 { 327 xml := make([]string, 0, length) 328 for _, noncurrentVersionTransition := range noncurrentVersionTransitions { 329 if noncurrentVersionTransition.NoncurrentDays > 0 { 330 storageClass := string(noncurrentVersionTransition.StorageClass) 331 if !isObs { 332 if storageClass == "WARM" { 333 storageClass = "STANDARD_IA" 334 } else if storageClass == "COLD" { 335 storageClass = "GLACIER" 336 } 337 } 338 xml = append(xml, fmt.Sprintf("<NoncurrentVersionTransition><NoncurrentDays>%d</NoncurrentDays>"+ 339 "<StorageClass>%s</StorageClass></NoncurrentVersionTransition>", 340 noncurrentVersionTransition.NoncurrentDays, storageClass)) 341 } 342 } 343 return strings.Join(xml, "") 344 } 345 return "" 346 } 347 func convertNoncurrentVersionExpirationToXml(noncurrentVersionExpiration NoncurrentVersionExpiration) string { 348 if noncurrentVersionExpiration.NoncurrentDays > 0 { 349 return fmt.Sprintf("<NoncurrentVersionExpiration><NoncurrentDays>%d</NoncurrentDays></NoncurrentVersionExpiration>", noncurrentVersionExpiration.NoncurrentDays) 350 } 351 return "" 352 } 353 354 // ConvertLifecyleConfigurationToXml converts BucketLifecyleConfiguration value to XML data and returns it 355 func ConvertLifecyleConfigurationToXml(input BucketLifecycleConfiguration, returnMd5 bool, isObs bool) (data string, md5 string) { 356 xml := make([]string, 0, 2+len(input.LifecycleRules)*9) 357 xml = append(xml, "<LifecycleConfiguration>") 358 for _, lifecyleRule := range input.LifecycleRules { 359 xml = append(xml, "<Rule>") 360 if lifecyleRule.ID != "" { 361 lifecyleRuleID := XmlTranscoding(lifecyleRule.ID) 362 xml = append(xml, fmt.Sprintf("<ID>%s</ID>", lifecyleRuleID)) 363 } 364 lifecyleRulePrefix := XmlTranscoding(lifecyleRule.Prefix) 365 xml = append(xml, fmt.Sprintf("<Prefix>%s</Prefix>", lifecyleRulePrefix)) 366 xml = append(xml, fmt.Sprintf("<Status>%s</Status>", lifecyleRule.Status)) 367 if ret := convertTransitionsToXml(lifecyleRule.Transitions, isObs); ret != "" { 368 xml = append(xml, ret) 369 } 370 if ret := convertExpirationToXml(lifecyleRule.Expiration); ret != "" { 371 xml = append(xml, ret) 372 } 373 if ret := convertNoncurrentVersionTransitionsToXml(lifecyleRule.NoncurrentVersionTransitions, isObs); ret != "" { 374 xml = append(xml, ret) 375 } 376 if ret := convertNoncurrentVersionExpirationToXml(lifecyleRule.NoncurrentVersionExpiration); ret != "" { 377 xml = append(xml, ret) 378 } 379 xml = append(xml, "</Rule>") 380 } 381 xml = append(xml, "</LifecycleConfiguration>") 382 data = strings.Join(xml, "") 383 if returnMd5 { 384 md5 = Base64Md5([]byte(data)) 385 } 386 return 387 } 388 389 // ConvertEncryptionConfigurationToXml converts BucketEncryptionConfiguration value to XML data and returns it 390 func ConvertEncryptionConfigurationToXml(input BucketEncryptionConfiguration, returnMd5 bool, isObs bool) (data string, md5 string) { 391 xml := make([]string, 0, 5) 392 xml = append(xml, "<ServerSideEncryptionConfiguration><Rule><ApplyServerSideEncryptionByDefault>") 393 394 algorithm := XmlTranscoding(input.SSEAlgorithm) 395 xml = append(xml, fmt.Sprintf("<SSEAlgorithm>%s</SSEAlgorithm>", algorithm)) 396 397 if input.KMSMasterKeyID != "" { 398 kmsKeyID := XmlTranscoding(input.KMSMasterKeyID) 399 xml = append(xml, fmt.Sprintf("<KMSMasterKeyID>%s</KMSMasterKeyID>", kmsKeyID)) 400 } 401 if input.ProjectID != "" { 402 projectID := XmlTranscoding(input.ProjectID) 403 xml = append(xml, fmt.Sprintf("<ProjectID>%s</ProjectID>", projectID)) 404 } 405 406 xml = append(xml, "</ApplyServerSideEncryptionByDefault></Rule></ServerSideEncryptionConfiguration>") 407 data = strings.Join(xml, "") 408 if returnMd5 { 409 md5 = Base64Md5([]byte(data)) 410 } 411 return 412 } 413 414 func converntFilterRulesToXml(filterRules []FilterRule, isObs bool) string { 415 if length := len(filterRules); length > 0 { 416 xml := make([]string, 0, length*4) 417 for _, filterRule := range filterRules { 418 xml = append(xml, "<FilterRule>") 419 if filterRule.Name != "" { 420 filterRuleName := XmlTranscoding(filterRule.Name) 421 xml = append(xml, fmt.Sprintf("<Name>%s</Name>", filterRuleName)) 422 } 423 if filterRule.Value != "" { 424 filterRuleValue := XmlTranscoding(filterRule.Value) 425 xml = append(xml, fmt.Sprintf("<Value>%s</Value>", filterRuleValue)) 426 } 427 xml = append(xml, "</FilterRule>") 428 } 429 if !isObs { 430 return fmt.Sprintf("<Filter><S3Key>%s</S3Key></Filter>", strings.Join(xml, "")) 431 } 432 return fmt.Sprintf("<Filter><Object>%s</Object></Filter>", strings.Join(xml, "")) 433 } 434 return "" 435 } 436 437 func converntEventsToXml(events []EventType, isObs bool) string { 438 if length := len(events); length > 0 { 439 xml := make([]string, 0, length) 440 if !isObs { 441 for _, event := range events { 442 xml = append(xml, fmt.Sprintf("<Event>%s%s</Event>", "s3:", event)) 443 } 444 } else { 445 for _, event := range events { 446 xml = append(xml, fmt.Sprintf("<Event>%s</Event>", event)) 447 } 448 } 449 return strings.Join(xml, "") 450 } 451 return "" 452 } 453 454 func converntConfigureToXml(topicConfiguration TopicConfiguration, xmlElem string, isObs bool) string { 455 xml := make([]string, 0, 6) 456 xml = append(xml, xmlElem) 457 if topicConfiguration.ID != "" { 458 topicConfigurationID := XmlTranscoding(topicConfiguration.ID) 459 xml = append(xml, fmt.Sprintf("<Id>%s</Id>", topicConfigurationID)) 460 } 461 topicConfigurationTopic := XmlTranscoding(topicConfiguration.Topic) 462 xml = append(xml, fmt.Sprintf("<Topic>%s</Topic>", topicConfigurationTopic)) 463 464 if ret := converntEventsToXml(topicConfiguration.Events, isObs); ret != "" { 465 xml = append(xml, ret) 466 } 467 if ret := converntFilterRulesToXml(topicConfiguration.FilterRules, isObs); ret != "" { 468 xml = append(xml, ret) 469 } 470 tempElem := xmlElem[0:1] + "/" + xmlElem[1:] 471 xml = append(xml, tempElem) 472 return strings.Join(xml, "") 473 } 474 475 // ConverntObsRestoreToXml converts RestoreObjectInput value to XML data and returns it 476 func ConverntObsRestoreToXml(restoreObjectInput RestoreObjectInput) string { 477 xml := make([]string, 0, 2) 478 xml = append(xml, fmt.Sprintf("<RestoreRequest><Days>%d</Days>", restoreObjectInput.Days)) 479 if restoreObjectInput.Tier != "Bulk" { 480 xml = append(xml, fmt.Sprintf("<RestoreJob><Tier>%s</Tier></RestoreJob>", restoreObjectInput.Tier)) 481 } 482 xml = append(xml, "</RestoreRequest>") 483 data := strings.Join(xml, "") 484 return data 485 } 486 487 // ConvertNotificationToXml converts BucketNotification value to XML data and returns it 488 func ConvertNotificationToXml(input BucketNotification, returnMd5 bool, isObs bool) (data string, md5 string) { 489 xml := make([]string, 0, 2+len(input.TopicConfigurations)*6) 490 xml = append(xml, "<NotificationConfiguration>") 491 for _, topicConfiguration := range input.TopicConfigurations { 492 ret := converntConfigureToXml(topicConfiguration, "<TopicConfiguration>", isObs) 493 xml = append(xml, ret) 494 } 495 xml = append(xml, "</NotificationConfiguration>") 496 data = strings.Join(xml, "") 497 if returnMd5 { 498 md5 = Base64Md5([]byte(data)) 499 } 500 return 501 } 502 503 // ConvertCompleteMultipartUploadInputToXml converts CompleteMultipartUploadInput value to XML data and returns it 504 func ConvertCompleteMultipartUploadInputToXml(input CompleteMultipartUploadInput, returnMd5 bool) (data string, md5 string) { 505 xml := make([]string, 0, 2+len(input.Parts)*4) 506 xml = append(xml, "<CompleteMultipartUpload>") 507 for _, part := range input.Parts { 508 xml = append(xml, "<Part>") 509 xml = append(xml, fmt.Sprintf("<PartNumber>%d</PartNumber>", part.PartNumber)) 510 xml = append(xml, fmt.Sprintf("<ETag>%s</ETag>", part.ETag)) 511 xml = append(xml, "</Part>") 512 } 513 xml = append(xml, "</CompleteMultipartUpload>") 514 data = strings.Join(xml, "") 515 if returnMd5 { 516 md5 = Base64Md5([]byte(data)) 517 } 518 return 519 } 520 521 func convertDeleteObjectsToXML(input DeleteObjectsInput) (data string, md5 string) { 522 xml := make([]string, 0, 4+len(input.Objects)*4) 523 xml = append(xml, "<Delete>") 524 if input.Quiet { 525 xml = append(xml, fmt.Sprintf("<Quiet>%t</Quiet>", input.Quiet)) 526 } 527 for _, obj := range input.Objects { 528 xml = append(xml, "<Object>") 529 key := XmlTranscoding(obj.Key) 530 xml = append(xml, fmt.Sprintf("<Key>%s</Key>", key)) 531 if obj.VersionId != "" { 532 xml = append(xml, fmt.Sprintf("<VersionId>%s</VersionId>", obj.VersionId)) 533 } 534 xml = append(xml, "</Object>") 535 } 536 xml = append(xml, "</Delete>") 537 data = strings.Join(xml, "") 538 md5 = Base64Md5([]byte(data)) 539 return 540 } 541 542 func parseSseHeader(responseHeaders map[string][]string) (sseHeader ISseHeader) { 543 if ret, ok := responseHeaders[HEADER_SSEC_ENCRYPTION]; ok { 544 sseCHeader := SseCHeader{Encryption: ret[0]} 545 if ret, ok = responseHeaders[HEADER_SSEC_KEY_MD5]; ok { 546 sseCHeader.KeyMD5 = ret[0] 547 } 548 sseHeader = sseCHeader 549 } else if ret, ok := responseHeaders[HEADER_SSEKMS_ENCRYPTION]; ok { 550 sseKmsHeader := SseKmsHeader{Encryption: ret[0]} 551 if ret, ok = responseHeaders[HEADER_SSEKMS_KEY]; ok { 552 sseKmsHeader.Key = ret[0] 553 } else if ret, ok = responseHeaders[HEADER_SSEKMS_ENCRYPT_KEY_OBS]; ok { 554 sseKmsHeader.Key = ret[0] 555 } 556 sseHeader = sseKmsHeader 557 } 558 return 559 } 560 561 func parseCorsHeader(output BaseModel) (AllowOrigin, AllowHeader, AllowMethod, ExposeHeader string, MaxAgeSeconds int) { 562 if ret, ok := output.ResponseHeaders[HEADER_ACCESS_CONRTOL_ALLOW_ORIGIN]; ok { 563 AllowOrigin = ret[0] 564 } 565 if ret, ok := output.ResponseHeaders[HEADER_ACCESS_CONRTOL_ALLOW_HEADERS]; ok { 566 AllowHeader = ret[0] 567 } 568 if ret, ok := output.ResponseHeaders[HEADER_ACCESS_CONRTOL_MAX_AGE]; ok { 569 MaxAgeSeconds = StringToInt(ret[0], 0) 570 } 571 if ret, ok := output.ResponseHeaders[HEADER_ACCESS_CONRTOL_ALLOW_METHODS]; ok { 572 AllowMethod = ret[0] 573 } 574 if ret, ok := output.ResponseHeaders[HEADER_ACCESS_CONRTOL_EXPOSE_HEADERS]; ok { 575 ExposeHeader = ret[0] 576 } 577 return 578 } 579 580 func parseUnCommonHeader(output *GetObjectMetadataOutput) { 581 if ret, ok := output.ResponseHeaders[HEADER_VERSION_ID]; ok { 582 output.VersionId = ret[0] 583 } 584 if ret, ok := output.ResponseHeaders[HEADER_WEBSITE_REDIRECT_LOCATION]; ok { 585 output.WebsiteRedirectLocation = ret[0] 586 } 587 if ret, ok := output.ResponseHeaders[HEADER_EXPIRATION]; ok { 588 output.Expiration = ret[0] 589 } 590 if ret, ok := output.ResponseHeaders[HEADER_RESTORE]; ok { 591 output.Restore = ret[0] 592 } 593 if ret, ok := output.ResponseHeaders[HEADER_OBJECT_TYPE]; ok { 594 output.ObjectType = ret[0] 595 } 596 if ret, ok := output.ResponseHeaders[HEADER_NEXT_APPEND_POSITION]; ok { 597 output.NextAppendPosition = ret[0] 598 } 599 } 600 601 // ParseGetObjectMetadataOutput sets GetObjectMetadataOutput field values with response headers 602 func ParseGetObjectMetadataOutput(output *GetObjectMetadataOutput) { 603 output.AllowOrigin, output.AllowHeader, output.AllowMethod, output.ExposeHeader, output.MaxAgeSeconds = parseCorsHeader(output.BaseModel) 604 parseUnCommonHeader(output) 605 if ret, ok := output.ResponseHeaders[HEADER_STORAGE_CLASS2]; ok { 606 output.StorageClass = ParseStringToStorageClassType(ret[0]) 607 } 608 if ret, ok := output.ResponseHeaders[HEADER_ETAG]; ok { 609 output.ETag = ret[0] 610 } 611 if ret, ok := output.ResponseHeaders[HEADER_CONTENT_TYPE]; ok { 612 output.ContentType = ret[0] 613 } 614 615 output.SseHeader = parseSseHeader(output.ResponseHeaders) 616 if ret, ok := output.ResponseHeaders[HEADER_LASTMODIFIED]; ok { 617 ret, err := time.Parse(time.RFC1123, ret[0]) 618 if err == nil { 619 output.LastModified = ret 620 } 621 } 622 if ret, ok := output.ResponseHeaders[HEADER_CONTENT_LENGTH]; ok { 623 output.ContentLength = StringToInt64(ret[0], 0) 624 } 625 626 output.Metadata = make(map[string]string) 627 628 for key, value := range output.ResponseHeaders { 629 if strings.HasPrefix(key, PREFIX_META) { 630 _key := key[len(PREFIX_META):] 631 output.ResponseHeaders[_key] = value 632 output.Metadata[_key] = value[0] 633 delete(output.ResponseHeaders, key) 634 } 635 } 636 637 } 638 639 // ParseCopyObjectOutput sets CopyObjectOutput field values with response headers 640 func ParseCopyObjectOutput(output *CopyObjectOutput) { 641 if ret, ok := output.ResponseHeaders[HEADER_VERSION_ID]; ok { 642 output.VersionId = ret[0] 643 } 644 output.SseHeader = parseSseHeader(output.ResponseHeaders) 645 if ret, ok := output.ResponseHeaders[HEADER_COPY_SOURCE_VERSION_ID]; ok { 646 output.CopySourceVersionId = ret[0] 647 } 648 } 649 650 // ParsePutObjectOutput sets PutObjectOutput field values with response headers 651 func ParsePutObjectOutput(output *PutObjectOutput) { 652 if ret, ok := output.ResponseHeaders[HEADER_VERSION_ID]; ok { 653 output.VersionId = ret[0] 654 } 655 output.SseHeader = parseSseHeader(output.ResponseHeaders) 656 if ret, ok := output.ResponseHeaders[HEADER_STORAGE_CLASS2]; ok { 657 output.StorageClass = ParseStringToStorageClassType(ret[0]) 658 } 659 if ret, ok := output.ResponseHeaders[HEADER_ETAG]; ok { 660 output.ETag = ret[0] 661 } 662 } 663 664 // ParseInitiateMultipartUploadOutput sets InitiateMultipartUploadOutput field values with response headers 665 func ParseInitiateMultipartUploadOutput(output *InitiateMultipartUploadOutput) { 666 output.SseHeader = parseSseHeader(output.ResponseHeaders) 667 } 668 669 // ParseUploadPartOutput sets UploadPartOutput field values with response headers 670 func ParseUploadPartOutput(output *UploadPartOutput) { 671 output.SseHeader = parseSseHeader(output.ResponseHeaders) 672 if ret, ok := output.ResponseHeaders[HEADER_ETAG]; ok { 673 output.ETag = ret[0] 674 } 675 } 676 677 // ParseCompleteMultipartUploadOutput sets CompleteMultipartUploadOutput field values with response headers 678 func ParseCompleteMultipartUploadOutput(output *CompleteMultipartUploadOutput) { 679 output.SseHeader = parseSseHeader(output.ResponseHeaders) 680 if ret, ok := output.ResponseHeaders[HEADER_VERSION_ID]; ok { 681 output.VersionId = ret[0] 682 } 683 } 684 685 // ParseCopyPartOutput sets CopyPartOutput field values with response headers 686 func ParseCopyPartOutput(output *CopyPartOutput) { 687 output.SseHeader = parseSseHeader(output.ResponseHeaders) 688 } 689 690 // ParseGetBucketMetadataOutput sets GetBucketMetadataOutput field values with response headers 691 func ParseGetBucketMetadataOutput(output *GetBucketMetadataOutput) { 692 output.AllowOrigin, output.AllowHeader, output.AllowMethod, output.ExposeHeader, output.MaxAgeSeconds = parseCorsHeader(output.BaseModel) 693 if ret, ok := output.ResponseHeaders[HEADER_STORAGE_CLASS]; ok { 694 output.StorageClass = ParseStringToStorageClassType(ret[0]) 695 } else if ret, ok := output.ResponseHeaders[HEADER_STORAGE_CLASS2]; ok { 696 output.StorageClass = ParseStringToStorageClassType(ret[0]) 697 } 698 if ret, ok := output.ResponseHeaders[HEADER_VERSION_OBS]; ok { 699 output.Version = ret[0] 700 } 701 if ret, ok := output.ResponseHeaders[HEADER_BUCKET_REGION]; ok { 702 output.Location = ret[0] 703 } else if ret, ok := output.ResponseHeaders[HEADER_BUCKET_LOCATION_OBS]; ok { 704 output.Location = ret[0] 705 } 706 if ret, ok := output.ResponseHeaders[HEADER_EPID_HEADERS]; ok { 707 output.Epid = ret[0] 708 } 709 if ret, ok := output.ResponseHeaders[HEADER_FS_FILE_INTERFACE]; ok { 710 output.FSStatus = parseStringToFSStatusType(ret[0]) 711 } else { 712 output.FSStatus = FSStatusDisabled 713 } 714 } 715 716 func parseContentHeader(output *SetObjectMetadataOutput) { 717 if ret, ok := output.ResponseHeaders[HEADER_CONTENT_DISPOSITION]; ok { 718 output.ContentDisposition = ret[0] 719 } 720 if ret, ok := output.ResponseHeaders[HEADER_CONTENT_ENCODING]; ok { 721 output.ContentEncoding = ret[0] 722 } 723 if ret, ok := output.ResponseHeaders[HEADER_CONTENT_LANGUAGE]; ok { 724 output.ContentLanguage = ret[0] 725 } 726 if ret, ok := output.ResponseHeaders[HEADER_CONTENT_TYPE]; ok { 727 output.ContentType = ret[0] 728 } 729 } 730 731 // ParseSetObjectMetadataOutput sets SetObjectMetadataOutput field values with response headers 732 func ParseSetObjectMetadataOutput(output *SetObjectMetadataOutput) { 733 if ret, ok := output.ResponseHeaders[HEADER_STORAGE_CLASS]; ok { 734 output.StorageClass = ParseStringToStorageClassType(ret[0]) 735 } else if ret, ok := output.ResponseHeaders[HEADER_STORAGE_CLASS2]; ok { 736 output.StorageClass = ParseStringToStorageClassType(ret[0]) 737 } 738 if ret, ok := output.ResponseHeaders[HEADER_METADATA_DIRECTIVE]; ok { 739 output.MetadataDirective = MetadataDirectiveType(ret[0]) 740 } 741 if ret, ok := output.ResponseHeaders[HEADER_CACHE_CONTROL]; ok { 742 output.CacheControl = ret[0] 743 } 744 parseContentHeader(output) 745 if ret, ok := output.ResponseHeaders[HEADER_EXPIRES]; ok { 746 output.Expires = ret[0] 747 } 748 if ret, ok := output.ResponseHeaders[HEADER_WEBSITE_REDIRECT_LOCATION]; ok { 749 output.WebsiteRedirectLocation = ret[0] 750 } 751 output.Metadata = make(map[string]string) 752 753 for key, value := range output.ResponseHeaders { 754 if strings.HasPrefix(key, PREFIX_META) { 755 _key := key[len(PREFIX_META):] 756 output.ResponseHeaders[_key] = value 757 output.Metadata[_key] = value[0] 758 delete(output.ResponseHeaders, key) 759 } 760 } 761 } 762 763 // ParseDeleteObjectOutput sets DeleteObjectOutput field values with response headers 764 func ParseDeleteObjectOutput(output *DeleteObjectOutput) { 765 if versionId, ok := output.ResponseHeaders[HEADER_VERSION_ID]; ok { 766 output.VersionId = versionId[0] 767 } 768 769 if deleteMarker, ok := output.ResponseHeaders[HEADER_DELETE_MARKER]; ok { 770 output.DeleteMarker = deleteMarker[0] == "true" 771 } 772 } 773 774 // ParseGetObjectOutput sets GetObjectOutput field values with response headers 775 func ParseGetObjectOutput(output *GetObjectOutput) { 776 ParseGetObjectMetadataOutput(&output.GetObjectMetadataOutput) 777 if ret, ok := output.ResponseHeaders[HEADER_DELETE_MARKER]; ok { 778 output.DeleteMarker = ret[0] == "true" 779 } 780 if ret, ok := output.ResponseHeaders[HEADER_CACHE_CONTROL]; ok { 781 output.CacheControl = ret[0] 782 } 783 if ret, ok := output.ResponseHeaders[HEADER_CONTENT_DISPOSITION]; ok { 784 output.ContentDisposition = ret[0] 785 } 786 if ret, ok := output.ResponseHeaders[HEADER_CONTENT_ENCODING]; ok { 787 output.ContentEncoding = ret[0] 788 } 789 if ret, ok := output.ResponseHeaders[HEADER_CONTENT_LANGUAGE]; ok { 790 output.ContentLanguage = ret[0] 791 } 792 if ret, ok := output.ResponseHeaders[HEADER_EXPIRES]; ok { 793 output.Expires = ret[0] 794 } 795 } 796 797 // ConvertRequestToIoReaderV2 converts req to XML data 798 func ConvertRequestToIoReaderV2(req interface{}) (io.Reader, string, error) { 799 data, err := TransToXml(req) 800 if err == nil { 801 if isDebugLogEnabled() { 802 doLog(LEVEL_DEBUG, "Do http request with data: %s", string(data)) 803 } 804 return bytes.NewReader(data), Base64Md5(data), nil 805 } 806 return nil, "", err 807 } 808 809 // ConvertRequestToIoReader converts req to XML data 810 func ConvertRequestToIoReader(req interface{}) (io.Reader, error) { 811 body, err := TransToXml(req) 812 if err == nil { 813 if isDebugLogEnabled() { 814 doLog(LEVEL_DEBUG, "Do http request with data: %s", string(body)) 815 } 816 return bytes.NewReader(body), nil 817 } 818 return nil, err 819 } 820 821 func parseBucketPolicyOutput(s reflect.Type, baseModel IBaseModel, body []byte) { 822 for i := 0; i < s.NumField(); i++ { 823 if s.Field(i).Tag == "json:\"body\"" { 824 reflect.ValueOf(baseModel).Elem().FieldByName(s.Field(i).Name).SetString(string(body)) 825 break 826 } 827 } 828 } 829 830 // ParseResponseToBaseModel gets response from OBS 831 func ParseResponseToBaseModel(resp *http.Response, baseModel IBaseModel, xmlResult bool, _ bool) (err error) { 832 readCloser, ok := baseModel.(IReadCloser) 833 if !ok { 834 defer func() { 835 errMsg := resp.Body.Close() 836 if errMsg != nil { 837 doLog(LEVEL_WARN, "Failed to close response body") 838 } 839 }() 840 var body []byte 841 body, err = ioutil.ReadAll(resp.Body) 842 if err == nil && len(body) > 0 { 843 if xmlResult { 844 err = ParseXml(body, baseModel) 845 } else { 846 s := reflect.TypeOf(baseModel).Elem() 847 if reflect.TypeOf(baseModel).Elem().Name() == "GetBucketPolicyOutput" { 848 parseBucketPolicyOutput(s, baseModel, body) 849 } else { 850 err = parseJSON(body, baseModel) 851 } 852 } 853 if err != nil { 854 doLog(LEVEL_ERROR, "Unmarshal error: %v", err) 855 } 856 } 857 } else { 858 readCloser.setReadCloser(resp.Body) 859 } 860 861 baseModel.setStatusCode(resp.StatusCode) 862 responseHeaders := cleanHeaderPrefix(resp.Header) 863 baseModel.setResponseHeaders(responseHeaders) 864 if values, ok := responseHeaders[HEADER_REQUEST_ID]; ok { 865 baseModel.setRequestId(values[0]) 866 } 867 return 868 } 869 870 // ParseResponseToObsError gets obsError from OBS 871 func ParseResponseToObsError(resp *http.Response, isObs bool) error { 872 obsError := ObsError{} 873 respError := ParseResponseToBaseModel(resp, &obsError, true, isObs) 874 if respError != nil { 875 doLog(LEVEL_WARN, "Parse response to BaseModel with error: %v", respError) 876 } 877 obsError.Status = resp.Status 878 return obsError 879 } 880 881 func parseStringToFSStatusType(value string) (ret FSStatusType) { 882 switch value { 883 case "Enabled": 884 ret = FSStatusEnabled 885 case "Disabled": 886 ret = FSStatusDisabled 887 default: 888 ret = "" 889 } 890 return 891 } 892 893 // convertReplicationConfigurationToXml converts BucketReplicationConfiguration value to XML data and returns it 894 func convertReplicationConfigurationToXml(input BucketReplicationConfiguration, returnMd5 bool, isObs bool) (data string, md5 string) { 895 xml := make([]string, 0, 3+len(input.ReplicationRules)*6) 896 897 xml = append(xml, "<ReplicationConfiguration>") 898 xml = append(xml, fmt.Sprintf("<Agency>%s</Agency>", XmlTranscoding(input.Agency))) 899 900 for _, rule := range input.ReplicationRules { 901 xml = append(xml, "<Rule>") 902 xml = append(xml, fmt.Sprintf("<Status>%s</Status>", rule.Status)) 903 xml = append(xml, fmt.Sprintf("<Prefix>%s</Prefix>", XmlTranscoding(rule.Prefix))) 904 905 xml = append(xml, fmt.Sprintf("<Destination><Bucket>%s</Bucket>", XmlTranscoding(rule.DestinationBucket))) 906 if rule.StorageClass != "" { 907 storageClass := string(rule.StorageClass) 908 if !isObs { 909 if storageClass == string(StorageClassWarm) { 910 storageClass = string(storageClassStandardIA) 911 } else if storageClass == string(StorageClassCold) { 912 storageClass = string(storageClassGlacier) 913 } 914 } 915 xml = append(xml, fmt.Sprintf("<StorageClass>%s</StorageClass>", storageClass)) 916 } 917 if rule.DeleteData != "" { 918 xml = append(xml, fmt.Sprintf("<DeleteData>%s</DeleteData>", rule.DeleteData)) 919 } 920 xml = append(xml, "</Destination>") 921 922 if rule.ID != "" { 923 xml = append(xml, fmt.Sprintf("<ID>%s</ID>", XmlTranscoding(rule.ID))) 924 } 925 926 if rule.HistoricalObjectReplication != "" { 927 xml = append(xml, fmt.Sprintf("<HistoricalObjectReplication>%s</HistoricalObjectReplication>", 928 rule.HistoricalObjectReplication)) 929 } 930 xml = append(xml, "</Rule>") 931 } 932 xml = append(xml, "</ReplicationConfiguration>") 933 data = strings.Join(xml, "") 934 if returnMd5 { 935 md5 = Base64Md5([]byte(data)) 936 } 937 return 938 } 939 940 // ConvertObjectLockConfigurationToXml converts BucketWormPolicy value to XML data and returns it 941 func ConvertObjectLockConfigurationToXml(input BucketWormPolicy, returnMd5 bool, isObs bool) (data string, md5 string) { 942 xml := make([]string, 0, 7) 943 xml = append(xml, "<ObjectLockConfiguration>") 944 945 if input.ObjectLockEnabled != "" { 946 objectLock := XmlTranscoding(input.ObjectLockEnabled) 947 xml = append(xml, fmt.Sprintf("<ObjectLockEnabled>%s</ObjectLockEnabled><Rule><DefaultRetention>", objectLock)) 948 } 949 950 if input.Days != "" { 951 days := XmlTranscoding(input.Days) 952 xml = append(xml, fmt.Sprintf("<Days>%s</Days>", days)) 953 } 954 955 if input.Mode != "" { 956 mode := XmlTranscoding(input.Mode) 957 xml = append(xml, fmt.Sprintf("<Mode>%s</Mode>", mode)) 958 } 959 960 if input.Years != "" { 961 years := XmlTranscoding(input.Years) 962 xml = append(xml, fmt.Sprintf("<Years>%s</Years>", years)) 963 } 964 965 if input.ObjectLockEnabled != "" { 966 xml = append(xml, "</DefaultRetention></Rule>") 967 } 968 969 xml = append(xml, "</ObjectLockConfiguration>") 970 data = strings.Join(xml, "") 971 if returnMd5 { 972 md5 = Base64Md5([]byte(data)) 973 } 974 return 975 }