Android CookieManager setCookie creates multiple cookies - android

In my android application I have a webview. It loads URLs from multiple domains. I need to delete all cookies from a specific domain. I want to keep cookies from other domains. But I need to delete all cookies from one domain. I'm open to all other solutions that handles my request. (note that domain uses both http and https)
But when I try to use CookieManager.setCookie, all available cookies for that domain didn't deleted. Multiple cookie keys appeear when I try to write to that keys.
I attach my code below. You can find results in comment lines. At the end of story I get this cookie. Note for multiple values:
"userid=12%34; token=12ased; remember_check=0; userid='-1'; token='-1'; remember_check='-1';"
My helper function that splits cookie string to get cookie keys:
public static Vector<String> getCookieAllKeysByCookieString(String pCookies) {
if (TextUtils.isEmpty(pCookies)) {
return null;
}
String[] cookieField = pCookies.split(";");
int len = cookieField.length;
for (int i = 0; i < len; i++) {
cookieField[i] = cookieField[i].trim();
}
Vector<String> allCookieField = new Vector<String>();
for (int i = 0; i < len; i++) {
if (TextUtils.isEmpty(cookieField[i])) {
continue;
}
if (!cookieField[i].contains("=")) {
continue;
}
String[] singleCookieField = cookieField[i].split("=");
allCookieField.add(singleCookieField[0]);
}
if (allCookieField.isEmpty()) {
return null;
}
return allCookieField;
}
I get present cookies:
// I take cookie string for specific URL
mCookieManager = CookieManager.getInstance();
String url2="https://mysite.com";
String cookieString = mCookieManager.getCookie(url2);
Toast.makeText(mContext, "cookie string:\n"+cookieString, Toast.LENGTH_SHORT).show();
// result is: userid=12%34; token=12ased; remember_check=0;
Then I call to replace old cookies.
Vector<String> cookie = CookieUtil.getCookieAllKeysByCookieString(cookieString);
if (cookie == null || cookie.isEmpty()) {
Toast.makeText(mContext, "cookie null", Toast.LENGTH_SHORT).show();
}
if (cookie != null) {
int len = cookie.size();
Toast.makeText(mContext, "cookie number: "+len, Toast.LENGTH_SHORT).show();
// result is, cookie number: 3
String cookieNames="";
for (int i = 0; i < len; i++) {
cookieNames += "\n"+cookie.get(i) ;
mCookieManager.setCookie(url2, cookie.get(i) + "='-1';");
}
Toast.makeText(mContext, "cookieNames:\n"+cookieNames, Toast.LENGTH_SHORT).show();
// result is: "cookienames: userid token remember_check"
mCookieSyncManager.sync();
cookieString = mCookieManager.getCookie(url2);
Toast.makeText(mContext, "cookie string:\n"+cookieString, Toast.LENGTH_SHORT).show();
mCookieSyncManager.sync();
// result is: "userid=12%34; token=12ased; remember_check=0; userid='-1'; token='-1'; remember_check='-1';"
}
Edit:
I also tried setCookie like this:
mCookieManager.setCookie(url2, cookie.get(i) + "=-1;");
mCookieManager.setCookie(url2, cookie.get(i) + "=-1");
Edit2: setCookie's signature is like this:
/**
* Sets a cookie for the given URL. Any existing cookie with the same host,
* path and name will be replaced with the new cookie. The cookie being set
* must not have expired and must not be a session cookie, otherwise it
* will be ignored.
*
* #param url the URL for which the cookie is set
* #param value the cookie as a string, using the format of the 'Set-Cookie'
* HTTP response header
*/
public void setCookie(String url, String value) {
throw new MustOverrideException();
}
Although I get same keys inside cookie string ("userid=12%34; token=12ased; remember_check=0; userid='-1'; token='-1'; remember_check='-1';") will they have different host or path ?

I've had a similar experience with the CookieManager in Android. Setting the same cookie will indeed add it as a new cookie.
Please try to implement this solution. It will enable you to flush the cookies you want to remove and then you'll be able to set the again as you desire.
Good luck!

First, we can delete the cookie via CookieManager's interfaces:
setCookie(URL, 'COOKIE_KEY=;');
Then, we need to find out the correct URL, considering both Domain and Path attributes of the cookie.
For example, the following cookie
document.cookie = 'COOKIE_NAME=COOKIE_VAL; path=/; domain=.example.com;'
can be deleted by
setCookie('.example.com', 'COOKIE_NAME=;')
and cannot be deleted by
setCookie('www.example.com/info.html', 'COOKIE_NAME=;')
Finally, here is an example to remove a cookie.
String[] kvPairs = CookieManager.getInstance().getCookie(url).split(" ");
for (String kvPair : kvPairs) {
String newPair = kvPair.replaceAll("=.*", "=;");
// Delete the cookie asynchronously.
CookieManager.getInstance().setCookie(url, newPair);
}

Related

Xamarin OnPageFinished --- getting cookies

I am overriding this method for my WebView. And I am using the proper domain. When debugging using an Android simulator I only see a partial list of all of the cookies. The one in particular I'm requiring the value of isn't listed. Any suggestions? Source code below.
public override void OnPageFinished(WebView view, string url)
{
base.OnPageFinished(view, url);
_cookieManager.Flush();
string customerId = null;
var cookies = _cookieManager.GetCookie(Constants.DCH_DOMAIN);
Log.Info(TAG, string.Format("Cookies retrieved as {0}.", cookies));
if (cookies != null)
{
string[] tempList = cookies.Split(';');
foreach (var pair in tempList)
{
if (pair.Contains("wishlist_customer_id"))
{
string[] tempPair = pair.Split('=');
customerId = tempPair[1];
if (customerId != null)
{
Log.Info(TAG, string.Format("Customer ID retrieved as {0}.", customerId));
PCLStorage(customerId);
}
}
}
}
UPDATE There is another domain that stores cookies as part of this web session. No different in path, HTML, same site, secure, etc. between the four cookies. I'll attach what Chrome on my workstation looks like, showing these four cookies. Then I'll attach what my Android simulator's device log looks like, where I output the cookie collection based on the subclassed WebViewClient. Only two of the four cookies appear to be there. I inserted a SystemClock.Sleep(5000) before retrieving the cookies, in order to give them a chance to fully populate as well.
Chrome session
WebViewClient session
you should do cookied synchronization before load url to ensure complete cookie data like :
_cookieManager.flush ();
webView.loadUrl(url);
then get cookies int the OnPageFinished method :
public override void OnPageFinished(WebView view, string url)
{
base.OnPageFinished(view, url);
string customerId = null;
var cookies = _cookieManager.GetCookie(url);
Log.Info(TAG, string.Format("Cookies retrieved as {0}.", cookies));
if (cookies != null)
{
string[] tempList = cookies.Split(';');
foreach (var pair in tempList)
{
if (pair.Contains("wishlist_customer_id"))
{
string[] tempPair = pair.Split('=');
customerId = tempPair[1];
if (customerId != null)
{
Log.Info(TAG, string.Format("Customer ID retrieved as {0}.", customerId));
PCLStorage(customerId);
}
}
}
}
Thanks to Leo Zhu the issue has been resolved. Apparently before I load the WebView URL I needed to flush the CookieManager instance. This resulted in all of the cookies for this particular resource to appear and be available for querying. Hope that this might help anyone else who runs into a similar challenge!

Android email client app tracking unread gmail emails

So I've been using javamail API as part of my android app. After a login to a gmail account, the user can write new emails, check the inbox and sent mails. The emails are displayed in a listview with the help of an adapter class. (more accurately the sender, the subject and the sending date is displayed, and if the user clicks on the listview item, then the mail content will be displayed too on a new activity). All this is working well.
I would like to display unread emails differently (unread in the gmail client too), like set the textSyle bold if the mail is unread, but i'm having trouble adding this feature. I've been trying to check the flags of each fetched email message, but for some reason i dont see these flags in the variables.
My code snippet for fetching the mails (display is not here, that's in the adapter class):
protected ArrayList<Email_Message> doInBackground(Void... args) {
try {
Properties properties = new Properties();
properties.put("mail.store.protocol", "imaps");
Session emailSession = Session.getDefaultInstance(properties);
Store store = emailSession.getStore("imaps");
store.connect("imap.gmail.com", username, password);
// create the folder object and open it
Folder emailFolder = store.getFolder("INBOX");
emailFolder.open(Folder.READ_WRITE);
Flags flags2 = emailFolder.getPermanentFlags(); //has 2 weird user flags in it ($phishing, $notphising) and systemflags = -2147483061 ???
Flags seen = new Flags(Flags.Flag.RECENT);
FlagTerm unseenFlagTerm = new FlagTerm(seen, false);
Message messages2[] = emailFolder.search(unseenFlagTerm); //this will net the same result as getMessages(), only here for testing
int test = emailFolder.getUnreadMessageCount(); //as far as i can tell this is working (i have 5000+ emails and 37 them are unread somewhere) but when i get a new email and the number goes up by 1 (38), and if i run the code again, after i already fetched the mails once, it's gonna be 37 again, and the mail marked as read in my gmail webclient too
// retrieve the messages from the folder in an array and print it
Message[] messages = emailFolder.getMessages();
int j = messages.length - 1;
for (int i = j - startIndex; i > j - startIndex - offset && i > (-1); i--) { //startIndex and offset are for displaying only 10 messages at the start and loading another 10 if the user scrolls to bottom
if (isCancelled()){
break;
}
Email_Message mailMessage = new Email_Message(); //my class for storing email messages
mailMessage.messageType = 1;
//some tricks to get the address in the right format
Address[] email_address = messages[i].getFrom();
String tempAddress = email_address[0].toString();
tempAddress = MimeUtility.decodeText(tempAddress);
//still tempering with address, not important
if(tempAddress.contains("=?")){
String[] AddressParts = tempAddress.split("\\?=");
mailMessage.messageAddress = AddressParts[1].substring(2);
}
else {
mailMessage.messageAddress = tempAddress;
}
Flags flags = messages[i].getFlags(); //user_flags = null, system_flags = 32
Flags.Flag[] systemflags = flags.getSystemFlags(); //has 1 item in it: bit = 32
String str[]= flags.getUserFlags(); //empty, these are all true for all my mails, not just one
mailMessage.messageDate = messages[i].getSentDate().toString();
mailMessage.messageSubject = messages[i].getSubject();
Object msgContent = messages[i].getContent();
String content = ""; //getting the content of the mail with these multipart stuffs
if (msgContent instanceof Multipart) {
Multipart multipart = (Multipart) msgContent;
Log.e("BodyPart", "MultiPartCount: " + multipart.getCount());
for (int k = 0; k < multipart.getCount(); k++) {
BodyPart bodyPart = multipart.getBodyPart(k);
String disposition = bodyPart.getDisposition();
if (disposition != null && (disposition.equalsIgnoreCase("ATTACHMENT"))) {
DataHandler handler = bodyPart.getDataHandler();
content = handler.getName();
} else {
content = bodyPart.getContent().toString();
}
}
} else
content = messages[i].getContent().toString();
mailMessage.messageContent = content;
EmailInbox.add(mailMessage);
}
// close the store and folder objects
emailFolder.close(false);
store.close();
} catch (NoSuchProviderException e) {
e.printStackTrace();
} catch (MessagingException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return EmailInbox;
}
I put some comments in the code to explain what i've found in the flags. What can I do to make this work? I already predict problems, like what happens after I read an unread mail in my app only, set its flag to seen, and when I start the activity again and fetch the mails, it's gonna be unread again, since I don't store them locally, but that's a problem after I managed to find a solution for this first.
Thanks for any help!
I'm not clear on how you're looking for the flags. Using messages[i].isSet(Flags.Flag.SEEN) will tell you if the SEEN flag has been set. Note that the SEEN flag will normally be set as soon as you access the content of the message, so you should not have to set it yourself.
Hint: use the InternetAddress.toUnicodeString method, or get the name and address separately using the getPersonal and getAddress methods. This will avoid any need to decode them yourself.

Strange Cookie Issue in Android WebView?

I have native application in Andriod and Iphone with ASP.NET as a back-end. I am sending request to my ASP.NET server API by appending Session_Id cookie using HttpClient classes which works great. But I also need to send the same session in WebView. The problem is that some times WebView send the Session_Id cookie and sometimes doesn't. It strange for me. I am not able to find why sometimes webview send the cookie and sometimes doesn't. Here is my code,
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myapp = ((....) getApplicationContext());
setTimeout(true);
try {
getActionBarHelper().setupHomeActivity();
} catch (Exception e) {
e.printStackTrace();
}
cm = (ConnectivityManager) this.getSystemService(Activity.CONNECTIVITY_SERVICE);
webView = new CustomWebView(this, this);
webView.getWebView().addJavascriptInterface(new MyJavaScriptInterface(), "android");
getTimeDiff();
if(MyApplication.INSTANCE.isLoggedin()){
Cookie sessionCookie = MyApplication.INSTANCE.getCookie();
List<Cookie> cookies = MyApplication.INSTANCE.getClient().getCookieStore().getCookies();
for (int i = 0; i < cookies.size(); i++) {
sessionCookie = cookies.get(i);
}
try{
if(sessionCookie!=null){
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
if (sessionCookie != null) {
cookieManager.removeSessionCookie();
String cookieString = sessionCookie.getName() + "="
+ sessionCookie.getValue() + "; domain="
+ sessionCookie.getDomain();
if (MyApplication.isDebug())
cookieManager.setCookie(sessionCookie.getDomain(),(sessionCookie.getName() + "=" + sessionCookie.getValue()));
CookieSyncManager.getInstance().sync();
}else{
if (MyApplication.isDebug())
Log.d("WebView", " Cookie is null: " );
}
}else{
if (MyApplication.isDebug())
Log.d("WebView", " Cookie is null: " );
}
}catch (Exception e) {
e.printStackTrace();
}
Log.e("WenView", "============================ ");
}
}
Your for loop appears to only include the single line that retrieves the cookie... so your logic that's setting the cookie below only gets called once, with whatever the last cookie happened to be. Since the cookies aren't ordered in the list, this probably explains why sometimes it works and sometimes it doesn't; it's working when your cookie is last, and failing otherwise.
The answer is to just expand your for loop to include ALL the logic underneath as well. Your copying code looks fine, and I expect it is if you can get it to work sometimes.

Replacing PostMethod with HttpPost in Android

While integrating Weibo's Android SDK into an app, I discovered the HttpClient class it contains uses an obsolete library that Android dislikes intensely (in fact I think they just pasted a Java SDK into an Android Eclipse project and shipped it). This library only seems to perform a single function within Weibo, which is to assemble POST requests (using the PostMethod class) and send them to the Weibo server. I blithely assumed it would be relatively straightforward to replace this with the standard Apache HttpPost, which is included in Android.
Unfortunately the Part class seems to have no straightforward equivalent. At least some of the Parts could be replaced by BasicNameValuePair classes, but there is a custom Part defined by Weibo that looks more like a ByteArrayEntity.
Examining the two methods called multPartUrl (not a typo) in
Weibo's source for HttpClient
the first of which is reproduced here (the second is very similar but pastes in a different type of content):
public Response multPartURL(String url, PostParameter[] params,ImageItem item,boolean authenticated) throws WeiboException{
PostMethod post = new PostMethod(url);
try {
org.apache.commons.httpclient.HttpClient client = new org.apache.commons.httpclient.HttpClient();
long t = System.currentTimeMillis();
Part[] parts=null;
if(params==null){
parts=new Part[1];
}else{
parts=new Part[params.length+1];
}
if (params != null ) {
int i=0;
for (PostParameter entry : params) {
parts[i++]=new StringPart( entry.getName(),(String)entry.getValue());
}
parts[parts.length-1]=new ByteArrayPart(item.getContent(), item.getName(), item.getImageType());
}
post.setRequestEntity( new MultipartRequestEntity(parts, post.getParams()) );
List<Header> headers = new ArrayList<Header>();
if (authenticated) {
if (basic == null && oauth == null) {
}
String authorization = null;
if (null != oauth) {
// use OAuth
authorization = oauth.generateAuthorizationHeader( "POST" , url, params, oauthToken);
} else if (null != basic) {
// use Basic Auth
authorization = this.basic;
} else {
throw new IllegalStateException(
"Neither user ID/password combination nor OAuth consumer key/secret combination supplied");
}
headers.add(new Header("Authorization", authorization));
log("Authorization: " + authorization);
}
client.getHostConfiguration().getParams().setParameter("http.default-headers", headers);
client.executeMethod(post);
Response response=new Response();
response.setResponseAsString(post.getResponseBodyAsString());
response.setStatusCode(post.getStatusCode());
log("multPartURL URL:" + url + ", result:" + response + ", time:" + (System.currentTimeMillis() - t));
return response;
} catch (Exception ex) {
throw new WeiboException(ex.getMessage(), ex, -1);
} finally {
post.releaseConnection();
}
}
it can be seen that a number of Parts are added to a MultiPartRequestEntity, the last of which is a byte array or a file.
What (if anything) is the equivalent to MultiPartRequestEntity in the more up to date Apache libraries?
Is there a way to add a byte array to a UrlEncodedFormEntity?
Is there, alternatively, a way to add name-value pairs to a ByteArrayEntity?
Is there something else I am missing completely?
The best answer I've found so far seems to be based on the answer to this question:
Post multipart request with Android SDK
This shows where to get the non-obsolete replacements for the libraries used by Weibo.
I've then replaced
Part[] parts=null;
if(params==null){
parts=new Part[1];
}else{
parts=new Part[params.length+1];
}
if (params != null ) {
int i=0;
for (PostParameter entry : params) {
parts[i++]=new StringPart( entry.getName(),(String)entry.getValue());
}
parts[parts.length-1]=new ByteArrayPart(item.getContent(), item.getName(), item.getImageType());
}
post.setRequestEntity( new MultipartRequestEntity(parts, post.getParams()) );
with
MultipartEntity entity = new MultipartEntity();
if(params!=null) {
for (PostParameter entry : params) {
entity.addPart(entry.getName(), new StringBody(entry.getValue()));
}
}
entity.addPart("filename", new ByteArrayBody(item.getContent(), item.getImageType(), item.getName()));
I'm not 100% certain about the last line as I've based that on Weibo's own class, which sets a filename parameter; however, it could well be called something else, though what is not clear.
I've then replaced the HttpClient with an AndroidHttpClient and used standard AndroidHttpClient methods to get the status code and an InputStream with the content from the entity; this stream, I am passing into the Weibo Response class and reading into the string it has as its main member. These assumptions may be wrong but I will continue to look into it.

Store cookie in sharedpreferences

I've been pulling my hair trying to figure this out: I'm making an HttpsURLConnection and using java.net.cookiemanager to manage my cookies (there's no way of using android.webkit.cookiemanager to HttpUrlConnection/HttpsUrlConnection as I have understood?). I need to save my longtime cookie to later connections.
Sadly I can't use http://loopj.com/android-async-http/ and it's PersistentCookieStore because I need to allow an untrusted certificate (using http://abhinavasblog.blogspot.se/2011/07/allow-untrusted-certificate-for-https.html). I've tried using their PersistentCookieStore alone but they are using apache cookies and I'm using java.net cookies...
This is what I've tried:
cManager = new CookieManager(null, CookiePolicy.ACCEPT_ALL);
private void setSharedPreferences(){
List<HttpCookie> cookies = cManager.getCookieStore().getCookies();
if (cookies.isEmpty()) {
Log.d(tag,"no cookies received");
} else {
for (int i = 0; i < cookies.size(); i++) {
if(cookies.get(i).getName().equals("rememberMe")) {
editor.putString(
"rememberMe", cookies.get(i).toString());
editor.commit();
}
}
}
}
And when I'm retrieving the cookie on next app launch:
SharedPreferences sharedPreferences = context.getSharedPreferences("PREF_NAME", Context.MODE_PRIVATE);
editor = sharedPreferences.edit();
String rememberString = sharedPreferences.getString("rememberMe", "none");
if (!rememberString.equals("none")) {
Log.d("rememberME är inte", "none!");
URI uriToCookie = null;
try {
uriToCookie = new URI("https://myservername.com");
} catch (URISyntaxException e) {
e.printStackTrace();
}
List<HttpCookie> cookieList = HttpCookie.parse(rememberString);
cManager.getCookieStore().add(uriToCookie, cookieList.get(0));
}
The cookie is added to cManager but is not recognized by the server.. I believe there is some sort of parse problem. Anyone got the solution?
I used this part:
cookies = ((AbstractHttpClient) httpClient).getCookieStore().getCookies();
Log.v("Cookie:", cookies.toString());
if (cookies.isEmpty()) {
} else {
for (int i = 0; i < cookies.size(); i++) {
if(cookies.get(i).getName().contentEquals("PHPSESSID")) {
PHPSESSID = cookies.get(i).getValue();
}
}
}
Just use the contentEquals to get the domain name and let it match with urs and store it. Oh and i used PHPSESSID as a string which i dumped in my sharedprefs for later
After looking at your code, I code be wrong but you are simply not storing the entire cookie:
for (int i = 0; i < cookies.size(); i++) {
if(cookies.get(i).getName().equals("rememberMe")){
editor.putString("rememberMe", cookies.get(i).toString());
editor.commit();
}
}
You get the List length of cookies with cookies.size() and loop and get all the cookies but you commit the save key-value "rememberMe" instead of appending or storing in separate keys. So basically, you are simply overwriting what you stored over and over.

Categories

Resources