Files
libstrophe-gh/src/ctx.c
Dmitry Podgorny 07a4f778d3 ctx: initialize field 'verbosity'
Uninitialized field can contain a garbage and break expected logs
behavior.
2021-12-02 00:08:34 +02:00

477 lines
14 KiB
C

/* ctx.c
** strophe XMPP client library -- run-time context implementation
**
** Copyright (C) 2005-2009 Collecta, Inc.
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This program is dual licensed under the MIT and GPLv3 licenses.
*/
/** @file
* Runtime contexts, library initialization and shutdown, and versioning.
*/
/** @defgroup Context Context objects
* These functions create and manipulate Strophe context objects.
*
* In order to support usage in a variety of environments, the
* Strophe library uses a runtime context object. This object
* contains the information on how to do memory allocation and
* logging. This allows the user to control how memory is allocated
* and what do to with log messages.
*
* These issues do not affect programs in the common case, but many
* environments require special treatment. Abstracting these into a runtime
* context object makes it easy to use Strophe on embedded platforms.
*
* Objects in Strophe are reference counted to ease memory management issues,
* but the context objects are not.
*/
/** @defgroup Init Initialization, shutdown, and versioning
* These functions initialize and shutdown the library, and also allow
* for API version checking. Failure to properly call these functions may
* result in strange (and platform dependent) behavior.
*
* Specifically, the socket library on Win32 platforms must be initialized
* before use (although this is not the case on POSIX systems). The TLS
* subsystem must also seed the random number generator.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include "strophe.h"
#include "common.h"
#include "resolver.h"
#include "util.h"
/* Workaround for systems without va_copy support. */
#if defined(_MSC_VER) && _MSC_VER < 1800 || \
!defined(_MSC_VER) && !defined(HAVE_DECL_VA_COPY)
#define va_copy(d, s) (memcpy(&d, &s, sizeof(va_list)))
#endif
/** Initialize the Strophe library.
* This function initializes subcomponents of the Strophe library and must
* be called for Strophe to operate correctly.
*
* @ingroup Init
*/
void xmpp_initialize(void)
{
sock_initialize();
resolver_initialize();
tls_initialize();
}
/** Shutdown the Strophe library.
*
* @ingroup Init
*/
void xmpp_shutdown(void)
{
tls_shutdown();
resolver_shutdown();
sock_shutdown();
}
/* version information */
#ifndef LIBXMPP_VERSION_MAJOR
/** @def LIBXMPP_VERSION_MAJOR
* The major version number of Strophe.
*/
#define LIBXMPP_VERSION_MAJOR (0)
#endif
#ifndef LIBXMPP_VERSION_MINOR
/** @def LIBXMPP_VERSION_MINOR
* The minor version number of Strophe.
*/
#define LIBXMPP_VERSION_MINOR (0)
#endif
#ifndef EVENT_LOOP_DEFAULT_TIMEOUT
/** @def EVENT_LOOP_DEFAULT_TIMEOUT
* The default timeout in milliseconds for the event loop.
* This is set to 1 second.
*/
#define EVENT_LOOP_DEFAULT_TIMEOUT 1000
#endif
/** Check that Strophe supports a specific API version.
*
* @param major the major version number
* @param minor the minor version number
*
* @return TRUE if the version is supported and FALSE if unsupported
*
* @ingroup Init
*/
int xmpp_version_check(int major, int minor)
{
return (major == LIBXMPP_VERSION_MAJOR) && (minor >= LIBXMPP_VERSION_MINOR);
}
/* We define the global default allocator, logger, and context here. */
/* Wrap stdlib routines malloc, free, and realloc for default memory
* management.
*/
static void *_malloc(size_t size, void *userdata)
{
UNUSED(userdata);
return malloc(size);
}
static void _free(void *p, void *userdata)
{
UNUSED(userdata);
free(p);
}
static void *_realloc(void *p, size_t size, void *userdata)
{
UNUSED(userdata);
return realloc(p, size);
}
/* default memory function map */
static xmpp_mem_t xmpp_default_mem = {
_malloc, /* use the thinly wrapped stdlib routines by default */
_free, _realloc, NULL};
/* log levels and names */
static const char *_xmpp_log_level_name[4] = {"DEBUG", "INFO", "WARN", "ERROR"};
static const xmpp_log_level_t _xmpp_default_logger_levels[] = {
XMPP_LEVEL_DEBUG, XMPP_LEVEL_INFO, XMPP_LEVEL_WARN, XMPP_LEVEL_ERROR};
/** Log a message.
* The default logger writes to stderr.
*
* @param userdata the opaque data used by the default logger. This contains
* the filter level in the default logger.
* @param level the level to log at
* @param area the area the log message is for
* @param msg the log message
*/
static void xmpp_default_logger(void *userdata,
xmpp_log_level_t level,
const char *area,
const char *msg)
{
xmpp_log_level_t filter_level = *(xmpp_log_level_t *)userdata;
if (level >= filter_level)
fprintf(stderr, "%s %s %s\n", area, _xmpp_log_level_name[level], msg);
}
static const xmpp_log_t _xmpp_default_loggers[] = {
{&xmpp_default_logger,
(void *)&_xmpp_default_logger_levels[XMPP_LEVEL_DEBUG]},
{&xmpp_default_logger,
(void *)&_xmpp_default_logger_levels[XMPP_LEVEL_INFO]},
{&xmpp_default_logger,
(void *)&_xmpp_default_logger_levels[XMPP_LEVEL_WARN]},
{&xmpp_default_logger,
(void *)&_xmpp_default_logger_levels[XMPP_LEVEL_ERROR]}};
/** Get a default logger with filtering.
* The default logger provides a basic logging setup which writes log
* messages to stderr. Only messages where level is greater than or
* equal to the filter level will be logged.
*
* @param level the highest level the logger will log at
*
* @return the log structure for the given level
*
* @ingroup Context
*/
xmpp_log_t *xmpp_get_default_logger(xmpp_log_level_t level)
{
/* clamp to the known range */
if (level > XMPP_LEVEL_ERROR)
level = XMPP_LEVEL_ERROR;
return (xmpp_log_t *)&_xmpp_default_loggers[level];
}
static xmpp_log_t xmpp_default_log = {NULL, NULL};
/* convenience functions for accessing the context */
/** Allocate memory in a Strophe context.
* All Strophe functions will use this to allocate memory.
*
* @param ctx a Strophe context object
* @param size the number of bytes to allocate
*
* @return a pointer to the allocated memory or NULL on an error
*/
void *xmpp_alloc(const xmpp_ctx_t *ctx, size_t size)
{
return ctx->mem->alloc(size, ctx->mem->userdata);
}
/** Free memory in a Strophe context.
* All Strophe functions will use this to free allocated memory.
*
* @param ctx a Strophe context object
* @param p a pointer referencing memory to be freed
*/
void xmpp_free(const xmpp_ctx_t *ctx, void *p)
{
ctx->mem->free(p, ctx->mem->userdata);
}
/** Reallocate memory in a Strophe context.
* All Strophe functions will use this to reallocate memory.
*
* @param ctx a Strophe context object
* @param p a pointer to previously allocated memory
* @param size the new size in bytes to allocate
*
* @return a pointer to the reallocated memory or NULL on an error
*/
void *xmpp_realloc(const xmpp_ctx_t *ctx, void *p, size_t size)
{
return ctx->mem->realloc(p, size, ctx->mem->userdata);
}
/** Write a log message to the logger.
* Write a log message to the logger for the context for the specified
* level and area. This function takes a printf-style format string and a
* variable argument list (in va_list) format. This function is not meant
* to be called directly, but is used via xmpp_error, xmpp_warn, xmpp_info,
* and xmpp_debug.
*
* @param ctx a Strophe context object
* @param level the level at which to log
* @param area the area to log for
* @param fmt a printf-style format string for the message
* @param ap variable argument list supplied for the format string
*/
void xmpp_log(const xmpp_ctx_t *ctx,
xmpp_log_level_t level,
const char *area,
const char *fmt,
va_list ap)
{
int oldret, ret;
char smbuf[1024];
char *buf;
va_list copy;
if (!ctx->log->handler)
return;
if (ctx->log->handler == xmpp_default_logger &&
level < *(xmpp_log_level_t *)ctx->log->userdata)
return;
va_copy(copy, ap);
ret = xmpp_vsnprintf(smbuf, sizeof(smbuf), fmt, ap);
if (ret >= (int)sizeof(smbuf)) {
buf = (char *)xmpp_alloc(ctx, ret + 1);
if (!buf) {
buf = NULL;
xmpp_error(ctx, "log", "Failed allocating memory for log message.");
va_end(copy);
return;
}
oldret = ret;
ret = xmpp_vsnprintf(buf, ret + 1, fmt, copy);
if (ret > oldret) {
xmpp_error(ctx, "log", "Unexpected error");
xmpp_free(ctx, buf);
va_end(copy);
return;
}
} else {
buf = smbuf;
}
va_end(copy);
ctx->log->handler(ctx->log->userdata, level, area, buf);
if (buf != smbuf)
xmpp_free(ctx, buf);
}
/** Write to the log at the ERROR level.
* This is a convenience function for writing to the log at the
* ERROR level. It takes a printf-style format string followed by a
* variable list of arguments for formatting.
*
* @param ctx a Strophe context object
* @param area the area to log for
* @param fmt a printf-style format string followed by a variable list of
* arguments to format
*/
void xmpp_error(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
xmpp_log(ctx, XMPP_LEVEL_ERROR, area, fmt, ap);
va_end(ap);
}
/** Write to the log at the WARN level.
* This is a convenience function for writing to the log at the WARN level.
* It takes a printf-style format string followed by a variable list of
* arguments for formatting.
*
* @param ctx a Strophe context object
* @param area the area to log for
* @param fmt a printf-style format string followed by a variable list of
* arguments to format
*/
void xmpp_warn(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
xmpp_log(ctx, XMPP_LEVEL_WARN, area, fmt, ap);
va_end(ap);
}
/** Write to the log at the INFO level.
* This is a convenience function for writing to the log at the INFO level.
* It takes a printf-style format string followed by a variable list of
* arguments for formatting.
*
* @param ctx a Strophe context object
* @param area the area to log for
* @param fmt a printf-style format string followed by a variable list of
* arguments to format
*/
void xmpp_info(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
xmpp_log(ctx, XMPP_LEVEL_INFO, area, fmt, ap);
va_end(ap);
}
/** Write to the log at the DEBUG level.
* This is a convenience function for writing to the log at the DEBUG level.
* It takes a printf-style format string followed by a variable list of
* arguments for formatting.
*
* @param ctx a Strophe context object
* @param area the area to log for
* @param fmt a printf-style format string followed by a variable list of
* arguments to format
*/
void xmpp_debug(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
xmpp_log(ctx, XMPP_LEVEL_DEBUG, area, fmt, ap);
va_end(ap);
}
/** Write to the log at the DEBUG level if verbosity is enabled.
* This is a convenience function for writing to the log at the DEBUG level.
* It takes a printf-style format string followed by a variable list of
* arguments for formatting.
*
* @param level the verbosity level
* @param ctx a Strophe context object
* @param area the area to log for
* @param fmt a printf-style format string followed by a variable list of
* arguments to format
*/
void xmpp_debug_verbose(
int level, const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
{
va_list ap;
if (ctx->verbosity < level)
return;
va_start(ap, fmt);
xmpp_log(ctx, XMPP_LEVEL_DEBUG, area, fmt, ap);
va_end(ap);
}
/** Create and initialize a Strophe context object.
* If mem is NULL, a default allocation setup will be used which
* wraps malloc(), free(), and realloc() from the standard library.
* If log is NULL, a default logger will be used which does no
* logging. Basic filtered logging to stderr can be done with the
* xmpp_get_default_logger() convenience function.
*
* @param mem a pointer to an xmpp_mem_t structure or NULL
* @param log a pointer to an xmpp_log_t structure or NULL
*
* @return the allocated Strophe context object or NULL on an error
*
* @ingroup Context
*/
xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t *mem, const xmpp_log_t *log)
{
xmpp_ctx_t *ctx = NULL;
if (mem == NULL)
ctx = xmpp_default_mem.alloc(sizeof(xmpp_ctx_t), NULL);
else
ctx = mem->alloc(sizeof(xmpp_ctx_t), mem->userdata);
if (ctx != NULL) {
if (mem != NULL)
ctx->mem = mem;
else
ctx->mem = &xmpp_default_mem;
if (log == NULL)
ctx->log = &xmpp_default_log;
else
ctx->log = log;
ctx->connlist = NULL;
ctx->timed_handlers = NULL;
ctx->loop_status = XMPP_LOOP_NOTSTARTED;
ctx->rand = xmpp_rand_new(ctx);
ctx->timeout = EVENT_LOOP_DEFAULT_TIMEOUT;
ctx->verbosity = 0;
if (ctx->rand == NULL) {
xmpp_free(ctx, ctx);
ctx = NULL;
}
}
return ctx;
}
/** Free a Strophe context object that is no longer in use.
*
* @param ctx a Strophe context object
*
* @ingroup Context
*/
void xmpp_ctx_free(xmpp_ctx_t *ctx)
{
/* mem and log are owned by their suppliers */
xmpp_rand_free(ctx, ctx->rand);
xmpp_free(ctx, ctx); /* pull the hole in after us */
}
/** Set the verbosity level of a Strophe context.
*
* @param ctx a Strophe context object
* @param level the verbosity level
*
* @ingroup Context
*/
void xmpp_ctx_set_verbosity(xmpp_ctx_t *ctx, int level)
{
ctx->verbosity = level;
}