mirror of
https://gitlab.com/biskuteri-cafe/JKomasto2.git
synced 2024-11-20 05:04:51 +01:00
e6fea4c061
(Before this, JKomasto and sometimes the Mastodon web client would get '411 Record Not Found' when submitting the same text after deleting and redrafting. Presumably the Mastodon server caches both whether an idempotency key was fulfilled and which post it leads to, and for some reason it looks up the second and fails.)
779 lines
24 KiB
Java
779 lines
24 KiB
Java
|
|
import cafe.biskuteri.hinoki.Tree;
|
|
import cafe.biskuteri.hinoki.JsonConverter;
|
|
import cafe.biskuteri.hinoki.DSVTokeniser;
|
|
import java.net.URL;
|
|
import java.net.URLConnection;
|
|
import java.net.HttpURLConnection;
|
|
import java.net.URI;
|
|
import java.net.URISyntaxException;
|
|
import java.net.URLEncoder;
|
|
import java.net.SocketTimeoutException;
|
|
import java.io.Reader;
|
|
import java.io.Writer;
|
|
import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
import java.io.InputStreamReader;
|
|
import java.io.OutputStreamWriter;
|
|
import java.io.File;
|
|
import java.io.FileReader;
|
|
import java.io.FileWriter;
|
|
import java.io.FileInputStream;
|
|
import java.io.BufferedReader;
|
|
import java.io.IOException;
|
|
import java.io.UnsupportedEncodingException;
|
|
|
|
class
|
|
MastodonApi {
|
|
|
|
private String
|
|
instanceUrl;
|
|
|
|
private Tree<String>
|
|
appCredentials,
|
|
accessToken,
|
|
accountDetails;
|
|
|
|
// - -%- -
|
|
|
|
private static final String
|
|
SCOPES = "read+write";
|
|
|
|
// ---%-@-%---
|
|
|
|
public String
|
|
getInstanceUrl() { return instanceUrl; }
|
|
|
|
public Tree<String>
|
|
getAppCredentials() { return appCredentials; }
|
|
|
|
public Tree<String>
|
|
getAccessToken() { return accessToken; }
|
|
|
|
public Tree<String>
|
|
getAccountDetails() { return accountDetails; }
|
|
|
|
|
|
public void
|
|
setInstanceUrl(String a) { instanceUrl = a; }
|
|
|
|
public void
|
|
setAppCredentials(Tree<String> a) { appCredentials = a; }
|
|
|
|
public void
|
|
setAccessToken(Tree<String> a) { accessToken = a; }
|
|
|
|
public void
|
|
setAccountDetails(Tree<String> a) { accountDetails = a; }
|
|
|
|
|
|
public void
|
|
testUrlConnection(String url, RequestListener handler)
|
|
{
|
|
try
|
|
{
|
|
URL endpoint = new URL(url);
|
|
HttpURLConnection conn = cast(endpoint.openConnection());
|
|
conn.connect();
|
|
|
|
wrapResponseInTree(conn, handler);
|
|
}
|
|
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
|
}
|
|
|
|
|
|
public void
|
|
getAppCredentials(RequestListener handler)
|
|
{
|
|
assert instanceUrl != null;
|
|
try
|
|
{
|
|
URL endpoint = new URL(instanceUrl + "/api/v1/apps");
|
|
HttpURLConnection conn = cast(endpoint.openConnection());
|
|
conn.setRequestMethod("POST");
|
|
conn.setDoOutput(true);
|
|
conn.connect();
|
|
|
|
Writer output = owriter(conn.getOutputStream());
|
|
output.write("client_name=JKomasto alpha");
|
|
output.write("&redirect_uris=urn:ietf:wg:oauth:2.0:oob");
|
|
output.write("&scopes=" + SCOPES);
|
|
output.close();
|
|
|
|
doStandardJsonReturn(conn, handler);
|
|
}
|
|
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
|
}
|
|
|
|
public URI
|
|
getAuthorisationURL()
|
|
{
|
|
assert instanceUrl != null;
|
|
assert appCredentials != null;
|
|
String clientId = appCredentials.get("client_id").value;
|
|
|
|
try
|
|
{
|
|
StringBuilder b = new StringBuilder();
|
|
b.append(instanceUrl);
|
|
b.append("/oauth/authorize");
|
|
// Be careful of the spelling!!
|
|
b.append("?response_type=code");
|
|
b.append("&redirect_uri=urn:ietf:wg:oauth:2.0:oob");
|
|
b.append("&scope=" + SCOPES);
|
|
b.append("&client_id=" + clientId);
|
|
|
|
return new URI(b.toString());
|
|
}
|
|
catch (URISyntaxException eUs) { assert false; return null; }
|
|
}
|
|
|
|
public void
|
|
getAccessToken(String authorisationCode, RequestListener handler)
|
|
{
|
|
assert instanceUrl != null;
|
|
assert appCredentials != null;
|
|
String id = appCredentials.get("client_id").value;
|
|
String secret = appCredentials.get("client_secret").value;
|
|
|
|
try
|
|
{
|
|
URL endpoint = new URL(instanceUrl + "/oauth/token");
|
|
HttpURLConnection conn = cast(endpoint.openConnection());
|
|
conn.setRequestMethod("POST");
|
|
conn.setDoOutput(true);
|
|
conn.connect();
|
|
|
|
Writer output = owriter(conn.getOutputStream());
|
|
output.write("client_id=" + id);
|
|
output.write("&client_secret=" + secret);
|
|
output.write("&redirect_uri=urn:ietf:wg:oauth:2.0:oob");
|
|
output.write("&grant_type=authorization_code");
|
|
output.write("&scope=" + SCOPES);
|
|
output.write("&code=" + authorisationCode);
|
|
output.close();
|
|
|
|
doStandardJsonReturn(conn, handler);
|
|
}
|
|
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
|
}
|
|
|
|
public void
|
|
getAccountDetails(RequestListener handler)
|
|
{
|
|
assert accessToken != null;
|
|
String token = accessToken.get("access_token").value;
|
|
|
|
try
|
|
{
|
|
String s = "/api/v1/accounts/verify_credentials";
|
|
URL endpoint = new URL(instanceUrl + s);
|
|
HttpURLConnection conn = cast(endpoint.openConnection());
|
|
String s2 = "Bearer " + token;
|
|
conn.setRequestProperty("Authorization", s2);
|
|
conn.connect();
|
|
|
|
doStandardJsonReturn(conn, handler);
|
|
}
|
|
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
|
}
|
|
|
|
public void
|
|
getTimelinePage(
|
|
TimelineType type,
|
|
int count, String maxId, String minId,
|
|
String accountId, String listId,
|
|
RequestListener handler)
|
|
{
|
|
String token = accessToken.get("access_token").value;
|
|
|
|
assert !(accountId != null && listId != null);
|
|
|
|
String url = instanceUrl + "/api/v1";
|
|
if (accountId != null)
|
|
{
|
|
url += "/accounts/" + accountId + "/statuses";
|
|
}
|
|
else if (listId != null)
|
|
{
|
|
url += "/lists/" + listId;
|
|
}
|
|
else switch (type)
|
|
{
|
|
case FEDERATED:
|
|
case LOCAL: url += "/timelines/public"; break;
|
|
case HOME: url += "/timelines/home"; break;
|
|
default: assert false;
|
|
}
|
|
url += "?limit=" + count;
|
|
if (maxId != null) url += "&max_id=" + maxId;
|
|
if (minId != null) url += "&min_id=" + minId;
|
|
// This is a GET endpoint, it rejects receiving
|
|
// query params through the body.
|
|
|
|
try
|
|
{
|
|
URL endpoint = new URL(url);
|
|
HttpURLConnection conn = cast(endpoint.openConnection());
|
|
String s2 = "Bearer " + token;
|
|
conn.setRequestProperty("Authorization", s2);
|
|
conn.connect();
|
|
|
|
doStandardJsonReturn(conn, handler);
|
|
}
|
|
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
|
}
|
|
|
|
public void
|
|
setPostFavourited(
|
|
String postId, boolean favourited,
|
|
RequestListener handler)
|
|
{
|
|
String token = accessToken.get("access_token").value;
|
|
|
|
String s1 = "/api/v1/statuses/" + postId;
|
|
String s2 = favourited ? "/favourite" : "/unfavourite";
|
|
String url = instanceUrl + s1 + s2;
|
|
try
|
|
{
|
|
URL endpoint = new URL(url);
|
|
HttpURLConnection conn = cast(endpoint.openConnection());
|
|
String s3 = "Bearer " + token;
|
|
conn.setRequestProperty("Authorization", s3);
|
|
conn.setRequestMethod("POST");
|
|
conn.connect();
|
|
|
|
doStandardJsonReturn(conn, handler);
|
|
}
|
|
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
|
}
|
|
|
|
public void
|
|
setPostBoosted(
|
|
String postId, boolean boosted,
|
|
RequestListener handler)
|
|
{
|
|
String token = accessToken.get("access_token").value;
|
|
|
|
String s1 = "/api/v1/statuses/" + postId;
|
|
String s2 = boosted ? "/reblog" : "/unreblog";
|
|
String url = instanceUrl + s1 + s2;
|
|
try
|
|
{
|
|
URL endpoint = new URL(url);
|
|
HttpURLConnection conn = cast(endpoint.openConnection());
|
|
String s3 = "Bearer " + token;
|
|
conn.setRequestProperty("Authorization", s3);
|
|
conn.setRequestMethod("POST");
|
|
conn.connect();
|
|
|
|
doStandardJsonReturn(conn, handler);
|
|
}
|
|
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
|
}
|
|
|
|
public void
|
|
submit(
|
|
String text, PostVisibility visibility,
|
|
String replyTo, String contentWarning,
|
|
RequestListener handler)
|
|
{
|
|
String token = accessToken.get("access_token").value;
|
|
|
|
String visibilityParam = "direct";
|
|
switch (visibility)
|
|
{
|
|
case PUBLIC: visibilityParam = "public"; break;
|
|
case UNLISTED: visibilityParam = "unlisted"; break;
|
|
case FOLLOWERS: visibilityParam = "private"; break;
|
|
case MENTIONED: visibilityParam = "direct"; break;
|
|
default: assert false;
|
|
}
|
|
|
|
String url = instanceUrl + "/api/v1/statuses";
|
|
try
|
|
{
|
|
text = encode(text);
|
|
contentWarning = encode(contentWarning);
|
|
|
|
URL endpoint = new URL(url);
|
|
HttpURLConnection conn = cast(endpoint.openConnection());
|
|
String s1 = "Bearer " + token;
|
|
conn.setRequestProperty("Authorization", s1);
|
|
String s2 = Integer.toString(handler.hashCode());
|
|
conn.setRequestProperty("Idempotency-Key", s2);
|
|
conn.setDoOutput(true);
|
|
conn.setRequestMethod("POST");
|
|
conn.connect();
|
|
|
|
Writer output = owriter(conn.getOutputStream());
|
|
output.write("status=" + text);
|
|
output.write("&visibility=" + visibilityParam);
|
|
if (replyTo != null) {
|
|
output.write("&in_reply_to_id=" + replyTo);
|
|
}
|
|
if (contentWarning != null) {
|
|
output.write("&spoiler_text=" + contentWarning);
|
|
}
|
|
output.close();
|
|
|
|
doStandardJsonReturn(conn, handler);
|
|
}
|
|
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
|
}
|
|
|
|
public void
|
|
getNotifications(
|
|
int count, String maxId, String minId,
|
|
RequestListener handler)
|
|
{
|
|
String token = accessToken.get("access_token").value;
|
|
|
|
String url = instanceUrl + "/api/v1/notifications";
|
|
url += "?limit=" + count;
|
|
if (maxId != null) url += "&max_id=" + maxId;
|
|
if (minId != null) url += "&min_id=" + minId;
|
|
|
|
try
|
|
{
|
|
URL endpoint = new URL(url);
|
|
HttpURLConnection conn = cast(endpoint.openConnection());
|
|
String s1 = "Bearer " + token;
|
|
conn.setRequestProperty("Authorization", s1);
|
|
conn.connect();
|
|
|
|
doStandardJsonReturn(conn, handler);
|
|
}
|
|
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
|
}
|
|
|
|
public void
|
|
deletePost(String postId, RequestListener handler)
|
|
{
|
|
String token = accessToken.get("access_token").value;
|
|
|
|
String url = instanceUrl + "/api/v1/statuses/" + postId;
|
|
try
|
|
{
|
|
URL endpoint = new URL(url);
|
|
HttpURLConnection conn = cast(endpoint.openConnection());
|
|
String s1 = "Bearer " + token;
|
|
conn.setRequestProperty("Authorization", s1);
|
|
conn.setRequestMethod("DELETE");
|
|
conn.connect();
|
|
|
|
doStandardJsonReturn(conn, handler);
|
|
}
|
|
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
|
}
|
|
|
|
public void
|
|
getSpecificPost(String postId, RequestListener handler)
|
|
{
|
|
String token = accessToken.get("access_token").value;
|
|
|
|
String url = instanceUrl + "/api/v1/statuses/" + postId;
|
|
try
|
|
{
|
|
URL endpoint = new URL(url);
|
|
HttpURLConnection conn = cast(endpoint.openConnection());
|
|
String s1 = "Bearer " + token;
|
|
conn.setRequestProperty("Authorization", s1);
|
|
conn.connect();
|
|
|
|
doStandardJsonReturn(conn, handler);
|
|
}
|
|
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
|
}
|
|
|
|
public void
|
|
getPostContext(String postId, RequestListener handler)
|
|
{
|
|
String token = accessToken.get("access_token").value;
|
|
|
|
String s1 = instanceUrl + "/api/v1/statuses/";
|
|
String s2 = postId + "/context";
|
|
String url = s1 + s2;
|
|
try
|
|
{
|
|
URL endpoint = new URL(url);
|
|
HttpURLConnection conn = cast(endpoint.openConnection());
|
|
String s3 = "Bearer " + token;
|
|
conn.setRequestProperty("Authorization", s3);
|
|
conn.connect();
|
|
|
|
doStandardJsonReturn(conn, handler);
|
|
}
|
|
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
|
}
|
|
|
|
public void
|
|
getAccounts(String query, RequestListener handler)
|
|
{
|
|
assert query != null;
|
|
String token = accessToken.get("access_token").value;
|
|
|
|
String url = instanceUrl + "/api/v1/accounts/search";
|
|
url += "?q=" + encode(query);
|
|
|
|
try
|
|
{
|
|
URL endpoint = new URL(url);
|
|
HttpURLConnection conn = cast(endpoint.openConnection());
|
|
String s1 = "Bearer " + token;
|
|
conn.setRequestProperty("Authorization", s1);
|
|
conn.connect();
|
|
|
|
doStandardJsonReturn(conn, handler);
|
|
}
|
|
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
|
}
|
|
|
|
public void
|
|
uploadFile(File file, RequestListener handler)
|
|
{
|
|
assert file != null;
|
|
assert file.canRead();
|
|
|
|
String bct =
|
|
"multipart/form-data; "
|
|
+ "boundary=\"JKomastoFileUpload\"";
|
|
String fsb = "--JKomastoFileUpload\r\n";
|
|
String feb = "\r\n--JKomastoFileUpload--\r\n";
|
|
String fcd =
|
|
"Content-Disposition: form-data; "
|
|
+ "name=\"file\"; "
|
|
+ "filename=\"" + file.getName() + "\"\r\n";
|
|
String fct = "Content-Type: image/png\r\n\r\n";
|
|
int contentLength = 0;
|
|
contentLength += fsb.length();
|
|
contentLength += feb.length();
|
|
contentLength += fcd.length();
|
|
contentLength += fct.length();
|
|
contentLength += file.length();
|
|
/*
|
|
* (知) This was an absurdity to debug. Contrary to
|
|
* cURL, Java sets default values for some headers,
|
|
* some of which are restricted, meaning you can't
|
|
* arbitrarily change them. Content-Length is one
|
|
* of them, set to 2^14-1 bytes. I'm pretty sure
|
|
* the file I was uploading was under this, but
|
|
* anyways one of the two parties was stopping me
|
|
* from finishing transferring my form data.
|
|
*
|
|
* They didn't mention this in the Javadocs.
|
|
* I noticed HttpURLConnection#setChunkedStreamingMode
|
|
* and #setFixedLengthStreamingMode by accident.
|
|
* Turns out, the latter is how I do what cURL and
|
|
* Firefox are doing - precalculate the exact size
|
|
* of the body and set the content length to it.
|
|
* Unfortunately, this is not flexible, we have to
|
|
* be exact. Thankfully, my answers pass..
|
|
*
|
|
* On the other side, Mastodon is obtuse as usual.
|
|
* They had code that basically throws a generic 500
|
|
* upon any sort of error from their library[1]. What
|
|
* problem the library had with my requests, I could
|
|
* never know. There is an undocumented requirement
|
|
* that you must put a filename in the content
|
|
* disposition. That one I found by guessing.
|
|
*
|
|
* I solved this with the help of -Djavax.net.debug,
|
|
* which revealed to me how my headers and body
|
|
* differed from cURL and Firefox. If this issue
|
|
* happens again, I advise giving up.
|
|
*
|
|
* [1] app/controllers/api/v1/media_controller.rb
|
|
* #create. 3 March 2022
|
|
*/
|
|
|
|
String token = accessToken.get("access_token").value;
|
|
String url = instanceUrl + "/api/v1/media/";
|
|
try
|
|
{
|
|
URL endpoint = new URL(url);
|
|
HttpURLConnection conn = cast(endpoint.openConnection());
|
|
String s1 = "Bearer " + token;
|
|
conn.setRequestProperty("Authorization", s1);
|
|
conn.setDoOutput(true);
|
|
conn.setRequestMethod("POST");
|
|
conn.setFixedLengthStreamingMode(contentLength);
|
|
conn.setRequestProperty("Content-Type", bct);
|
|
conn.setRequestProperty("Accept", "*/*");
|
|
conn.connect();
|
|
|
|
OutputStream ostream = conn.getOutputStream();
|
|
InputStream istream = new FileInputStream(file);
|
|
|
|
ostream.write(fsb.getBytes());
|
|
ostream.write(fcd.getBytes());
|
|
ostream.write(fct.getBytes());
|
|
|
|
int c; while ((c = istream.read()) != -1)
|
|
ostream.write(c);
|
|
|
|
ostream.write(feb.getBytes());
|
|
istream.close();
|
|
ostream.close();
|
|
|
|
wrapResponseInTree(conn, handler);
|
|
}
|
|
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
|
}
|
|
|
|
public void
|
|
monitorTimeline(
|
|
TimelineType type, ServerSideEventsListener handler)
|
|
{
|
|
String token = accessToken.get("access_token").value;
|
|
|
|
String url = instanceUrl + "/api/v1/streaming";
|
|
switch (type)
|
|
{
|
|
case FEDERATED: url += "/public"; break;
|
|
case LOCAL: url += "/public/local"; break;
|
|
case HOME: url += "/user"; break;
|
|
default: assert false;
|
|
}
|
|
|
|
try
|
|
{
|
|
URL endpoint = new URL(url);
|
|
HttpURLConnection conn = cast(endpoint.openConnection());
|
|
String s = "Bearer " + token;
|
|
conn.setRequestProperty("Authorization", s);
|
|
conn.connect();
|
|
|
|
int code = conn.getResponseCode();
|
|
if (code >= 300)
|
|
{
|
|
Reader input = ireader(conn.getErrorStream());
|
|
Tree<String> response = JsonConverter.convert(input);
|
|
input.close();
|
|
handler.requestFailed(code, response);
|
|
return;
|
|
}
|
|
|
|
conn.setReadTimeout(500);
|
|
Reader input = ireader(conn.getInputStream());
|
|
BufferedReader br = new BufferedReader(input);
|
|
Thread thread = Thread.currentThread();
|
|
while (true) try
|
|
{
|
|
String line = br.readLine();
|
|
if (line != null) handler.lineReceived(line);
|
|
}
|
|
catch (SocketTimeoutException eSt)
|
|
{
|
|
if (thread.interrupted()) break;
|
|
}
|
|
}
|
|
catch (IOException eIo) { handler.connectionFailed(eIo); }
|
|
}
|
|
|
|
// - -%- -
|
|
|
|
private void
|
|
doStandardJsonReturn(
|
|
HttpURLConnection conn, RequestListener handler)
|
|
throws IOException
|
|
{
|
|
int code = conn.getResponseCode();
|
|
if (code >= 300)
|
|
{
|
|
Reader input = ireader(conn.getErrorStream());
|
|
Tree<String> response = JsonConverter.convert(input);
|
|
input.close();
|
|
handler.requestFailed(code, response);
|
|
return;
|
|
}
|
|
|
|
Reader input = ireader(conn.getInputStream());
|
|
Tree<String> response = JsonConverter.convert(input);
|
|
input.close();
|
|
handler.requestSucceeded(response);
|
|
}
|
|
|
|
private void
|
|
wrapResponseInTree(
|
|
HttpURLConnection conn, RequestListener handler)
|
|
throws IOException
|
|
{
|
|
int code = conn.getResponseCode();
|
|
if (code >= 300)
|
|
{
|
|
Reader input = ireader(conn.getErrorStream());
|
|
Tree<String> response = fromPlain(input);
|
|
input.close();
|
|
handler.requestFailed(code, response);
|
|
return;
|
|
}
|
|
|
|
Reader input = ireader(conn.getInputStream());
|
|
Tree<String> response = fromPlain(input);
|
|
input.close();
|
|
handler.requestSucceeded(response);
|
|
}
|
|
|
|
// - -%- -
|
|
|
|
public static void
|
|
debugPrint(Tree<String> tree)
|
|
{
|
|
debugPrint(tree, "");
|
|
}
|
|
|
|
public static void
|
|
debugPrint(Tree<String> tree, String prefix)
|
|
{
|
|
System.err.print(prefix);
|
|
System.err.print(deescape(tree.key));
|
|
System.err.print(": ");
|
|
System.err.println(deescape(tree.value));
|
|
for (Tree<String> child: tree)
|
|
debugPrint(child, prefix + " ");
|
|
}
|
|
|
|
// - -%- -
|
|
|
|
private static String
|
|
deescape(String string)
|
|
{
|
|
if (string == null) return string;
|
|
string = string.replaceAll("\n", "\\\\n");
|
|
return string;
|
|
}
|
|
|
|
private static Tree<String>
|
|
fromPlain(Reader r)
|
|
throws IOException
|
|
{
|
|
StringBuilder b = new StringBuilder();
|
|
int c; while ((c = r.read()) != -1) b.append((char)c);
|
|
|
|
Tree<String> leaf = new Tree<String>();
|
|
leaf.key = "body";
|
|
leaf.value = b.toString();
|
|
Tree<String> doc = new Tree<String>();
|
|
doc.add(leaf);
|
|
return doc;
|
|
}
|
|
|
|
private static String
|
|
encode(String s)
|
|
{
|
|
try {
|
|
if (s == null) return null;
|
|
return URLEncoder.encode(s, "UTF-8");
|
|
}
|
|
catch (UnsupportedEncodingException eUe) {
|
|
assert false;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private static HttpURLConnection
|
|
cast(URLConnection conn)
|
|
{
|
|
return (HttpURLConnection)conn;
|
|
}
|
|
|
|
private static InputStreamReader
|
|
ireader(InputStream is)
|
|
throws IOException
|
|
{
|
|
assert is != null;
|
|
return new InputStreamReader(is);
|
|
}
|
|
|
|
private static OutputStreamWriter
|
|
owriter(OutputStream os)
|
|
throws IOException
|
|
{
|
|
assert os != null;
|
|
return new OutputStreamWriter(os);
|
|
}
|
|
|
|
// ---%-@-%---
|
|
|
|
public void
|
|
loadCache()
|
|
throws IOException
|
|
{
|
|
FileReader r = new FileReader(getCachePath());
|
|
DSVTokeniser.Options o = new DSVTokeniser.Options();
|
|
Tree<String> row1 = DSVTokeniser.tokenise(r, o);
|
|
Tree<String> row2 = DSVTokeniser.tokenise(r, o);
|
|
Tree<String> row3 = DSVTokeniser.tokenise(r, o);
|
|
assert !row1.get(0).value.equals(o.endOfStreamValue);
|
|
assert !row2.get(0).value.equals(o.endOfStreamValue);
|
|
assert !row3.get(0).value.equals(o.endOfStreamValue);
|
|
r.close();
|
|
|
|
// Prepare to bark like mad.
|
|
boolean yes10 = !row1.get(0).value.equals(o.endOfStreamValue);
|
|
boolean yes20 = !row2.get(0).value.equals(o.endOfStreamValue);
|
|
boolean yes30 = !row3.get(0).value.equals(o.endOfStreamValue);
|
|
boolean yes11 = row1.size() == 1;
|
|
boolean yes21 = row2.size() == 2;
|
|
boolean yes31 = row3.size() == 1;
|
|
boolean all = yes10 & yes20 & yes30 & yes11 & yes21 & yes31;
|
|
if (!all) {
|
|
throw new IOException("Cache has invalid format!");
|
|
}
|
|
|
|
setInstanceUrl(row1.get(0).value);
|
|
appCredentials = new Tree<String>();
|
|
appCredentials.add(new Tree<String>());
|
|
appCredentials.add(new Tree<String>());
|
|
appCredentials.get(0).key = "client_id";
|
|
appCredentials.get(0).value = row2.get(0).value;
|
|
appCredentials.get(1).key = "client_secret";
|
|
appCredentials.get(1).value = row2.get(1).value;
|
|
accessToken = new Tree<String>();
|
|
accessToken.add(new Tree<String>());
|
|
accessToken.get(0).key = "access_token";
|
|
accessToken.get(0).value = row3.get(0).value;
|
|
}
|
|
|
|
public void
|
|
saveToCache()
|
|
throws IOException
|
|
{
|
|
String f10 = instanceUrl;
|
|
String f20 = appCredentials.get("client_id").value;
|
|
String f21 = appCredentials.get("client_secret").value;
|
|
String f30 = accessToken.get("access_token").value;
|
|
|
|
f10 = f10.replaceAll(":", "\\\\:") + "\n";
|
|
f20 = f20.replaceAll(":", "\\\\:") + ":";
|
|
f21 = f21.replaceAll(":", "\\\\:") + "\n";
|
|
f30 = f30.replaceAll(":", "\\\\:") + "\n";
|
|
|
|
FileWriter w = new FileWriter(getCachePath());
|
|
w.write(f10);
|
|
w.write(f20);
|
|
w.write(f21);
|
|
w.write(f30);
|
|
w.close();
|
|
}
|
|
|
|
// - -%- -
|
|
|
|
private static String
|
|
getCachePath()
|
|
{
|
|
String userHome = System.getProperty("user.home");
|
|
String osName = System.getProperty("os.name");
|
|
boolean isWindows = osName.contains("Windows");
|
|
boolean isUnix = !isWindows;
|
|
// We assume. If you're running JKomasto in classic Mac OS
|
|
// for some reason, you should probably edit the code..
|
|
String configDir = isWindows ? "AppData/Local" : ".config";
|
|
String filename = "jkomasto.cache.dsv";
|
|
String path = userHome + "/" + configDir + "/" + filename;
|
|
return path;
|
|
}
|
|
|
|
}
|