aboutsummaryrefslogtreecommitdiff
path: root/src/org/slcl/core
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi.dcr@tutanota.com>2024-01-07 23:49:03 +0100
committerXavier Del Campo Romero <xavi.dcr@tutanota.com>2024-05-17 23:28:51 +0200
commit40f6d425a4429b16936cc8bb4900a23c3362a123 (patch)
treecc60a4a6995604f095952d7c3cda83913c92b61a /src/org/slcl/core
parenta3bfeb15064ab85900e28f0f3f84b88e99c9a466 (diff)
WIP
Diffstat (limited to 'src/org/slcl/core')
-rw-r--r--src/org/slcl/core/Cookie.java156
-rw-r--r--src/org/slcl/core/Login.java93
2 files changed, 249 insertions, 0 deletions
diff --git a/src/org/slcl/core/Cookie.java b/src/org/slcl/core/Cookie.java
new file mode 100644
index 0000000..86f64b5
--- /dev/null
+++ b/src/org/slcl/core/Cookie.java
@@ -0,0 +1,156 @@
+/*
+ * slcl-android, an Android frontend for slcl
+ * Copyright (C) 2023-2024 Xavier Del Campo Romero
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package org.slcl.core;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+
+public final class Cookie {
+ final String username, cookie;
+ final CookieDate date;
+
+ private final class Results {
+ final String username, cookie;
+ final CookieDate date;
+
+ Results(String username, String cookie, final CookieDate date)
+ {
+ this.username = username;
+ this.cookie = cookie;
+ this.date = date;
+ }
+ }
+
+ public Cookie(final File f)
+ throws IOException {
+ final FileInputStream fis = new FileInputStream(f);
+ final InputStreamReader ir = new InputStreamReader(fis);
+ final BufferedReader r = new BufferedReader(ir);
+ final Results res = from_data(null, r.readLine());
+
+ username = res.username;
+ cookie = res.cookie;
+ date = res.date;
+ fis.close();
+ }
+
+ private final class CookieDate {
+ final private String FMT = "EEE, dd MMM yyyy hh:mm:ss z";
+ Date date;
+
+ public CookieDate(final String[] tokens) throws IOException
+ {
+ for (int i = 1; i < tokens.length; i++) {
+ final String[] date_tokens = tokens[i].split("=");
+
+ if (date_tokens.length != 2) {
+ continue;
+ }
+
+ final String header = date_tokens[0].trim();
+
+ if (!header.equalsIgnoreCase("Expires")) {
+ continue;
+ }
+
+ ParsePosition pos = new ParsePosition(0);
+ final SimpleDateFormat fmt = new SimpleDateFormat(FMT);
+ final Date d = fmt.parse(date_tokens[1], pos);
+
+ if (d != null) {
+ // TODO: check pos.
+ date = d;
+ return;
+ }
+ }
+
+ throw new IOException("missing expiration date");
+ }
+
+ public String toString()
+ {
+ final SimpleDateFormat fmt = new SimpleDateFormat(FMT);
+
+ return fmt.format(date, null, null).toString();
+ }
+
+ public Date getDate() {
+ return date;
+ }
+ }
+
+ private Results from_data(final String exp_username, final String data)
+ throws IOException
+ {
+ final int sep = data.indexOf('=');
+
+ if (sep <= 0) {
+ throw new IOException("expected key=value format");
+ }
+
+ final String username = data.substring(0, sep);
+
+ if (exp_username != null
+ && !username.equals(exp_username)) {
+ throw new IOException("wrong username");
+ }
+
+ final String[] tokens = data.substring(sep + 1).split(";");
+
+ if (tokens.length < 2) {
+ throw new IOException("expected at least two tokens");
+ }
+
+ return new Results(username, tokens[0], new CookieDate(tokens));
+ }
+
+ public Cookie(final String exp_username, final String data)
+ throws IOException
+ {
+ final Results r = from_data(exp_username, data);
+
+ username = r.username;
+ cookie = r.cookie;
+ date = r.date;
+ }
+
+ public void store(final File f) throws IOException
+ {
+ final FileOutputStream fos = new FileOutputStream(f);
+ final String datestr = "Expires=" + date.toString();
+
+ fos.write(cookie.getBytes());
+ fos.write(';');
+ fos.write(datestr.getBytes());
+ fos.write('\n');
+ fos.close();
+ }
+
+ public String getCookie() {
+ return cookie;
+ }
+}
diff --git a/src/org/slcl/core/Login.java b/src/org/slcl/core/Login.java
new file mode 100644
index 0000000..d5b9267
--- /dev/null
+++ b/src/org/slcl/core/Login.java
@@ -0,0 +1,93 @@
+/*
+ * slcl-android, an Android frontend for slcl
+ * Copyright (C) 2023-2024 Xavier Del Campo Romero
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package org.slcl.core;
+
+import java.io.OutputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.HttpURLConnection;
+import java.util.List;
+import java.util.Map;
+import javax.net.ssl.HttpsURLConnection;
+
+public final class Login {
+ public String login(final boolean https, String url, final String username,
+ final String password) throws IOException {
+
+ if (!url.startsWith("https://") && https) {
+ url = "https://" + url;
+ }
+ else if (!url.startsWith("http://")) {
+ url = "http://" + url;
+ }
+
+ url += "/login";
+
+ final String data = "username=" + username + "&password=" + password;
+
+ URL u = new URL(url);
+ HttpURLConnection c;
+
+ if (https) {
+ c = (HttpsURLConnection)u.openConnection();
+ }
+ else {
+ c = (HttpURLConnection)u.openConnection();
+ }
+
+ c.setRequestMethod("POST");
+ c.setInstanceFollowRedirects(false);
+ c.setReadTimeout(5000);
+
+ OutputStream os = c.getOutputStream();
+
+ os.write(data.getBytes());
+
+ final int code = c.getResponseCode();
+
+ switch (code)
+ {
+ case HttpURLConnection.HTTP_FORBIDDEN:
+ throw new IOException("Forbidden");
+
+ case HttpURLConnection.HTTP_UNAUTHORIZED:
+ throw new IOException("Unauthorized");
+
+ case HttpURLConnection.HTTP_SEE_OTHER:
+ {
+ final Map<String,List<String>> headers = c.getHeaderFields();
+ final List<String> cookies = headers.get("Set-Cookie");
+
+ if (cookies != null) {
+ if (cookies.size() != 1) {
+ throw new IOException("Expected only 1 cookie");
+ }
+
+ return cookies.toArray(new String[0])[0];
+ } else {
+ throw new IOException("No Cookie found");
+ }
+ }
+
+ default:
+ throw new IOException("Unexpected HTTP status " + code);
+ }
+ }
+}