github.com/huaweicloud/golangsdk@v0.0.0-20210831081626-d823fe11ceba/openstack/obs/convert.go (about)

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