/*
This file is part of GNUnet.
Copyright (C) 2021--2024 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define _(String) ((const char*) String)
#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_tagging.h"
#include "gnunet_chat_ticket.h"
#include "gnunet_chat_util.h"
#include "gnunet_chat_lib_intern.c"
#define GNUNET_CHAT_VERSION_ASSERT() {\
GNUNET_assert(\
(GNUNET_MESSENGER_VERSION == ((GNUNET_CHAT_VERSION >> 16L) & 0xFFFFFFFFL))\
);\
}
static const uint32_t block_anonymity_level = 1;
static const uint32_t block_content_priority = 100;
static const uint32_t block_replication_level = 1;
struct GNUNET_CHAT_Handle*
GNUNET_CHAT_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
GNUNET_CHAT_ContextMessageCallback msg_cb, void *msg_cls)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!cfg)
return NULL;
return handle_create_from_config(
cfg,
msg_cb,
msg_cls
);
}
void
GNUNET_CHAT_stop (struct GNUNET_CHAT_Handle *handle)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!handle) || (handle->destruction))
return;
handle->destruction = GNUNET_SCHEDULER_add_at_with_priority(
GNUNET_TIME_absolute_get(),
GNUNET_SCHEDULER_PRIORITY_URGENT,
task_handle_destruction,
handle
);
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_account_create (struct GNUNET_CHAT_Handle *handle,
const char* name)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!handle) || (handle->destruction) || (!name))
return GNUNET_SYSERR;
char *low = util_get_lower(name);
int result = handle_create_account(handle, low);
GNUNET_free(low);
return result;
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_account_delete(struct GNUNET_CHAT_Handle *handle,
const char* name)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!handle) || (handle->destruction) || (!name))
return GNUNET_SYSERR;
return handle_delete_account(handle, name);
}
int
GNUNET_CHAT_iterate_accounts (const struct GNUNET_CHAT_Handle *handle,
GNUNET_CHAT_AccountCallback callback,
void *cls)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!handle) || (handle->destruction))
return GNUNET_SYSERR;
int result = 0;
struct GNUNET_CHAT_InternalAccounts *accounts = handle->accounts_head;
while (accounts)
{
if ((!(accounts->account)) || (accounts->op))
goto skip_account;
result++;
if ((callback) && (GNUNET_YES != callback(cls, handle, accounts->account)))
break;
skip_account:
accounts = accounts->next;
}
return result;
}
void
GNUNET_CHAT_connect (struct GNUNET_CHAT_Handle *handle,
const struct GNUNET_CHAT_Account *account)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!handle) || (handle->destruction))
return;
if (handle->connection)
GNUNET_SCHEDULER_cancel(handle->connection);
if (handle->current == account)
{
handle->next = NULL;
handle->connection = NULL;
return;
}
if (handle->current)
{
handle->next = account;
handle->connection = NULL;
GNUNET_CHAT_disconnect(handle);
return;
}
handle->next = account;
handle->connection = GNUNET_SCHEDULER_add_now(
task_handle_connection,
handle
);
}
void
GNUNET_CHAT_disconnect (struct GNUNET_CHAT_Handle *handle)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!handle) || (handle->destruction))
return;
if (handle->connection)
GNUNET_SCHEDULER_cancel(handle->connection);
if (!(handle->current))
{
handle->next = NULL;
handle->connection = NULL;
return;
}
handle->connection = GNUNET_SCHEDULER_add_now(
task_handle_disconnection,
handle
);
}
const struct GNUNET_CHAT_Account*
GNUNET_CHAT_get_connected (const struct GNUNET_CHAT_Handle *handle)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!handle) || (handle->destruction))
return NULL;
return handle->current;
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_update (struct GNUNET_CHAT_Handle *handle)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!handle) || (handle->destruction))
return GNUNET_SYSERR;
return handle_update(handle);
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_set_name (struct GNUNET_CHAT_Handle *handle,
const char *name)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!handle) || (handle->destruction))
return GNUNET_SYSERR;
if (!name)
return GNUNET_NO;
char *low = util_get_lower(name);
int result;
if (handle->current)
result = handle_rename_account(handle, handle->current->name, low);
else
result = GNUNET_OK;
if (GNUNET_OK != result)
return result;
result = GNUNET_MESSENGER_set_name(handle->messenger, low);
GNUNET_free(low);
return result;
}
const char*
GNUNET_CHAT_get_name (const struct GNUNET_CHAT_Handle *handle)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!handle) || (handle->destruction))
return NULL;
return GNUNET_MESSENGER_get_name(handle->messenger);
}
const char*
GNUNET_CHAT_get_key (const struct GNUNET_CHAT_Handle *handle)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!handle) || (handle->destruction))
return NULL;
return handle->public_key;
}
void
GNUNET_CHAT_set_attribute (struct GNUNET_CHAT_Handle *handle,
const char *name,
const char *value,
struct GNUNET_TIME_Relative expires)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!handle) || (handle->destruction))
return;
const struct GNUNET_CRYPTO_PrivateKey *key = handle_get_key(
handle
);
if ((!key) || (!name))
return;
struct GNUNET_CHAT_AttributeProcess *attributes;
attributes = internal_attributes_create_store(handle, name, expires);
if (!attributes)
return;
if (value)
{
enum GNUNET_GenericReturnValue result;
result = GNUNET_RECLAIM_attribute_string_to_value(
GNUNET_RECLAIM_ATTRIBUTE_TYPE_STRING,
value,
&(attributes->data),
&(attributes->attribute->data_size)
);
if (GNUNET_OK != result)
{
internal_attributes_destroy(attributes);
return;
}
attributes->attribute->type = GNUNET_RECLAIM_ATTRIBUTE_TYPE_STRING;
attributes->attribute->data = attributes->data;
}
attributes->iter = GNUNET_RECLAIM_get_attributes_start(
handle->reclaim,
key,
cb_task_error_iterate_attribute,
attributes,
cb_store_attribute,
attributes,
cb_task_finish_iterate_attribute,
attributes
);
}
void
GNUNET_CHAT_delete_attribute (struct GNUNET_CHAT_Handle *handle,
const char *name)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!handle) || (handle->destruction))
return;
const struct GNUNET_CRYPTO_PrivateKey *key = handle_get_key(
handle
);
if ((!key) || (!name))
return;
struct GNUNET_CHAT_AttributeProcess *attributes;
attributes = internal_attributes_create(handle, name);
if (!attributes)
return;
attributes->iter = GNUNET_RECLAIM_get_attributes_start(
handle->reclaim,
key,
cb_task_error_iterate_attribute,
attributes,
cb_delete_attribute,
attributes,
cb_task_finish_iterate_attribute,
attributes
);
}
void
GNUNET_CHAT_get_attributes (struct GNUNET_CHAT_Handle *handle,
GNUNET_CHAT_AttributeCallback callback,
void *cls)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!handle) || (handle->destruction))
return;
const struct GNUNET_CRYPTO_PrivateKey *key = handle_get_key(
handle
);
if (!key)
return;
struct GNUNET_CHAT_AttributeProcess *attributes;
attributes = internal_attributes_create(handle, NULL);
if (!attributes)
return;
attributes->callback = callback;
attributes->closure = cls;
attributes->iter = GNUNET_RECLAIM_get_attributes_start(
handle->reclaim,
key,
cb_task_error_iterate_attribute,
attributes,
cb_iterate_attribute,
attributes,
cb_task_finish_iterate_attribute,
attributes
);
}
void
GNUNET_CHAT_share_attribute_with (struct GNUNET_CHAT_Handle *handle,
struct GNUNET_CHAT_Contact *contact,
const char *name)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!handle) || (handle->destruction) || (!contact))
return;
const struct GNUNET_CRYPTO_PrivateKey *key = handle_get_key(
handle
);
const struct GNUNET_CRYPTO_PublicKey *pubkey = contact_get_key(
contact
);
if ((!key) || (!pubkey) || (!name))
return;
struct GNUNET_CHAT_AttributeProcess *attributes;
attributes = internal_attributes_create_share(handle, contact, name);
if (!attributes)
return;
attributes->iter = GNUNET_RECLAIM_get_attributes_start(
handle->reclaim,
key,
cb_task_error_iterate_attribute,
attributes,
cb_share_attribute,
attributes,
cb_task_finish_iterate_attribute,
attributes
);
}
void
GNUNET_CHAT_unshare_attribute_from (struct GNUNET_CHAT_Handle *handle,
struct GNUNET_CHAT_Contact *contact,
const char *name)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!handle) || (handle->destruction) || (!contact))
return;
const struct GNUNET_CRYPTO_PrivateKey *key = handle_get_key(
handle
);
if ((!key) || (!name))
return;
struct GNUNET_CHAT_TicketProcess *tickets;
tickets = internal_tickets_create(handle, contact, name);
if (!tickets)
return;
tickets->iter = GNUNET_RECLAIM_ticket_iteration_start(
handle->reclaim,
key,
cb_task_error_iterate_ticket,
tickets,
cb_iterate_ticket_check,
tickets,
cb_task_finish_iterate_ticket,
tickets
);
}
void
GNUNET_CHAT_get_shared_attributes (struct GNUNET_CHAT_Handle *handle,
struct GNUNET_CHAT_Contact *contact,
GNUNET_CHAT_ContactAttributeCallback callback,
void *cls)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!handle) || (handle->destruction) || (!contact))
return;
const struct GNUNET_CRYPTO_PrivateKey *key = handle_get_key(
handle
);
if (!key)
return;
struct GNUNET_CHAT_TicketProcess *tickets;
tickets = internal_tickets_create(handle, contact, NULL);
if (!tickets)
return;
tickets->callback = callback;
tickets->closure = cls;
tickets->iter = GNUNET_RECLAIM_ticket_iteration_start(
handle->reclaim,
key,
cb_task_error_iterate_ticket,
tickets,
cb_iterate_ticket,
tickets,
cb_task_finish_iterate_ticket,
tickets
);
}
struct GNUNET_CHAT_Uri*
GNUNET_CHAT_uri_parse (const char *uri,
char **emsg)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!uri)
return NULL;
return uri_parse_from_string(uri, emsg);
}
char*
GNUNET_CHAT_uri_to_string (const struct GNUNET_CHAT_Uri *uri)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!uri)
return NULL;
return uri_to_string(uri);
}
enum GNUNET_CHAT_UriType
GNUNET_CHAT_uri_get_type (const struct GNUNET_CHAT_Uri *uri)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!uri)
return GNUNET_CHAT_URI_TYPE_UNKNOWN;
return uri->type;
}
void
GNUNET_CHAT_uri_destroy (struct GNUNET_CHAT_Uri *uri)
{
GNUNET_CHAT_VERSION_ASSERT();
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)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!handle) || (handle->destruction))
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)
{
GNUNET_CHAT_VERSION_ASSERT();
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)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!handle) || (handle->destruction) || (!(handle->gns)) ||
(!uri) || (GNUNET_CHAT_URI_TYPE_CHAT != uri->type))
return;
struct GNUNET_CHAT_UriLookups *lookups = GNUNET_new(
struct GNUNET_CHAT_UriLookups
);
lookups->handle = handle;
lookups->uri = uri_create_chat(
&(uri->chat.zone),
uri->chat.label
);
lookups->request = GNUNET_GNS_lookup(
handle->gns,
lookups->uri->chat.label,
&(uri->chat.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
);
}
struct GNUNET_CHAT_File*
GNUNET_CHAT_request_file (struct GNUNET_CHAT_Handle *handle,
const struct GNUNET_CHAT_Uri *uri)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!handle) || (handle->destruction) ||
(!uri) || (GNUNET_CHAT_URI_TYPE_FS != uri->type))
return NULL;
if (!GNUNET_FS_uri_test_chk(uri->fs.uri))
return NULL;
const struct GNUNET_HashCode *hash = GNUNET_FS_uri_chk_get_file_hash(
uri->fs.uri
);
if (!hash)
return NULL;
struct GNUNET_CHAT_File *file = GNUNET_CONTAINER_multihashmap_get(
handle->files,
hash
);
if (file)
return file;
file = file_create_from_chk_uri(handle, uri->fs.uri);
if (!file)
return NULL;
if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(handle->files, hash, file,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
{
file_destroy(file);
file = NULL;
}
return file;
}
struct GNUNET_CHAT_File*
GNUNET_CHAT_upload_file (struct GNUNET_CHAT_Handle *handle,
const char *path,
GNUNET_CHAT_FileUploadCallback callback,
void *cls)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!handle) || (handle->destruction) ||
(!path))
return NULL;
struct GNUNET_HashCode hash;
if (GNUNET_OK != util_hash_file(path, &hash))
return NULL;
char *filename = handle_create_file_path(
handle, &hash
);
if (!filename)
return NULL;
struct GNUNET_CHAT_File *file = GNUNET_CONTAINER_multihashmap_get(
handle->files,
&hash
);
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;
}
char* p = GNUNET_strdup(path);
file = file_create_from_disk(
handle,
basename(p),
&hash,
NULL
);
GNUNET_free(p);
if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(
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 = block_anonymity_level;
bo.content_priority = block_content_priority;
bo.replication_level = block_replication_level;
bo.expiration_time = GNUNET_TIME_absolute_get_forever_();
struct GNUNET_FS_FileInformation* fi = GNUNET_FS_file_information_create_from_file(
handle->fs,
file,
filename,
NULL,
file->meta,
GNUNET_YES,
&bo
);
file->publish = GNUNET_FS_publish_start(
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, NULL, callback, cls);
return file;
}
int
GNUNET_CHAT_iterate_files (struct GNUNET_CHAT_Handle *handle,
GNUNET_CHAT_FileCallback callback,
void *cls)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!handle) || (handle->destruction))
return GNUNET_SYSERR;
struct GNUNET_CHAT_IterateFiles it;
it.handle = handle;
it.cb = callback;
it.cls = cls;
return GNUNET_CONTAINER_multihashmap_iterate(
handle->files,
it_iterate_files,
&it
);
}
void
GNUNET_CHAT_set_user_pointer (struct GNUNET_CHAT_Handle *handle,
void *user_pointer)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!handle) || (handle->destruction))
return;
handle->user_pointer = user_pointer;
}
void*
GNUNET_CHAT_get_user_pointer (const struct GNUNET_CHAT_Handle *handle)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!handle) || (handle->destruction))
return NULL;
return handle->user_pointer;
}
int
GNUNET_CHAT_iterate_contacts (struct GNUNET_CHAT_Handle *handle,
GNUNET_CHAT_ContactCallback callback,
void *cls)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!handle) || (handle->destruction) || (!(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)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!account)
return NULL;
return account->name;
}
void
GNUNET_CHAT_account_get_attributes (struct GNUNET_CHAT_Handle *handle,
const struct GNUNET_CHAT_Account *account,
GNUNET_CHAT_AccountAttributeCallback callback,
void *cls)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!handle) || (handle->destruction) || (!account))
return;
const struct GNUNET_CRYPTO_PrivateKey *key = account_get_key(
account
);
if (!key)
return;
struct GNUNET_CHAT_AttributeProcess *attributes;
attributes = internal_attributes_create_request(handle, account);
if (!attributes)
return;
attributes->account_callback = callback;
attributes->closure = cls;
attributes->iter = GNUNET_RECLAIM_get_attributes_start(
handle->reclaim,
key,
cb_task_error_iterate_attribute,
attributes,
cb_iterate_attribute,
attributes,
cb_task_finish_iterate_attribute,
attributes
);
}
void
GNUNET_CHAT_account_set_user_pointer (struct GNUNET_CHAT_Account *account,
void *user_pointer)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!account)
return;
account->user_pointer = user_pointer;
}
void*
GNUNET_CHAT_account_get_user_pointer (const struct GNUNET_CHAT_Account *account)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!account)
return NULL;
return account->user_pointer;
}
struct GNUNET_CHAT_Group *
GNUNET_CHAT_group_create (struct GNUNET_CHAT_Handle *handle,
const char* topic)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!handle) || (handle->destruction) ||
(!(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)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!handle) || (handle->destruction) || (!(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
);
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_contact_delete (struct GNUNET_CHAT_Contact *contact)
{
GNUNET_CHAT_VERSION_ASSERT();
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
);
context_delete(contact->context, GNUNET_YES);
context_destroy(contact->context);
contact_destroy(contact);
return GNUNET_OK;
}
void
GNUNET_CHAT_contact_set_name (struct GNUNET_CHAT_Contact *contact,
const char *name)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!contact) || (!(contact->context)) ||
(contact->context->topic))
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)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!contact)
return NULL;
if ((contact->context) && (! contact->context->topic) &&
(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)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!contact)
return NULL;
return contact->public_key;
}
struct GNUNET_CHAT_Context*
GNUNET_CHAT_contact_get_context (struct GNUNET_CHAT_Contact *contact)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!contact)
return NULL;
if (contact->context)
return contact->context;
struct GNUNET_CHAT_Context *context = contact_find_context(
contact,
GNUNET_NO
);
if ((context) && (GNUNET_CHAT_CONTEXT_TYPE_CONTACT == context->type))
goto attach_return;
context = context_create_from_contact(contact->handle, contact->member);
attach_return:
if (context)
contact->context = context;
return context;
}
void
GNUNET_CHAT_contact_set_user_pointer (struct GNUNET_CHAT_Contact *contact,
void *user_pointer)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!contact)
return;
contact->user_pointer = user_pointer;
}
void*
GNUNET_CHAT_contact_get_user_pointer (const struct GNUNET_CHAT_Contact *contact)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!contact)
return NULL;
return contact->user_pointer;
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_contact_is_owned (const struct GNUNET_CHAT_Contact *contact)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!contact)
return GNUNET_SYSERR;
return contact->owned;
}
void
GNUNET_CHAT_contact_set_blocked (struct GNUNET_CHAT_Contact *contact,
enum GNUNET_GenericReturnValue blocked)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!contact)
return;
struct GNUNET_CHAT_ContactIterateContexts it;
it.contact = contact;
it.tag = NULL;
if (GNUNET_NO == blocked)
it.cb = contact_untag;
else if (GNUNET_YES == blocked)
it.cb = contact_tag;
else
return;
GNUNET_CONTAINER_multihashmap_iterate(
contact->joined,
it_contact_iterate_contexts,
&it
);
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_contact_is_blocked (const struct GNUNET_CHAT_Contact *contact)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!contact)
return GNUNET_SYSERR;
return contact_is_tagged(contact, NULL, NULL);
}
void
GNUNET_CHAT_contact_tag (struct GNUNET_CHAT_Contact *contact,
const char *tag)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!contact) || (!tag) || (!tag[0]))
return;
struct GNUNET_CHAT_ContactIterateContexts it;
it.contact = contact;
it.tag = tag;
it.cb = contact_tag;
GNUNET_CONTAINER_multihashmap_iterate(
contact->joined,
it_contact_iterate_contexts,
&it
);
}
void
GNUNET_CHAT_contact_untag (struct GNUNET_CHAT_Contact *contact,
const char *tag)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!contact) || (!tag) || (!tag[0]))
return;
struct GNUNET_CHAT_ContactIterateContexts it;
it.contact = contact;
it.tag = tag;
it.cb = contact_untag;
GNUNET_CONTAINER_multihashmap_iterate(
contact->joined,
it_contact_iterate_contexts,
&it
);
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_contact_is_tagged (const struct GNUNET_CHAT_Contact *contact,
const char *tag)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!contact) || (!tag) || (!tag[0]))
return GNUNET_SYSERR;
return contact_is_tagged(contact, NULL, tag);
}
void
GNUNET_CHAT_contact_get_attributes (struct GNUNET_CHAT_Contact *contact,
GNUNET_CHAT_ContactAttributeCallback callback,
void *cls)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!contact)
return;
struct GNUNET_CHAT_InternalTickets *tickets;
tickets = contact->tickets_head;
while (tickets)
{
ticket_consume(
tickets->ticket,
callback,
cls
);
tickets = tickets->next;
}
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_group_leave (struct GNUNET_CHAT_Group *group)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!group) || (group->destruction))
return GNUNET_SYSERR;
group->context->deleted = GNUNET_YES;
group->destruction = GNUNET_SCHEDULER_add_now(
task_group_destruction,
group
);
return GNUNET_OK;
}
void
GNUNET_CHAT_group_set_name (struct GNUNET_CHAT_Group *group,
const char *name)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!group) || (!(group->context)))
return;
context_update_nick(group->context, name);
if (group->context->room)
context_write_records(group->context);
}
const char*
GNUNET_CHAT_group_get_name (const struct GNUNET_CHAT_Group *group)
{
GNUNET_CHAT_VERSION_ASSERT();
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)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!group)
return;
group->user_pointer = user_pointer;
}
void*
GNUNET_CHAT_group_get_user_pointer (const struct GNUNET_CHAT_Group *group)
{
GNUNET_CHAT_VERSION_ASSERT();
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)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!group) || (!contact) || (!contact->member))
return;
struct GNUNET_CHAT_Context *context = contact_find_context(
contact,
GNUNET_YES
);
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;
memset(&msg, 0, sizeof(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)
{
GNUNET_CHAT_VERSION_ASSERT();
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
);
}
void
GNUNET_CHAT_member_set_user_pointer (struct GNUNET_CHAT_Group *group,
const struct GNUNET_CHAT_Contact *member,
void *user_pointer)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!group) || (!(group->context)) || (!member))
return;
struct GNUNET_ShortHashCode hash;
util_shorthash_from_member(member->member, &hash);
GNUNET_CONTAINER_multishortmap_put(
group->context->member_pointers,
&hash,
user_pointer,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE
);
}
void*
GNUNET_CHAT_member_get_user_pointer (const struct GNUNET_CHAT_Group *group,
const struct GNUNET_CHAT_Contact *member)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!group) || (!(group->context)) || (!member))
return NULL;
struct GNUNET_ShortHashCode hash;
util_shorthash_from_member(member->member, &hash);
return GNUNET_CONTAINER_multishortmap_get(
group->context->member_pointers,
&hash
);
}
struct GNUNET_CHAT_Context*
GNUNET_CHAT_group_get_context (struct GNUNET_CHAT_Group *group)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!group)
return NULL;
return group->context;
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_context_get_status (const struct GNUNET_CHAT_Context *context)
{
GNUNET_CHAT_VERSION_ASSERT();
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)
{
GNUNET_CHAT_VERSION_ASSERT();
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,
GNUNET_YES
);
if (!other)
goto cleanup_contact;
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))
goto cleanup_contact;
struct GNUNET_MESSENGER_Room *room = GNUNET_MESSENGER_open_room(
handle->messenger, &key
);
if (!room)
goto cleanup_contact;
context_update_room(context, room, GNUNET_YES);
if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(
handle->contexts, &key, context,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
goto cleanup_room;
handle_send_room_name(handle, room);
struct GNUNET_MESSENGER_Message msg;
memset(&msg, 0, sizeof(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);
return;
cleanup_room:
GNUNET_MESSENGER_close_room(room);
cleanup_contact:
contact_destroy(contact);
}
struct GNUNET_CHAT_Contact*
GNUNET_CHAT_context_get_contact (struct GNUNET_CHAT_Context *context)
{
GNUNET_CHAT_VERSION_ASSERT();
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)
{
GNUNET_CHAT_VERSION_ASSERT();
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)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!context)
return;
context->user_pointer = user_pointer;
}
void*
GNUNET_CHAT_context_get_user_pointer (const struct GNUNET_CHAT_Context *context)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!context)
return NULL;
return context->user_pointer;
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_context_send_text (struct GNUNET_CHAT_Context *context,
const char *text)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!context) || (!text) || (!(context->room)))
return GNUNET_SYSERR;
struct GNUNET_MESSENGER_Message msg;
memset(&msg, 0, sizeof(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;
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_context_send_read_receipt (struct GNUNET_CHAT_Context *context,
const struct GNUNET_CHAT_Message *message)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!context) || (!(context->room)))
return GNUNET_SYSERR;
char zero = '\0';
struct GNUNET_MESSENGER_Message msg;
memset(&msg, 0, sizeof(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 ((GNUNET_YES != message_has_msg(message)) ||
(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)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!context) || (!path) || (!(context->room)))
return NULL;
struct GNUNET_HashCode hash;
if (GNUNET_OK != util_hash_file(path, &hash))
return NULL;
char *filename = handle_create_file_path(
context->handle, &hash
);
if (!filename)
return NULL;
struct GNUNET_CHAT_File *file = GNUNET_CONTAINER_multihashmap_get(
context->handle->files,
&hash
);
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 = block_anonymity_level;
bo.content_priority = block_content_priority;
bo.replication_level = block_replication_level;
bo.expiration_time = GNUNET_TIME_absolute_get_forever_();
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;
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_context_share_file (struct GNUNET_CHAT_Context *context,
const struct GNUNET_CHAT_File *file)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!context) || (!file) ||
(!(file->name)) || (strlen(file->name) > NAME_MAX) ||
(!(file->uri)) || (!(context->room)))
return GNUNET_SYSERR;
struct GNUNET_MESSENGER_Message msg;
memset(&msg, 0, sizeof(msg));
msg.header.kind = GNUNET_MESSENGER_KIND_FILE;
if (file->key)
GNUNET_memcpy(&(msg.body.file.key), file->key,
sizeof(struct GNUNET_CRYPTO_SymmetricSessionKey));
else
memset(&(msg.body.file.key), 0, sizeof(msg.body.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;
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_context_send_tag (struct GNUNET_CHAT_Context *context,
const struct GNUNET_CHAT_Message *message,
const char *tag)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!context) || (!message) || (!tag) || (!(context->room)))
return GNUNET_SYSERR;
char *tag_value = GNUNET_strdup(tag);
struct GNUNET_MESSENGER_Message msg;
memset(&msg, 0, sizeof(msg));
msg.header.kind = GNUNET_MESSENGER_KIND_TAG;
GNUNET_memcpy(&(msg.body.tag.hash), &(message->hash),
sizeof(struct GNUNET_HashCode));
msg.body.tag.tag = tag_value;
GNUNET_MESSENGER_send_message(
context->room,
&msg,
NULL
);
GNUNET_free(tag_value);
return GNUNET_OK;
}
int
GNUNET_CHAT_context_iterate_messages (struct GNUNET_CHAT_Context *context,
GNUNET_CHAT_ContextMessageCallback callback,
void *cls)
{
GNUNET_CHAT_VERSION_ASSERT();
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)
{
GNUNET_CHAT_VERSION_ASSERT();
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
);
}
enum GNUNET_CHAT_MessageKind
GNUNET_CHAT_message_get_kind (const struct GNUNET_CHAT_Message *message)
{
GNUNET_CHAT_VERSION_ASSERT();
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_LOGOUT:
return GNUNET_CHAT_KIND_LOGOUT;
case GNUNET_CHAT_FLAG_CREATE_ACCOUNT:
return GNUNET_CHAT_KIND_CREATED_ACCOUNT;
case GNUNET_CHAT_FLAG_DELETE_ACCOUNT:
return GNUNET_CHAT_KIND_DELETED_ACCOUNT;
case GNUNET_CHAT_FLAG_UPDATE_ACCOUNT:
return GNUNET_CHAT_KIND_UPDATE_ACCOUNT;
case GNUNET_CHAT_FLAG_UPDATE_CONTEXT:
return GNUNET_CHAT_KIND_UPDATE_CONTEXT;
case GNUNET_CHAT_FLAG_ATTRIBUTES:
return GNUNET_CHAT_KIND_ATTRIBUTES;
case GNUNET_CHAT_FLAG_SHARE_ATTRIBUTES:
return GNUNET_CHAT_KIND_SHARED_ATTRIBUTES;
default:
break;
}
if (GNUNET_YES != message_has_msg(message))
return GNUNET_CHAT_KIND_UNKNOWN;
return util_message_kind_from_kind(message->msg->header.kind);
}
struct GNUNET_TIME_Absolute
GNUNET_CHAT_message_get_timestamp (const struct GNUNET_CHAT_Message *message)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!message) || (GNUNET_YES != message_has_msg(message)))
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)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!message) || (GNUNET_YES != message_has_msg(message)) ||
(!(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);
}
struct GNUNET_CHAT_Contact*
GNUNET_CHAT_message_get_recipient (const struct GNUNET_CHAT_Message *message)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!message) || (GNUNET_YES != message_has_msg(message)) ||
(!(message->context)) || (!(message->context->room)))
return NULL;
const struct GNUNET_MESSENGER_Contact *recipient = GNUNET_MESSENGER_get_recipient(
message->context->room, &(message->hash)
);
if (!recipient)
return NULL;
return handle_get_contact_from_messenger(message->context->handle, recipient);
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_message_is_sent (const struct GNUNET_CHAT_Message *message)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!message)
return GNUNET_SYSERR;
if (message->flags & GNUNET_MESSENGER_FLAG_SENT)
return GNUNET_YES;
else
return GNUNET_NO;
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_message_is_private (const struct GNUNET_CHAT_Message *message)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!message)
return GNUNET_SYSERR;
if (message->flags & GNUNET_MESSENGER_FLAG_PRIVATE)
return GNUNET_YES;
else
return GNUNET_NO;
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_message_is_recent (const struct GNUNET_CHAT_Message *message)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!message)
return GNUNET_SYSERR;
if (message->flags & GNUNET_MESSENGER_FLAG_RECENT)
return GNUNET_YES;
else
return GNUNET_NO;
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_message_is_update (const struct GNUNET_CHAT_Message *message)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!message)
return GNUNET_SYSERR;
if (message->flags & GNUNET_MESSENGER_FLAG_UPDATE)
return GNUNET_YES;
else
return GNUNET_NO;
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_message_is_deleted (const struct GNUNET_CHAT_Message *message)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!message)
return GNUNET_SYSERR;
if ((GNUNET_CHAT_FLAG_NONE == message->flag) &&
((message->flags & GNUNET_MESSENGER_FLAG_DELETE) ||
(!message->msg)))
return GNUNET_YES;
else
return GNUNET_NO;
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_message_is_tagged (const struct GNUNET_CHAT_Message *message,
const char *tag)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!message) || (!(message->context)))
return GNUNET_SYSERR;
const struct GNUNET_CHAT_Tagging *tagging = GNUNET_CONTAINER_multihashmap_get(
message->context->taggings, &(message->hash));
if (!tagging)
return GNUNET_NO;
if (tagging_iterate(tagging, GNUNET_NO, tag, NULL, NULL) > 0)
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)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!message) || (GNUNET_YES != message_has_msg(message)) ||
(!(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)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!message)
return NULL;
if (GNUNET_CHAT_FLAG_WARNING == message->flag)
return message->warning;
else if (GNUNET_CHAT_FLAG_ATTRIBUTES == message->flag)
return message->attr;
if (GNUNET_YES != message_has_msg(message))
return NULL;
if (GNUNET_MESSENGER_KIND_TEXT == message->msg->header.kind)
return message->msg->body.text.text;
else if (GNUNET_MESSENGER_KIND_FILE == message->msg->header.kind)
return message->msg->body.file.name;
else if (GNUNET_MESSENGER_KIND_TAG == message->msg->header.kind)
return message->msg->body.tag.tag;
else
return NULL;
}
const struct GNUNET_CHAT_Account*
GNUNET_CHAT_message_get_account (const struct GNUNET_CHAT_Message *message)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!message)
return NULL;
if ((message->context) && (message->context->handle))
return message->context->handle->current;
else
return message->account;
}
struct GNUNET_CHAT_File*
GNUNET_CHAT_message_get_file (const struct GNUNET_CHAT_Message *message)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!message) || (GNUNET_YES != message_has_msg(message)) ||
(!(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)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!message) || (GNUNET_YES != message_has_msg(message)) ||
(!(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)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!message) || (GNUNET_YES != message_has_msg(message)) ||
(!(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 if (GNUNET_MESSENGER_KIND_TAG == message->msg->header.kind)
target = GNUNET_CONTAINER_multihashmap_get(
message->context->messages, &(message->msg->body.tag.hash));
else
target = NULL;
return target;
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_message_delete (const struct GNUNET_CHAT_Message *message,
struct GNUNET_TIME_Relative delay)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!message) || (GNUNET_YES != message_has_msg(message)) ||
(!(message->context)))
return GNUNET_SYSERR;
GNUNET_MESSENGER_delete_message(message->context->room, &(message->hash), delay);
return GNUNET_OK;
}
int
GNUNET_CHAT_message_iterate_tags (const struct GNUNET_CHAT_Message *message,
GNUNET_CHAT_MessageCallback callback,
void *cls)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!message) || (!(message->context)))
return GNUNET_SYSERR;
const struct GNUNET_CHAT_Tagging *tagging = GNUNET_CONTAINER_multihashmap_get(
message->context->taggings, &(message->hash));
if (!tagging)
return 0;
return tagging_iterate(tagging, GNUNET_YES, NULL, callback, cls);
}
const char*
GNUNET_CHAT_file_get_name (const struct GNUNET_CHAT_File *file)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!file)
return NULL;
return file->name;
}
const struct GNUNET_HashCode*
GNUNET_CHAT_file_get_hash (const struct GNUNET_CHAT_File *file)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!file)
return NULL;
return &(file->hash);
}
uint64_t
GNUNET_CHAT_file_get_size (const struct GNUNET_CHAT_File *file)
{
GNUNET_CHAT_VERSION_ASSERT();
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)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!file)
return 0;
char *filename = handle_create_file_path(
file->handle, &(file->hash)
);
if (!filename)
return 0;
uint64_t size;
if (GNUNET_OK != GNUNET_DISK_file_size(filename, &size, GNUNET_NO, GNUNET_YES))
size = 0;
GNUNET_free(filename);
return size;
}
struct GNUNET_CHAT_Uri*
GNUNET_CHAT_file_get_uri (const struct GNUNET_CHAT_File *file)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!file) || (!(file->uri)))
return NULL;
return uri_create_file(file->uri);
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_file_is_uploading (const struct GNUNET_CHAT_File *file)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!file) || (0 == (file->status & GNUNET_CHAT_FILE_STATUS_PUBLISH)))
return GNUNET_NO;
else
return GNUNET_YES;
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_file_is_ready (const struct GNUNET_CHAT_File *file)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!file) || (file->status & GNUNET_CHAT_FILE_STATUS_MASK))
return GNUNET_NO;
const uint64_t size = GNUNET_CHAT_file_get_size(file);
const uint64_t local_size = GNUNET_CHAT_file_get_local_size(file);
if (size != local_size)
return GNUNET_NO;
else
return GNUNET_YES;
}
const char*
GNUNET_CHAT_file_open_preview (struct GNUNET_CHAT_File *file)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!file)
return NULL;
if (file->preview)
return file->preview;
char *filename = handle_create_file_path(
file->handle, &(file->hash)
);
if (!filename)
return NULL;
if (GNUNET_YES != GNUNET_DISK_file_test(filename))
goto free_filename;
if (!(file->key))
{
file->preview = filename;
return file->preview;
}
file->preview = GNUNET_DISK_mktemp(
file->name? 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)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!file) || (!(file->preview)))
return;
if (!(file->key))
goto skip_filename;
char *filename = handle_create_file_path(
file->handle, &(file->hash)
);
if (!filename)
goto skip_filename;
if (0 != strcmp(filename, file->preview))
remove(file->preview);
GNUNET_free(filename);
skip_filename:
GNUNET_free(file->preview);
file->preview = NULL;
}
void
GNUNET_CHAT_file_set_user_pointer (struct GNUNET_CHAT_File *file,
void *user_pointer)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!file)
return;
file->user_pointer = user_pointer;
}
void*
GNUNET_CHAT_file_get_user_pointer (const struct GNUNET_CHAT_File *file)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!file)
return NULL;
return file->user_pointer;
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_file_is_downloading (const struct GNUNET_CHAT_File *file)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!file) || (0 == (file->status & GNUNET_CHAT_FILE_STATUS_DOWNLOAD)))
return GNUNET_NO;
else
return GNUNET_YES;
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_file_start_download (struct GNUNET_CHAT_File *file,
GNUNET_CHAT_FileDownloadCallback callback,
void *cls)
{
GNUNET_CHAT_VERSION_ASSERT();
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;
}
char *filename = handle_create_file_path(
file->handle, &(file->hash)
);
if (!filename)
return GNUNET_SYSERR;
const uint64_t size = GNUNET_FS_uri_chk_get_file_size(file->uri);
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);
goto free_filename;
}
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;
free_filename:
GNUNET_free(filename);
return GNUNET_OK;
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_file_pause_download (struct GNUNET_CHAT_File *file)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!file)
return GNUNET_SYSERR;
GNUNET_FS_download_suspend(file->download);
return GNUNET_OK;
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_file_resume_download (struct GNUNET_CHAT_File *file)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!file)
return GNUNET_SYSERR;
GNUNET_FS_download_resume(file->download);
return GNUNET_OK;
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_file_stop_download (struct GNUNET_CHAT_File *file)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!file)
return GNUNET_SYSERR;
GNUNET_FS_download_stop(file->download, GNUNET_YES);
file->download = NULL;
return GNUNET_OK;
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_file_is_unindexing (const struct GNUNET_CHAT_File *file)
{
GNUNET_CHAT_VERSION_ASSERT();
if ((!file) || (0 == (file->status & GNUNET_CHAT_FILE_STATUS_UNINDEX)))
return GNUNET_NO;
else
return GNUNET_YES;
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_file_unindex (struct GNUNET_CHAT_File *file,
GNUNET_CHAT_FileUnindexCallback callback,
void *cls)
{
GNUNET_CHAT_VERSION_ASSERT();
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;
char *filename = handle_create_file_path(
file->handle, &(file->hash)
);
if (!filename)
return GNUNET_SYSERR;
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)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!invitation)
return;
struct GNUNET_PeerIdentity door;
GNUNET_PEER_resolve(invitation->door, &door);
struct GNUNET_MESSENGER_Room *room = GNUNET_MESSENGER_enter_room(
invitation->context->handle->messenger,
&door, &(invitation->key)
);
handle_send_room_name(invitation->context->handle, room);
}
void
GNUNET_CHAT_invitation_reject (struct GNUNET_CHAT_Invitation *invitation)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!invitation)
return;
const struct GNUNET_MESSENGER_Contact *sender = GNUNET_MESSENGER_get_sender(
invitation->context->room, &(invitation->hash)
);
if (!sender)
return;
struct GNUNET_MESSENGER_Message msg;
memset(&msg, 0, sizeof(msg));
msg.header.kind = GNUNET_MESSENGER_KIND_TAG;
GNUNET_memcpy(&(msg.body.tag.hash), &(invitation->hash),
sizeof(struct GNUNET_HashCode));
msg.body.tag.tag = NULL;
GNUNET_MESSENGER_send_message(invitation->context->room, &msg, sender);
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_invitation_is_accepted (const struct GNUNET_CHAT_Invitation *invitation)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!invitation)
return GNUNET_NO;
return GNUNET_CONTAINER_multihashmap_contains(
invitation->context->handle->contexts,
&(invitation->key)
);
}
enum GNUNET_GenericReturnValue
GNUNET_CHAT_invitation_is_rejected (const struct GNUNET_CHAT_Invitation *invitation)
{
GNUNET_CHAT_VERSION_ASSERT();
if (!invitation)
return GNUNET_NO;
const struct GNUNET_CHAT_Tagging *tagging = GNUNET_CONTAINER_multihashmap_get(
invitation->context->taggings, &(invitation->hash));
if (!tagging)
return GNUNET_NO;
if (tagging_iterate(tagging, GNUNET_NO, NULL, NULL, NULL) > 0)
return GNUNET_YES;
else
return GNUNET_NO;
}