Fix problem with unencoded data in URI sent to EAS

* We need to encode the itemId and collectionId for SmartForward and
  SmartReply
* Add unit test for the fix
* Small change + test to EasSyncService usage of URI encoding to use
  a non-deprecated method

Bug: 2787725
Change-Id: I428b308b56cc359b8cdd9e42bc3f42c65b6797dc
This commit is contained in:
Marc Blank 2010-06-28 12:30:13 -07:00
parent 8d8f86e899
commit 51f5b2f3fa
4 changed files with 105 additions and 9 deletions

View File

@ -36,6 +36,7 @@ import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.RemoteException;
import java.io.File;
@ -71,6 +72,11 @@ public class EasOutboxService extends EasSyncService {
}
}
/*package*/ String generateSmartSendCmd(boolean reply, String itemId, String collectionId) {
return (reply ? "SmartReply" : "SmartForward") + "&ItemId=" + Uri.encode(itemId) +
"&CollectionId=" + Uri.encode(collectionId);
}
/**
* Send a single message via EAS
* Note that we mark messages SEND_FAILED when there is a permanent failure, rather than an
@ -130,11 +136,12 @@ public class EasOutboxService extends EasSyncService {
new InputStreamEntity(inputStream, tmpFile.length());
// Create the appropriate command and POST it to the server
String cmd = "SendMail&SaveInSent=T";
String cmd = "SendMail";
if (smartSend) {
cmd = reply ? "SmartReply" : "SmartForward";
cmd += "&ItemId=" + itemId + "&CollectionId=" + collectionId + "&SaveInSent=T";
cmd = generateSmartSendCmd(reply, itemId, collectionId);
}
cmd += "&SaveInSent=T";
userLog("Send cmd: " + cmd);
HttpResponse resp = sendHttpClientPost(cmd, inputEntity, SEND_MAIL_TIMEOUT);

View File

@ -77,6 +77,7 @@ import android.content.ContentValues;
import android.content.Context;
import android.content.Entity;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.SystemClock;
@ -94,7 +95,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.lang.Thread.State;
import java.net.URI;
import java.net.URLEncoder;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.HashMap;
@ -185,7 +185,7 @@ public class EasSyncService extends AbstractSyncService {
protected String mDeviceId = null;
/*package*/ String mDeviceType = "Android";
/*package*/ String mAuthString = null;
private String mCmdString = null;
/*package*/ String mCmdString = null;
public String mHostAddress;
public String mUserName;
public String mPassword;
@ -1099,17 +1099,16 @@ public class EasSyncService extends AbstractSyncService {
* in all HttpPost commands. This should be called if these strings are null, or if mUserName
* and/or mPassword are changed
*/
@SuppressWarnings("deprecation")
private void cacheAuthAndCmdString() {
String safeUserName = URLEncoder.encode(mUserName);
String safeUserName = Uri.encode(mUserName);
String cs = mUserName + ':' + mPassword;
mAuthString = "Basic " + Base64.encodeToString(cs.getBytes(), Base64.NO_WRAP);
mCmdString = "&User=" + safeUserName + "&DeviceId=" + mDeviceId +
"&DeviceType=" + mDeviceType;
}
private String makeUriString(String cmd, String extra) throws IOException {
// Cache the authentication string and the command string
/*package*/ String makeUriString(String cmd, String extra) throws IOException {
// Cache the authentication string and the command string
if (mAuthString == null || mCmdString == null) {
cacheAuthAndCmdString();
}

View File

@ -0,0 +1,48 @@
/*
* Copyright (C) 2010 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.exchange;
import com.android.email.provider.EmailContent.Mailbox;
import android.content.Context;
import android.test.AndroidTestCase;
/**
* You can run this entire test case with:
* runtest -c com.android.exchange.EasOutboxServiceTests email
*/
public class EasOutboxServiceTests extends AndroidTestCase {
Context mMockContext;
@Override
public void setUp() throws Exception {
super.setUp();
mMockContext = getContext();
}
public void testGenerateSmartSendCmd() {
EasOutboxService svc = new EasOutboxService(mMockContext, new Mailbox());
// Test encoding of collection id
String cmd = svc.generateSmartSendCmd(true, "1339085683659694034", "Mail:^f");
assertEquals("SmartReply&ItemId=1339085683659694034&CollectionId=Mail%3A%5Ef", cmd);
// Test encoding of item id
cmd = svc.generateSmartSendCmd(false, "14:3", "6");
assertEquals("SmartForward&ItemId=14%3A3&CollectionId=6", cmd);
}
}

View File

@ -25,6 +25,7 @@ import org.apache.http.client.methods.HttpRequestBase;
import android.content.Context;
import android.test.AndroidTestCase;
import android.util.Base64;
import java.io.File;
import java.io.IOException;
@ -35,6 +36,12 @@ import java.io.IOException;
*/
public class EasSyncServiceTests extends AndroidTestCase {
static private final String USER = "user";
static private final String PASSWORD = "password";
static private final String HOST = "xxx.host.zzz";
static private final String ID = "id";
static private final String TYPE = "type";
Context mMockContext;
@Override
@ -130,4 +137,39 @@ public class EasSyncServiceTests extends AndroidTestCase {
assertEquals(Eas.SUPPORTED_PROTOCOL_EX2007_SP1_DOUBLE,
Eas.getProtocolVersionDouble(Eas.SUPPORTED_PROTOCOL_EX2007_SP1));
}
private EasSyncService setupService(String user) {
EasSyncService svc = new EasSyncService();
svc.mUserName = user;
svc.mPassword = PASSWORD;
svc.mDeviceId = ID;
svc.mDeviceType = TYPE;
svc.mHostAddress = HOST;
return svc;
}
public void testMakeUriString() throws IOException {
// Simple user name and command
EasSyncService svc = setupService(USER);
String uriString = svc.makeUriString("OPTIONS", null);
// These next two should now be cached
assertNotNull(svc.mAuthString);
assertNotNull(svc.mCmdString);
assertEquals("Basic " + Base64.encodeToString((USER+":"+PASSWORD).getBytes(),
Base64.NO_WRAP), svc.mAuthString);
assertEquals("&User=" + USER + "&DeviceId=" + ID + "&DeviceType=" + TYPE, svc.mCmdString);
assertEquals("https://" + HOST + "/Microsoft-Server-ActiveSync?Cmd=OPTIONS" +
svc.mCmdString, uriString);
// User name that requires encoding
String user = "name_with_underscore@foo%bar.com";
svc = setupService(user);
uriString = svc.makeUriString("OPTIONS", null);
assertEquals("Basic " + Base64.encodeToString((user+":"+PASSWORD).getBytes(),
Base64.NO_WRAP), svc.mAuthString);
String safeUserName = "name_with_underscore%40foo%25bar.com";
assertEquals("&User=" + safeUserName + "&DeviceId=" + ID + "&DeviceType=" + TYPE,
svc.mCmdString);
assertEquals("https://" + HOST + "/Microsoft-Server-ActiveSync?Cmd=OPTIONS" +
svc.mCmdString, uriString);
}
}