/*
This file is part of GNUnet.
Copyright (C) 2021--2022 GNUnet e.V.
GNUnet is free software: you can redistribute it and/or modify it
under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License,
or (at your option) any later version.
GNUnet 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
Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
SPDX-License-Identifier: AGPL3.0-or-later
*/
/*
* @author Tobias Frisch
* @file gnunet_chat_lib.c
*/
#include "gnunet_chat_lib.h"
#include
#include "gnunet_chat_contact.h"
#include "gnunet_chat_context.h"
#include "gnunet_chat_file.h"
#include "gnunet_chat_group.h"
#include "gnunet_chat_handle.h"
#include "gnunet_chat_invitation.h"
#include "gnunet_chat_lobby.h"
#include "gnunet_chat_message.h"
#include "gnunet_chat_util.h"
#include "gnunet_chat_lib_intern.c"
struct GNUNET_CHAT_Handle*
GNUNET_CHAT_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
const char *directory,
GNUNET_CHAT_ContextMessageCallback msg_cb, void *msg_cls)
{
if (!cfg)
return NULL;
return handle_create_from_config(
cfg, directory,
msg_cb, msg_cls
);
}
void
GNUNET_CHAT_stop (struct GNUNET_CHAT_Handle *handle)
{
if (!handle)
return;
handle_destroy(handle);
}
int
GNUNET_CHAT_account_create (struct GNUNET_CHAT_Handle *handle,
const char* name)
{
if ((!handle) || (!name))
return GNUNET_SYSERR;
struct GNUNET_CHAT_InternalAccounts *accounts = handle->accounts_head;
while (accounts)
{
if (!(accounts->account))
goto skip_account;
if ((accounts->account->name) &&
(0 == strcmp(accounts->account->name, name)))
return GNUNET_NO;
skip_account:
accounts = accounts->next;
}
if (handle->creation_op)
GNUNET_IDENTITY_cancel(handle->creation_op);
handle->creation_op = GNUNET_IDENTITY_create(
handle->identity,
name,
NULL,
GNUNET_IDENTITY_TYPE_ECDSA,
cb_account_creation,
handle
);
return (handle->creation_op? GNUNET_OK : GNUNET_SYSERR);
}
int
GNUNET_CHAT_iterate_accounts (const struct GNUNET_CHAT_Handle *handle,
GNUNET_CHAT_AccountCallback callback,
void *cls)
{
if (!handle)
return GNUNET_SYSERR;
int result = 0;
struct GNUNET_CHAT_InternalAccounts *accounts = handle->accounts_head;
while (accounts)
{
if (!(accounts->account))
return GNUNET_SYSERR;
result++;
if ((callback) && (GNUNET_YES != callback(cls, handle, accounts->account)))
break;
accounts = accounts->next;
}
return result;
}
void
GNUNET_CHAT_connect (struct GNUNET_CHAT_Handle *handle,
const struct GNUNET_CHAT_Account *account)
{
if (!handle)
return;
if (handle->current)
handle_disconnect(handle);
if (!account)
return;
handle_connect(handle, account);
}
void
GNUNET_CHAT_disconnect (struct GNUNET_CHAT_Handle *handle)
{
if ((!handle) || (!(handle->current)))
return;
handle_disconnect(handle);
}
const struct GNUNET_CHAT_Account*
GNUNET_CHAT_get_connected (const struct GNUNET_CHAT_Handle *handle)
{
if (!handle)
return NULL;
return handle->current;
}
int
GNUNET_CHAT_update (struct GNUNET_CHAT_Handle *handle)
{
if (!handle)
return GNUNET_SYSERR;
return GNUNET_MESSENGER_update(handle->messenger);
}
int
GNUNET_CHAT_set_name (struct GNUNET_CHAT_Handle *handle,
const char *name)
{
if (!handle)
return GNUNET_SYSERR;
if (!name)
return GNUNET_NO;
return GNUNET_MESSENGER_set_name(handle->messenger, name);
}
const char*
GNUNET_CHAT_get_name (const struct GNUNET_CHAT_Handle *handle)
{
if (!handle)
return NULL;
return GNUNET_MESSENGER_get_name(handle->messenger);
}
const char*
GNUNET_CHAT_get_key (const struct GNUNET_CHAT_Handle *handle)
{
if (!handle)
return NULL;
return handle->public_key;
}
struct GNUNET_CHAT_Uri*
GNUNET_CHAT_uri_parse (const char *uri,
char **emsg)
{
if (!uri)
return NULL;
const size_t prefix_len = strlen(GNUNET_CHAT_URI_PREFIX);
if (0 != strncmp(GNUNET_CHAT_URI_PREFIX, uri, prefix_len))
{
if (emsg)
*emsg = GNUNET_strdup (_ ("CHAT URI malformed (invalid prefix)"));
return NULL;
}
struct GNUNET_IDENTITY_PublicKey zone;
const char *data = uri + prefix_len;
char *end = strchr(data, '.');
if (!end)
{
if (emsg)
*emsg = GNUNET_strdup (_ ("CHAT URI malformed (zone key missing)"));
return NULL;
}
char *zone_data = GNUNET_strndup(data, (size_t) (end - data));
if (GNUNET_OK != GNUNET_IDENTITY_public_key_from_string(zone_data, &zone))
{
GNUNET_free(zone_data);
if (emsg)
*emsg = GNUNET_strdup (_ ("CHAT URI malformed (zone key invalid)"));
return NULL;
}
GNUNET_free(zone_data);
return uri_create(&zone, end + 1);
}
char*
GNUNET_CHAT_uri_to_string (const struct GNUNET_CHAT_Uri *uri)
{
if (!uri)
return NULL;
char *key_string = GNUNET_IDENTITY_public_key_to_string(&(uri->zone));
char *string;
GNUNET_asprintf(
&string,
"gnunet://chat/%s.%s",
key_string,
uri->label
);
GNUNET_free(key_string);
return string;
}
void
GNUNET_CHAT_uri_destroy (struct GNUNET_CHAT_Uri *uri)
{
if (!uri)
return;
uri_destroy(uri);
}
struct GNUNET_CHAT_Lobby*
GNUNET_CHAT_lobby_open (struct GNUNET_CHAT_Handle *handle,
struct GNUNET_TIME_Relative delay,
GNUNET_CHAT_LobbyCallback callback,
void *cls)
{
if (!handle)
return NULL;
struct GNUNET_CHAT_InternalLobbies *lobbies = GNUNET_new(
struct GNUNET_CHAT_InternalLobbies
);
lobbies->lobby = lobby_create(handle);
GNUNET_CONTAINER_DLL_insert(
handle->lobbies_head,
handle->lobbies_tail,
lobbies
);
lobby_open(lobbies->lobby, delay, callback, cls);
return lobbies->lobby;
}
void
GNUNET_CHAT_lobby_close (struct GNUNET_CHAT_Lobby *lobby)
{
if (!lobby)
return;
struct GNUNET_CHAT_InternalLobbies *lobbies = lobby->handle->lobbies_head;
while (lobbies)
{
if (lobbies->lobby == lobby)
{
GNUNET_CONTAINER_DLL_remove(
lobby->handle->lobbies_head,
lobby->handle->lobbies_tail,
lobbies
);
GNUNET_free(lobbies);
break;
}
lobbies = lobbies->next;
}
lobby_destroy(lobby);
}
void
GNUNET_CHAT_lobby_join (struct GNUNET_CHAT_Handle *handle,
const struct GNUNET_CHAT_Uri *uri)
{
if ((!handle) || (!uri) || (!(handle->gns)))
return;
struct GNUNET_CHAT_UriLookups *lookups = GNUNET_new(
struct GNUNET_CHAT_UriLookups
);
lookups->handle = handle;
lookups->uri = uri_create(&(uri->zone), uri->label);
lookups->request = GNUNET_GNS_lookup(
handle->gns,
lookups->uri->label,
&(uri->zone),
GNUNET_GNSRECORD_TYPE_MESSENGER_ROOM_ENTRY,
GNUNET_GNS_LO_DEFAULT,
cb_lobby_lookup,
lookups
);
GNUNET_CONTAINER_DLL_insert(
handle->lookups_head,
handle->lookups_tail,
lookups
);
}
void
GNUNET_CHAT_set_user_pointer (struct GNUNET_CHAT_Handle *handle,
void *user_pointer)
{
if (!handle)
return;
handle->user_pointer = user_pointer;
}
void*
GNUNET_CHAT_get_user_pointer (const struct GNUNET_CHAT_Handle *handle)
{
if (!handle)
return NULL;
return handle->user_pointer;
}
int
GNUNET_CHAT_iterate_contacts (struct GNUNET_CHAT_Handle *handle,
GNUNET_CHAT_ContactCallback callback,
void *cls)
{
if ((!handle) || (!(handle->contacts)))
return GNUNET_SYSERR;
struct GNUNET_CHAT_HandleIterateContacts it;
it.handle = handle;
it.cb = callback;
it.cls = cls;
return GNUNET_CONTAINER_multishortmap_iterate(
handle->contacts, it_handle_iterate_contacts, &it
);
}
const char*
GNUNET_CHAT_account_get_name (const struct GNUNET_CHAT_Account *account)
{
if (!account)
return NULL;
return account->name;
}
void
GNUNET_CHAT_account_set_user_pointer (struct GNUNET_CHAT_Account *account,
void *user_pointer)
{
if (!account)
return;
account->user_pointer = user_pointer;
}
void*
GNUNET_CHAT_account_get_user_pointer (const struct GNUNET_CHAT_Account *account)
{
if (!account)
return NULL;
return account->user_pointer;
}
struct GNUNET_CHAT_Group *
GNUNET_CHAT_group_create (struct GNUNET_CHAT_Handle *handle,
const char* topic)
{
if ((!handle) || (!(handle->groups)) || (!(handle->contexts)))
return NULL;
struct GNUNET_HashCode key;
if (topic)
GNUNET_CRYPTO_hash(topic, strlen(topic), &key);
else
GNUNET_CRYPTO_random_block(GNUNET_CRYPTO_QUALITY_WEAK, &key, sizeof(key));
if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains(
handle->contexts, &key))
return NULL;
struct GNUNET_MESSENGER_Room *room = GNUNET_MESSENGER_open_room(
handle->messenger, &key
);
if (!room)
return NULL;
struct GNUNET_CHAT_Context *context = context_create_from_room(handle, room);
handle_send_room_name(handle, room);
context->type = GNUNET_CHAT_CONTEXT_TYPE_GROUP;
util_set_name_field(topic, &(context->topic));
if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(
handle->contexts, &key, context,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
goto destroy_context;
struct GNUNET_CHAT_Group *group = group_create_from_context(handle, context);
if (context->topic)
group_publish(group);
if (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(
handle->groups, &key, group,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
{
context_write_records(context);
return group;
}
group_destroy(group);
GNUNET_CONTAINER_multihashmap_remove(handle->contexts, &key, context);
destroy_context:
context_destroy(context);
return NULL;
}
int
GNUNET_CHAT_iterate_groups (struct GNUNET_CHAT_Handle *handle,
GNUNET_CHAT_GroupCallback callback,
void *cls)
{
if ((!handle) || (!(handle->groups)))
return GNUNET_SYSERR;
struct GNUNET_CHAT_HandleIterateGroups it;
it.handle = handle;
it.cb = callback;
it.cls = cls;
return GNUNET_CONTAINER_multihashmap_iterate(
handle->groups, it_handle_iterate_groups, &it
);
}
int
GNUNET_CHAT_contact_delete (struct GNUNET_CHAT_Contact *contact)
{
if (!contact)
return GNUNET_SYSERR;
struct GNUNET_ShortHashCode shorthash;
util_shorthash_from_member(contact->member, &shorthash);
GNUNET_CONTAINER_multishortmap_remove(
contact->handle->contacts, &shorthash, contact
);
const struct GNUNET_HashCode *key = GNUNET_MESSENGER_room_get_key(
contact->context->room
);
GNUNET_CONTAINER_multihashmap_remove(
contact->handle->contexts, key, contact->context
);
GNUNET_MESSENGER_close_room(contact->context->room);
context_delete_records(contact->context);
context_destroy(contact->context);
contact_destroy(contact);
return GNUNET_OK;
}
void
GNUNET_CHAT_contact_set_name (struct GNUNET_CHAT_Contact *contact,
const char *name)
{
if ((!contact) || (!(contact->context)))
return;
context_update_nick(contact->context, name);
if (contact->context->room)
context_write_records(contact->context);
}
const char*
GNUNET_CHAT_contact_get_name (const struct GNUNET_CHAT_Contact *contact)
{
if (!contact)
return NULL;
if ((contact->context) && (contact->context->nick))
return contact->context->nick;
return GNUNET_MESSENGER_contact_get_name(contact->member);
}
const char*
GNUNET_CHAT_contact_get_key (const struct GNUNET_CHAT_Contact *contact)
{
if (!contact)
return NULL;
return contact->public_key;
}
struct GNUNET_CHAT_Context*
GNUNET_CHAT_contact_get_context (struct GNUNET_CHAT_Contact *contact)
{
if (!contact)
return NULL;
if (contact->context)
return contact->context;
struct GNUNET_CHAT_Context *context = contact_find_context(contact);
if ((context) && (GNUNET_CHAT_CONTEXT_TYPE_CONTACT == context->type))
return context;
context = context_create_from_contact(contact->handle, contact->member);
if (context)
contact->context = context;
return context;
}
void
GNUNET_CHAT_contact_set_user_pointer (struct GNUNET_CHAT_Contact *contact,
void *user_pointer)
{
if (!contact)
return;
contact->user_pointer = user_pointer;
}
void*
GNUNET_CHAT_contact_get_user_pointer (const struct GNUNET_CHAT_Contact *contact)
{
if (!contact)
return NULL;
return contact->user_pointer;
}
int
GNUNET_CHAT_contact_is_owned (const struct GNUNET_CHAT_Contact *contact)
{
if (!contact)
return GNUNET_SYSERR;
return contact->is_owned;
}
int
GNUNET_CHAT_group_leave (struct GNUNET_CHAT_Group *group)
{
if (!group)
return GNUNET_SYSERR;
const struct GNUNET_HashCode *key = GNUNET_MESSENGER_room_get_key(
group->context->room
);
GNUNET_CONTAINER_multihashmap_remove(
group->handle->groups, key, group
);
GNUNET_CONTAINER_multihashmap_remove(
group->handle->contexts, key, group->context
);
GNUNET_MESSENGER_close_room(group->context->room);
context_delete_records(group->context);
context_destroy(group->context);
group_destroy(group);
return GNUNET_OK;
}
void
GNUNET_CHAT_group_set_name (struct GNUNET_CHAT_Group *group,
const char *name)
{
if ((!group) || (!(group->context)))
return;
context_update_nick(group->context, name);
context_write_records(group->context);
}
const char*
GNUNET_CHAT_group_get_name (const struct GNUNET_CHAT_Group *group)
{
if ((!group) || (!(group->context)))
return NULL;
if (group->context->nick)
return group->context->nick;
return group->context->topic;
}
void
GNUNET_CHAT_group_set_user_pointer (struct GNUNET_CHAT_Group *group,
void *user_pointer)
{
if (!group)
return;
group->user_pointer = user_pointer;
}
void*
GNUNET_CHAT_group_get_user_pointer (const struct GNUNET_CHAT_Group *group)
{
if (!group)
return NULL;
return group->user_pointer;
}
void
GNUNET_CHAT_group_invite_contact (const struct GNUNET_CHAT_Group *group,
struct GNUNET_CHAT_Contact *contact)
{
if ((!group) || (!contact))
return;
struct GNUNET_CHAT_Context *context = contact_find_context(contact);
if (!context)
return;
const struct GNUNET_HashCode *key = GNUNET_MESSENGER_room_get_key(
group->context->room
);
handle_send_room_name(group->handle, GNUNET_MESSENGER_open_room(
group->handle->messenger, key
));
struct GNUNET_MESSENGER_Message msg;
msg.header.kind = GNUNET_MESSENGER_KIND_INVITE;
GNUNET_CRYPTO_get_peer_identity(group->handle->cfg, &(msg.body.invite.door));
GNUNET_memcpy(&(msg.body.invite.key), key, sizeof(msg.body.invite.key));
GNUNET_MESSENGER_send_message(context->room, &msg, contact->member);
}
int
GNUNET_CHAT_group_iterate_contacts (const struct GNUNET_CHAT_Group *group,
GNUNET_CHAT_GroupContactCallback callback,
void *cls)
{
if (!group)
return GNUNET_SYSERR;
struct GNUNET_CHAT_GroupIterateContacts it;
it.group = group;
it.cb = callback;
it.cls = cls;
return GNUNET_MESSENGER_iterate_members(
group->context->room, it_group_iterate_contacts, &it
);
}
struct GNUNET_CHAT_Context*
GNUNET_CHAT_group_get_context (struct GNUNET_CHAT_Group *group)
{
if (!group)
return NULL;
return group->context;
}
int
GNUNET_CHAT_context_get_status (const struct GNUNET_CHAT_Context *context)
{
if ((!context) || (!(context->room)))
return GNUNET_SYSERR;
if ((GNUNET_CHAT_CONTEXT_TYPE_UNKNOWN == context->type) ||
(1 >= GNUNET_MESSENGER_iterate_members(context->room, NULL, NULL)))
return GNUNET_NO;
return GNUNET_OK;
}
void
GNUNET_CHAT_context_request (struct GNUNET_CHAT_Context *context)
{
if ((!context) || (context->room))
return;
struct GNUNET_CHAT_Handle *handle = context->handle;
if ((!handle) || (!(context->contact)))
return;
struct GNUNET_CHAT_Contact *contact = contact_create_from_member(
handle, context->contact
);
if (!contact)
return;
context->type = GNUNET_CHAT_CONTEXT_TYPE_CONTACT;
struct GNUNET_CHAT_Context *other = contact_find_context(contact);
if ((!other) || (!(other->room)))
return;
struct GNUNET_HashCode key;
GNUNET_CRYPTO_random_block(GNUNET_CRYPTO_QUALITY_WEAK, &key, sizeof(key));
if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains(
handle->contexts, &key))
return;
struct GNUNET_MESSENGER_Room *room = GNUNET_MESSENGER_open_room(
handle->messenger, &key
);
if (!room)
return;
context_update_room(context, room);
handle_send_room_name(handle, room);
struct GNUNET_MESSENGER_Message msg;
msg.header.kind = GNUNET_MESSENGER_KIND_INVITE;
GNUNET_CRYPTO_get_peer_identity(handle->cfg, &(msg.body.invite.door));
GNUNET_memcpy(&(msg.body.invite.key), &key, sizeof(msg.body.invite.key));
GNUNET_MESSENGER_send_message(other->room, &msg, context->contact);
}
struct GNUNET_CHAT_Contact*
GNUNET_CHAT_context_get_contact (struct GNUNET_CHAT_Context *context)
{
if ((!context) || (GNUNET_CHAT_CONTEXT_TYPE_CONTACT != context->type))
return NULL;
if (context->contact)
return handle_get_contact_from_messenger(context->handle, context->contact);
struct GNUNET_MESSENGER_Room *room = context->room;
struct GNUNET_CHAT_RoomFindContact find;
find.ignore_key = GNUNET_MESSENGER_get_key(context->handle->messenger);
find.contact = NULL;
int member_count = GNUNET_MESSENGER_iterate_members(
room,
it_room_find_contact,
&find
);
if ((!find.contact) || (member_count > 2))
return NULL;
return handle_get_contact_from_messenger(context->handle, find.contact);
}
struct GNUNET_CHAT_Group*
GNUNET_CHAT_context_get_group (struct GNUNET_CHAT_Context *context)
{
if ((!context) || (GNUNET_CHAT_CONTEXT_TYPE_GROUP != context->type))
return NULL;
if (!(context->room))
return NULL;
return handle_get_group_from_messenger(context->handle, context->room);
}
void
GNUNET_CHAT_context_set_user_pointer (struct GNUNET_CHAT_Context *context,
void *user_pointer)
{
if (!context)
return;
context->user_pointer = user_pointer;
}
void*
GNUNET_CHAT_context_get_user_pointer (const struct GNUNET_CHAT_Context *context)
{
if (!context)
return NULL;
return context->user_pointer;
}
int
GNUNET_CHAT_context_send_text (struct GNUNET_CHAT_Context *context,
const char *text)
{
if ((!context) || (!text) || (!(context->room)))
return GNUNET_SYSERR;
struct GNUNET_MESSENGER_Message msg;
msg.header.kind = GNUNET_MESSENGER_KIND_TEXT;
msg.body.text.text = GNUNET_strdup(text);
GNUNET_MESSENGER_send_message(context->room, &msg, NULL);
GNUNET_free(msg.body.text.text);
return GNUNET_OK;
}
int
GNUNET_CHAT_context_send_read_receipt (struct GNUNET_CHAT_Context *context,
const struct GNUNET_CHAT_Message *message)
{
if ((!context) || (!(context->room)))
return GNUNET_SYSERR;
char zero = '\0';
struct GNUNET_MESSENGER_Message msg;
msg.header.kind = GNUNET_MESSENGER_KIND_TEXT;
msg.body.text.text = &zero;
const struct GNUNET_MESSENGER_Contact *receiver = NULL;
if (!message)
goto skip_filter;
if (GNUNET_CHAT_FLAG_NONE != message->flag)
return GNUNET_SYSERR;
if (message->flags & GNUNET_MESSENGER_FLAG_SENT)
return GNUNET_OK;
if (message->flags & GNUNET_MESSENGER_FLAG_PRIVATE)
{
receiver = GNUNET_MESSENGER_get_sender(context->room, &(message->hash));
if (!receiver)
return GNUNET_SYSERR;
}
if ((!(message->msg)) ||
(GNUNET_MESSENGER_KIND_TEXT != message->msg->header.kind))
goto skip_filter;
if ((!(message->msg->body.text.text)) ||
(!(message->msg->body.text.text[0])))
return GNUNET_SYSERR;
skip_filter:
GNUNET_MESSENGER_send_message(context->room, &msg, receiver);
return GNUNET_OK;
}
struct GNUNET_CHAT_File*
GNUNET_CHAT_context_send_file (struct GNUNET_CHAT_Context *context,
const char *path,
GNUNET_CHAT_FileUploadCallback callback,
void *cls)
{
if ((!context) || (!path) || (!(context->room)))
return NULL;
if (!(context->handle->directory))
return NULL;
struct GNUNET_HashCode hash;
if (GNUNET_OK != util_hash_file(path, &hash))
return NULL;
const char *directory = handle_get_directory(context->handle);
if (!directory)
return NULL;
struct GNUNET_CHAT_File *file = GNUNET_CONTAINER_multihashmap_get(
context->handle->files,
&hash
);
char *filename;
util_get_filename (
directory, "files", &hash, &filename
);
if (file)
goto file_binding;
if ((GNUNET_YES == GNUNET_DISK_file_test(filename)) ||
(GNUNET_OK != GNUNET_DISK_directory_create_for_file(filename)) ||
(GNUNET_OK != GNUNET_DISK_file_copy(path, filename)))
{
GNUNET_free(filename);
return NULL;
}
struct GNUNET_CRYPTO_SymmetricSessionKey key;
GNUNET_CRYPTO_symmetric_create_session_key(&key);
if (GNUNET_OK != util_encrypt_file(filename, &hash, &key))
{
GNUNET_free(filename);
return NULL;
}
char* p = GNUNET_strdup(path);
file = file_create_from_disk(
context->handle,
basename(p),
&hash,
&key
);
GNUNET_free(p);
if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(
context->handle->files, &hash, file,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
{
file_destroy(file);
GNUNET_free(filename);
return NULL;
}
struct GNUNET_FS_BlockOptions bo;
bo.anonymity_level = 1;
bo.content_priority = 100;
bo.replication_level = 1;
bo.expiration_time = GNUNET_TIME_absolute_add(
GNUNET_TIME_absolute_get(), GNUNET_TIME_relative_get_hour_()
);
struct GNUNET_FS_FileInformation* fi = GNUNET_FS_file_information_create_from_file(
context->handle->fs,
file,
filename,
NULL,
file->meta,
GNUNET_YES,
&bo
);
file->publish = GNUNET_FS_publish_start(
context->handle->fs, fi,
NULL, NULL, NULL,
GNUNET_FS_PUBLISH_OPTION_NONE
);
if (file->publish)
file->status |= GNUNET_CHAT_FILE_STATUS_PUBLISH;
GNUNET_free(filename);
file_binding:
file_bind_upload(file, context, callback, cls);
return file;
}
int
GNUNET_CHAT_context_share_file (struct GNUNET_CHAT_Context *context,
const struct GNUNET_CHAT_File *file)
{
if ((!context) || (!file) || (strlen(file->name) > NAME_MAX) ||
(!(context->room)))
return GNUNET_SYSERR;
struct GNUNET_MESSENGER_Message msg;
msg.header.kind = GNUNET_MESSENGER_KIND_FILE;
GNUNET_memcpy(&(msg.body.file.key), &(file->key), sizeof(file->key));
GNUNET_memcpy(&(msg.body.file.hash), &(file->hash), sizeof(file->hash));
GNUNET_strlcpy(msg.body.file.name, file->name, NAME_MAX);
msg.body.file.uri = GNUNET_FS_uri_to_string(file->uri);
GNUNET_MESSENGER_send_message(context->room, &msg, NULL);
GNUNET_free(msg.body.file.uri);
return GNUNET_OK;
}
int
GNUNET_CHAT_context_iterate_messages (struct GNUNET_CHAT_Context *context,
GNUNET_CHAT_ContextMessageCallback callback,
void *cls)
{
if (!context)
return GNUNET_SYSERR;
struct GNUNET_CHAT_ContextIterateMessages it;
it.context = context;
it.cb = callback;
it.cls = cls;
return GNUNET_CONTAINER_multihashmap_iterate(
context->messages, it_context_iterate_messages, &it
);
}
int
GNUNET_CHAT_context_iterate_files (struct GNUNET_CHAT_Context *context,
GNUNET_CHAT_ContextFileCallback callback,
void *cls)
{
if (!context)
return GNUNET_SYSERR;
struct GNUNET_CHAT_ContextIterateFiles it;
it.context = context;
it.cb = callback;
it.cls = cls;
return GNUNET_CONTAINER_multihashmap_iterate(
context->files, it_context_iterate_files, &it
);
}
void
GNUNET_CHAT_member_set_user_pointer (struct GNUNET_CHAT_Context *context,
const struct GNUNET_CHAT_Contact *member,
void *user_pointer)
{
if ((!context) || (!member))
return;
struct GNUNET_ShortHashCode hash;
util_shorthash_from_member(member->member, &hash);
GNUNET_CONTAINER_multishortmap_put(
context->member_pointers,
&hash,
user_pointer,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE
);
}
void*
GNUNET_CHAT_member_get_user_pointer (const struct GNUNET_CHAT_Context *context,
const struct GNUNET_CHAT_Contact *member)
{
if ((!context) || (!member))
return NULL;
struct GNUNET_ShortHashCode hash;
util_shorthash_from_member(member->member, &hash);
return GNUNET_CONTAINER_multishortmap_get(
context->member_pointers,
&hash
);
}
enum GNUNET_CHAT_MessageKind
GNUNET_CHAT_message_get_kind (const struct GNUNET_CHAT_Message *message)
{
if (!message)
return GNUNET_CHAT_KIND_UNKNOWN;
switch (message->flag)
{
case GNUNET_CHAT_FLAG_WARNING:
return GNUNET_CHAT_KIND_WARNING;
case GNUNET_CHAT_FLAG_REFRESH:
return GNUNET_CHAT_KIND_REFRESH;
case GNUNET_CHAT_FLAG_LOGIN:
return GNUNET_CHAT_KIND_LOGIN;
case GNUNET_CHAT_FLAG_UPDATE:
return GNUNET_CHAT_KIND_UPDATE;
default:
break;
}
if (!(message->msg))
return GNUNET_CHAT_KIND_UNKNOWN;
switch (message->msg->header.kind)
{
case GNUNET_MESSENGER_KIND_JOIN:
return GNUNET_CHAT_KIND_JOIN;
case GNUNET_MESSENGER_KIND_LEAVE:
return GNUNET_CHAT_KIND_LEAVE;
case GNUNET_MESSENGER_KIND_NAME:
case GNUNET_MESSENGER_KIND_KEY:
case GNUNET_MESSENGER_KIND_ID:
return GNUNET_CHAT_KIND_CONTACT;
case GNUNET_MESSENGER_KIND_INVITE:
return GNUNET_CHAT_KIND_INVITATION;
case GNUNET_MESSENGER_KIND_TEXT:
return GNUNET_CHAT_KIND_TEXT;
case GNUNET_MESSENGER_KIND_FILE:
return GNUNET_CHAT_KIND_FILE;
case GNUNET_MESSENGER_KIND_PRIVATE:
return GNUNET_CHAT_KIND_WHISPER;
case GNUNET_MESSENGER_KIND_DELETE:
return GNUNET_CHAT_KIND_DELETION;
default:
return GNUNET_CHAT_KIND_UNKNOWN;
}
}
struct GNUNET_TIME_Absolute
GNUNET_CHAT_message_get_timestamp (const struct GNUNET_CHAT_Message *message)
{
if ((!message) || (!(message->msg)))
return GNUNET_TIME_absolute_get_zero_();
return GNUNET_TIME_absolute_ntoh(message->msg->header.timestamp);
}
struct GNUNET_CHAT_Contact*
GNUNET_CHAT_message_get_sender (const struct GNUNET_CHAT_Message *message)
{
if ((!message) || (GNUNET_CHAT_FLAG_NONE != message->flag) ||
(!(message->context)) || (!(message->context->room)))
return NULL;
const struct GNUNET_MESSENGER_Contact *sender = GNUNET_MESSENGER_get_sender(
message->context->room, &(message->hash)
);
if (!sender)
return NULL;
return handle_get_contact_from_messenger(message->context->handle, sender);
}
int
GNUNET_CHAT_message_is_sent (const struct GNUNET_CHAT_Message *message)
{
if (!message)
return GNUNET_SYSERR;
if (message->flags & GNUNET_MESSENGER_FLAG_SENT)
return GNUNET_YES;
else
return GNUNET_NO;
}
int
GNUNET_CHAT_message_is_private (const struct GNUNET_CHAT_Message *message)
{
if (!message)
return GNUNET_SYSERR;
if (message->flags & GNUNET_MESSENGER_FLAG_PRIVATE)
return GNUNET_YES;
else
return GNUNET_NO;
}
int
GNUNET_CHAT_message_get_read_receipt (const struct GNUNET_CHAT_Message *message,
GNUNET_CHAT_MessageReadReceiptCallback callback,
void *cls)
{
if ((!message) || (!(message->msg)) || (!(message->context)))
return GNUNET_SYSERR;
struct GNUNET_CHAT_MessageIterateReadReceipts it;
it.message = message;
it.cb = callback;
it.cls = cls;
return GNUNET_MESSENGER_iterate_members(
message->context->room, it_message_iterate_read_receipts, &it
);
}
const char*
GNUNET_CHAT_message_get_text (const struct GNUNET_CHAT_Message *message)
{
if ((!message) || (!(message->msg)))
return NULL;
if (GNUNET_CHAT_FLAG_WARNING == message->flag)
return message->warning;
if (GNUNET_MESSENGER_KIND_FILE == message->msg->header.kind)
return message->msg->body.file.name;
if (GNUNET_MESSENGER_KIND_TEXT != message->msg->header.kind)
return NULL;
return message->msg->body.text.text;
}
struct GNUNET_CHAT_File*
GNUNET_CHAT_message_get_file (const struct GNUNET_CHAT_Message *message)
{
if ((!message) || (!(message->msg)) || (!(message->context)))
return NULL;
if (GNUNET_MESSENGER_KIND_FILE != message->msg->header.kind)
return NULL;
return GNUNET_CONTAINER_multihashmap_get(
message->context->handle->files,
&(message->msg->body.file.hash)
);
}
struct GNUNET_CHAT_Invitation*
GNUNET_CHAT_message_get_invitation (const struct GNUNET_CHAT_Message *message)
{
if ((!message) || (!(message->msg)) || (!(message->context)))
return NULL;
if (GNUNET_MESSENGER_KIND_INVITE != message->msg->header.kind)
return NULL;
return GNUNET_CONTAINER_multihashmap_get(
message->context->invites,
&(message->hash)
);
}
const struct GNUNET_CHAT_Message*
GNUNET_CHAT_message_get_target (const struct GNUNET_CHAT_Message *message)
{
if ((!message) || (!(message->msg)) || (!(message->context)))
return NULL;
struct GNUNET_CHAT_Message *target;
if (GNUNET_MESSENGER_KIND_DELETE == message->msg->header.kind)
target = GNUNET_CONTAINER_multihashmap_get(
message->context->messages, &(message->msg->body.deletion.hash)
);
else
target = NULL;
return target;
}
int
GNUNET_CHAT_message_delete (const struct GNUNET_CHAT_Message *message,
struct GNUNET_TIME_Relative delay)
{
if ((!message) || (!(message->msg)) || (!(message->context)))
return GNUNET_SYSERR;
struct GNUNET_MESSENGER_Message msg;
msg.header.kind = GNUNET_MESSENGER_KIND_DELETE;
GNUNET_memcpy(&(msg.body.deletion.hash), &(message->hash), sizeof(message->hash));
msg.body.deletion.delay = GNUNET_TIME_relative_hton(delay);
GNUNET_MESSENGER_send_message(message->context->room, &msg, NULL);
return GNUNET_OK;
}
const char*
GNUNET_CHAT_file_get_name (const struct GNUNET_CHAT_File *file)
{
if (!file)
return NULL;
return file->name;
}
const struct GNUNET_HashCode*
GNUNET_CHAT_file_get_hash (const struct GNUNET_CHAT_File *file)
{
if (!file)
return NULL;
return &(file->hash);
}
uint64_t
GNUNET_CHAT_file_get_size (const struct GNUNET_CHAT_File *file)
{
if ((!file) || (!(file->uri)))
return 0;
return GNUNET_FS_uri_chk_get_file_size(file->uri);
}
uint64_t
GNUNET_CHAT_file_get_local_size (const struct GNUNET_CHAT_File *file)
{
if (!file)
return 0;
const char *directory = handle_get_directory(file->handle);
if (!directory)
return 0;
char *filename;
util_get_filename (
directory, "files", &(file->hash), &filename
);
uint64_t size;
if (GNUNET_OK != GNUNET_DISK_file_size(filename, &size, GNUNET_NO, GNUNET_YES))
size = 0;
GNUNET_free(filename);
return size;
}
int
GNUNET_CHAT_file_is_uploading (const struct GNUNET_CHAT_File *file)
{
if ((!file) || (0 == (file->status & GNUNET_CHAT_FILE_STATUS_PUBLISH)))
return GNUNET_NO;
else
return GNUNET_YES;
}
const char*
GNUNET_CHAT_file_open_preview (struct GNUNET_CHAT_File *file)
{
if (!file)
return NULL;
if (file->preview)
return file->preview;
const char *directory = handle_get_directory(file->handle);
if (!directory)
return NULL;
char *filename;
util_get_filename (
directory, "files", &(file->hash), &filename
);
if (GNUNET_YES != GNUNET_DISK_file_test(filename))
goto free_filename;
file->preview = GNUNET_DISK_mktemp(file->name);
if (!(file->preview))
goto free_filename;
remove(file->preview);
if ((GNUNET_OK != GNUNET_DISK_file_copy(filename, file->preview)) ||
(GNUNET_OK != util_decrypt_file(file->preview,
&(file->hash),
&(file->key))))
{
GNUNET_free(file->preview);
file->preview = NULL;
}
free_filename:
GNUNET_free(filename);
return file->preview;
}
void
GNUNET_CHAT_file_close_preview (struct GNUNET_CHAT_File *file)
{
if ((!file) || (!(file->preview)))
return;
remove(file->preview);
GNUNET_free(file->preview);
file->preview = NULL;
}
void
GNUNET_CHAT_file_set_user_pointer (struct GNUNET_CHAT_File *file,
void *user_pointer)
{
if (!file)
return;
file->user_pointer = user_pointer;
}
void*
GNUNET_CHAT_file_get_user_pointer (const struct GNUNET_CHAT_File *file)
{
if (!file)
return NULL;
return file->user_pointer;
}
int
GNUNET_CHAT_file_is_downloading (const struct GNUNET_CHAT_File *file)
{
if ((!file) || (0 == (file->status & GNUNET_CHAT_FILE_STATUS_DOWNLOAD)))
return GNUNET_NO;
else
return GNUNET_YES;
}
int
GNUNET_CHAT_file_start_download (struct GNUNET_CHAT_File *file,
GNUNET_CHAT_FileDownloadCallback callback,
void *cls)
{
if ((!file) || (!(file->uri)))
return GNUNET_SYSERR;
if (file->download)
{
file_bind_downlaod(file, callback, cls);
GNUNET_FS_download_resume(file->download);
return GNUNET_OK;
}
const char *directory = handle_get_directory(file->handle);
if (!directory)
return GNUNET_SYSERR;
const uint64_t size = GNUNET_FS_uri_chk_get_file_size(file->uri);
char *filename;
util_get_filename (
directory, "files", &(file->hash), &filename
);
uint64_t offset;
if (GNUNET_OK != GNUNET_DISK_file_size(filename, &offset, GNUNET_NO, GNUNET_YES))
offset = 0;
if (offset >= size)
{
if (callback)
callback(cls, file, size, size);
return GNUNET_OK;
}
file_bind_downlaod(file, callback, cls);
const uint64_t remaining = (size - offset);
file->download = GNUNET_FS_download_start(
file->handle->fs,
file->uri,
file->meta,
filename,
NULL,
offset,
remaining,
1,
GNUNET_FS_DOWNLOAD_OPTION_NONE,
file,
NULL
);
if (file->download)
file->status |= GNUNET_CHAT_FILE_STATUS_DOWNLOAD;
GNUNET_free(filename);
return GNUNET_OK;
}
int
GNUNET_CHAT_file_pause_download (struct GNUNET_CHAT_File *file)
{
if (!file)
return GNUNET_SYSERR;
GNUNET_FS_download_suspend(file->download);
return GNUNET_OK;
}
int
GNUNET_CHAT_file_resume_download (struct GNUNET_CHAT_File *file)
{
if (!file)
return GNUNET_SYSERR;
GNUNET_FS_download_resume(file->download);
return GNUNET_OK;
}
int
GNUNET_CHAT_file_stop_download (struct GNUNET_CHAT_File *file)
{
if (!file)
return GNUNET_SYSERR;
GNUNET_FS_download_stop(file->download, GNUNET_YES);
file->download = NULL;
return GNUNET_OK;
}
int
GNUNET_CHAT_file_is_unindexing (const struct GNUNET_CHAT_File *file)
{
if ((!file) || (0 == (file->status & GNUNET_CHAT_FILE_STATUS_UNINDEX)))
return GNUNET_NO;
else
return GNUNET_YES;
}
int
GNUNET_CHAT_file_unindex (struct GNUNET_CHAT_File *file,
GNUNET_CHAT_FileUnindexCallback callback,
void *cls)
{
if (!file)
return GNUNET_SYSERR;
if (file->publish)
{
GNUNET_FS_publish_stop(file->publish);
file->publish = NULL;
return GNUNET_OK;
}
file_bind_unindex(file, callback, cls);
if (file->unindex)
return GNUNET_OK;
const char *directory = handle_get_directory(file->handle);
if (!directory)
return GNUNET_SYSERR;
char *filename;
util_get_filename (
directory, "files", &(file->hash), &filename
);
file->unindex = GNUNET_FS_unindex_start(
file->handle->fs, filename, file
);
if (file->unindex)
file->status |= GNUNET_CHAT_FILE_STATUS_UNINDEX;
GNUNET_free(filename);
return GNUNET_OK;
}
void
GNUNET_CHAT_invitation_accept (struct GNUNET_CHAT_Invitation *invitation)
{
if (!invitation)
return;
struct GNUNET_PeerIdentity door;
GNUNET_PEER_resolve(invitation->door, &door);
GNUNET_MESSENGER_enter_room(
invitation->context->handle->messenger,
&door, &(invitation->key)
);
}