1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.libraries.rcs.simpleclient.protocol.cpim; 18 19 import android.text.TextUtils; 20 import com.google.auto.value.AutoValue; 21 import com.google.common.base.Ascii; 22 import com.google.common.base.Utf8; 23 import com.google.common.collect.ImmutableMap; 24 import com.google.common.io.CharStreams; 25 import java.io.BufferedReader; 26 import java.io.ByteArrayInputStream; 27 import java.io.IOException; 28 import java.io.InputStreamReader; 29 import java.util.Map; 30 import java.util.regex.Matcher; 31 import java.util.regex.Pattern; 32 33 /** 34 * The CPIM implementation as per RFC 3862. This class supports minimal fields that is required to 35 * represent a simple message for test purpose. 36 */ 37 @AutoValue 38 public abstract class SimpleCpimMessage { 39 private static final String CRLF = "\r\n"; 40 private static final String COLSP = ": "; 41 private static final Pattern NAMESPACE_HEADER_PATTERN = 42 Pattern.compile("NS:\\s+(\\S+)\\s+<(.+)>"); 43 private static final Pattern HEADER_PATTERN = Pattern.compile("([^\\s:]+):\\s+(.+)"); 44 namespaces()45 public abstract ImmutableMap<String, String> namespaces(); 46 headers()47 public abstract ImmutableMap<String, String> headers(); 48 contentType()49 public abstract String contentType(); 50 content()51 public abstract String content(); 52 encode()53 public String encode() { 54 StringBuilder builder = new StringBuilder(); 55 for (Map.Entry<String, String> entry : namespaces().entrySet()) { 56 builder 57 .append("NS: ") 58 .append(entry.getKey()) 59 .append(" <") 60 .append(entry.getValue()) 61 .append(">") 62 .append(CRLF); 63 } 64 65 for (Map.Entry<String, String> entry : headers().entrySet()) { 66 builder.append(entry.getKey()).append(COLSP).append(entry.getValue()).append(CRLF); 67 } 68 69 builder.append(CRLF); 70 builder.append("Content-Type").append(COLSP).append(contentType()); 71 builder.append(CRLF); 72 builder.append("Content-Length").append(COLSP).append(Utf8.encodedLength(content())); 73 builder.append(CRLF).append(CRLF); 74 builder.append(content()); 75 76 return builder.toString(); 77 } 78 parse(byte[] content)79 public static SimpleCpimMessage parse(byte[] content) throws IOException { 80 BufferedReader reader = 81 new BufferedReader(new InputStreamReader(new ByteArrayInputStream(content))); 82 Builder builder = newBuilder(); 83 84 String line = reader.readLine(); 85 while (!TextUtils.isEmpty(line)) { 86 Matcher namespaceMatcher = NAMESPACE_HEADER_PATTERN.matcher(line); 87 Matcher headerMatcher = HEADER_PATTERN.matcher(line); 88 if (namespaceMatcher.matches()) { 89 builder.addNamespace(namespaceMatcher.group(1), namespaceMatcher.group(2)); 90 } else if (headerMatcher.matches()) { 91 builder.addHeader(headerMatcher.group(1), headerMatcher.group(2)); 92 } 93 94 line = reader.readLine(); 95 } 96 97 line = reader.readLine(); 98 while (!TextUtils.isEmpty(line)) { 99 Matcher headerMatcher = HEADER_PATTERN.matcher(line); 100 if (headerMatcher.matches()) { 101 if (Ascii.equalsIgnoreCase("content-type", headerMatcher.group(1))) { 102 builder.setContentType(headerMatcher.group(2)); 103 } 104 } 105 106 line = reader.readLine(); 107 } 108 109 String body = CharStreams.toString(reader); 110 builder.setContent(body); 111 112 return builder.build(); 113 } 114 115 @AutoValue.Builder 116 public abstract static class Builder { namespacesBuilder()117 public abstract ImmutableMap.Builder<String, String> namespacesBuilder(); 118 headersBuilder()119 public abstract ImmutableMap.Builder<String, String> headersBuilder(); 120 setContentType(String value)121 public abstract Builder setContentType(String value); 122 setContent(String value)123 public abstract Builder setContent(String value); 124 build()125 public abstract SimpleCpimMessage build(); 126 addNamespace(String name, String value)127 public Builder addNamespace(String name, String value) { 128 namespacesBuilder().put(name, value); 129 return this; 130 } 131 addHeader(String name, String value)132 public Builder addHeader(String name, String value) { 133 headersBuilder().put(name, value); 134 return this; 135 } 136 } 137 newBuilder()138 public static SimpleCpimMessage.Builder newBuilder() { 139 return new AutoValue_SimpleCpimMessage.Builder(); 140 } 141 } 142