Move HTML export logic to a separate file and finish it up

This commit is contained in:
Alexander Bakker 2022-12-05 21:44:01 +01:00
parent 2c36149a3d
commit 7a1e4e1d77
3 changed files with 120 additions and 88 deletions

View file

@ -0,0 +1,109 @@
package com.beemdevelopment.aegis.vault;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.util.Base64;
import com.beemdevelopment.aegis.R;
import com.beemdevelopment.aegis.encoding.Base32;
import com.beemdevelopment.aegis.encoding.Hex;
import com.beemdevelopment.aegis.helpers.QrCodeHelper;
import com.beemdevelopment.aegis.otp.GoogleAuthInfo;
import com.beemdevelopment.aegis.otp.HotpInfo;
import com.beemdevelopment.aegis.otp.MotpInfo;
import com.beemdevelopment.aegis.otp.OtpInfo;
import com.beemdevelopment.aegis.otp.YandexInfo;
import com.google.common.html.HtmlEscapers;
import com.google.zxing.WriterException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Collection;
public class VaultHtmlExporter {
private VaultHtmlExporter() {
}
public static void export(Context context, PrintStream ps, Collection<VaultEntry> entries) throws WriterException, IOException {
ps.print("<html><head><title>");
ps.print(context.getString(R.string.export_html_title));
ps.print("</title></head><body>");
ps.print("<h1>");
ps.print(context.getString(R.string.export_html_title));
ps.print("</h1>");
ps.print("<table>");
ps.print("<tr>");
ps.print("<th>Issuer</th>");
ps.print("<th>Name</th>");
ps.print("<th>Type</th>");
ps.print("<th>QR Code</th>");
ps.print("<th>UUID</th>");
ps.print("<th>Note</th>");
ps.print("<th>Favorite</th>");
ps.print("<th>Algo</th>");
ps.print("<th>Digits</th>");
ps.print("<th>Secret</th>");
ps.print("<th>Counter</th>");
ps.print("<th>PIN</th>");
ps.print("</tr>");
for (VaultEntry entry : entries) {
ps.print("<tr>");
OtpInfo info = entry.getInfo();
GoogleAuthInfo gaInfo = new GoogleAuthInfo(info, entry.getName(), entry.getIssuer());
appendRow(ps, entry.getIssuer());
appendRow(ps, entry.getName());
appendRow(ps, info.getType());
appendQrRow(ps, gaInfo.getUri().toString());
appendRow(ps, entry.getUUID().toString());
appendRow(ps, entry.getNote());
appendRow(ps, Boolean.toString(entry.isFavorite()));
appendRow(ps, info.getAlgorithm(false));
appendRow(ps, Integer.toString(info.getDigits()));
if (info instanceof MotpInfo) {
appendRow(ps, Hex.encode(info.getSecret()));
} else {
appendRow(ps, Base32.encode(info.getSecret()));
}
if (info instanceof HotpInfo) {
appendRow(ps, Long.toString(((HotpInfo) info).getCounter()));
} else {
appendRow(ps, "-");
}
if (info instanceof YandexInfo) {
appendRow(ps, ((YandexInfo) info).getPin());
} else if (info instanceof MotpInfo) {
appendRow(ps, ((MotpInfo) info).getPin());
} else {
appendRow(ps, "-");
}
ps.print("</tr>");
}
ps.print("</table></body>");
ps.print("<style>table,td,th{border:1px solid #000;border-collapse:collapse;text-align:center}td:not(.qr),th{padding:1em}</style>");
ps.print("</html>");
}
private static void appendRow(PrintStream ps, String s) {
ps.print("<td>");
ps.print(escape(s));
ps.print("</td>");
}
private static void appendQrRow(PrintStream ps, String s) throws IOException, WriterException {
ps.print("<td class='qr'><img src=\"data:image/png;base64,");
Bitmap bm = QrCodeHelper.encodeToBitmap(s, 256, 256, Color.WHITE);
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
bm.compress(Bitmap.CompressFormat.PNG, 100, baos);
String encoded = Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT);
ps.print(encoded);
}
ps.print("\"/></td>");
}
private static String escape(String s) {
return HtmlEscapers.htmlEscaper().escape(s);
}
}

View file

@ -1,28 +1,18 @@
package com.beemdevelopment.aegis.vault;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.util.Base64;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.util.AtomicFile;
import com.beemdevelopment.aegis.R;
import com.beemdevelopment.aegis.encoding.Base32;
import com.beemdevelopment.aegis.helpers.QrCodeHelper;
import com.beemdevelopment.aegis.otp.GoogleAuthInfo;
import com.beemdevelopment.aegis.otp.HotpInfo;
import com.beemdevelopment.aegis.otp.OtpInfo;
import com.beemdevelopment.aegis.util.IOUtils;
import com.google.common.html.HtmlEscapers;
import com.google.zxing.WriterException;
import org.json.JSONObject;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@ -32,9 +22,9 @@ import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.text.Collator;
import java.util.Collection;
import java.util.Objects;
import java.util.TreeSet;
import java.util.UUID;
import java.util.stream.Collectors;
public class VaultRepository {
public static final String FILENAME = "aegis.json";
@ -215,82 +205,15 @@ public class VaultRepository {
* Username and QR Code and writing it to the given OutputStream.
*/
public void exportHtml(OutputStream outStream, @Nullable Vault.EntryFilter filter) throws VaultRepositoryException {
try {
PrintStream printStream = new PrintStream(outStream, false, StandardCharsets.UTF_8.name());
printStream.print("<html><head><title>");
printStream.print(_context.getString(R.string.export_html_title));
printStream.print("</title></head><body>");
printStream.print("<h1>");
printStream.print(_context.getString(R.string.export_html_title));
printStream.print("</h1>");
printStream.print("<table>");
printStream.print("<tr>");
printStream.print("<th>Issuer</th>");
printStream.print("<th>Username</th>");
printStream.print("<th>Type</th>");
printStream.print("<th>QR Code</th>");
printStream.print("<th>UUID</th>");
printStream.print("<th>Note</th>");
printStream.print("<th>Favorite</th>");
printStream.print("<th>Algo</th>");
printStream.print("<th>Digits</th>");
printStream.print("<th>Secret</th>");
printStream.print("<th>Counter</th>");
printStream.print("</tr>");
for (VaultEntry entry : getEntries()) {
if (filter == null || filter.includeEntry(entry)) {
printStream.print("<tr>");
GoogleAuthInfo info = new GoogleAuthInfo(entry.getInfo(), entry.getName(), entry.getIssuer());
OtpInfo otpInfo = info.getOtpInfo();
printStream.print("<td>");
printStream.print(HtmlEscapers.htmlEscaper().escape(info.getIssuer()));
printStream.print("</td>");
printStream.print("<td>");
printStream.print(HtmlEscapers.htmlEscaper().escape(entry.getName()));
printStream.print("</td>");
printStream.print("<td>");
printStream.print(HtmlEscapers.htmlEscaper().escape(otpInfo.getType()));
printStream.print("</td>");
Bitmap bm = QrCodeHelper.encodeToBitmap(info.getUri().toString(),256, 256, Color.WHITE);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.PNG, 100, baos);
byte[] b = baos.toByteArray();
String encodedImage = Base64.encodeToString(b, Base64.DEFAULT);
printStream.print("<td class='qr'><img src=\"data:image/png;base64,");
printStream.print(encodedImage);
printStream.print("\"/></td>");
printStream.print("<td>");
printStream.print(HtmlEscapers.htmlEscaper().escape(entry.getUUID().toString()));
printStream.print("</td>");
printStream.print("<td>");
printStream.print(HtmlEscapers.htmlEscaper().escape(entry.getNote()));
printStream.print("</td>");
printStream.print("<td>");
printStream.print(HtmlEscapers.htmlEscaper().escape(entry.isFavorite() ? "true" : "false"));
printStream.print("</td>");
printStream.print("<td>");
printStream.print(HtmlEscapers.htmlEscaper().escape(otpInfo.getAlgorithm(false)));
printStream.print("</td>");
printStream.print("<td>");
printStream.print(HtmlEscapers.htmlEscaper().escape(Integer.toString(otpInfo.getDigits())));
printStream.print("</td>");
printStream.print("<td>");
printStream.print(HtmlEscapers.htmlEscaper().escape(Base32.encode(otpInfo.getSecret())));
printStream.print("</td>");
printStream.print("<td>");
if (Objects.equals(otpInfo.getTypeId(), HotpInfo.ID)) {
printStream.print(HtmlEscapers.htmlEscaper().escape(Long.toString(((HotpInfo) otpInfo).getCounter())));
} else {
printStream.print("-");
}
printStream.print("</td>");
printStream.print("</tr>");
}
};
printStream.print("</table></body>");
printStream.print("<style>table,td,th{border:1px solid #000;border-collapse:collapse;text-align:center}td:not(.qr),th{padding:1em}</style>");
printStream.print("</html>");
printStream.flush();
Collection<VaultEntry> entries = getEntries();
if (filter != null) {
entries = entries.stream()
.filter(filter::includeEntry)
.collect(Collectors.toList());
}
try (PrintStream ps = new PrintStream(outStream, false, StandardCharsets.UTF_8.name())) {
VaultHtmlExporter.export(_context, ps, entries);
} catch (WriterException | IOException e) {
throw new VaultRepositoryException(e);
}