190 lines
7.5 KiB
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();
|
|
}
|
|
|
|
}
|