/** * Checks if the new name to set is valid in the file system * * <p>The only way to be sure is trying to create a file with that name. It's made in the temporal * directory for downloads, out of any account, and then removed. * * <p>IMPORTANT: The test must be made in the same file system where files are download. The * internal storage could be formatted with a different file system. * * <p>TODO move this method, and maybe FileDownload.get***Path(), to a class with utilities * specific for the interactions with the file system * * @return 'True' if a temporal file named with the name to set could be created in the file * system where local files are stored. * @throws IOException When the temporal folder can not be created. */ private boolean isValidNewName() throws IOException { // check tricky names if (mNewName == null || mNewName.length() <= 0 || mNewName.contains(File.separator) || mNewName.contains("%")) { return false; } // create a test file String tmpFolderName = FileStorageUtils.getTemporalPath(""); File testFile = new File(tmpFolderName + mNewName); File tmpFolder = testFile.getParentFile(); tmpFolder.mkdirs(); if (!tmpFolder.isDirectory()) { throw new IOException("Unexpected error: temporal directory could not be created"); } try { testFile .createNewFile(); // return value is ignored; it could be 'false' because the file already // existed, that doesn't invalidate the name } catch (IOException e) { Log_OC.i(TAG, "Test for validity of name " + mNewName + " in the file system failed"); return false; } boolean result = (testFile.exists() && testFile.isFile()); // cleaning ; result is ignored, since there is not much we could do in case of failure, but // repeat and repeat... testFile.delete(); return result; }
/** {@inheritDoc} */ @Override public Bundle addAccount( AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException { Log_OC.i(TAG, "Adding account with type " + accountType + " and auth token " + authTokenType); final Bundle bundle = new Bundle(); AccountManager accountManager = AccountManager.get(mContext); Account[] accounts = accountManager.getAccountsByType(MainApp.getAccountType()); if (mContext.getResources().getBoolean(R.bool.multiaccount_support) || accounts.length < 1) { try { validateAccountType(accountType); } catch (AuthenticatorException e) { Log_OC.e(TAG, "Failed to validate account type " + accountType + ": " + e.getMessage()); e.printStackTrace(); return e.getFailureBundle(); } final Intent intent = new Intent(mContext, AuthenticatorActivity.class); intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response); intent.putExtra(KEY_AUTH_TOKEN_TYPE, authTokenType); intent.putExtra(KEY_REQUIRED_FEATURES, requiredFeatures); intent.putExtra(KEY_LOGIN_OPTIONS, options); intent.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_CREATE); setIntentFlags(intent); bundle.putParcelable(AccountManager.KEY_INTENT, intent); } else { // Return an error bundle.putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION); final String message = String.format( mContext.getString(R.string.auth_unsupported_multiaccount), mContext.getString(R.string.app_name)); bundle.putString(AccountManager.KEY_ERROR_MESSAGE, message); mHandler.post( new Runnable() { @Override public void run() { Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show(); } }); } return bundle; }
/** Service initialization */ @Override public void onCreate() { super.onCreate(); Log_OC.i(TAG, "mPendingUploads size:" + mPendingUploads.size()); mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); HandlerThread thread = new HandlerThread("FileUploaderThread", Process.THREAD_PRIORITY_BACKGROUND); thread.start(); mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper, this); mBinder = new FileUploaderBinder(); }
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().requestFeature(Window.FEATURE_NO_TITLE); mParents = new Stack<String>(); mParents.add(""); if (prepareStreamsToUpload()) { mAccountManager = (AccountManager) getSystemService(Context.ACCOUNT_SERVICE); Account[] accounts = mAccountManager.getAccountsByType(MainApp.getAccountType()); if (accounts.length == 0) { Log_OC.i(TAG, "No ownCloud account is available"); showDialog(DIALOG_NO_ACCOUNT); } else if (accounts.length > 1) { Log_OC.i(TAG, "More then one ownCloud is available"); showDialog(DIALOG_MULTIPLE_ACCOUNT); } else { mAccount = accounts[0]; mStorageManager = new FileDataStorageManager(mAccount, getContentResolver()); populateDirectoryList(); } } else { showDialog(DIALOG_NO_STREAM); } }
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); Log_OC.i(TAG, "result received. req: " + requestCode + " res: " + resultCode); if (requestCode == REQUEST_CODE_SETUP_ACCOUNT) { dismissDialog(DIALOG_NO_ACCOUNT); if (resultCode == RESULT_CANCELED) { finish(); } Account[] accounts = mAccountManager.getAccountsByType(MainApp.getAuthTokenType()); if (accounts.length == 0) { showDialog(DIALOG_NO_ACCOUNT); } else { // there is no need for checking for is there more then one // account at this point // since account setup can set only one account at time mAccount = accounts[0]; populateDirectoryList(); } } }
/** * Core upload method: sends the file(s) to upload * * @param uploadKey Key to access the upload to perform, contained in mPendingUploads */ public void uploadFile(String uploadKey) { synchronized (mPendingUploads) { mCurrentUpload = mPendingUploads.get(uploadKey); } if (mCurrentUpload != null) { notifyUploadStart(mCurrentUpload); RemoteOperationResult uploadResult = null, grantResult = null; try { /// prepare client object to send requests to the ownCloud server if (mUploadClient == null || !mLastAccount.equals(mCurrentUpload.getAccount())) { mLastAccount = mCurrentUpload.getAccount(); mStorageManager = new FileDataStorageManager(mLastAccount, getContentResolver()); mUploadClient = OwnCloudClientFactory.createOwnCloudClient(mLastAccount, getApplicationContext()); } /// check the existence of the parent folder for the file to upload String remoteParentPath = new File(mCurrentUpload.getRemotePath()).getParent(); remoteParentPath = remoteParentPath.endsWith(OCFile.PATH_SEPARATOR) ? remoteParentPath : remoteParentPath + OCFile.PATH_SEPARATOR; grantResult = grantFolderExistence(remoteParentPath); /// perform the upload if (grantResult.isSuccess()) { OCFile parent = mStorageManager.getFileByPath(remoteParentPath); mCurrentUpload.getFile().setParentId(parent.getFileId()); uploadResult = mCurrentUpload.execute(mUploadClient); if (uploadResult.isSuccess()) { saveUploadedFile(); } } else { uploadResult = grantResult; } } catch (AccountsException e) { Log_OC.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e); uploadResult = new RemoteOperationResult(e); } catch (IOException e) { Log_OC.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e); uploadResult = new RemoteOperationResult(e); } finally { synchronized (mPendingUploads) { mPendingUploads.remove(uploadKey); Log_OC.i(TAG, "Remove CurrentUploadItem from pending upload Item Map."); } if (uploadResult.isException()) { // enforce the creation of a new client object for next uploads; this grant that a new // socket will // be created in the future if the current exception is due to an abrupt lose of network // connection mUploadClient = null; } } /// notify result notifyUploadResult(uploadResult, mCurrentUpload); sendFinalBroadcast(mCurrentUpload, uploadResult); } }
/** * Entry point to add one or several files to the queue of uploads. * * <p>New uploads are added calling to startService(), resulting in a call to this method. This * ensures the service will keep on working although the caller activity goes away. */ @Override public int onStartCommand(Intent intent, int flags, int startId) { if (!intent.hasExtra(KEY_ACCOUNT) || !intent.hasExtra(KEY_UPLOAD_TYPE) || !(intent.hasExtra(KEY_LOCAL_FILE) || intent.hasExtra(KEY_FILE))) { Log_OC.e(TAG, "Not enough information provided in intent"); return Service.START_NOT_STICKY; } int uploadType = intent.getIntExtra(KEY_UPLOAD_TYPE, -1); if (uploadType == -1) { Log_OC.e(TAG, "Incorrect upload type provided"); return Service.START_NOT_STICKY; } Account account = intent.getParcelableExtra(KEY_ACCOUNT); String[] localPaths = null, remotePaths = null, mimeTypes = null; OCFile[] files = null; if (uploadType == UPLOAD_SINGLE_FILE) { if (intent.hasExtra(KEY_FILE)) { files = new OCFile[] {intent.getParcelableExtra(KEY_FILE)}; } else { localPaths = new String[] {intent.getStringExtra(KEY_LOCAL_FILE)}; remotePaths = new String[] {intent.getStringExtra(KEY_REMOTE_FILE)}; mimeTypes = new String[] {intent.getStringExtra(KEY_MIME_TYPE)}; } } else { // mUploadType == UPLOAD_MULTIPLE_FILES if (intent.hasExtra(KEY_FILE)) { files = (OCFile[]) intent.getParcelableArrayExtra(KEY_FILE); // TODO // will // this // casting // work // fine? } else { localPaths = intent.getStringArrayExtra(KEY_LOCAL_FILE); remotePaths = intent.getStringArrayExtra(KEY_REMOTE_FILE); mimeTypes = intent.getStringArrayExtra(KEY_MIME_TYPE); } } FileDataStorageManager storageManager = new FileDataStorageManager(account, getContentResolver()); boolean forceOverwrite = intent.getBooleanExtra(KEY_FORCE_OVERWRITE, false); boolean isInstant = intent.getBooleanExtra(KEY_INSTANT_UPLOAD, false); int localAction = intent.getIntExtra(KEY_LOCAL_BEHAVIOUR, LOCAL_BEHAVIOUR_COPY); if (intent.hasExtra(KEY_FILE) && files == null) { Log_OC.e(TAG, "Incorrect array for OCFiles provided in upload intent"); return Service.START_NOT_STICKY; } else if (!intent.hasExtra(KEY_FILE)) { if (localPaths == null) { Log_OC.e(TAG, "Incorrect array for local paths provided in upload intent"); return Service.START_NOT_STICKY; } if (remotePaths == null) { Log_OC.e(TAG, "Incorrect array for remote paths provided in upload intent"); return Service.START_NOT_STICKY; } if (localPaths.length != remotePaths.length) { Log_OC.e(TAG, "Different number of remote paths and local paths!"); return Service.START_NOT_STICKY; } files = new OCFile[localPaths.length]; for (int i = 0; i < localPaths.length; i++) { files[i] = obtainNewOCFileToUpload( remotePaths[i], localPaths[i], ((mimeTypes != null) ? mimeTypes[i] : (String) null), storageManager); if (files[i] == null) { // TODO @andomaex add failure Notification return Service.START_NOT_STICKY; } } } AccountManager aMgr = AccountManager.get(this); String version = aMgr.getUserData(account, OwnCloudAccount.Constants.KEY_OC_VERSION); String versionString = aMgr.getUserData(account, OwnCloudAccount.Constants.KEY_OC_VERSION_STRING); OwnCloudVersion ocv = new OwnCloudVersion(version, versionString); boolean chunked = FileUploader.chunkedUploadIsSupported(ocv); AbstractList<String> requestedUploads = new Vector<String>(); String uploadKey = null; UploadFileOperation newUpload = null; try { for (int i = 0; i < files.length; i++) { uploadKey = buildRemoteName(account, files[i].getRemotePath()); newUpload = new UploadFileOperation( account, files[i], chunked, isInstant, forceOverwrite, localAction, getApplicationContext()); if (isInstant) { newUpload.setRemoteFolderToBeCreated(); } mPendingUploads.putIfAbsent( uploadKey, newUpload); // Grants that the file only upload once time newUpload.addDatatransferProgressListener(this); newUpload.addDatatransferProgressListener((FileUploaderBinder) mBinder); requestedUploads.add(uploadKey); } } catch (IllegalArgumentException e) { Log_OC.e(TAG, "Not enough information provided in intent: " + e.getMessage()); return START_NOT_STICKY; } catch (IllegalStateException e) { Log_OC.e(TAG, "Bad information provided in intent: " + e.getMessage()); return START_NOT_STICKY; } catch (Exception e) { Log_OC.e(TAG, "Unexpected exception while processing upload intent", e); return START_NOT_STICKY; } if (requestedUploads.size() > 0) { Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; msg.obj = requestedUploads; mServiceHandler.sendMessage(msg); } Log_OC.i(TAG, "mPendingUploads size:" + mPendingUploads.size()); return Service.START_NOT_STICKY; }