diff --git a/.gitignore b/.gitignore
index 402113aaf..5d8fc1bad 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,3 +46,7 @@ qrc_*
Makefile
*.qm
/.qmake.stash
+
+# Rust
+target/
+.cargo/
diff --git a/.travis.yml b/.travis.yml
index 6bd884b0f..5843441bd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -10,6 +10,9 @@ branches:
matrix:
fast_finish: true
+ allow_failures:
+ - rust: beta
+ - rust: nightly
include:
- os: linux
env: JOB=verify-commit-format
@@ -24,6 +27,18 @@ matrix:
# the actual compilin'
- os: linux
env: JOB=build-ubuntu-14-04
+ - os: linux
+ env: JOB=build-rust
+ language: rust
+ rust: stable
+ - os: linux
+ env: JOB=build-rust
+ language: rust
+ rust: beta
+ - os: linux
+ env: JOB=build-rust
+ language: rust
+ rust: nightly
- os: osx
osx_image: xcode7.3
env: JOB=build-osx
diff --git a/.travis/build-rust.sh b/.travis/build-rust.sh
new file mode 100755
index 000000000..6ff00ff1d
--- /dev/null
+++ b/.travis/build-rust.sh
@@ -0,0 +1,71 @@
+#!/bin/bash
+#
+# Copyright © 2016 by The qTox Project Contributors
+#
+# This program is libre software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+
+# used in travis to:
+# - pull in dependencies for building libsodium
+# - build required libsodium
+
+# Fail out on error
+set -eu -o pipefail
+
+readonly LIBSODIUM_VER="1.0.11"
+
+build_libsodium() {
+ sudo apt-get update -qq
+ sudo apt-get install -y \
+ build-essential \
+ libtool \
+ autotools-dev \
+ automake \
+ checkinstall \
+ check \
+ git \
+ yasm \
+ pkg-config || yes
+
+ git clone https://github.com/jedisct1/libsodium.git libsodium \
+ --branch $LIBSODIUM_VER \
+ --depth 1
+
+ cd libsodium
+ ./autogen.sh
+ ./configure
+ make -j$(nproc)
+ sudo checkinstall \
+ --install \
+ --pkgname libsodium \
+ --pkgversion $LIBSODIUM_VER \
+ --nodoc \
+ -y
+ sudo ldconfig
+ cd ..
+}
+
+build_rust_bits() {
+ # TODO: make it a loop over paths once there are more rust bits
+ cd tools/update-server/qtox-updater-sign
+ cargo build --verbose
+ cargo test --verbose
+ # add `cargo doc` once it's needed?
+}
+
+main() {
+ build_libsodium
+ build_rust_bits
+}
+main
diff --git a/tools/update-server/qtox-updater-sign/Cargo.lock b/tools/update-server/qtox-updater-sign/Cargo.lock
new file mode 100644
index 000000000..633b2f118
--- /dev/null
+++ b/tools/update-server/qtox-updater-sign/Cargo.lock
@@ -0,0 +1,41 @@
+[root]
+name = "qtox-updater-sign"
+version = "0.1.0"
+dependencies = [
+ "sodiumoxide 0.0.12 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "libsodium-sys"
+version = "0.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "serde"
+version = "0.7.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "sodiumoxide"
+version = "0.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libsodium-sys 0.0.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 0.7.15 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
diff --git a/tools/update-server/qtox-updater-sign/Cargo.toml b/tools/update-server/qtox-updater-sign/Cargo.toml
new file mode 100644
index 000000000..3685eb3e0
--- /dev/null
+++ b/tools/update-server/qtox-updater-sign/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "qtox-updater-sign"
+version = "0.1.0"
+authors = ["Zetok Zalbavar "]
+
+[dependencies]
+sodiumoxide = "0.0.12"
diff --git a/tools/update-server/qtox-updater-sign/README.md b/tools/update-server/qtox-updater-sign/README.md
new file mode 100644
index 000000000..d3101114d
--- /dev/null
+++ b/tools/update-server/qtox-updater-sign/README.md
@@ -0,0 +1,26 @@
+# qtox-updater-sign
+
+Simple program for signing releases.
+
+Requires a file named `qtox-updater-skey` in the working directory that
+contains the secret key.
+
+To sign a release, either supply the name of the file to be signed as an
+argument, or pipe data via stdin.
+
+```bash
+./qtox-updater-sign sign-this-binary > signature_file
+# or
+./qtox-updater-sign < sign-this-binary > signature_file
+```
+
+# Compiling
+
+Requires `libsodium` and Rust.
+
+To build a debug version: `cargo build`
+
+To build a release version: `cargo build --release`
+
+This will produce `qtox-updater-sign` binary in `target/debug` or
+`target/release` directory respectively.
diff --git a/tools/update-server/qtox-updater-sign/main.cpp b/tools/update-server/qtox-updater-sign/main.cpp
deleted file mode 100644
index 21f1c252d..000000000
--- a/tools/update-server/qtox-updater-sign/main.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-#include
-#include
-#include
-
-using namespace std;
-
-int main(int argc, char* argv[])
-{
- QFile io;
- QByteArray msg;
- if (argc > 1)
- {
- msg = QByteArray(argv[1]);
- }
- else
- {
- io.open(stdin, QIODevice::ReadOnly);
- msg = io.readAll();
- io.close();
- }
- io.open(stdout, QIODevice::WriteOnly);
-
- QFile skeyFile("qtox-updater-skey");
- if (!skeyFile.open(QIODevice::ReadOnly))
- {
- io.write("ERROR: qtox-updater-sign can't open the secret (private) key file\n");
- io.close();
- return 1;
- }
- QByteArray skeyData = skeyFile.readAll();
- skeyFile.close();
-
- unsigned char sig[crypto_sign_BYTES];
- crypto_sign_detached(sig, nullptr, (unsigned char*)msg.data(), msg.size(), (unsigned char*)skeyData.data());
-
- io.write((char*)sig, crypto_sign_BYTES);
- io.write(msg);
- io.close();
-
- return 0;
-}
-
diff --git a/tools/update-server/qtox-updater-sign/qtox-updater-sign.pro b/tools/update-server/qtox-updater-sign/qtox-updater-sign.pro
deleted file mode 100644
index 5f9c65af3..000000000
--- a/tools/update-server/qtox-updater-sign/qtox-updater-sign.pro
+++ /dev/null
@@ -1,7 +0,0 @@
-TEMPLATE = app
-CONFIG += console c++11
-QT += core
-
-SOURCES += main.cpp
-
-LIBS += -lsodium
diff --git a/tools/update-server/qtox-updater-sign/src/main.rs b/tools/update-server/qtox-updater-sign/src/main.rs
new file mode 100644
index 000000000..4fb2a865a
--- /dev/null
+++ b/tools/update-server/qtox-updater-sign/src/main.rs
@@ -0,0 +1,66 @@
+/*
+ Copyright © 2016 by The qTox Project Contributors
+
+ This file is part of qTox, a Qt-based graphical interface for Tox.
+
+ qTox is libre software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ qTox is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with qTox. If not, see .
+*/
+
+extern crate sodiumoxide;
+
+use std::io::prelude::*;
+use std::io;
+use std::fs::File;
+
+use sodiumoxide::crypto::sign::*;
+
+/// file that contains secret key
+const SKEY_FNAME: &'static str = "qtox-updater-skey";
+
+/// if there is more than just the program name in args, treat "1st" arg as the
+/// data to sign
+/// otherwise just read bytes from stdin
+fn read_from_file_or_stdin(buf: &mut Vec) {
+ if std::env::args().count() > 1 {
+ let data = std::env::args().nth(1).expect("Failed to get fname");
+ buf.extend_from_slice(&data.into_bytes());
+ } else {
+ io::stdin().read_to_end(buf).expect("Failed to read stdin");
+ }
+}
+
+/// get SecretKey from `SKEY_FNAME` file
+fn get_secret_key() -> SecretKey {
+ let mut skey_file = File::open(SKEY_FNAME)
+ .expect(&format!("ERROR: {} can't open the secret (private) key file\n",
+ std::env::args().next().unwrap()));
+
+ let mut skey_bytes = Vec::with_capacity(SECRETKEYBYTES);
+ skey_file.read_to_end(&mut skey_bytes)
+ .expect(&format!("Failed to read {}", SKEY_FNAME));
+
+ SecretKey::from_slice(&skey_bytes[..SECRETKEYBYTES])
+ .expect("Failed to get right amount of bytes for SecretKey")
+}
+
+
+fn main() {
+ let mut plaintext = Vec::new();
+ read_from_file_or_stdin(&mut plaintext);
+
+ let sk = get_secret_key();
+ let signed = sign(&plaintext, &sk);
+
+ io::stdout().write_all(&signed).expect("Failed to write signature");
+}