1 /* ownCloud Android client application 
   2  *   Copyright (C) 2011  Bartek Przybylski 
   4  *   This program is free software: you can redistribute it and/or modify 
   5  *   it under the terms of the GNU General Public License as published by 
   6  *   the Free Software Foundation, either version 3 of the License, or 
   7  *   (at your option) any later version. 
   9  *   This program is distributed in the hope that it will be useful, 
  10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of 
  11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  12  *   GNU General Public License for more details. 
  14  *   You should have received a copy of the GNU General Public License 
  15  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. 
  19 package eu
.alefzero
.owncloud
.syncadapter
; 
  21 import java
.io
.IOException
; 
  22 import java
.net
.UnknownHostException
; 
  23 import java
.util
.Date
; 
  24 import java
.util
.LinkedList
; 
  26 import org
.apache
.http
.HttpHost
; 
  27 import org
.apache
.http
.HttpRequest
; 
  28 import org
.apache
.http
.HttpResponse
; 
  29 import org
.apache
.http
.client
.ClientProtocolException
; 
  30 import org
.apache
.http
.conn
.ConnectionKeepAliveStrategy
; 
  31 import org
.apache
.http
.impl
.auth
.BasicScheme
; 
  32 import org
.apache
.http
.impl
.client
.DefaultHttpClient
; 
  33 import org
.apache
.http
.protocol
.BasicHttpContext
; 
  34 import org
.apache
.http
.protocol
.HttpContext
; 
  36 import android
.accounts
.Account
; 
  37 import android
.accounts
.AccountManager
; 
  38 import android
.accounts
.AuthenticatorException
; 
  39 import android
.accounts
.OperationCanceledException
; 
  40 import android
.content
.AbstractThreadedSyncAdapter
; 
  41 import android
.content
.ContentProviderClient
; 
  42 import android
.content
.Context
; 
  43 import android
.net
.Uri
; 
  44 import android
.text
.TextUtils
; 
  45 import android
.util
.Log
; 
  46 import eu
.alefzero
.owncloud
.authenticator
.AccountAuthenticator
; 
  47 import eu
.alefzero
.owncloud
.datamodel
.OCFile
; 
  48 import eu
.alefzero
.webdav
.HttpPropFind
; 
  49 import eu
.alefzero
.webdav
.TreeNode
; 
  50 import eu
.alefzero
.webdav
.TreeNode
.NodeProperty
; 
  51 import eu
.alefzero
.webdav
.WebdavClient
; 
  52 import eu
.alefzero
.webdav
.WebdavUtils
; 
  55  * Base SyncAdapter for OwnCloud Designed to be subclassed for the concrete 
  56  * SyncAdapter, like ConcatsSync, CalendarSync, FileSync etc.. 
  61 public abstract class AbstractOwnCloudSyncAdapter 
extends 
  62                 AbstractThreadedSyncAdapter 
{ 
  64         private AccountManager accountManager
; 
  65         private Account account
; 
  66         private ContentProviderClient contentProvider
; 
  67         private Date lastUpdated
; 
  69         private HttpHost mHost
; 
  70         private WebdavClient mClient 
= null
; 
  71         private static String TAG 
= "AbstractOwnCloudSyncAdapter"; 
  73         public AbstractOwnCloudSyncAdapter(Context context
, boolean autoInitialize
) { 
  74                 super(context
, autoInitialize
); 
  75                 this.setAccountManager(AccountManager
.get(context
)); 
  78         public AccountManager 
getAccountManager() { 
  79                 return accountManager
; 
  82         public void setAccountManager(AccountManager accountManager
) { 
  83                 this.accountManager 
= accountManager
; 
  86         public Account 
getAccount() { 
  90         public void setAccount(Account account
) { 
  91                 this.account 
= account
; 
  94         public ContentProviderClient 
getContentProvider() { 
  95                 return contentProvider
; 
  98         public void setContentProvider(ContentProviderClient contentProvider
) { 
  99                 this.contentProvider 
= contentProvider
; 
 102         public Date 
getLastUpdated() { 
 106         public void setLastUpdated(Date lastUpdated
) { 
 107                 this.lastUpdated 
= lastUpdated
; 
 110         protected ConnectionKeepAliveStrategy 
getKeepAliveStrategy() { 
 111                 return new ConnectionKeepAliveStrategy() { 
 112                         public long getKeepAliveDuration(HttpResponse response
, 
 113                                         HttpContext context
) { 
 114                                 // Change keep alive straategy basing on response: ie 
 115                                 // forbidden/not found/etc 
 116                                 // should have keep alive 0 
 117                                 // default return: 5s 
 118                                 int statusCode 
= response
.getStatusLine().getStatusCode(); 
 120                                 // HTTP 400, 500 Errors as well as HTTP 118 - Connection timed 
 122                                 if ((statusCode 
>= 400 && statusCode 
<= 418) 
 123                                                 || (statusCode 
>= 421 && statusCode 
<= 426) 
 124                                                 || (statusCode 
>= 500 && statusCode 
<= 510) 
 125                                                 || statusCode 
== 118) { 
 134         protected HttpPropFind 
getPropFindQuery() 
 135                         throws OperationCanceledException
, AuthenticatorException
, 
 137                 HttpPropFind query 
= new HttpPropFind(getUri().toString()); 
 138                 query
.setHeader("Content-type", "text/xml"); 
 139                 query
.setHeader("User-Agent", "Android-ownCloud"); 
 143         protected HttpResponse 
fireRawRequest(HttpRequest query
) 
 144                         throws ClientProtocolException
, OperationCanceledException
, 
 145                         AuthenticatorException
, IOException 
{ 
 146                 BasicHttpContext httpContext 
= new BasicHttpContext(); 
 147                 BasicScheme basicAuth 
= new BasicScheme(); 
 148                 httpContext
.setAttribute("preemptive-auth", basicAuth
); 
 150                 HttpResponse response 
= getClient().execute(mHost
, query
, httpContext
); 
 154         protected TreeNode 
fireRequest(HttpRequest query
) 
 155                         throws ClientProtocolException
, OperationCanceledException
, 
 156                         AuthenticatorException
, IOException 
{ 
 157                 HttpResponse response 
= fireRawRequest(query
); 
 159                 TreeNode root 
= new TreeNode(); 
 160                 root
.setProperty(TreeNode
.NodeProperty
.NAME
, ""); 
 161                 this.parseResponse(response
, getUri(), getClient(), mHost
, 
 162                                 root
.getChildList(), false
, 0); 
 166         protected Uri 
getUri() { 
 167                 return Uri
.parse(this.getAccountManager().getUserData(getAccount(), 
 168                                 AccountAuthenticator
.KEY_OC_URL
)); 
 171         private DefaultHttpClient 
getClient() throws OperationCanceledException
, 
 172                         AuthenticatorException
, IOException 
{ 
 173                 if (mClient 
== null
) { 
 174                         String username 
= getAccount().name
.split("@")[0]; 
 175                         String password 
= this.getAccountManager().blockingGetAuthToken( 
 176                                         getAccount(), AccountAuthenticator
.AUTH_TOKEN_TYPE
, true
); 
 177                         if (this.getAccountManager().getUserData(getAccount(), 
 178                                         AccountAuthenticator
.KEY_OC_URL
) == null
) { 
 179                                 throw new UnknownHostException(); 
 183                         mClient 
= new WebdavClient(uri
); 
 184                         mClient
.setCredentials(username
, password
); 
 185                         mClient
.allowUnsignedCertificates(); 
 186                         mHost 
= mClient
.getTargetHost(); 
 189                 return mClient
.getHttpClient(); 
 192         private void parseResponse(HttpResponse resp
, Uri uri
, 
 193                         DefaultHttpClient client
, HttpHost targetHost
, 
 194                         LinkedList
<TreeNode
> insertList
, boolean sf
, long parent_id
) 
 195                         throws IOException
, OperationCanceledException
, 
 196                         AuthenticatorException 
{ 
 197                 boolean skipFirst 
= sf
, override_parent 
= !sf
; 
 198                 for (TreeNode n 
: WebdavUtils
.parseResponseToNodes(resp
.getEntity() 
 204                         String path 
= n
.stripPathFromFilename(uri
.getPath()); 
 206                         long mod 
= n
.getProperty(NodeProperty
.LAST_MODIFIED_DATE
) == null ? 
0 
 208                                                         .getProperty(NodeProperty
.LAST_MODIFIED_DATE
)); 
 209                         OCFile file 
= new OCFile(getContentProvider(), getAccount(), 
 210                                         n
.getProperty(NodeProperty
.PATH
)); 
 211                         if (file
.fileExtist() && file
.getModificationTimestamp() >= mod
) { 
 212                                 Log
.d(TAG
, "No update for file/dir " + file
.getFileName() 
 215                                 Log
.d(TAG
, "File " + n
.getProperty(NodeProperty
.PATH
) 
 217                                                 + (file
.fileExtist() ? 
"updated" : "created")); 
 218                                 long len 
= n
.getProperty(NodeProperty
.CONTENT_LENGTH
) == null ? 
0 
 220                                                                 .getProperty(NodeProperty
.CONTENT_LENGTH
)); 
 221                                 long create 
= n
.getProperty(NodeProperty
.CREATE_DATE
) == null ? 
0 
 223                                                                 .getProperty(NodeProperty
.CREATE_DATE
)); 
 224                                 file 
= OCFile
.createNewFile(getContentProvider(), getAccount(), 
 225                                                 n
.getProperty(NodeProperty
.PATH
), len
, create
, mod
, 
 226                                                 n
.getProperty(NodeProperty
.RESOURCE_TYPE
), parent_id
); 
 228                                 if (override_parent
) { 
 229                                         parent_id 
= file
.getFileId(); 
 230                                         override_parent 
= false
; 
 234                         if (!TextUtils
.isEmpty(n
.getProperty(NodeProperty
.NAME
)) 
 235                                         && n
.getProperty(NodeProperty
.RESOURCE_TYPE
).equals("DIR")) { 
 237                                 HttpPropFind method 
= new HttpPropFind(uri
.getPath() + path
 
 238                                                 + n
.getProperty(NodeProperty
.NAME
).replace(" ", "%20") 
 240                                 HttpResponse response 
= fireRawRequest(method
); 
 241                                 parseResponse(response
, uri
, client
, targetHost
, 
 242                                                 n
.getChildList(), true
, file
.getFileId());