From: AsamK Date: Thu, 26 Nov 2015 16:02:28 +0000 (+0100) Subject: Implement updateGroup X-Git-Tag: v0.1.0~2 X-Git-Url: https://git.nmode.ca/signal-cli/commitdiff_plain/fb862e4dde44caf0b5f32f6d55ea00e27146af0d Implement updateGroup --- diff --git a/src/main/java/cli/GroupInfo.java b/src/main/java/cli/GroupInfo.java index f3060d08..fe31baf9 100644 --- a/src/main/java/cli/GroupInfo.java +++ b/src/main/java/cli/GroupInfo.java @@ -2,9 +2,9 @@ package cli; import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.ArrayList; import java.util.Collection; -import java.util.List; +import java.util.HashSet; +import java.util.Set; public class GroupInfo { @JsonProperty @@ -14,11 +14,15 @@ public class GroupInfo { public String name; @JsonProperty - public List members = new ArrayList<>(); + public Set members = new HashSet<>(); @JsonProperty public long avatarId; + public GroupInfo(byte[] groupId) { + this.groupId = groupId; + } + public GroupInfo(@JsonProperty("groupId") byte[] groupId, @JsonProperty("name") String name, @JsonProperty("members") Collection members, @JsonProperty("avatarId") long avatarId) { this.groupId = groupId; this.name = name; diff --git a/src/main/java/cli/Main.java b/src/main/java/cli/Main.java index db2a941b..8aedd958 100644 --- a/src/main/java/cli/Main.java +++ b/src/main/java/cli/Main.java @@ -26,6 +26,7 @@ import org.whispersystems.textsecure.api.messages.multidevice.TextSecureSyncMess import org.whispersystems.textsecure.api.push.exceptions.EncapsulatedExceptions; import org.whispersystems.textsecure.api.push.exceptions.NetworkFailureException; import org.whispersystems.textsecure.api.push.exceptions.UnregisteredUserException; +import org.whispersystems.textsecure.api.util.InvalidNumberException; import java.io.File; import java.io.FileInputStream; @@ -99,14 +100,14 @@ public class Main { try { GroupInfo g = m.getGroupInfo(Base64.decode(ns.getString("group"))); if (g == null) { - System.err.println("Failed to send to grup \"" + ns.getString("group") + "\": Unknown group"); + System.err.println("Failed to send to group \"" + ns.getString("group") + "\": Unknown group"); System.err.println("Aborting sending."); System.exit(1); } groupId = g.groupId; - recipients = g.members; + recipients = new ArrayList<>(g.members); } catch (IOException e) { - System.err.println("Failed to send to grup \"" + ns.getString("group") + "\": " + e.getMessage()); + System.err.println("Failed to send to group \"" + ns.getString("group") + "\": " + e.getMessage()); System.err.println("Aborting sending."); System.exit(1); } @@ -123,11 +124,7 @@ public class Main { textSecureAttachments = new ArrayList<>(attachments.size()); for (String attachment : attachments) { try { - File attachmentFile = new File(attachment); - InputStream attachmentStream = new FileInputStream(attachmentFile); - final long attachmentSize = attachmentFile.length(); - String mime = Files.probeContentType(Paths.get(attachment)); - textSecureAttachments.add(new TextSecureAttachmentStream(attachmentStream, mime, attachmentSize, null)); + textSecureAttachments.add(createAttachment(attachment)); } catch (IOException e) { System.err.println("Failed to add attachment \"" + attachment + "\": " + e.getMessage()); System.err.println("Aborting sending."); @@ -186,14 +183,82 @@ public class Main { try { GroupInfo g = m.getGroupInfo(Base64.decode(ns.getString("group"))); if (g == null) { - System.err.println("Failed to send to grup \"" + ns.getString("group") + "\": Unknown group"); + System.err.println("Failed to send to group \"" + ns.getString("group") + "\": Unknown group"); System.err.println("Aborting sending."); System.exit(1); } - sendQuitGroupMessage(m, g.members, g.groupId); + sendQuitGroupMessage(m, new ArrayList<>(g.members), g.groupId); } catch (IOException e) { - System.err.println("Failed to send to grup \"" + ns.getString("group") + "\": " + e.getMessage()); + System.err.println("Failed to send to group \"" + ns.getString("group") + "\": " + e.getMessage()); + System.err.println("Aborting sending."); + System.exit(1); + } + break; + case "updateGroup": + if (!m.isRegistered()) { + System.err.println("User is not registered."); + System.exit(1); + } + + try { + GroupInfo g; + if (ns.getString("group") != null) { + g = m.getGroupInfo(Base64.decode(ns.getString("group"))); + if (g == null) { + System.err.println("Failed to send to group \"" + ns.getString("group") + "\": Unknown group"); + System.err.println("Aborting sending."); + System.exit(1); + } + } else { + // Create new group + g = new GroupInfo(Util.getSecretBytes(16)); + g.members.add(m.getUsername()); + System.out.println("Creating new group \"" + Base64.encodeBytes(g.groupId) + "\" …"); + } + + String name = ns.getString("name"); + if (name != null) { + g.name = name; + } + + final List members = ns.getList("member"); + + if (members != null) { + for (String member : members) { + try { + g.members.add(m.canonicalizeNumber(member)); + } catch (InvalidNumberException e) { + System.err.println("Failed to add member \"" + member + "\" to group: " + e.getMessage()); + System.err.println("Aborting…"); + System.exit(1); + } + } + } + + TextSecureGroup.Builder group = TextSecureGroup.newBuilder(TextSecureGroup.Type.UPDATE) + .withId(g.groupId) + .withName(g.name) + .withMembers(new ArrayList<>(g.members)); + + String avatar = ns.getString("avatar"); + if (avatar != null) { + try { + group.withAvatar(createAttachment(avatar)); + // TODO + g.avatarId = 0; + } catch (IOException e) { + System.err.println("Failed to add attachment \"" + avatar + "\": " + e.getMessage()); + System.err.println("Aborting sending."); + System.exit(1); + } + } + + m.setGroupInfo(g); + + sendUpdateGroupMessage(m, group.build()); + } catch (IOException e) { + System.err.println("Failed to send to group \"" + ns.getString("group") + "\": " + e.getMessage()); System.err.println("Aborting sending."); System.exit(1); } @@ -204,6 +269,14 @@ public class Main { System.exit(0); } + private static TextSecureAttachmentStream createAttachment(String attachment) throws IOException { + File attachmentFile = new File(attachment); + InputStream attachmentStream = new FileInputStream(attachmentFile); + final long attachmentSize = attachmentFile.length(); + String mime = Files.probeContentType(Paths.get(attachment)); + return new TextSecureAttachmentStream(attachmentStream, mime, attachmentSize, null); + } + private static Namespace parseArgs(String[] args) { ArgumentParser parser = ArgumentParsers.newArgumentParser("textsecure-cli") .defaultHelp(true) @@ -251,6 +324,17 @@ public class Main { .required(true) .help("Specify the recipient group ID."); + Subparser parserUpdateGroup = subparsers.addParser("updateGroup"); + parserUpdateGroup.addArgument("-g", "--group") + .help("Specify the recipient group ID."); + parserUpdateGroup.addArgument("-n", "--name") + .help("Specify the new group name."); + parserUpdateGroup.addArgument("-a", "--avatar") + .help("Specify a new group avatar image file"); + parserUpdateGroup.addArgument("-m", "--member") + .nargs("*") + .help("Specify one or more members to add to the group"); + Subparser parserReceive = subparsers.addParser("receive"); parserReceive.addArgument("-t", "--timeout") .type(int.class) @@ -298,7 +382,10 @@ public class Main { private static void sendQuitGroupMessage(Manager m, List recipients, byte[] groupId) { final TextSecureDataMessage.Builder messageBuilder = TextSecureDataMessage.newBuilder(); - TextSecureGroup group = TextSecureGroup.newBuilder(TextSecureGroup.Type.QUIT).withId(groupId).build(); + TextSecureGroup group = TextSecureGroup.newBuilder(TextSecureGroup.Type.QUIT) + .withId(groupId) + .build(); + messageBuilder.asGroupMessage(group); TextSecureDataMessage message = messageBuilder.build(); @@ -306,6 +393,16 @@ public class Main { sendMessage(m, message, recipients); } + private static void sendUpdateGroupMessage(Manager m, TextSecureGroup g) { + final TextSecureDataMessage.Builder messageBuilder = TextSecureDataMessage.newBuilder(); + + messageBuilder.asGroupMessage(g); + + TextSecureDataMessage message = messageBuilder.build(); + + sendMessage(m, message, g.getMembers().get()); + } + private static void sendMessage(Manager m, TextSecureDataMessage message, List recipients) { try { m.sendMessage(recipients, message); diff --git a/src/main/java/cli/Manager.java b/src/main/java/cli/Manager.java index a1d58f03..a0fb5b01 100644 --- a/src/main/java/cli/Manager.java +++ b/src/main/java/cli/Manager.java @@ -44,9 +44,7 @@ import org.whispersystems.textsecure.api.util.InvalidNumberException; import org.whispersystems.textsecure.api.util.PhoneNumberFormatter; import java.io.*; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; +import java.util.*; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -248,7 +246,7 @@ class Manager { TextSecureMessageSender messageSender = new TextSecureMessageSender(URL, TRUST_STORE, username, password, axolotlStore, USER_AGENT, Optional.absent()); - List recipientsTS = new ArrayList<>(recipients.size()); + Set recipientsTS = new HashSet<>(recipients.size()); for (String recipient : recipients) { try { recipientsTS.add(getPushAddress(recipient)); @@ -259,7 +257,7 @@ class Manager { } } - messageSender.sendMessage(recipientsTS, message); + messageSender.sendMessage(new ArrayList<>(recipientsTS), message); if (message.isEndSession()) { for (TextSecureAddress recipient : recipientsTS) { @@ -309,20 +307,32 @@ class Manager { TextSecureGroup groupInfo = message.getGroupInfo().get(); switch (groupInfo.getType()) { case UPDATE: - long avatarId = 0; + group = groupStore.getGroup(groupInfo.getGroupId()); + if (group == null) { + group = new GroupInfo(groupInfo.getGroupId()); + } + if (groupInfo.getAvatar().isPresent()) { TextSecureAttachment avatar = groupInfo.getAvatar().get(); if (avatar.isPointer()) { - avatarId = avatar.asPointer().getId(); + long avatarId = avatar.asPointer().getId(); try { retrieveAttachment(avatar.asPointer()); + group.avatarId = avatarId; } catch (IOException | InvalidMessageException e) { System.err.println("Failed to retrieve group avatar (" + avatarId + "): " + e.getMessage()); } } } - group = new GroupInfo(groupInfo.getGroupId(), groupInfo.getName().get(), groupInfo.getMembers().get(), avatarId); + if (groupInfo.getName().isPresent()) { + group.name = groupInfo.getName().get(); + } + + if (groupInfo.getMembers().isPresent()) { + group.members.addAll(groupInfo.getMembers().get()); + } + groupStore.updateGroup(group); break; case DELIVER: @@ -419,7 +429,7 @@ class Manager { return outputFile; } - private String canonicalizeNumber(String number) throws InvalidNumberException { + public String canonicalizeNumber(String number) throws InvalidNumberException { String localNumber = username; return PhoneNumberFormatter.formatNumber(number, localNumber); } @@ -432,4 +442,12 @@ class Manager { public GroupInfo getGroupInfo(byte[] groupId) { return groupStore.getGroup(groupId); } + + public void setGroupInfo(GroupInfo group) { + groupStore.updateGroup(group); + } + + public String getUsername() { + return username; + } } diff --git a/src/main/java/cli/Util.java b/src/main/java/cli/Util.java index 907c4ed1..215e14b8 100644 --- a/src/main/java/cli/Util.java +++ b/src/main/java/cli/Util.java @@ -9,7 +9,7 @@ class Util { return Base64.encodeBytes(secret); } - private static byte[] getSecretBytes(int size) { + public static byte[] getSecretBytes(int size) { byte[] secret = new byte[size]; getSecureRandom().nextBytes(secret); return secret;