Commit 0248068f authored by Mohammed Sadiq's avatar Mohammed Sadiq
Browse files

Initial commit

parents
_build
*.swp
*~
\#*#
.\#*
build
.buildconfig
.flatpak-builder
GPATH
GTAGS
GRTAGS
\ No newline at end of file
This diff is collapsed.
<div align="center">
<a href="https://puri.sm">
<img src="https://path/to/image/raw/master/data/icons/icon.png" width="150" />
</a>
<br>
<a href="https://puri.sm"><b>libcmatrix</b></a>
<br>
A matrix protocol library writting in GObjectified C
<br>
<a href="https://source.puris.sm/mohammed.sadiq/libcmatrix/pipelines"><img
src="https://source.puris.sm/mohammed.sadiq/libcmatrix/badges/main/pipeline.svg" /></a>
<a href="https://source.puris.sm/mohammed.sadiq/libcmatrix/coverage"><img
src="https://source.puris.sm/mohammed.sadiq/libcmatrix/badges/main/coverage.svg" /></a>
</div>
---
libcmatrix is a [Matrix][matrix] client library written in
GObjectified C library.
You could use the library if you are writing a matrix client
in C.
libcmatrix requires GObject and Gio, and the library assumes
that a glib main event loop is running (All GTK apps have one)
libcmatrix handles all E2EE transparently. The messages/events
are not stored and the client should store them for chat history,
which may change in the future.
## Dependencies
- glib >= 2.66
- gio >= 2.66
- libsoup-2
- ligbcrypt
- libolm3
- libsqlite3 >= 3.34
Source Repository: [GitLab][gitlab]
Issues and Feature Requests: [GitLab][issues]
## Getting started
See `examples` directory for examples on how to use the library.
You shall have to use libcmatrix as a subproject. Currently
libcmatrix provides no API nor ABI guarantee until it's stable
enough.
<!-- Links referenced elsewhere -->
<!-- To be updated -->
[matrix]: https://matrix.org
[home]: https://puri.sm
[coverage]: https://source.puris.sm/mohammed.sadiq/libcmatrix/coverage
[gitlab]: https://source.puris.sm/mohammed.sadiq/libcmatrix/
[issues]: https://source.puris.sm/mohammed.sadiq/libcmatrix/issues
example_items = [
'simple-client',
]
foreach item: example_items
executable(
item,
item + '.c',
include_directories: src_inc,
link_with: cmatrix_lib,
dependencies: cmatrix_deps,
)
endforeach
/* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
/* client.c
*
* Copyright 2022 Purism SPC
*
* Author(s):
* Mohammed Sadiq <sadiq@sadiqpk.org>
*
* SPDX-License-Identifier: LGPL-2.1-or-later OR CC0-1.0
*/
#include <stdio.h>
#define CMATRIX_USE_EXPERIMENTAL_API
#include "cmatrix.h"
CmMatrix *matrix;
CmClient *client;
static void
simple_account_sync_cb (gpointer object,
CmClient *cm_client,
CmAction action,
const char *event,
const char *json,
GError *err)
{
puts ("\n\n\n");
if (event)
g_message ("client event: %s", event);
/* You shall see encrypted room contents as encrypted text here */
/* You can use cm_room_decrypt_content() for "m.room.encrypted" type events */
if (json)
g_message ("client json: %s", json);
if (err)
g_warning ("client error: %s", err->message);
puts ("\n\n\n");
}
static void
simple_get_homeserver_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
g_autoptr(GError) error = NULL;
const char *server;
char homeserver[255];
server = cm_client_get_homeserver_finish (client, result, &error);
/* It's okay to not able to get homeserver from username */
if (error)
g_message ("Failed to guess/verify homeserver: %s", error->message);
else if (server)
g_warning ("autofetched homeserver: %s", server);
/* Not having a homeserver set means we failed to guess it from provided login id */
/* So ask user for one. */
while (!cm_client_get_homeserver (client))
{
printf ("input your Matrix homeserver address: ");
scanf ("%s", homeserver);
if (!cm_client_set_homeserver (client, homeserver))
g_warning ("'%s' is not a valid homeserver uri (did you forget to "
"prefix with 'https://')", homeserver);
}
/* The sync callback runs for every /sync response and other interesting events (like wrong password error) */
cm_client_set_sync_callback (client,
(CmCallback)simple_account_sync_cb,
NULL, NULL);
/* Now, enable the client, and the client will start to sync, executing the callback on events */
cm_client_set_enabled (client, TRUE);
}
static void
simple_joined_rooms_changed_cb (GListModel *list,
guint position,
guint removed,
guint added,
gpointer user_data)
{
puts ("\n\n\n");
g_warning ("joined rooms changed");
g_warning ("total number of items: %u", g_list_model_get_n_items (list));
for (guint i = 0; i < g_list_model_get_n_items (list); i++)
{
g_autoptr(CmRoom) room = NULL;
room = g_list_model_get_item (list, i);
g_warning ("room name: %s, room id: %s",
cm_room_get_name (room),
cm_room_get_id (room));
}
puts ("\n\n\n");
}
static void
simple_matrix_open_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
g_autoptr(GError) error = NULL;
char username[255], password[255];
GListModel *joined_rooms;
if (!cm_matrix_open_finish (matrix, result, &error))
g_error ("Error opening db: %s", error->message);
printf ("input your Matrix username: ");
scanf ("%s", username);
printf ("input your Matrix password: ");
scanf ("%s", password);
puts ("");
g_message ("username: %s, password: %s", username, password);
client = cm_matrix_client_new (matrix);
joined_rooms = cm_client_get_joined_rooms (client);
g_signal_connect_object (joined_rooms, "items-changed",
G_CALLBACK (simple_joined_rooms_changed_cb), client,
0);
if (!cm_client_set_login_id (client, username))
g_error ("'%s' isn't a valid username", username);
cm_client_set_password (client, password);
cm_client_set_device_name (client, "Example CMatrix");
/* try if we can get a valid homeserver from username */
cm_client_get_homeserver_async (client, NULL,
simple_get_homeserver_cb, NULL);
}
int
main (void)
{
GMainLoop *main_loop;
g_autofree char *db_dir = NULL;
/* Create a matrix object */
matrix = g_object_new (CM_TYPE_MATRIX, NULL);
/* Ask matrix to open/create the db which will be used to store keys, session data, etc. */
db_dir = g_build_filename (g_get_user_data_dir (), "CMatrix", "simple-client", NULL);
cm_matrix_open_async (matrix, db_dir, "matrix.db", NULL,
simple_matrix_open_cb, NULL);
main_loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (main_loop);
}
project('libcmatrix', 'c',
version: '0.0.0',
license: 'LGPL-2.1-or-later',
meson_version: '>= 0.59.0',
default_options: [ 'warning_level=2', 'buildtype=debugoptimized', 'c_std=gnu18' ],
)
gnome = import('gnome')
i18n = import('i18n')
VERSION = meson.project_version()
ABI_MAJOR = VERSION.split('.')[0]
ABI_MINOR = VERSION.split('.')[1]
ABI_MICRO = VERSION.split('.')[2]
prefix = get_option('prefix')
bindir = join_paths(prefix, get_option('bindir'))
datadir = join_paths(prefix, get_option('datadir'))
localedir = join_paths(prefix, get_option('localedir'))
libdir = join_paths(prefix, get_option('libdir'))
cc = meson.get_compiler('c')
global_c_args = [
'-DCMATRIX_COMPILATION',
]
test_c_args = [
'-Wcast-align',
'-Wdate-time',
'-Wdeclaration-after-statement',
['-Werror=format-security', '-Werror=format=2'],
'-Wendif-labels',
'-Werror=incompatible-pointer-types',
'-Werror=missing-declarations',
'-Werror=overflow',
'-Werror=return-type',
'-Werror=shift-count-overflow',
'-Werror=shift-overflow=2',
'-Werror=implicit-fallthrough=3',
'-Wfloat-equal',
'-Wformat-nonliteral',
'-Wformat-security',
'-Winit-self',
'-Wmaybe-uninitialized',
'-Wmissing-field-initializers',
'-Wmissing-include-dirs',
'-Wmissing-noreturn',
'-Wnested-externs',
'-Wno-strict-aliasing',
'-Wno-unused-parameter',
'-Wold-style-definition',
'-Wpointer-arith',
'-Wredundant-decls',
'-Wshadow',
'-Wsign-compare',
'-Wstrict-prototypes',
'-Wswitch-default',
'-Wswitch-enum',
'-Wtype-limits',
'-Wundef',
'-Wunused-function',
]
if get_option('buildtype') != 'plain'
test_c_args += '-fstack-protector-strong'
endif
foreach arg: test_c_args
if cc.has_multi_arguments(arg)
global_c_args += arg
endif
endforeach
add_project_arguments(
global_c_args,
language: 'c'
)
add_project_arguments([
'-DHAVE_CONFIG_H',
'-DCMATRIX_COMPILATION',
], language: 'c')
config_h = configuration_data()
config_h.set10('HAVE_EXPLICIT_BZERO', cc.has_function('explicit_bzero'))
config_h.set_quoted('GETTEXT_PACKAGE', 'libcmatrix')
config_h.set_quoted('LOCALEDIR', localedir)
libolm_dep = cc.find_library('olm', required: true)
if (cc.has_function('olm_account_unpublished_fallback_key', dependencies: libolm_dep))
config_h.set('OLM_ACCOUNT_PICKLE_V4', true)
else
config_h.set('OLM_ACCOUNT_PICKLE_V4', false)
endif
if (cc.has_function('olm_pk_key_from_private', dependencies: libolm_dep))
config_h.set('HAVE_OLM3', true)
else
config_h.set('HAVE_OLM2', true)
endif
configure_file(
output: 'config.h',
configuration: config_h,
)
root_inc = include_directories('.')
src_inc = include_directories('src')
gio_dep = dependency('gio-2.0', version: '>= 2.66')
cmatrix_deps = [
dependency('libgcrypt'),
dependency('libsoup-2.4'),
dependency('json-glib-1.0'),
dependency('sqlite3', version: '>=3.26.0'),
libolm_dep,
gio_dep,
cc.find_library('m', required: false),
]
subdir('src')
subdir('tests')
if (get_option('build-examples'))
subdir('examples')
endif
# Subproject
option('package_subdir', type: 'string',
description: 'Subdirectory to append to all installed files, for use as subproject'
)
option('build-examples', type: 'boolean', value: true, description : 'Build examples')
/* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2022 Purism SPC
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#if !defined(_CMATRIX_TAKEN) && !defined(CMATRIX_COMPILATION)
# error "Only <cmatrix.h> can be included directly."
#endif
#include <glib-object.h>
#include <gio/gio.h>
#include "cm-db-private.h"
#include "cm-enc-private.h"
#include "cm-net-private.h"
#include "cm-client.h"
G_BEGIN_DECLS
int cm_client_pop_event_id (CmClient *self);
CmDb *cm_client_get_db (CmClient *self);
CmNet *cm_client_get_net (CmClient *self);
CmEnc *cm_client_get_enc (CmClient *self);
void cm_client_set_db (CmClient *self,
CmDb *db);
void cm_client_save (CmClient *self,
gboolean force);
const char *cm_client_get_next_batch (CmClient *self);
void cm_client_claim_keys_async (CmClient *self,
GListModel *member_list,
GAsyncReadyCallback callback,
gpointer user_data);
JsonObject *cm_client_claim_keys_finish (CmClient *self,
GAsyncResult *result,
GError **error);
void cm_client_upload_group_keys_async (CmClient *self,
const char *room_id,
GListModel *member_list,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean cm_client_upload_group_keys_finish (CmClient *self,
GAsyncResult *result,
GError **error);
G_END_DECLS
This diff is collapsed.
/* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2022 Purism SPC
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#if !defined(_CMATRIX_TAKEN) && !defined(CMATRIX_COMPILATION)
# error "Only <cmatrix.h> can be included directly."
#endif
#include <glib-object.h>
#include <gio/gio.h>
G_BEGIN_DECLS
#include "cm-enums.h"
#define CM_TYPE_CLIENT (cm_client_get_type ())
G_DECLARE_FINAL_TYPE (CmClient, cm_client, CM, CLIENT, GObject)
typedef void (*CmCallback) (gpointer object,
CmClient *self,
CmAction action,
const char *event,
const char *json,
GError *err);
CmClient *cm_client_new (void);
void cm_client_set_enabled (CmClient *self,
gboolean enable);
gboolean cm_client_get_enabled (CmClient *self);
void cm_client_set_sync_callback (CmClient *self,
CmCallback callback,
gpointer callback_data,
GDestroyNotify callback_data_destroy);
gboolean cm_client_set_user_id (CmClient *self,
const char *matrix_user_id);
const char *cm_client_get_user_id (CmClient *self);
gboolean cm_client_set_login_id (CmClient *self,
const char *login_id);
const char *cm_client_get_login_id (CmClient *self);
gboolean cm_client_set_homeserver (CmClient *self,
const char *homeserver);
const char *cm_client_get_homeserver (CmClient *self);
void cm_client_set_password (CmClient *self,
const char *password);
const char *cm_client_get_password (CmClient *self);
void cm_client_set_access_token (CmClient *self,
const char *access_token);
const char *cm_client_get_access_token (CmClient *self);
void cm_client_set_device_id (CmClient *self,
const char *device_id);
const char *cm_client_get_device_id (CmClient *self);
void cm_client_set_device_name (CmClient *self,
const char *device_name);
const char *cm_client_get_device_name (CmClient *self);
void cm_client_set_pickle_key (CmClient *self,
const char *pickle_key);
const char *cm_client_get_pickle_key (CmClient *self);
const char *cm_client_get_ed25519_key (CmClient *self);
void cm_client_get_homeserver_async (CmClient *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
const char *cm_client_get_homeserver_finish (CmClient *self,
GAsyncResult *result,
GError **error);
gboolean cm_client_can_connect (CmClient *self);
void cm_client_start_sync (CmClient *self);
gboolean cm_client_is_sync (CmClient *self);
void cm_client_stop_sync (CmClient *self);
gboolean cm_client_get_logging_in (CmClient *self);
gboolean cm_client_get_logged_in (CmClient *self);
GListModel *cm_client_get_joined_rooms (CmClient *self);
void cm_client_get_file_async (CmClient *self,
const char *uri,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GInputStream *cm_client_get_file_finish (CmClient *self,
GAsyncResult *result,
GError **error);
void cm_client_get_user_info_async (CmClient *self,
const char *user_id,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean cm_client_get_user_info_finish (CmClient *self,
char **name,
char **avatar_url,
GAsyncResult *result,
GError **error);
void cm_client_set_name_async (CmClient *self,
const char *name,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean cm_client_set_name_finish (CmClient *self,
GAsyncResult *result,
GError **error);
void cm_client_set_user_avatar_async (CmClient *self,
GFile *image_file,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean cm_client_set_user_avatar_finish (CmClient *self,
GAsyncResult *result,
GError **error);
void cm_client_get_3pid_async (CmClient *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean cm_client_get_3pid_finish (CmClient *self,
GPtrArray **emails,
GPtrArray **phones,
GAsyncResult *result,
GError **error);
void cm_client_delete_3pid_async (CmClient *self,
const char *value,
const char *type,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean cm_client_delete_3pid_finish (CmClient *self,
GAsyncResult *result,
GError **error);
G_END_DECLS
/* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
/* cm-common.c
*
* Copyright 2022 Purism SPC
*
* Author(s):
* Mohammed Sadiq <sadiq@sadiqpk.org>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "cm-common.h"
/**
* matrix_error_quark:
*
* Get the Matrix Error Quark.
*
* Returns: a #GQuark.
**/
G_DEFINE_QUARK (cm-error-quark, cm_error)
/* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
/* cm-common.h
*
* Copyright 2022 Purism SPC
*
* Author(s):
* Mohammed Sadiq <sadiq@sadiqpk.org>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include <glib-object.h>
#define CM_ERROR (cm_error_quark ())
GQuark cm_error_quark (void);
/* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
/* cm-db.h
*
* Copyright 2022 Purism SPC
*
* Author(s):
* Mohammed Sadiq <sadiq@sadiqpk.org>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include <gio/gio.h>