Commit c6a6a89f authored by Nick Catalano's avatar Nick Catalano

Merge master into feature branch

parents 9394a4c5 8f8089d3
stages:
- check for updates
- build
- test
- publish
- cleanup
......@@ -42,6 +43,9 @@ build-java-11:
- schedules
script:
- make installDist
artifacts:
paths:
- build/
deb:build:
stage: build
......@@ -57,6 +61,8 @@ deb:build:
- cp -rv ../signald_${VERSION}* .
except:
- schedules
cache:
key: deb-builder
artifacts:
paths:
- "signald_*"
......@@ -108,6 +114,21 @@ deb:publish:
dependencies:
- deb:build
test:integration:
image: openjdk:11-jdk
stage: test
before_script:
- cp tools/log4j2.xml src/main/resources/log4j2.xml
script:
- ./gradlew integrationTest --info
variables:
SIGNAL_URL: $SIGNAL_TEST_SERVER
artifacts:
reports:
junit: build/test-results/integrationTest/TEST-*.xml
dependencies:
- build-java-11
deb:cleanup:
stage: cleanup
image: registry.git.callpipe.com/finn/debian-repo-builder:latest
......
......@@ -19,6 +19,10 @@ deb:
installDist distTar:
$(GRADLE) $@
integrationTest:
export SIGNAL_URL=https://signal-server.signald.org
$(GRADLE) $@
setup:
sudo mkdir -p /var/run/signald
sudo chown $(shell whoami) /var/run/signald
......@@ -16,8 +16,24 @@ repositories {
mavenCentral()
}
sourceSets {
integrationTest {
java {
compileClasspath += main.output + test.output
runtimeClasspath += main.output + test.output
srcDir file('src/integration-tests/java')
}
resources.srcDir file('src/integration-tests/resources')
}
}
configurations {
integrationTestCompile.extendsFrom testCompile
integrationTestRuntime.extendsFrom testRuntime
}
dependencies {
compile 'com.github.turasa:signal-service-java:2.13.0_unofficial_2'
compile 'com.github.turasa:signal-service-java:2.13.9_unofficial_1'
compile 'org.bouncycastle:bcprov-jdk15on:1.64'
compile 'com.kohlschutter.junixsocket:junixsocket-common:2.2.0'
compile 'com.kohlschutter.junixsocket:junixsocket-native-common:2.2.0'
......@@ -26,6 +42,8 @@ dependencies {
compile 'io.sentry:sentry-log4j2:1.7.28'
compile 'org.slf4j:slf4j-nop:1.8.0-beta4'
compile 'info.picocli:picocli:4.0.4'
testCompile 'org.junit.jupiter:junit-jupiter-api:5.3.1'
testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.3.1'
}
......@@ -92,3 +110,11 @@ task checkLibVersions {
}
}
}
task integrationTest(type: Test) {
useJUnitPlatform()
testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath
outputs.upToDateWhen { false }
}
......@@ -7,7 +7,8 @@ export DH_VERBOSE=1
dh $@ --with javahelper --with systemd # use the javahelper extension
override_dh_auto_build:
dh_auto_build -- installDist
# dh_auto_build -- installDist
./gradlew installDist
override_dh_install:
$(RM) build/install/signald/bin/signald.bat
......
#Fri Mar 22 11:48:16 PDT 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.3-bin.zip
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
......@@ -28,16 +44,16 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
warn () {
echo "$*"
}
die ( ) {
die () {
echo
echo "$*"
echo
......@@ -155,7 +171,7 @@ if $cygwin ; then
fi
# Escape application args
save ( ) {
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
......
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem http://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
......@@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
......
package io.finn.signald;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RunnableMain implements Runnable {
private String socketPath;
public RunnableMain(String s) {
this.socketPath = s;
}
public void run() {
System.out.println("Starting signald with socket " + this.socketPath);
Main.main(new String[]{"-v", "-s", this.socketPath});
}
}
package io.finn.signald;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.Assertions;
import org.newsclub.net.unix.AFUNIXSocket;
import org.newsclub.net.unix.AFUNIXSocketAddress;
import java.io.File;
import java.io.BufferedReader;
import java.io.PrintWriter;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ThreadLocalRandom;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;
import io.finn.signald.testhelpers.RequestBuilder;
import io.finn.signald.testhelpers.MiscHelpers;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestRequest {
private Thread signaldMain;
private static File SOCKET_FILE = new File(System.getProperty("java.io.tmpdir"), "signald.sock");
private AFUNIXSocket socket;
private PrintWriter writer;
private BufferedReader reader;
static final Logger logger = LoggerFactory.getLogger(TestRequest.class);
private ObjectMapper mpr = new ObjectMapper();
@BeforeAll
public void startSignald() throws InterruptedException {
signaldMain = new Thread(new RunnableMain(SOCKET_FILE.getAbsolutePath()), "main");
signaldMain.start();
while(!SOCKET_FILE.exists()) {
logger.info("Waiting for " + SOCKET_FILE.getAbsolutePath() + " to exist...");
TimeUnit.SECONDS.sleep(1);
}
}
@AfterAll
public void stopSignald() {
signaldMain.interrupt();
}
@BeforeEach
public void connectSocket() throws IOException {
socket = AFUNIXSocket.newInstance();
socket.connect(new AFUNIXSocketAddress(SOCKET_FILE));
writer = new PrintWriter(socket.getOutputStream(), true);
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println(reader.readLine());
}
@AfterEach
public void disconnectSocket() throws IOException {
socket.close();
}
@DisplayName("Register a new account")
@Test
public void RegisterAccount() throws IOException {
String username = String.format("+1202555%04d", ThreadLocalRandom.current().nextInt(0, 10000));
writer.println(RequestBuilder.register(username));
JsonNode root = mpr.readTree(reader.readLine());
Assertions.assertEquals(root.findValue("type").textValue(), "verification_required");
String code = MiscHelpers.getVerificationCode(username);
System.out.println("Got verification code " + code);
writer.println(RequestBuilder.verify(username, code));
root = (JsonNode)mpr.readTree(reader.readLine());
System.out.println(root);
Assertions.assertEquals(root.findValue("type").textValue(), "verification_succeeded");
}
@DisplayName("List registered accounts")
@Test
public void ListAccounts() throws IOException {
writer.println(RequestBuilder.listAccounts());
JsonNode root = mpr.readTree(reader.readLine());
Assertions.assertEquals(root.findValue("type").textValue(), "account_list");
for(final JsonNode user : root.get("data")) {
if(user != null && user.get("username") != null) {
System.out.println("Got account " + user.get("username").textValue());
} else {
System.out.println("No accounts found, but no errors either!");
}
}
}
}
package io.finn.signald.testhelpers;
import okhttp3.OkHttpClient;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
import io.finn.signald.BuildConfig;
public class MiscHelpers {
public static String getVerificationCode(String username) throws IOException {
OkHttpClient client = new OkHttpClient();
RequestBody body = new MultipartBody.Builder().setType(MultipartBody.FORM).addFormDataPart("number", username).build();
Request request = new Request.Builder().url(BuildConfig.SIGNAL_URL + "/helper/verification-code").post(body).build();
Response response = client.newCall(request).execute();
return response.body().string().trim();
}
}
package io.finn.signald.testhelpers;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
public class RequestBuilder {
private static ObjectMapper mpr = new ObjectMapper();
public static String register(String username) {
ObjectNode req = mpr.createObjectNode();
req.put("type", "register");
req.put("username", username);
return req.toString();
}
public static String verify(String username, String code) {
ObjectNode req = mpr.createObjectNode();
req.put("type", "verify");
req.put("username", username);
req.put("code", code);
return req.toString();
}
public static String listAccounts() {
ObjectNode req = mpr.createObjectNode();
req.put("type", "list_accounts");
return req.toString();
}
}
......@@ -38,6 +38,7 @@ class JsonAttachment {
String preview;
String key;
String digest;
String blurhash;
JsonAttachment() {}
......@@ -73,6 +74,10 @@ class JsonAttachment {
this.caption = pointer.getCaption().get();
}
if(pointer.getBlurHash().isPresent()) {
this.blurhash = pointer.getBlurHash().get();
}
if( m != null) {
File file = m.getAttachmentFile(pointer.getId());
if( file.exists()) {
......
......@@ -26,12 +26,9 @@ class JsonUntrustedIdentityException {
public String fingerprint;
public String safety_number;
JsonUntrustedIdentityException(UntrustedIdentityException e, Manager m) {
this.number = e.getE164Number();
IdentityKey key = e.getIdentityKey();
JsonUntrustedIdentityException(IdentityKey key, String number, Manager m) {
this.number = number;
this.fingerprint = Hex.toStringCondensed(key.getPublicKey().serialize());
this.safety_number = m.computeSafetyNumber(this.number, key);
}
}
This diff is collapsed.
......@@ -23,6 +23,7 @@ import org.whispersystems.signalservice.internal.util.Base64;
import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.push.ContactTokenDetails;
import org.whispersystems.signalservice.api.messages.SendMessageResult;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream;
......@@ -30,7 +31,6 @@ import org.whispersystems.signalservice.api.push.exceptions.NetworkFailureExcept
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.asamk.signal.AttachmentInvalidException;
import org.asamk.signal.UserAlreadyExists;
import org.asamk.signal.GroupNotFoundException;
......@@ -230,7 +230,7 @@ public class SocketHandler implements Runnable {
mime = "application/octet-stream";
}
attachments.add(new SignalServiceAttachmentStream(attachmentStream, mime, attachmentSize, Optional.of(attachmentFile.getName()), attachment.voiceNote, attachment.getPreview(), attachment.width, attachment.height, Optional.fromNullable(attachment.caption), null));
attachments.add(new SignalServiceAttachmentStream(attachmentStream, mime, attachmentSize, Optional.of(attachmentFile.getName()), attachment.voiceNote, attachment.getPreview(), attachment.width, attachment.height, Optional.fromNullable(attachment.caption), Optional.fromNullable(attachment.blurhash), null));
} catch (IOException e) {
throw new AttachmentInvalidException(attachment.filename, e);
}
......@@ -238,17 +238,41 @@ public class SocketHandler implements Runnable {
}
try {
List<SendMessageResult> sendMessageResults;
if(request.recipientGroupId != null) {
byte[] groupId = Base64.decode(request.recipientGroupId);
manager.sendGroupMessage(request.messageBody, attachments, groupId, quote);
sendMessageResults = manager.sendGroupMessage(request.messageBody, attachments, groupId, quote);
} else {
manager.sendMessage(request.messageBody, attachments, request.recipientNumber, quote);
sendMessageResults = manager.sendMessage(request.messageBody, attachments, request.recipientNumber, quote);
}
this.reply("success", new JsonStatusMessage(0, "success"), request.id);
} catch(EncapsulatedExceptions e) {
for(UntrustedIdentityException i: e.getUntrustedIdentityExceptions()) {
this.reply("untrusted_identity", new JsonUntrustedIdentityException(i, manager), request.id);
for(SendMessageResult result: sendMessageResults) {
SendMessageResult.Success success = result.getSuccess();
if(success != null) {
if(success.isUnidentified()) {
this.reply("success", new JsonStatusMessage(0, "successfully send unidentified message"), request.id);
}
if(success.isNeedsSync()) {
this.reply("success", new JsonStatusMessage(1, "isNeedsSync = true"), request.id);
}
}
if(result.isNetworkFailure()) {
// TODO: Log more info about what message failed, who it failed to, and any other info needed to resend
this.reply("network_failure", null, request.id);
}
if(result.isUnregisteredFailure()) {
this.reply("unregistered_user", null, request.id);
}
SendMessageResult.IdentityFailure identityFailure = result.getIdentityFailure();
if(identityFailure != null) {
this.reply("untrusted_identity", new JsonUntrustedIdentityException(identityFailure.getIdentityKey(), result.getAddress().getNumber(), manager), request.id);
}
}
} catch(EncapsulatedExceptions e) {
for(UnregisteredUserException i: e.getUnregisteredUserExceptions()) {
this.reply("unregistered_user", new JsonUnregisteredUserException(i), request.id);
}
......
-----BEGIN CERTIFICATE-----
MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
-----END CERTIFICATE-----
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="warn" packages="org.apache.logging.log4j.core,io.sentry.log4j2">
<appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
<Sentry name="Sentry" />
</appenders>
<loggers>
<root level="DEBUG">
<appender-ref ref="Console" />
<!-- Note that the Sentry logging threshold is overridden to the WARN level -->
<appender-ref ref="Sentry" level="WARN" />
</root>
</loggers>
</configuration>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment