Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
e
os
Notes
Commits
a30b9887
Commit
a30b9887
authored
Jan 12, 2022
by
Michael Enoma
👽
Browse files
Server Resonse / Notes CLient
parent
bc62277a
Pipeline
#156968
failed with stage
in 2 minutes and 33 seconds
Changes
4
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
app/src/main/java/it/niedermann/owncloud/notes/importaccount/ImportAccountActivity.java
View file @
a30b9887
...
...
@@ -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
);
}
}
...
...
app/src/main/java/it/niedermann/owncloud/notes/shared/util/NotesClient.java
0 → 100644
View file @
a30b9887
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
);
}
}
app/src/main/java/it/niedermann/owncloud/notes/shared/util/NotesClientUtil.java
0 → 100644
View file @
a30b9887
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
;
}
}
}
app/src/main/java/it/niedermann/owncloud/notes/shared/util/ServerResponse.java
0 → 100644
View file @
a30b9887
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
);
}
}
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment