diff options
| author | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2024-01-07 23:49:03 +0100 |
|---|---|---|
| committer | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2024-05-17 23:28:51 +0200 |
| commit | 40f6d425a4429b16936cc8bb4900a23c3362a123 (patch) | |
| tree | cc60a4a6995604f095952d7c3cda83913c92b61a /src/org/slcl/core | |
| parent | a3bfeb15064ab85900e28f0f3f84b88e99c9a466 (diff) | |
WIP
Diffstat (limited to 'src/org/slcl/core')
| -rw-r--r-- | src/org/slcl/core/Cookie.java | 156 | ||||
| -rw-r--r-- | src/org/slcl/core/Login.java | 93 |
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); + } + } +} |
