Skip to content

Commit

Permalink
More work on API stability
Browse files Browse the repository at this point in the history
  • Loading branch information
Auties00 committed Oct 9, 2023
1 parent 53b9b5b commit a60c028
Show file tree
Hide file tree
Showing 62 changed files with 1,666 additions and 2,002 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/target/
.test/
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@
<maven.nexus.plugin.version>1.6.13</maven.nexus.plugin.version>
<bouncy.castle.version>1.70</bouncy.castle.version>
<zxing.version>3.5.1</zxing.version>
<protoc.version>3.0.0</protoc.version>
<protoc.version>3.0.1</protoc.version>
<checker.framework.version>3.37.0</checker.framework.version>
<junit.version>5.10.0-M1</junit.version>
<jna.version>5.13.0</jna.version>
Expand Down
11 changes: 10 additions & 1 deletion src/main/java/it/auties/whatsapp/api/ErrorHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,19 @@ static ErrorHandler defaultErrorHandler(Consumer<Throwable> printer) {
if(printer != null) {
printer.accept(throwable);
}
if (location == INITIAL_APP_STATE_SYNC || (location == CRYPTOGRAPHY && type != ClientType.MOBILE) || (location == MESSAGE && throwable instanceof HmacValidationException)) {

if(location == CRYPTOGRAPHY && type == ClientType.MOBILE) {
logger.log(WARNING, "Reconnecting");
return Result.RECONNECT;
}

if (location == INITIAL_APP_STATE_SYNC
|| location == CRYPTOGRAPHY
|| (location == MESSAGE && throwable instanceof HmacValidationException)) {
logger.log(WARNING, "Socket failure at %s".formatted(location));
return Result.RESTORE;
}

logger.log(WARNING, "Ignored failure");
return Result.DISCARD;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,27 +47,27 @@ public T verificationCodeSupplier(@NonNull Supplier<String> verificationCodeSupp
}

/**
* Sets the handler that provides the captcha newsletters when verifying an account
* Happens only on business devices
* Sets the handler that provides the verification code when verifying an account
*
* @param verificationCaptchaSupplier the non-null supplier
* @param verificationCodeSupplier the non-null supplier
* @return the same instance
*/
@SuppressWarnings("unchecked")
public T verificationCaptchaSupplier(@NonNull Function<VerificationCodeResponse, String> verificationCaptchaSupplier) {
this.verificationCaptchaSupplier = AsyncCaptchaCodeSupplier.of(verificationCaptchaSupplier);
public T verificationCodeSupplier(@NonNull AsyncVerificationCodeSupplier verificationCodeSupplier) {
this.verificationCodeSupplier = verificationCodeSupplier;
return (T) this;
}

/**
* Sets the handler that provides the verification code when verifying an account
* Sets the handler that provides the captcha newsletters when verifying an account
* Happens only on business devices
*
* @param verificationCodeSupplier the non-null supplier
* @param verificationCaptchaSupplier the non-null supplier
* @return the same instance
*/
@SuppressWarnings("unchecked")
public T verificationCodeSupplier(@NonNull AsyncVerificationCodeSupplier verificationCodeSupplier) {
this.verificationCodeSupplier = verificationCodeSupplier;
public T verificationCaptchaSupplier(@NonNull Function<VerificationCodeResponse, String> verificationCaptchaSupplier) {
this.verificationCaptchaSupplier = AsyncCaptchaCodeSupplier.of(verificationCaptchaSupplier);
return (T) this;
}

Expand Down
299 changes: 36 additions & 263 deletions src/main/java/it/auties/whatsapp/api/Whatsapp.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import it.auties.whatsapp.model.chat.ChatBuilder;
import it.auties.whatsapp.model.jid.Jid;
import it.auties.whatsapp.model.mobile.PhoneNumber;
import it.auties.whatsapp.model.newsletter.Newsletter;
import it.auties.whatsapp.util.Smile;
import it.auties.whatsapp.util.Validate;
import org.checkerframework.checker.nullness.qual.NonNull;
Expand All @@ -18,6 +19,7 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

Expand All @@ -29,6 +31,7 @@
public class DefaultControllerSerializer implements ControllerSerializer {
private static final Path DEFAULT_DIRECTORY = Path.of(System.getProperty("user.home") + "/.cobalt/");
private static final String CHAT_PREFIX = "chat_";
private static final String NEWSLETTER_PREFIX = "newsletter_";
private static final String STORE_NAME = "store.smile";
private static final String KEYS_NAME = "keys.smile";
private static final ControllerSerializer DEFAULT_SERIALIZER = new DefaultControllerSerializer();
Expand Down Expand Up @@ -162,7 +165,11 @@ public CompletableFuture<Void> serializeStore(Store store, boolean async) {
}

var chatsFutures = serializeChatsAsync(store);
var result = CompletableFuture.allOf(chatsFutures).thenRunAsync(() -> {
var newslettersFutures = serializeNewslettersAsync(store);
var dependableFutures = Stream.of(chatsFutures, newslettersFutures)
.flatMap(Arrays::stream)
.toArray(CompletableFuture[]::new);
var result = CompletableFuture.allOf(dependableFutures).thenRunAsync(() -> {
var storePath = getSessionFile(store, STORE_NAME);
writeFile(store, STORE_NAME, storePath);
});
Expand Down Expand Up @@ -191,6 +198,19 @@ private CompletableFuture<Void> serializeChatAsync(Store store, Chat chat) {
return CompletableFuture.runAsync(() -> writeFile(chat, fileName, outputFile));
}

private CompletableFuture<?>[] serializeNewslettersAsync(Store store) {
return store.newsletters()
.stream()
.map(newsletter -> serializeNewsletterAsync(store, newsletter))
.toArray(CompletableFuture[]::new);
}

private CompletableFuture<Void> serializeNewsletterAsync(Store store, Newsletter newsletter) {
var fileName = NEWSLETTER_PREFIX + newsletter.jid() + ".smile";
var outputFile = getSessionFile(store, fileName);
return CompletableFuture.runAsync(() -> writeFile(newsletter, fileName, outputFile));
}

private void writeFile(Object object, String fileName, Path outputFile) {
try {
var tempFile = Files.createTempFile(fileName, ".tmp");
Expand Down Expand Up @@ -298,8 +318,8 @@ public CompletableFuture<Void> attributeStore(Store store) {
return CompletableFuture.completedFuture(null);
}
try (var walker = Files.walk(directory)) {
var futures = walker.filter(entry -> entry.getFileName().toString().startsWith(CHAT_PREFIX))
.map(entry -> CompletableFuture.runAsync(() -> deserializeChat(store, entry)))
var futures = walker.map(entry -> handleStoreFile(store, entry))
.filter(Objects::nonNull)
.toArray(CompletableFuture[]::new);
var result = CompletableFuture.allOf(futures);
attributeStoreSerializers.put(store.uuid(), result);
Expand All @@ -309,6 +329,37 @@ public CompletableFuture<Void> attributeStore(Store store) {
}
}

private CompletableFuture<Void> handleStoreFile(Store store, Path entry) {
return switch (FileType.of(entry)) {
case UNKNOWN -> null;
case NEWSLETTER -> CompletableFuture.runAsync(() -> deserializeNewsletter(store, entry));
case CHAT -> CompletableFuture.runAsync(() -> deserializeChat(store, entry));
};
}

private enum FileType {
UNKNOWN(null),
CHAT(CHAT_PREFIX),
NEWSLETTER(NEWSLETTER_PREFIX);

private final String prefix;

FileType(String prefix) {
this.prefix = prefix;
}

private static FileType of(Path path) {
return Arrays.stream(values())
.filter(entry -> entry.prefix() != null && path.getFileName().toString().startsWith(entry.prefix()))
.findFirst()
.orElse(UNKNOWN);
}

private String prefix() {
return prefix;
}
}

@Override
public void deleteSession(@NonNull Controller<?> controller) {
try {
Expand Down Expand Up @@ -381,12 +432,39 @@ private Chat rescueChat(Path entry) {
.build();
}

private void deserializeNewsletter(Store store, Path newsletterFile) {
try (var input = new GZIPInputStream(Files.newInputStream(newsletterFile))) {
store.addNewsletter(Smile.readValue(input, Newsletter.class));
} catch (IOException exception) {
store.addNewsletter(rescueNewsletter(newsletterFile));
}
}

private Newsletter rescueNewsletter(Path entry) {
try {
Files.deleteIfExists(entry);
} catch (IOException ignored) {

}
var newsletterName = entry.getFileName().toString()
.replaceFirst(CHAT_PREFIX, "")
.replace(".smile", "")
.replaceAll("~~", ":");
return new Newsletter(Jid.of(newsletterName), null, null, null);
}

private Path getHome(ClientType type) {
return baseDirectory.resolve(type == ClientType.MOBILE ? "mobile" : "web");
}

private Path getSessionDirectory(ClientType clientType, String path) {
return getHome(clientType).resolve(path);
try {
var result = getHome(clientType).resolve(path);
Files.createDirectories(result.getParent());
return result;
}catch (IOException exception) {
throw new UncheckedIOException(exception);
}
}

private Path getSessionFile(Store store, String fileName) {
Expand Down
8 changes: 4 additions & 4 deletions src/main/java/it/auties/whatsapp/controller/Keys.java
Original file line number Diff line number Diff line change
Expand Up @@ -576,12 +576,12 @@ public boolean initialAppSync() {
return this.readCounter;
}

public byte[] writeKey() {
return this.writeKey;
public Optional<byte[]> writeKey() {
return Optional.ofNullable(this.writeKey);
}

public byte[] readKey() {
return this.readKey;
public Optional<byte[]> readKey() {
return Optional.ofNullable(this.readKey);
}

public Keys setCompanionKeyPair(SignalKeyPair companionKeyPair) {
Expand Down
Loading

0 comments on commit a60c028

Please sign in to comment.