]> nmode's Git Repositories - signal-cli/commitdiff
Extract util methods to separate classes
authorAsamK <asamk@gmx.de>
Sun, 18 Nov 2018 09:45:26 +0000 (10:45 +0100)
committerAsamK <asamk@gmx.de>
Sun, 18 Nov 2018 09:45:26 +0000 (10:45 +0100)
src/main/java/org/asamk/signal/Main.java
src/main/java/org/asamk/signal/Manager.java
src/main/java/org/asamk/signal/util/DateUtils.java [new file with mode: 0644]
src/main/java/org/asamk/signal/util/Hex.java
src/main/java/org/asamk/signal/util/IOUtils.java [new file with mode: 0644]
src/main/java/org/asamk/signal/util/Util.java

index 7f85f28077809f02b26c439fdfd2d6f80c8bf46d..03d1c3ccb84101dc5c0e8e09d959180c33a2dbfd 100644 (file)
@@ -31,7 +31,10 @@ import org.asamk.Signal;
 import org.asamk.signal.storage.contacts.ContactInfo;
 import org.asamk.signal.storage.groups.GroupInfo;
 import org.asamk.signal.storage.protocol.JsonIdentityKeyStore;
+import org.asamk.signal.util.DateUtils;
 import org.asamk.signal.util.Hex;
+import org.asamk.signal.util.IOUtils;
+import org.asamk.signal.util.Util;
 import org.freedesktop.dbus.DBusConnection;
 import org.freedesktop.dbus.DBusSigHandler;
 import org.freedesktop.dbus.exceptions.DBusException;
@@ -52,15 +55,14 @@ import org.whispersystems.signalservice.internal.util.Base64;
 
 import java.io.File;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.StringWriter;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.nio.charset.Charset;
 import java.security.Security;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
@@ -69,8 +71,6 @@ public class Main {
     public static final String SIGNAL_BUSNAME = "org.asamk.Signal";
     public static final String SIGNAL_OBJECTPATH = "/org/asamk/Signal";
 
-    private static final TimeZone tzUTC = TimeZone.getTimeZone("UTC");
-
     public static void main(String[] args) {
         // Workaround for BKS truststore
         Security.insertProviderAt(new org.bouncycastle.jce.provider.BouncyCastleProvider(), 1);
@@ -317,8 +317,8 @@ public class Main {
                         for (DeviceInfo d : devices) {
                             System.out.println("Device " + d.getId() + (d.getId() == m.getDeviceId() ? " (this device)" : "") + ":");
                             System.out.println(" Name: " + d.getName());
-                            System.out.println(" Created: " + formatTimestamp(d.getCreated()));
-                            System.out.println(" Last seen: " + formatTimestamp(d.getLastSeen()));
+                            System.out.println(" Created: " + DateUtils.formatTimestamp(d.getCreated()));
+                            System.out.println(" Last seen: " + DateUtils.formatTimestamp(d.getLastSeen()));
                         }
                     } catch (IOException e) {
                         e.printStackTrace();
@@ -373,7 +373,7 @@ public class Main {
                         String messageText = ns.getString("message");
                         if (messageText == null) {
                             try {
-                                messageText = readAll(System.in);
+                                messageText = IOUtils.readAll(System.in, Charset.defaultCharset());
                             } catch (IOException e) {
                                 System.err.println("Failed to read message from stdin: " + e.getMessage());
                                 System.err.println("Aborting sending.");
@@ -425,7 +425,7 @@ public class Main {
                                 @Override
                                 public void handle(Signal.MessageReceived s) {
                                     System.out.print(String.format("Envelope from: %s\nTimestamp: %s\nBody: %s\n",
-                                            s.getSender(), formatTimestamp(s.getTimestamp()), s.getMessage()));
+                                            s.getSender(), DateUtils.formatTimestamp(s.getTimestamp()), s.getMessage()));
                                     if (s.getGroupId().length > 0) {
                                         System.out.println("Group info:");
                                         System.out.println("  Id: " + Base64.encodeBytes(s.getGroupId()));
@@ -443,7 +443,7 @@ public class Main {
                                 @Override
                                 public void handle(Signal.ReceiptReceived s) {
                                     System.out.print(String.format("Receipt from: %s\nTimestamp: %s\n",
-                                            s.getSender(), formatTimestamp(s.getTimestamp())));
+                                            s.getSender(), DateUtils.formatTimestamp(s.getTimestamp())));
                                 }
                             });
                         } catch (UnsatisfiedLinkError e) {
@@ -708,7 +708,7 @@ public class Main {
     }
 
     private static void printIdentityFingerprint(Manager m, String theirUsername, JsonIdentityKeyStore.Identity theirId) {
-        String digits = formatSafetyNumber(m.computeSafetyNumber(theirUsername, theirId.getIdentityKey()));
+        String digits = Util.formatSafetyNumber(m.computeSafetyNumber(theirUsername, theirId.getIdentityKey()));
         System.out.println(String.format("%s: %s Added: %s Fingerprint: %s Safety Number: %s", theirUsername,
                 theirId.getTrustLevel(), theirId.getDateAdded(), Hex.toStringCondensed(theirId.getFingerprint()), digits));
     }
@@ -723,16 +723,6 @@ public class Main {
         }
     }
 
-    private static String formatSafetyNumber(String digits) {
-        final int partCount = 12;
-        int partSize = digits.length() / partCount;
-        StringBuilder f = new StringBuilder(digits.length() + partCount);
-        for (int i = 0; i < partCount; i++) {
-            f.append(digits.substring(i * partSize, (i * partSize) + partSize)).append(" ");
-        }
-        return f.toString();
-    }
-
     private static void handleGroupNotFoundException(GroupNotFoundException e) {
         System.err.println("Failed to send to group: " + e.getMessage());
         System.err.println("Aborting sending.");
@@ -956,18 +946,6 @@ public class Main {
         System.err.println("Failed to send message: " + e.getMessage());
     }
 
-    private static String readAll(InputStream in) throws IOException {
-        StringWriter output = new StringWriter();
-        byte[] buffer = new byte[4096];
-        long count = 0;
-        int n;
-        while (-1 != (n = System.in.read(buffer))) {
-            output.write(new String(buffer, 0, n, Charset.defaultCharset()));
-            count += n;
-        }
-        return output.toString();
-    }
-
     private static class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
         final Manager m;
 
@@ -983,7 +961,7 @@ public class Main {
             if (source.getRelay().isPresent()) {
                 System.out.println("Relayed by: " + source.getRelay().get());
             }
-            System.out.println("Timestamp: " + formatTimestamp(envelope.getTimestamp()));
+            System.out.println("Timestamp: " + DateUtils.formatTimestamp(envelope.getTimestamp()));
             if (envelope.isUnidentifiedSender()) {
                 System.out.println("Sent by unidentified/sealed sender");
             }
@@ -1029,7 +1007,7 @@ public class Main {
                             System.out.println("Received sync read messages list");
                             for (ReadMessage rm : syncMessage.getRead().get()) {
                                 ContactInfo fromContact = m.getContact(rm.getSender());
-                                System.out.println("From: " + (fromContact == null ? "" : "“" + fromContact.name + "” ") + rm.getSender() + " Message timestamp: " + formatTimestamp(rm.getTimestamp()));
+                                System.out.println("From: " + (fromContact == null ? "" : "“" + fromContact.name + "” ") + rm.getSender() + " Message timestamp: " + DateUtils.formatTimestamp(rm.getTimestamp()));
                             }
                         }
                         if (syncMessage.getRequest().isPresent()) {
@@ -1052,9 +1030,9 @@ public class Main {
                             } else {
                                 to = "Unknown";
                             }
-                            System.out.println("To: " + to + " , Message timestamp: " + formatTimestamp(sentTranscriptMessage.getTimestamp()));
+                            System.out.println("To: " + to + " , Message timestamp: " + DateUtils.formatTimestamp(sentTranscriptMessage.getTimestamp()));
                             if (sentTranscriptMessage.getExpirationStartTimestamp() > 0) {
-                                System.out.println("Expiration started at: " + formatTimestamp(sentTranscriptMessage.getExpirationStartTimestamp()));
+                                System.out.println("Expiration started at: " + DateUtils.formatTimestamp(sentTranscriptMessage.getExpirationStartTimestamp()));
                             }
                             SignalServiceDataMessage message = sentTranscriptMessage.getMessage();
                             handleSignalServiceDataMessage(message);
@@ -1071,7 +1049,7 @@ public class Main {
                             System.out.println("Received sync message with verified identities:");
                             final VerifiedMessage verifiedMessage = syncMessage.getVerified().get();
                             System.out.println(" - " + verifiedMessage.getDestination() + ": " + verifiedMessage.getVerified());
-                            String safetyNumber = formatSafetyNumber(m.computeSafetyNumber(verifiedMessage.getDestination(), verifiedMessage.getIdentityKey()));
+                            String safetyNumber = Util.formatSafetyNumber(m.computeSafetyNumber(verifiedMessage.getDestination(), verifiedMessage.getIdentityKey()));
                             System.out.println("   " + safetyNumber);
                         }
                         if (syncMessage.getConfiguration().isPresent()) {
@@ -1111,7 +1089,7 @@ public class Main {
                     if (content.getReceiptMessage().isPresent()) {
                         System.out.println("Received a receipt message");
                         SignalServiceReceiptMessage receiptMessage = content.getReceiptMessage().get();
-                        System.out.println(" - When: " + formatTimestamp(receiptMessage.getWhen()));
+                        System.out.println(" - When: " + DateUtils.formatTimestamp(receiptMessage.getWhen()));
                         if (receiptMessage.isDeliveryReceipt()) {
                             System.out.println(" - Is delivery receipt");
                         }
@@ -1120,14 +1098,14 @@ public class Main {
                         }
                         System.out.println(" - Timestamps:");
                         for (long timestamp : receiptMessage.getTimestamps()) {
-                            System.out.println("    " + formatTimestamp(timestamp));
+                            System.out.println("    " + DateUtils.formatTimestamp(timestamp));
                         }
                     }
                     if (content.getTypingMessage().isPresent()) {
                         System.out.println("Received a typing message");
                         SignalServiceTypingMessage typingMessage = content.getTypingMessage().get();
                         System.out.println(" - Action: " + typingMessage.getAction());
-                        System.out.println(" - Timestamp: " + formatTimestamp(typingMessage.getTimestamp()));
+                        System.out.println(" - Timestamp: " + DateUtils.formatTimestamp(typingMessage.getTimestamp()));
                         if (typingMessage.getGroupId().isPresent()) {
                             GroupInfo group = m.getGroup(typingMessage.getGroupId().get());
                             if (group != null) {
@@ -1145,7 +1123,7 @@ public class Main {
         }
 
         private void handleSignalServiceDataMessage(SignalServiceDataMessage message) {
-            System.out.println("Message timestamp: " + formatTimestamp(message.getTimestamp()));
+            System.out.println("Message timestamp: " + DateUtils.formatTimestamp(message.getTimestamp()));
 
             if (message.getBody().isPresent()) {
                 System.out.println("Body: " + message.getBody().get());
@@ -1334,11 +1312,4 @@ public class Main {
             }
         }
     }
-
-    private static String formatTimestamp(long timestamp) {
-        Date date = new Date(timestamp);
-        final DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); // Quoted "Z" to indicate UTC, no timezone offset
-        df.setTimeZone(tzUTC);
-        return timestamp + " (" + df.format(date) + ")";
-    }
 }
index 8e478d78d556df5a11c76f1260c6fff6d701af49..dc3916de31cfd8464bf8e174ecb28698133cb5d2 100644 (file)
@@ -35,6 +35,7 @@ import org.asamk.signal.storage.protocol.JsonIdentityKeyStore;
 import org.asamk.signal.storage.protocol.JsonSignalProtocolStore;
 import org.asamk.signal.storage.threads.JsonThreadStore;
 import org.asamk.signal.storage.threads.ThreadInfo;
+import org.asamk.signal.util.IOUtils;
 import org.asamk.signal.util.KeyUtils;
 import org.asamk.signal.util.Util;
 import org.signal.libsignal.metadata.*;
@@ -81,23 +82,17 @@ import org.whispersystems.signalservice.internal.util.Base64;
 import java.io.*;
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.net.URLDecoder;
 import java.net.URLEncoder;
 import java.nio.channels.Channels;
 import java.nio.channels.FileChannel;
 import java.nio.channels.FileLock;
 import java.nio.file.Files;
-import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.nio.file.StandardCopyOption;
-import java.nio.file.attribute.PosixFilePermission;
-import java.nio.file.attribute.PosixFilePermissions;
 import java.util.*;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
-import static java.nio.file.attribute.PosixFilePermission.*;
-
 class Manager implements Signal {
     private final static String URL = "https://textsecure-service.whispersystems.org";
     private final static String CDN_URL = "https://cdn.signal.org";
@@ -189,30 +184,10 @@ class Manager implements Signal {
 
     private File getMessageCacheFile(String sender, long now, long timestamp) throws IOException {
         String cachePath = getMessageCachePath(sender);
-        createPrivateDirectories(cachePath);
+        IOUtils.createPrivateDirectories(cachePath);
         return new File(cachePath + "/" + now + "_" + timestamp);
     }
 
-    private static void createPrivateDirectories(String path) throws IOException {
-        final Path file = new File(path).toPath();
-        try {
-            Set<PosixFilePermission> perms = EnumSet.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE);
-            Files.createDirectories(file, PosixFilePermissions.asFileAttribute(perms));
-        } catch (UnsupportedOperationException e) {
-            Files.createDirectories(file);
-        }
-    }
-
-    private static void createPrivateFile(String path) throws IOException {
-        final Path file = new File(path).toPath();
-        try {
-            Set<PosixFilePermission> perms = EnumSet.of(OWNER_READ, OWNER_WRITE);
-            Files.createFile(file, PosixFilePermissions.asFileAttribute(perms));
-        } catch (UnsupportedOperationException e) {
-            Files.createFile(file);
-        }
-    }
-
     public boolean userExists() {
         if (username == null) {
             return false;
@@ -238,9 +213,9 @@ class Manager implements Signal {
         if (fileChannel != null)
             return;
 
-        createPrivateDirectories(dataPath);
+        IOUtils.createPrivateDirectories(dataPath);
         if (!new File(getFileName()).exists()) {
-            createPrivateFile(getFileName());
+            IOUtils.createPrivateFile(getFileName());
         }
         fileChannel = new RandomAccessFile(new File(getFileName()), "rw").getChannel();
         lock = fileChannel.tryLock();
@@ -334,7 +309,7 @@ class Manager implements Signal {
                 File attachmentFile = getAttachmentFile(g.getAvatarId());
                 if (!avatarFile.exists() && attachmentFile.exists()) {
                     try {
-                        createPrivateDirectories(avatarsPath);
+                        IOUtils.createPrivateDirectories(avatarsPath);
                         Files.copy(attachmentFile.toPath(), avatarFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
                     } catch (Exception e) {
                         // Ignore
@@ -459,30 +434,8 @@ class Manager implements Signal {
         accountManager.removeDevice(deviceId);
     }
 
-    public static Map<String, String> getQueryMap(String query) {
-        String[] params = query.split("&");
-        Map<String, String> map = new HashMap<>();
-        for (String param : params) {
-            String name = null;
-            final String[] paramParts = param.split("=");
-            try {
-                name = URLDecoder.decode(paramParts[0], "utf-8");
-            } catch (UnsupportedEncodingException e) {
-                // Impossible
-            }
-            String value = null;
-            try {
-                value = URLDecoder.decode(paramParts[1], "utf-8");
-            } catch (UnsupportedEncodingException e) {
-                // Impossible
-            }
-            map.put(name, value);
-        }
-        return map;
-    }
-
     public void addDeviceLink(URI linkUri) throws IOException, InvalidKeyException {
-        Map<String, String> query = getQueryMap(linkUri.getRawQuery());
+        Map<String, String> query = Util.getQueryMap(linkUri.getRawQuery());
         String deviceIdentifier = query.get("uuid");
         String publicKeyEncoded = query.get("pub_key");
 
@@ -672,18 +625,6 @@ class Manager implements Signal {
         sendMessageLegacy(messageBuilder, g.members);
     }
 
-    private static String join(CharSequence separator, Iterable<? extends CharSequence> list) {
-        StringBuilder buf = new StringBuilder();
-        for (CharSequence str : list) {
-            if (buf.length() > 0) {
-                buf.append(separator);
-            }
-            buf.append(str);
-        }
-
-        return buf.toString();
-    }
-
     public byte[] sendUpdateGroupMessage(byte[] groupId, String name, Collection<String> members, String avatarFile) throws IOException, EncapsulatedExceptions, GroupNotFoundException, AttachmentInvalidException {
         GroupInfo g;
         if (groupId == null) {
@@ -720,14 +661,14 @@ class Manager implements Signal {
                 for (ContactTokenDetails contact : contacts) {
                     newMembers.remove(contact.getNumber());
                 }
-                System.err.println("Failed to add members " + join(", ", newMembers) + " to group: Not registered on Signal");
+                System.err.println("Failed to add members " + Util.join(", ", newMembers) + " to group: Not registered on Signal");
                 System.err.println("Aborting…");
                 System.exit(1);
             }
         }
 
         if (avatarFile != null) {
-            createPrivateDirectories(avatarsPath);
+            IOUtils.createPrivateDirectories(avatarsPath);
             File aFile = getGroupAvatarFile(g.groupId);
             Files.copy(Paths.get(avatarFile), aFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
         }
@@ -920,7 +861,7 @@ class Manager implements Signal {
         return UnidentifiedAccess.deriveAccessKeyFrom(profileKey);
     }
 
-    private static byte[] getTargetUnidentifiedAccessKey(SignalServiceAddress recipient) {
+    private byte[] getTargetUnidentifiedAccessKey(SignalServiceAddress recipient) {
         // TODO implement
         return null;
     }
@@ -1330,7 +1271,7 @@ class Manager implements Signal {
                 if (syncMessage.getGroups().isPresent()) {
                     File tmpFile = null;
                     try {
-                        tmpFile = Util.createTempFile();
+                        tmpFile = IOUtils.createTempFile();
                         try (InputStream attachmentAsStream = retrieveAttachmentAsStream(syncMessage.getGroups().get().asPointer(), tmpFile)) {
                             DeviceGroupsInputStream s = new DeviceGroupsInputStream(attachmentAsStream);
                             DeviceGroup g;
@@ -1372,7 +1313,7 @@ class Manager implements Signal {
                 if (syncMessage.getContacts().isPresent()) {
                     File tmpFile = null;
                     try {
-                        tmpFile = Util.createTempFile();
+                        tmpFile = IOUtils.createTempFile();
                         final ContactsMessage contactsMessage = syncMessage.getContacts().get();
                         try (InputStream attachmentAsStream = retrieveAttachmentAsStream(contactsMessage.getContactsStream().asPointer(), tmpFile)) {
                             DeviceContactsInputStream s = new DeviceContactsInputStream(attachmentAsStream);
@@ -1506,7 +1447,7 @@ class Manager implements Signal {
     }
 
     private File retrieveContactAvatarAttachment(SignalServiceAttachment attachment, String number) throws IOException, InvalidMessageException {
-        createPrivateDirectories(avatarsPath);
+        IOUtils.createPrivateDirectories(avatarsPath);
         if (attachment.isPointer()) {
             SignalServiceAttachmentPointer pointer = attachment.asPointer();
             return retrieveAttachment(pointer, getContactAvatarFile(number), false);
@@ -1521,7 +1462,7 @@ class Manager implements Signal {
     }
 
     private File retrieveGroupAvatarAttachment(SignalServiceAttachment attachment, byte[] groupId) throws IOException, InvalidMessageException {
-        createPrivateDirectories(avatarsPath);
+        IOUtils.createPrivateDirectories(avatarsPath);
         if (attachment.isPointer()) {
             SignalServiceAttachmentPointer pointer = attachment.asPointer();
             return retrieveAttachment(pointer, getGroupAvatarFile(groupId), false);
@@ -1536,7 +1477,7 @@ class Manager implements Signal {
     }
 
     private File retrieveAttachment(SignalServiceAttachmentPointer pointer) throws IOException, InvalidMessageException {
-        createPrivateDirectories(attachmentsPath);
+        IOUtils.createPrivateDirectories(attachmentsPath);
         return retrieveAttachment(pointer, getAttachmentFile(pointer.getId()), true);
     }
 
@@ -1571,7 +1512,7 @@ class Manager implements Signal {
 
         final SignalServiceMessageReceiver messageReceiver = new SignalServiceMessageReceiver(serviceConfiguration, username, password, deviceId, signalingKey, USER_AGENT, null, timer);
 
-        File tmpFile = Util.createTempFile();
+        File tmpFile = IOUtils.createTempFile();
         try (InputStream input = messageReceiver.retrieveAttachment(pointer, tmpFile, MAX_ATTACHMENT_SIZE)) {
             try (OutputStream output = new FileOutputStream(outputFile)) {
                 byte[] buffer = new byte[4096];
@@ -1615,7 +1556,7 @@ class Manager implements Signal {
     }
 
     private void sendGroups() throws IOException, UntrustedIdentityException {
-        File groupsFile = Util.createTempFile();
+        File groupsFile = IOUtils.createTempFile();
 
         try {
             try (OutputStream fos = new FileOutputStream(groupsFile)) {
@@ -1650,7 +1591,7 @@ class Manager implements Signal {
     }
 
     private void sendContacts() throws IOException, UntrustedIdentityException {
-        File contactsFile = Util.createTempFile();
+        File contactsFile = IOUtils.createTempFile();
 
         try {
             try (OutputStream fos = new FileOutputStream(contactsFile)) {
diff --git a/src/main/java/org/asamk/signal/util/DateUtils.java b/src/main/java/org/asamk/signal/util/DateUtils.java
new file mode 100644 (file)
index 0000000..c9b9252
--- /dev/null
@@ -0,0 +1,21 @@
+package org.asamk.signal.util;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+public class DateUtils {
+
+    private static final TimeZone tzUTC = TimeZone.getTimeZone("UTC");
+
+    private DateUtils() {
+    }
+
+    public static String formatTimestamp(long timestamp) {
+        Date date = new Date(timestamp);
+        final DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); // Quoted "Z" to indicate UTC, no timezone offset
+        df.setTimeZone(tzUTC);
+        return timestamp + " (" + df.format(date) + ")";
+    }
+}
index 623c5cf84be3f816a6151322dc7eea45324a9f30..9f88579105fba9a60de19762554035d49f1d2afe 100644 (file)
@@ -6,6 +6,9 @@ public class Hex {
             '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
     };
 
+    private Hex() {
+    }
+
     public static String toStringCondensed(byte[] bytes) {
         StringBuffer buf = new StringBuffer();
         for (int i = 0; i < bytes.length; i++) {
diff --git a/src/main/java/org/asamk/signal/util/IOUtils.java b/src/main/java/org/asamk/signal/util/IOUtils.java
new file mode 100644 (file)
index 0000000..69128d0
--- /dev/null
@@ -0,0 +1,55 @@
+package org.asamk.signal.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.PosixFilePermissions;
+import java.util.EnumSet;
+import java.util.Set;
+
+import static java.nio.file.attribute.PosixFilePermission.*;
+
+public class IOUtils {
+
+    private IOUtils() {
+    }
+
+    public static File createTempFile() throws IOException {
+        return File.createTempFile("signal_tmp_", ".tmp");
+    }
+
+    public static String readAll(InputStream in, Charset charset) throws IOException {
+        StringWriter output = new StringWriter();
+        byte[] buffer = new byte[4096];
+        int n;
+        while (-1 != (n = in.read(buffer))) {
+            output.write(new String(buffer, 0, n, charset));
+        }
+        return output.toString();
+    }
+
+    public static void createPrivateDirectories(String path) throws IOException {
+        final Path file = new File(path).toPath();
+        try {
+            Set<PosixFilePermission> perms = EnumSet.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE);
+            Files.createDirectories(file, PosixFilePermissions.asFileAttribute(perms));
+        } catch (UnsupportedOperationException e) {
+            Files.createDirectories(file);
+        }
+    }
+
+    public static void createPrivateFile(String path) throws IOException {
+        final Path file = new File(path).toPath();
+        try {
+            Set<PosixFilePermission> perms = EnumSet.of(OWNER_READ, OWNER_WRITE);
+            Files.createFile(file, PosixFilePermissions.asFileAttribute(perms));
+        } catch (UnsupportedOperationException e) {
+            Files.createFile(file);
+        }
+    }
+}
index c0efd27118e949e53a923a152ad1a7bb7c551248..eec7d2f782fdb95846400fdc603fdbc310520cac 100644 (file)
@@ -1,10 +1,56 @@
 package org.asamk.signal.util;
 
-import java.io.File;
-import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.HashMap;
+import java.util.Map;
 
 public class Util {
-    public static File createTempFile() throws IOException {
-        return File.createTempFile("signal_tmp_", ".tmp");
+
+    private Util() {
+    }
+
+    public static String formatSafetyNumber(String digits) {
+        final int partCount = 12;
+        int partSize = digits.length() / partCount;
+        StringBuilder f = new StringBuilder(digits.length() + partCount);
+        for (int i = 0; i < partCount; i++) {
+            f.append(digits, i * partSize, (i * partSize) + partSize).append(" ");
+        }
+        return f.toString();
+    }
+
+    public static Map<String, String> getQueryMap(String query) {
+        String[] params = query.split("&");
+        Map<String, String> map = new HashMap<>();
+        for (String param : params) {
+            String name = null;
+            final String[] paramParts = param.split("=");
+            try {
+                name = URLDecoder.decode(paramParts[0], "utf-8");
+            } catch (UnsupportedEncodingException e) {
+                // Impossible
+            }
+            String value = null;
+            try {
+                value = URLDecoder.decode(paramParts[1], "utf-8");
+            } catch (UnsupportedEncodingException e) {
+                // Impossible
+            }
+            map.put(name, value);
+        }
+        return map;
+    }
+
+    public static String join(CharSequence separator, Iterable<? extends CharSequence> list) {
+        StringBuilder buf = new StringBuilder();
+        for (CharSequence str : list) {
+            if (buf.length() > 0) {
+                buf.append(separator);
+            }
+            buf.append(str);
+        }
+
+        return buf.toString();
     }
 }