replicant-packages_apps_Email/imap2/src/com/android/imap2/ImapId.java

190 lines
7.5 KiB
Java

/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.imap2;
import android.content.Context;
import android.os.Build;
import android.telephony.TelephonyManager;
import android.util.Base64;
import android.util.Log;
import com.android.emailcommon.Device;
import com.android.emailcommon.Logging;
import com.android.emailcommon.VendorPolicyLoader;
import com.google.common.annotations.VisibleForTesting;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.regex.Pattern;
public class ImapId {
private static String sImapId;
/**
* Return, or create and return, an string suitable for use in an IMAP ID message.
* This is constructed similarly to the way the browser sets up its user-agent strings.
* See RFC 2971 for more details. The output of this command will be a series of key-value
* pairs delimited by spaces (there is no point in returning a structured result because
* this will be sent as-is to the IMAP server). No tokens, parenthesis or "ID" are included,
* because some connections may append additional values.
*
* The following IMAP ID keys may be included:
* name Android package name of the program
* os "android"
* os-version "version; model; build-id"
* vendor Vendor of the client/server
* x-android-device-model Model (only revealed if release build)
* x-android-net-operator Mobile network operator (if known)
* AGUID A device+account UID
*
* In addition, a vendor policy .apk can append key/value pairs.
*
* @param userName the username of the account
* @param host the host (server) of the account
* @param capabilities a list of the capabilities from the server
* @return a String for use in an IMAP ID message.
*/
public static String getImapId(Context context, String userName, String host,
String capabilities) {
// The first section is global to all IMAP connections, and generates the fixed
// values in any IMAP ID message
synchronized (ImapId.class) {
if (sImapId == null) {
TelephonyManager tm =
(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
String networkOperator = tm.getNetworkOperatorName();
if (networkOperator == null) networkOperator = "";
sImapId = makeCommonImapId(context.getPackageName(), Build.VERSION.RELEASE,
Build.VERSION.CODENAME, Build.MODEL, Build.ID, Build.MANUFACTURER,
networkOperator);
}
}
// This section is per Store, and adds in a dynamic elements like UID's.
// We don't cache the result of this work, because the caller does anyway.
StringBuilder id = new StringBuilder(sImapId);
// Optionally add any vendor-supplied id keys
String vendorId = VendorPolicyLoader.getInstance(context).getImapIdValues(userName, host,
capabilities);
if (vendorId != null) {
id.append(' ');
id.append(vendorId);
}
// Generate a UID that mixes a "stable" device UID with the email address
try {
String devUID = Device.getConsistentDeviceId(context);
MessageDigest messageDigest;
messageDigest = MessageDigest.getInstance("SHA-1");
messageDigest.update(userName.getBytes());
messageDigest.update(devUID.getBytes());
byte[] uid = messageDigest.digest();
String hexUid = Base64.encodeToString(uid, Base64.NO_WRAP);
id.append(" \"AGUID\" \"");
id.append(hexUid);
id.append('\"');
} catch (NoSuchAlgorithmException e) {
Log.d(Logging.LOG_TAG, "couldn't obtain SHA-1 hash for device UID");
}
return id.toString();
}
/**
* Helper function that actually builds the static part of the IMAP ID string. This is
* separated from getImapId for testability. There is no escaping or encoding in IMAP ID so
* any rogue chars must be filtered here.
*
* @param packageName context.getPackageName()
* @param version Build.VERSION.RELEASE
* @param codeName Build.VERSION.CODENAME
* @param model Build.MODEL
* @param id Build.ID
* @param vendor Build.MANUFACTURER
* @param networkOperator TelephonyManager.getNetworkOperatorName()
* @return the static (never changes) portion of the IMAP ID
*/
@VisibleForTesting
static String makeCommonImapId(String packageName, String version,
String codeName, String model, String id, String vendor, String networkOperator) {
// Before building up IMAP ID string, pre-filter the input strings for "legal" chars
// This is using a fairly arbitrary char set intended to pass through most reasonable
// version, model, and vendor strings: a-z A-Z 0-9 - _ + = ; : . , / <space>
// The most important thing is *not* to pass parens, quotes, or CRLF, which would break
// the format of the IMAP ID list.
Pattern p = Pattern.compile("[^a-zA-Z0-9-_\\+=;:\\.,/ ]");
packageName = p.matcher(packageName).replaceAll("");
version = p.matcher(version).replaceAll("");
codeName = p.matcher(codeName).replaceAll("");
model = p.matcher(model).replaceAll("");
id = p.matcher(id).replaceAll("");
vendor = p.matcher(vendor).replaceAll("");
networkOperator = p.matcher(networkOperator).replaceAll("");
// "name" "com.android.email"
StringBuffer sb = new StringBuffer("\"name\" \"");
sb.append(packageName);
sb.append("\"");
// "os" "android"
sb.append(" \"os\" \"android\"");
// "os-version" "version; build-id"
sb.append(" \"os-version\" \"");
if (version.length() > 0) {
sb.append(version);
} else {
// default to "1.0"
sb.append("1.0");
}
// add the build ID or build #
if (id.length() > 0) {
sb.append("; ");
sb.append(id);
}
sb.append("\"");
// "vendor" "the vendor"
if (vendor.length() > 0) {
sb.append(" \"vendor\" \"");
sb.append(vendor);
sb.append("\"");
}
// "x-android-device-model" the device model (on release builds only)
if ("REL".equals(codeName)) {
if (model.length() > 0) {
sb.append(" \"x-android-device-model\" \"");
sb.append(model);
sb.append("\"");
}
}
// "x-android-mobile-net-operator" "name of network operator"
if (networkOperator.length() > 0) {
sb.append(" \"x-android-mobile-net-operator\" \"");
sb.append(networkOperator);
sb.append("\"");
}
return sb.toString();
}
}