diff --git a/src/com/android/exchange/EasOutboxService.java b/src/com/android/exchange/EasOutboxService.java index 34b6d7f4e..b0b39b7d6 100644 --- a/src/com/android/exchange/EasOutboxService.java +++ b/src/com/android/exchange/EasOutboxService.java @@ -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); diff --git a/src/com/android/exchange/EasSyncService.java b/src/com/android/exchange/EasSyncService.java index a4464dc1a..f58604c50 100644 --- a/src/com/android/exchange/EasSyncService.java +++ b/src/com/android/exchange/EasSyncService.java @@ -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(); } diff --git a/tests/src/com/android/exchange/EasOutboxServiceTests.java b/tests/src/com/android/exchange/EasOutboxServiceTests.java new file mode 100644 index 000000000..df6bd6c13 --- /dev/null +++ b/tests/src/com/android/exchange/EasOutboxServiceTests.java @@ -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); + } +} diff --git a/tests/src/com/android/exchange/EasSyncServiceTests.java b/tests/src/com/android/exchange/EasSyncServiceTests.java index 2f070d523..320a1d111 100644 --- a/tests/src/com/android/exchange/EasSyncServiceTests.java +++ b/tests/src/com/android/exchange/EasSyncServiceTests.java @@ -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); + } }