Initial /sendfile OMEMO encryption
This commit is contained in:
@@ -73,6 +73,7 @@
|
|||||||
#include "plugins/plugins.h"
|
#include "plugins/plugins.h"
|
||||||
#include "ui/ui.h"
|
#include "ui/ui.h"
|
||||||
#include "ui/window_list.h"
|
#include "ui/window_list.h"
|
||||||
|
#include "omemo/crypto.h"
|
||||||
#include "xmpp/xmpp.h"
|
#include "xmpp/xmpp.h"
|
||||||
#include "xmpp/connection.h"
|
#include "xmpp/connection.h"
|
||||||
#include "xmpp/contact.h"
|
#include "xmpp/contact.h"
|
||||||
@@ -4809,7 +4810,9 @@ gboolean
|
|||||||
cmd_sendfile(ProfWin* window, const char* const command, gchar** args)
|
cmd_sendfile(ProfWin* window, const char* const command, gchar** args)
|
||||||
{
|
{
|
||||||
jabber_conn_status_t conn_status = connection_get_status();
|
jabber_conn_status_t conn_status = connection_get_status();
|
||||||
char* filename = args[0];
|
char *filename = args[0];
|
||||||
|
char *filepath = NULL;
|
||||||
|
unsigned char *key = NULL;
|
||||||
|
|
||||||
// expand ~ to $HOME
|
// expand ~ to $HOME
|
||||||
if (filename[0] == '~' && filename[1] == '/') {
|
if (filename[0] == '~' && filename[1] == '/') {
|
||||||
@@ -4820,45 +4823,103 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args)
|
|||||||
filename = strdup(filename);
|
filename = strdup(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
filepath = strdup(filename);
|
||||||
|
|
||||||
if (conn_status != JABBER_CONNECTED) {
|
if (conn_status != JABBER_CONNECTED) {
|
||||||
cons_show("You are not currently connected.");
|
cons_show("You are not currently connected.");
|
||||||
free(filename);
|
goto out;
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window->type != WIN_CHAT && window->type != WIN_PRIVATE && window->type != WIN_MUC) {
|
if (window->type != WIN_CHAT && window->type != WIN_PRIVATE && window->type != WIN_MUC) {
|
||||||
cons_show_error("Unsupported window for file transmission.");
|
cons_show_error("Unsupported window for file transmission.");
|
||||||
free(filename);
|
goto out;
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (window->type) {
|
switch (window->type) {
|
||||||
case WIN_MUC:
|
case WIN_MUC:
|
||||||
{
|
case WIN_CHAT:
|
||||||
ProfMucWin* mucwin = (ProfMucWin*)window;
|
{
|
||||||
assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
|
ProfChatWin *chatwin = (ProfChatWin*)window;
|
||||||
|
assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
|
||||||
|
|
||||||
// only omemo, no pgp/otr available in MUCs
|
if (chatwin->is_omemo && !prefs_get_boolean(PREF_OMEMO_SENDFILE)) {
|
||||||
if (mucwin->is_omemo && !prefs_get_boolean(PREF_OMEMO_SENDFILE)) {
|
int tmpfd;
|
||||||
cons_show_error("Uploading unencrypted files disabled. See /omemo sendfile, /otr sendfile, /pgp sendfile.");
|
GError *err = NULL;
|
||||||
win_println(window, THEME_ERROR, "-", "Sending encrypted files via http_upload is not possible yet.");
|
char *tmppath = NULL;
|
||||||
free(filename);
|
|
||||||
return TRUE;
|
tmpfd = g_file_open_tmp("profanity.XXXXXX", &tmppath, &err);
|
||||||
|
if (err != NULL) {
|
||||||
|
cons_show_error("Unable to create temporary file for encrypted transfer.");
|
||||||
|
win_println(window, THEME_ERROR, "-", "Unable to create temporary file for encrypted transfer.");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct stat tmpst;
|
||||||
|
if (fstat(tmpfd, &tmpst)) {
|
||||||
|
cons_show_error("Cannot determine file size.");
|
||||||
|
win_println(window, THEME_ERROR, "-", "Cannot determine file size.");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *tmpfile = fdopen(tmpfd, "wb");
|
||||||
|
if (tmpfile == NULL) {
|
||||||
|
cons_show_error("Unable to open temporary file.");
|
||||||
|
win_println(window, THEME_ERROR, "-", "Unable to open temporary file.");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *infile = fopen(filepath, "rb");
|
||||||
|
if (infile == NULL) {
|
||||||
|
cons_show_error("Unable to open file.");
|
||||||
|
win_println(window, THEME_ERROR, "-", "Unable to open file.");
|
||||||
|
close(tmpfd);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
int crypt_res = GPG_ERR_NO_ERROR;
|
||||||
|
|
||||||
|
// TODO(wstrm): Move these to omemo/crypto.c
|
||||||
|
unsigned char nonce[AES256_GCM_NONCE_LENGTH];
|
||||||
|
key = gcry_malloc_secure(AES256_GCM_KEY_LENGTH);
|
||||||
|
if (key == NULL) {
|
||||||
|
cons_show_error("Cannot allocate secure memory for encryption.");
|
||||||
|
win_println(window, THEME_ERROR, "-", "Cannot allocate secure memory for encryption.");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
key = gcry_random_bytes_secure(AES256_GCM_KEY_LENGTH, GCRY_VERY_STRONG_RANDOM);
|
||||||
|
gcry_create_nonce(nonce, AES256_GCM_NONCE_LENGTH);
|
||||||
|
|
||||||
|
crypt_res = aes256gcm_encrypt_file(infile, tmpfile, tmpst.st_size, key, nonce);
|
||||||
|
|
||||||
|
if (crypt_res != 0) {
|
||||||
|
cons_show_error("Failed to encrypt file.");
|
||||||
|
win_println(window, THEME_ERROR, "-", "Failed to encrypt file.");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(filepath);
|
||||||
|
filepath = tmppath;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((chatwin->pgp_send && !prefs_get_boolean(PREF_PGP_SENDFILE))
|
||||||
|
|| (chatwin->is_otr && !prefs_get_boolean(PREF_OTR_SENDFILE))) {
|
||||||
|
cons_show_error("Uploading unencrypted files disabled. See /omemo sendfile, /otr sendfile, /pgp sendfile.");
|
||||||
|
win_println(window, THEME_ERROR, "-", "Sending encrypted files via http_upload is not possible yet.");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
case WIN_PRIVATE:
|
||||||
}
|
{
|
||||||
case WIN_CHAT:
|
// We don't support encryption in private MUC windows.
|
||||||
{
|
break;
|
||||||
ProfChatWin* chatwin = (ProfChatWin*)window;
|
}
|
||||||
assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
|
default:
|
||||||
|
cons_show_error("Unsupported window for file transmission.");
|
||||||
if ((chatwin->is_omemo && !prefs_get_boolean(PREF_OMEMO_SENDFILE))
|
goto out;
|
||||||
|| (chatwin->pgp_send && !prefs_get_boolean(PREF_PGP_SENDFILE))
|
|
||||||
|| (chatwin->is_otr && !prefs_get_boolean(PREF_OTR_SENDFILE))) {
|
|
||||||
cons_show_error("Uploading unencrypted files disabled. See /omemo sendfile, /otr sendfile, /pgp sendfile.");
|
|
||||||
win_println(window, THEME_ERROR, "-", "Sending encrypted files via http_upload is not possible yet.");
|
|
||||||
free(filename);
|
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -4875,14 +4936,12 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args)
|
|||||||
|
|
||||||
if (access(filename, R_OK) != 0) {
|
if (access(filename, R_OK) != 0) {
|
||||||
cons_show_error("Uploading '%s' failed: File not found!", filename);
|
cons_show_error("Uploading '%s' failed: File not found!", filename);
|
||||||
free(filename);
|
goto out;
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_regular_file(filename)) {
|
if (!is_regular_file(filename)) {
|
||||||
cons_show_error("Uploading '%s' failed: Not a file!", filename);
|
cons_show_error("Uploading '%s' failed: Not a file!", filename);
|
||||||
free(filename);
|
goto out;
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTPUpload* upload = malloc(sizeof(HTTPUpload));
|
HTTPUpload* upload = malloc(sizeof(HTTPUpload));
|
||||||
@@ -4894,6 +4953,14 @@ cmd_sendfile(ProfWin* window, const char* const command, gchar** args)
|
|||||||
|
|
||||||
iq_http_upload_request(upload);
|
iq_http_upload_request(upload);
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (key != NULL)
|
||||||
|
gcry_free(key);
|
||||||
|
if (filename != NULL)
|
||||||
|
free(filename);
|
||||||
|
if (filepath != NULL)
|
||||||
|
free(filepath);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,9 @@
|
|||||||
#include "omemo/omemo.h"
|
#include "omemo/omemo.h"
|
||||||
#include "omemo/crypto.h"
|
#include "omemo/crypto.h"
|
||||||
|
|
||||||
|
#define AES256_GCM_TAG_LENGTH 16
|
||||||
|
#define AES256_GCM_BUFFER_SIZE 1024
|
||||||
|
|
||||||
int
|
int
|
||||||
omemo_crypto_init(void)
|
omemo_crypto_init(void)
|
||||||
{
|
{
|
||||||
@@ -373,3 +376,99 @@ out:
|
|||||||
gcry_cipher_close(hd);
|
gcry_cipher_close(hd);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int aes256gcm_crypt_file(FILE *in, FILE *out, off_t file_size,
|
||||||
|
unsigned char key[], unsigned char nonce[], bool encrypt) {
|
||||||
|
|
||||||
|
if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) {
|
||||||
|
fputs("libgcrypt has not been initialized\n", stderr);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!encrypt) {
|
||||||
|
file_size -= AES256_GCM_TAG_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
gcry_error_t res;
|
||||||
|
gcry_cipher_hd_t hd;
|
||||||
|
|
||||||
|
res = gcry_cipher_open(&hd, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_GCM,
|
||||||
|
GCRY_CIPHER_SECURE);
|
||||||
|
if (res != GPG_ERR_NO_ERROR) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = gcry_cipher_setkey(hd, key, AES256_GCM_KEY_LENGTH);
|
||||||
|
if (res != GPG_ERR_NO_ERROR) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = gcry_cipher_setiv(hd, nonce, AES256_GCM_NONCE_LENGTH);
|
||||||
|
if (res != GPG_ERR_NO_ERROR) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char buffer[AES256_GCM_BUFFER_SIZE];
|
||||||
|
|
||||||
|
int bytes = 0;
|
||||||
|
off_t bytes_read = 0, bytes_available = 0, read_size = 0;
|
||||||
|
while (bytes_read < file_size) {
|
||||||
|
bytes_available = file_size - bytes_read;
|
||||||
|
if (!bytes_available) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytes_available < AES256_GCM_BUFFER_SIZE) {
|
||||||
|
read_size = bytes_available;
|
||||||
|
gcry_cipher_final(hd); // Signal last round of bytes.
|
||||||
|
} else {
|
||||||
|
read_size = AES256_GCM_BUFFER_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = fread(buffer, 1, read_size, in);
|
||||||
|
bytes_read += bytes;
|
||||||
|
|
||||||
|
if (encrypt) {
|
||||||
|
res = gcry_cipher_encrypt(hd, buffer, bytes, NULL, 0);
|
||||||
|
} else {
|
||||||
|
res = gcry_cipher_decrypt(hd, buffer, bytes, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res != GPG_ERR_NO_ERROR) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
fwrite(buffer, 1, bytes, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char tag[AES256_GCM_TAG_LENGTH];
|
||||||
|
|
||||||
|
if (encrypt) {
|
||||||
|
// Append authentication tag at the end of the file.
|
||||||
|
res = gcry_cipher_gettag(hd, tag, AES256_GCM_TAG_LENGTH);
|
||||||
|
if (res != GPG_ERR_NO_ERROR) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
fwrite(tag, 1, AES256_GCM_TAG_LENGTH, out);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Read and verify authentication tag stored at the end of the file.
|
||||||
|
bytes = fread(tag, 1, AES256_GCM_TAG_LENGTH, in);
|
||||||
|
res = gcry_cipher_checktag(hd, tag, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
gcry_cipher_close(hd);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int aes256gcm_encrypt_file(FILE *in, FILE *out, off_t file_size,
|
||||||
|
unsigned char key[], unsigned char nonce[]) {
|
||||||
|
return aes256gcm_crypt_file(in, out, file_size, key, nonce, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
int aes256gcm_decrypt_file(FILE *in, FILE *out, off_t file_size,
|
||||||
|
unsigned char key[], unsigned char nonce[]) {
|
||||||
|
return aes256gcm_crypt_file(in, out, file_size, key, nonce, false);
|
||||||
|
}
|
||||||
|
|||||||
@@ -32,12 +32,16 @@
|
|||||||
* source files in the program, then also delete it here.
|
* source files in the program, then also delete it here.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
#include <signal/signal_protocol_types.h>
|
#include <signal/signal_protocol_types.h>
|
||||||
|
|
||||||
#define AES128_GCM_KEY_LENGTH 16
|
#define AES128_GCM_KEY_LENGTH 16
|
||||||
#define AES128_GCM_IV_LENGTH 12
|
#define AES128_GCM_IV_LENGTH 12
|
||||||
#define AES128_GCM_TAG_LENGTH 16
|
#define AES128_GCM_TAG_LENGTH 16
|
||||||
|
|
||||||
|
#define AES256_GCM_KEY_LENGTH 32
|
||||||
|
#define AES256_GCM_NONCE_LENGTH 12
|
||||||
|
|
||||||
int omemo_crypto_init(void);
|
int omemo_crypto_init(void);
|
||||||
/**
|
/**
|
||||||
* Callback for a secure random number generator.
|
* Callback for a secure random number generator.
|
||||||
@@ -176,7 +180,13 @@ int aes128gcm_encrypt(unsigned char* ciphertext, size_t* ciphertext_len,
|
|||||||
const unsigned char* const plaintext, size_t plaintext_len,
|
const unsigned char* const plaintext, size_t plaintext_len,
|
||||||
const unsigned char* const iv, const unsigned char* const key);
|
const unsigned char* const iv, const unsigned char* const key);
|
||||||
|
|
||||||
int aes128gcm_decrypt(unsigned char* plaintext,
|
int aes128gcm_decrypt(unsigned char *plaintext,
|
||||||
size_t* plaintext_len, const unsigned char* const ciphertext,
|
size_t *plaintext_len, const unsigned char *const ciphertext,
|
||||||
size_t ciphertext_len, const unsigned char* const iv, size_t iv_len,
|
size_t ciphertext_len, const unsigned char *const iv, size_t iv_len,
|
||||||
const unsigned char* const key, const unsigned char* const tag);
|
const unsigned char *const key, const unsigned char *const tag);
|
||||||
|
|
||||||
|
int aes256gcm_encrypt_file(FILE *in, FILE *out, off_t file_size,
|
||||||
|
unsigned char key[], unsigned char nonce[]);
|
||||||
|
|
||||||
|
int aes256gcm_decrypt_file(FILE *in, FILE *out, off_t file_size,
|
||||||
|
unsigned char key[], unsigned char nonce[]);
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ http_file_put(void* userdata)
|
|||||||
|
|
||||||
pthread_mutex_lock(&lock);
|
pthread_mutex_lock(&lock);
|
||||||
char* msg;
|
char* msg;
|
||||||
if (asprintf(&msg, "Uploading '%s': 0%%", upload->filename) == -1) {
|
if (asprintf(&msg, "Uploading '%s': 0%%", upload->filepath) == -1) {
|
||||||
msg = strdup(FALLBACK_MSG);
|
msg = strdup(FALLBACK_MSG);
|
||||||
}
|
}
|
||||||
win_print_http_upload(upload->window, msg, upload->put_url);
|
win_print_http_upload(upload->window, msg, upload->put_url);
|
||||||
@@ -186,8 +186,8 @@ http_file_put(void* userdata)
|
|||||||
|
|
||||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, "profanity");
|
curl_easy_setopt(curl, CURLOPT_USERAGENT, "profanity");
|
||||||
|
|
||||||
if (!(fd = fopen(upload->filename, "rb"))) {
|
if (!(fd = fopen(upload->filepath, "rb"))) {
|
||||||
if (asprintf(&err, "failed to open '%s'", upload->filename) == -1) {
|
if (asprintf(&err, "failed to open '%s'", upload->filepath) == -1) {
|
||||||
err = NULL;
|
err = NULL;
|
||||||
}
|
}
|
||||||
goto end;
|
goto end;
|
||||||
@@ -294,6 +294,7 @@ end:
|
|||||||
pthread_mutex_unlock(&lock);
|
pthread_mutex_unlock(&lock);
|
||||||
|
|
||||||
free(upload->filename);
|
free(upload->filename);
|
||||||
|
free(upload->filepath);
|
||||||
free(upload->mime_type);
|
free(upload->mime_type);
|
||||||
free(upload->get_url);
|
free(upload->get_url);
|
||||||
free(upload->put_url);
|
free(upload->put_url);
|
||||||
@@ -303,18 +304,18 @@ end:
|
|||||||
}
|
}
|
||||||
|
|
||||||
char*
|
char*
|
||||||
file_mime_type(const char* const file_name)
|
file_mime_type(const char* const filepath)
|
||||||
{
|
{
|
||||||
char* out_mime_type;
|
char* out_mime_type;
|
||||||
char file_header[FILE_HEADER_BYTES];
|
char file_header[FILE_HEADER_BYTES];
|
||||||
FILE* fd;
|
FILE *fd;
|
||||||
if (!(fd = fopen(file_name, "rb"))) {
|
if (!(fd = fopen(filepath, "rb"))) {
|
||||||
return strdup(FALLBACK_MIMETYPE);
|
return strdup(FALLBACK_MIMETYPE);
|
||||||
}
|
}
|
||||||
size_t file_header_size = fread(file_header, 1, FILE_HEADER_BYTES, fd);
|
size_t file_header_size = fread(file_header, 1, FILE_HEADER_BYTES, fd);
|
||||||
fclose(fd);
|
fclose(fd);
|
||||||
|
|
||||||
char* content_type = g_content_type_guess(file_name, (unsigned char*)file_header, file_header_size, NULL);
|
char *content_type = g_content_type_guess(filepath, (unsigned char*)file_header, file_header_size, NULL);
|
||||||
if (content_type != NULL) {
|
if (content_type != NULL) {
|
||||||
char* mime_type = g_content_type_get_mime_type(content_type);
|
char* mime_type = g_content_type_get_mime_type(content_type);
|
||||||
out_mime_type = strdup(mime_type);
|
out_mime_type = strdup(mime_type);
|
||||||
@@ -326,11 +327,10 @@ file_mime_type(const char* const file_name)
|
|||||||
return out_mime_type;
|
return out_mime_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
off_t
|
off_t file_size(const char* const filepath)
|
||||||
file_size(const char* const filename)
|
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
stat(filename, &st);
|
stat(filepath, &st);
|
||||||
return st.st_size;
|
return st.st_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,9 +45,9 @@
|
|||||||
|
|
||||||
#include "ui/win_types.h"
|
#include "ui/win_types.h"
|
||||||
|
|
||||||
typedef struct http_upload_t
|
typedef struct http_upload_t {
|
||||||
{
|
char *filename;
|
||||||
char* filename;
|
char *filepath;
|
||||||
off_t filesize;
|
off_t filesize;
|
||||||
curl_off_t bytes_sent;
|
curl_off_t bytes_sent;
|
||||||
char* mime_type;
|
char* mime_type;
|
||||||
@@ -60,8 +60,8 @@ typedef struct http_upload_t
|
|||||||
|
|
||||||
void* http_file_put(void* userdata);
|
void* http_file_put(void* userdata);
|
||||||
|
|
||||||
char* file_mime_type(const char* const file_name);
|
char* file_mime_type(const char* const filepath);
|
||||||
off_t file_size(const char* const file_name);
|
off_t file_size(const char* const filepath);
|
||||||
|
|
||||||
void http_upload_cancel_processes(ProfWin* window);
|
void http_upload_cancel_processes(ProfWin* window);
|
||||||
void http_upload_add_upload(HTTPUpload* upload);
|
void http_upload_add_upload(HTTPUpload* upload);
|
||||||
|
|||||||
@@ -2401,9 +2401,9 @@ _http_upload_response_id_handler(xmpp_stanza_t* const stanza, void* const userda
|
|||||||
if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) {
|
if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) {
|
||||||
char* error_message = stanza_get_error_message(stanza);
|
char* error_message = stanza_get_error_message(stanza);
|
||||||
if (from) {
|
if (from) {
|
||||||
cons_show_error("Uploading '%s' failed for %s: %s", upload->filename, from, error_message);
|
cons_show_error("Uploading '%s' failed for %s: %s", upload->filepath, from, error_message);
|
||||||
} else {
|
} else {
|
||||||
cons_show_error("Uploading '%s' failed: %s", upload->filename, error_message);
|
cons_show_error("Uploading '%s' failed: %s", upload->filepath, error_message);
|
||||||
}
|
}
|
||||||
free(error_message);
|
free(error_message);
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
10
tests/unittests/test_omemo.c
Normal file
10
tests/unittests/test_omemo.c
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#include <cmocka.h>
|
||||||
|
#include <setjmp.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "omemo/crypto.h"
|
||||||
|
|
||||||
|
void test_omemo_aesgcm256_encrypt_file(void **state) {}
|
||||||
|
void test_omemo_aesgcm256_encrypt_file(void **state) {}
|
||||||
2
tests/unittests/test_omemo.h
Normal file
2
tests/unittests/test_omemo.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
void test_omemo_aesgcm256_encrypt_file(void **state);
|
||||||
|
void test_omemo_aesgcm256_decrypt_file(void **state);
|
||||||
Reference in New Issue
Block a user