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  }