Commit a30b9887 authored by Michael Enoma's avatar Michael Enoma 👽
Browse files

Server Resonse / Notes CLient

parent bc62277a
Pipeline #156968 failed with stage
in 2 minutes and 33 seconds
......@@ -41,6 +41,66 @@ import it.niedermann.owncloud.notes.persistence.entity.Account;
import it.niedermann.owncloud.notes.shared.model.Capabilities;
import it.niedermann.owncloud.notes.shared.model.IResponseCallback;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.http.SslCertificate;
import android.net.http.SslError;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.webkit.SslErrorHandler;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import foundation.e.cert4android.CustomCertManager;
import foundation.e.cert4android.IOnCertificateDecision;
import butterknife.BindView;
import butterknife.ButterKnife;
import foundation.e.notes.R;
import foundation.e.notes.persistence.NoteSQLiteOpenHelper;
import foundation.e.notes.persistence.NoteServerSyncHelper;
import foundation.e.notes.util.ExceptionHandler;
import foundation.e.notes.util.NotesClientUtil;
import foundation.e.notes.util.NotesClientUtil.LoginStatus;
import androidx.annotation.ColorInt;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.textfield.TextInputLayout;
import java.io.ByteArrayInputStream;
import java.net.URLDecoder;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import static android.os.Process.killProcess;
import static android.os.Process.myPid;
public class ImportAccountActivity extends AppCompatActivity {
......@@ -111,6 +171,12 @@ public class ImportAccountActivity extends AppCompatActivity {
}
});
binding.addButton1.setOnClickListener((v) -> {
binding.addButton1.setEnabled(false);
binding.status.setVisibility(View.GONE);
}
}
......
package it.niedermann.owncloud.notes.shared.util;
import androidx.annotation.WorkerThread;
import android.util.Base64;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import foundation.e.cert4android.CustomCertManager;
import foundation.e.notes.model.CloudNote;
import foundation.e.notes.BuildConfig;
import foundation.e.notes.model.CloudNote;
import foundation.e.notes.util.ServerResponse.NoteResponse;
import foundation.e.notes.util.ServerResponse.NotesResponse;
@WorkerThread
public class NotesClient {
/**
* This entity class is used to return relevant data of the HTTP reponse.
*/
public static class ResponseData {
private final String content;
private final String etag;
private final long lastModified;
public ResponseData(String content, String etag, long lastModified) {
this.content = content;
this.etag = etag;
this.lastModified = lastModified;
}
public String getContent() {
return content;
}
public String getETag() {
return etag;
}
public long getLastModified() {
return lastModified;
}
}
public static final String METHOD_GET = "GET";
public static final String METHOD_PUT = "PUT";
public static final String METHOD_POST = "POST";
public static final String METHOD_DELETE = "DELETE";
public static final String JSON_ID = "id";
public static final String JSON_TITLE = "title";
public static final String JSON_CONTENT = "content";
public static final String JSON_FAVORITE = "favorite";
public static final String JSON_CATEGORY = "category";
public static final String JSON_ETAG = "etag";
public static final String JSON_MODIFIED = "modified";
private static final String application_json = "application/json";
private String url = "";
private String username = "";
private String password = "";
public NotesClient(String url, String username, String password) {
this.url = url;
this.username = username;
this.password = password;
}
public NotesResponse getNotes(CustomCertManager ccm, long lastModified, String lastETag) throws JSONException, IOException {
String url = "notes";
if (lastModified > 0) {
url += "?pruneBefore=" + lastModified;
}
return new NotesResponse(requestServer(ccm, url, METHOD_GET, null, lastETag));
}
/**
* Fetches a Note by ID from Server
*
* @param id long - ID of the wanted note
* @return Requested Note
* @throws JSONException
* @throws IOException
*/
@SuppressWarnings("unused")
public NoteResponse getNoteById(CustomCertManager ccm, long id) throws JSONException, IOException {
return new NoteResponse(requestServer(ccm, "notes/" + id, METHOD_GET, null, null));
}
private NoteResponse putNote(CustomCertManager ccm, CloudNote note, String path, String method) throws JSONException, IOException {
JSONObject paramObject = new JSONObject();
paramObject.accumulate(JSON_CONTENT, note.getContent());
paramObject.accumulate(JSON_MODIFIED, note.getModified().getTimeInMillis() / 1000);
paramObject.accumulate(JSON_FAVORITE, note.isFavorite());
paramObject.accumulate(JSON_CATEGORY, note.getCategory());
return new NoteResponse(requestServer(ccm, path, method, paramObject, null));
}
/**
* Creates a Note on the Server
*
* @param note {@link CloudNote} - the new Note
* @return Created Note including generated Title, ID and lastModified-Date
* @throws JSONException
* @throws IOException
*/
public NoteResponse createNote(CustomCertManager ccm, CloudNote note) throws JSONException, IOException {
return putNote(ccm, note, "notes", METHOD_POST);
}
public NoteResponse editNote(CustomCertManager ccm, CloudNote note) throws JSONException, IOException {
return putNote(ccm, note, "notes/" + note.getRemoteId(), METHOD_PUT);
}
public void deleteNote(CustomCertManager ccm, long noteId) throws IOException {
this.requestServer(ccm, "notes/" + noteId, METHOD_DELETE, null, null);
}
/**
* Request-Method for POST, PUT with or without JSON-Object-Parameter
*
* @param target Filepath to the wanted function
* @param method GET, POST, DELETE or PUT
* @param params JSON Object which shall be transferred to the server.
* @return Body of answer
* @throws MalformedURLException
* @throws IOException
*/
private ResponseData requestServer(CustomCertManager ccm, String target, String method, JSONObject params, String lastETag)
throws IOException {
StringBuffer result = new StringBuffer();
// setup connection
String targetURL = url + "index.php/apps/notes/api/v0.2/" + target;
HttpURLConnection con = SupportUtil.getHttpURLConnection(ccm, targetURL);
con.setRequestMethod(method);
con.setRequestProperty(
"Authorization",
"Basic " + Base64.encodeToString((username + ":" + password).getBytes(), Base64.NO_WRAP));
// https://github.com/square/retrofit/issues/805#issuecomment-93426183
con.setRequestProperty( "Connection", "Close");
con.setRequestProperty("User-Agent", "nextcloud-notes/" + BuildConfig.VERSION_NAME + " (Android)");
if (lastETag != null && METHOD_GET.equals(method)) {
con.setRequestProperty("If-None-Match", lastETag);
}
con.setConnectTimeout(10 * 1000); // 10 seconds
Log.d(getClass().getSimpleName(), method + " " + targetURL);
// send request data (optional)
byte[] paramData = null;
if (params != null) {
paramData = params.toString().getBytes();
Log.d(getClass().getSimpleName(), "Params: " + params);
con.setFixedLengthStreamingMode(paramData.length);
con.setRequestProperty("Content-Type", application_json);
con.setDoOutput(true);
OutputStream os = con.getOutputStream();
os.write(paramData);
os.flush();
os.close();
}
// read response data
int responseCode = con.getResponseCode();
Log.d(getClass().getSimpleName(), "HTTP response code: " + responseCode);
if (responseCode == HttpURLConnection.HTTP_NOT_MODIFIED) {
throw new ServerResponse.NotModifiedException();
}
BufferedReader rd = new BufferedReader(new InputStreamReader(con.getInputStream()));
String line;
while ((line = rd.readLine()) != null) {
result.append(line);
}
// create response object
String etag = con.getHeaderField("ETag");
long lastModified = con.getHeaderFieldDate("Last-Modified", 0) / 1000;
Log.i(getClass().getSimpleName(), "Result length: " + result.length() + (paramData == null ? "" : "; Request length: " + paramData.length));
Log.d(getClass().getSimpleName(), "ETag: " + etag + "; Last-Modified: " + lastModified + " (" + con.getHeaderField("Last-Modified") + ")");
// return these header fields since they should only be saved after successful processing the result!
return new ResponseData(result.toString(), etag, lastModified);
}
}
package it.niedermann.owncloud.notes.shared.util;
import androidx.annotation.StringRes;
import android.util.Base64;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import foundation.e.cert4android.CustomCertManager;
import foundation.e.notes.R;
/**
* Utils for Validation etc
* Created by stefan on 25.09.15.
*/
public class NotesClientUtil {
public enum LoginStatus {
OK(0),
AUTH_FAILED(R.string.error_username_password_invalid),
CONNECTION_FAILED(R.string.error_io),
NO_NETWORK(R.string.error_no_network),
JSON_FAILED(R.string.error_json),
SERVER_FAILED(R.string.error_server);
@StringRes
public final int str;
LoginStatus(@StringRes int str) {
this.str = str;
}
}
/**
* Checks if the given url String starts with http:// or https://
*
* @param url String
* @return true, if the given String is only http
*/
public static boolean isHttp(String url) {
return url != null && url.length() > 4 && url.startsWith("http") && url.charAt(4) != 's';
}
/**
* Strips the api part from the path of a given url, handles trailing slash and missing protocol
*
* @param url String
* @return formatted URL
*/
public static String formatURL(String url) {
if (!url.endsWith("/")) {
url += "/";
}
if (!url.startsWith("http://") && !url.startsWith("https://")) {
url = "https://" + url;
}
String[] replacements = new String[]{"notes/", "v0.2/", "api/", "notes/", "apps/", "index.php/"};
for (String replacement : replacements) {
if (url.endsWith(replacement)) {
url = url.substring(0, url.length() - replacement.length());
}
}
return url;
}
/**
* @param url String
* @param username String
* @param password String
* @return Username and Password are a valid Login-Combination for the given URL.
*/
public static LoginStatus isValidLogin(CustomCertManager ccm, String url, String username, String password) {
try {
String targetURL = url + "index.php/apps/notes/api/v0.2/notes";
HttpURLConnection con = SupportUtil.getHttpURLConnection(ccm, targetURL);
con.setRequestMethod("GET");
con.setRequestProperty(
"Authorization",
"Basic "
+ new String(Base64.encode((username + ":"
+ password).getBytes(), Base64.NO_WRAP)));
con.setConnectTimeout(10 * 1000); // 10 seconds
con.connect();
Log.v(NotesClientUtil.class.getSimpleName(), "Establishing connection to server");
if (con.getResponseCode() == 200) {
Log.v(NotesClientUtil.class.getSimpleName(), "" + con.getResponseMessage());
StringBuilder result = new StringBuilder();
BufferedReader rd = new BufferedReader(new InputStreamReader(con.getInputStream()));
String line;
while ((line = rd.readLine()) != null) {
result.append(line);
}
Log.v(NotesClientUtil.class.getSimpleName(), result.toString());
new JSONArray(result.toString());
return LoginStatus.OK;
} else if (con.getResponseCode() >= 401 && con.getResponseCode() <= 403) {
return LoginStatus.AUTH_FAILED;
} else {
return LoginStatus.SERVER_FAILED;
}
} catch (MalformedURLException | SocketTimeoutException e) {
Log.e(NotesClientUtil.class.getSimpleName(), "Exception", e);
return LoginStatus.CONNECTION_FAILED;
} catch (IOException e) {
Log.e(NotesClientUtil.class.getSimpleName(), "Exception", e);
return LoginStatus.CONNECTION_FAILED;
} catch (JSONException e) {
Log.e(NotesClientUtil.class.getSimpleName(), "Exception", e);
return LoginStatus.JSON_FAILED;
}
}
/**
* Pings a server and checks if there is a installed ownCloud instance
*
* @param url String URL to server
* @return true if there is a installed instance, false if not
*/
public static boolean isValidURL(CustomCertManager ccm, String url) {
StringBuilder result = new StringBuilder();
try {
HttpURLConnection con = SupportUtil.getHttpURLConnection(ccm, url + "status.php");
con.setRequestMethod(NotesClient.METHOD_GET);
con.setConnectTimeout(10 * 1000); // 10 seconds
BufferedReader rd = new BufferedReader(new InputStreamReader(con.getInputStream()));
String line;
while ((line = rd.readLine()) != null) {
result.append(line);
}
JSONObject response = new JSONObject(result.toString());
return response.getBoolean("installed");
} catch (IOException | JSONException | NullPointerException e) {
return false;
}
}
}
package it.niedermann.owncloud.notes.shared.util;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import foundation.e.notes.model.CloudNote;
import foundation.e.notes.model.CloudNote;
/**
* Provides entity classes for handling server responses with a single note ({@link NoteResponse}) or a list of notes ({@link NotesResponse}).
*/
public class ServerResponse {
public static class NotModifiedException extends IOException {
}
public static class NoteResponse extends ServerResponse {
public NoteResponse(NotesClient.ResponseData response) {
super(response);
}
public CloudNote getNote() throws JSONException {
return getNoteFromJSON(new JSONObject(getContent()));
}
}
public static class NotesResponse extends ServerResponse {
public NotesResponse(NotesClient.ResponseData response) {
super(response);
}
public List<CloudNote> getNotes() throws JSONException {
List<CloudNote> notesList = new ArrayList<>();
JSONArray notes = new JSONArray(getContent());
for (int i = 0; i < notes.length(); i++) {
JSONObject json = notes.getJSONObject(i);
notesList.add(getNoteFromJSON(json));
}
return notesList;
}
}
private final NotesClient.ResponseData response;
public ServerResponse(NotesClient.ResponseData response) {
this.response = response;
}
protected String getContent() {
return response.getContent();
}
public String getETag() {
return response.getETag();
}
public long getLastModified() {
return response.getLastModified();
}
protected CloudNote getNoteFromJSON(JSONObject json) throws JSONException {
long id = 0;
String title = "";
String content = "";
Calendar modified = null;
boolean favorite = false;
String category = null;
String etag = null;
if (!json.isNull(NotesClient.JSON_ID)) {
id = json.getLong(NotesClient.JSON_ID);
}
if (!json.isNull(NotesClient.JSON_TITLE)) {
title = json.getString(NotesClient.JSON_TITLE);
}
if (!json.isNull(NotesClient.JSON_CONTENT)) {
content = json.getString(NotesClient.JSON_CONTENT);
}
if (!json.isNull(NotesClient.JSON_MODIFIED)) {
modified = GregorianCalendar.getInstance();
modified.setTimeInMillis(json.getLong(NotesClient.JSON_MODIFIED) * 1000);
}
if (!json.isNull(NotesClient.JSON_FAVORITE)) {
favorite = json.getBoolean(NotesClient.JSON_FAVORITE);
}
if (!json.isNull(NotesClient.JSON_CATEGORY)) {
category = json.getString(NotesClient.JSON_CATEGORY);
}
if (!json.isNull(NotesClient.JSON_ETAG)) {
etag = json.getString(NotesClient.JSON_ETAG);
}
return new CloudNote(id, modified, title, content, favorite, category, etag);
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment