Line data Source code
1 : /* SPDX-License-Identifier: MIT OR GPL-3.0-only */
2 : /* ctx.c
3 : ** strophe XMPP client library -- run-time context implementation
4 : **
5 : ** Copyright (C) 2005-2009 Collecta, Inc.
6 : **
7 : ** This software is provided AS-IS with no warranty, either express
8 : ** or implied.
9 : **
10 : ** This program is dual licensed under the MIT or GPLv3 licenses.
11 : */
12 :
13 : /** @file
14 : * Runtime contexts, library initialization and shutdown, and versioning.
15 : */
16 :
17 : /** @defgroup Context Context objects
18 : * These functions create and manipulate Strophe context objects.
19 : *
20 : * In order to support usage in a variety of environments, the
21 : * Strophe library uses a runtime context object. This object
22 : * contains the information on how to do memory allocation and
23 : * logging. This allows the user to control how memory is allocated
24 : * and what do to with log messages.
25 : *
26 : * These issues do not affect programs in the common case, but many
27 : * environments require special treatment. Abstracting these into a runtime
28 : * context object makes it easy to use Strophe on embedded platforms.
29 : *
30 : * Objects in Strophe are reference counted to ease memory management issues,
31 : * but the context objects are not.
32 : */
33 :
34 : /** @defgroup Init Initialization, shutdown, and versioning
35 : * These functions initialize and shutdown the library, and also allow
36 : * for API version checking. Failure to properly call these functions may
37 : * result in strange (and platform dependent) behavior.
38 : *
39 : * Specifically, the socket library on Win32 platforms must be initialized
40 : * before use (although this is not the case on POSIX systems). The TLS
41 : * subsystem must also seed the random number generator.
42 : */
43 :
44 : #include <stdlib.h>
45 : #include <stdio.h>
46 : #include <stdarg.h>
47 : #include <string.h>
48 :
49 : #include "strophe.h"
50 : #include "common.h"
51 : #include "resolver.h"
52 : #include "util.h"
53 :
54 : #ifndef va_copy
55 : #ifdef HAVE_VA_COPY
56 : #define va_copy(dest, src) va_copy(dest, src)
57 : #else
58 : #ifdef HAVE___VA_COPY
59 : #define va_copy(dest, src) __va_copy(dest, src)
60 : #else
61 : #ifndef VA_LIST_IS_ARRAY
62 : #define va_copy(dest, src) (dest) = (src)
63 : #else
64 : #include <string.h>
65 : #define va_copy(dest, src) \
66 : memcpy((char *)(dest), (char *)(src), sizeof(va_list))
67 : #endif
68 : #endif
69 : #endif
70 : #endif
71 :
72 : /** Initialize the Strophe library.
73 : * This function initializes subcomponents of the Strophe library and must
74 : * be called for Strophe to operate correctly.
75 : *
76 : * @ingroup Init
77 : */
78 2 : void xmpp_initialize(void)
79 : {
80 2 : sock_initialize();
81 2 : resolver_initialize();
82 2 : tls_initialize();
83 2 : }
84 :
85 : /** Shutdown the Strophe library.
86 : *
87 : * @ingroup Init
88 : */
89 2 : void xmpp_shutdown(void)
90 : {
91 2 : tls_shutdown();
92 2 : resolver_shutdown();
93 2 : sock_shutdown();
94 2 : }
95 :
96 : /* version information */
97 :
98 : #ifndef LIBXMPP_VERSION_MAJOR
99 : /** @def LIBXMPP_VERSION_MAJOR
100 : * The major version number of Strophe.
101 : */
102 : #define LIBXMPP_VERSION_MAJOR (0)
103 : #endif
104 : #ifndef LIBXMPP_VERSION_MINOR
105 : /** @def LIBXMPP_VERSION_MINOR
106 : * The minor version number of Strophe.
107 : */
108 : #define LIBXMPP_VERSION_MINOR (0)
109 : #endif
110 :
111 : #ifndef EVENT_LOOP_DEFAULT_TIMEOUT
112 : /** @def EVENT_LOOP_DEFAULT_TIMEOUT
113 : * The default timeout in milliseconds for the event loop.
114 : * This is set to 1 second.
115 : */
116 : #define EVENT_LOOP_DEFAULT_TIMEOUT 1000
117 : #endif
118 :
119 : /** Check that Strophe supports a specific API version.
120 : *
121 : * @param major the major version number
122 : * @param minor the minor version number
123 : *
124 : * @return TRUE if the version is supported and FALSE if unsupported
125 : *
126 : * @ingroup Init
127 : */
128 0 : int xmpp_version_check(int major, int minor)
129 : {
130 0 : return (major == LIBXMPP_VERSION_MAJOR) && (minor >= LIBXMPP_VERSION_MINOR);
131 : }
132 :
133 : /* We define the global default allocator, logger, and context here. */
134 :
135 : /* Wrap stdlib routines malloc, free, and realloc for default memory
136 : * management.
137 : */
138 150 : static void *_malloc(size_t size, void *userdata)
139 : {
140 150 : UNUSED(userdata);
141 150 : return malloc(size);
142 : }
143 :
144 230 : static void _free(void *p, void *userdata)
145 : {
146 230 : UNUSED(userdata);
147 230 : free(p);
148 230 : }
149 :
150 0 : static void *_realloc(void *p, size_t size, void *userdata)
151 : {
152 0 : UNUSED(userdata);
153 0 : return realloc(p, size);
154 : }
155 :
156 : /* default memory function map */
157 : static xmpp_mem_t xmpp_default_mem = {
158 : _malloc, /* use the thinly wrapped stdlib routines by default */
159 : _free, _realloc, NULL};
160 :
161 : /* log levels and names */
162 : static const char *_xmpp_log_level_name[4] = {"DEBUG", "INFO", "WARN", "ERROR"};
163 : static const xmpp_log_level_t _xmpp_default_logger_levels[] = {
164 : XMPP_LEVEL_DEBUG, XMPP_LEVEL_INFO, XMPP_LEVEL_WARN, XMPP_LEVEL_ERROR};
165 :
166 : /** Log a message.
167 : * The default logger writes to stderr.
168 : *
169 : * @param userdata the opaque data used by the default logger. This contains
170 : * the filter level in the default logger.
171 : * @param level the level to log at
172 : * @param area the area the log message is for
173 : * @param msg the log message
174 : */
175 27 : static void xmpp_default_logger(void *userdata,
176 : xmpp_log_level_t level,
177 : const char *area,
178 : const char *msg)
179 : {
180 27 : xmpp_log_level_t filter_level = *(xmpp_log_level_t *)userdata;
181 27 : if (level >= filter_level)
182 27 : fprintf(stderr, "%s %s %s\n", area, _xmpp_log_level_name[level], msg);
183 27 : }
184 :
185 : static const xmpp_log_t _xmpp_default_loggers[] = {
186 : {&xmpp_default_logger,
187 : (void *)&_xmpp_default_logger_levels[XMPP_LEVEL_DEBUG]},
188 : {&xmpp_default_logger,
189 : (void *)&_xmpp_default_logger_levels[XMPP_LEVEL_INFO]},
190 : {&xmpp_default_logger,
191 : (void *)&_xmpp_default_logger_levels[XMPP_LEVEL_WARN]},
192 : {&xmpp_default_logger,
193 : (void *)&_xmpp_default_logger_levels[XMPP_LEVEL_ERROR]}};
194 :
195 : /** Get a default logger with filtering.
196 : * The default logger provides a basic logging setup which writes log
197 : * messages to stderr. Only messages where level is greater than or
198 : * equal to the filter level will be logged.
199 : *
200 : * @param level the highest level the logger will log at
201 : *
202 : * @return the log structure for the given level
203 : *
204 : * @ingroup Context
205 : */
206 1 : xmpp_log_t *xmpp_get_default_logger(xmpp_log_level_t level)
207 : {
208 : /* clamp to the known range */
209 1 : if (level > XMPP_LEVEL_ERROR)
210 : level = XMPP_LEVEL_ERROR;
211 :
212 1 : return (xmpp_log_t *)&_xmpp_default_loggers[level];
213 : }
214 :
215 : static xmpp_log_t xmpp_default_log = {NULL, NULL};
216 :
217 : /* convenience functions for accessing the context */
218 :
219 : /** Allocate memory in a Strophe context.
220 : * All Strophe functions will use this to allocate memory.
221 : *
222 : * @param ctx a Strophe context object
223 : * @param size the number of bytes to allocate
224 : *
225 : * @return a pointer to the allocated memory or NULL on an error
226 : */
227 710 : void *strophe_alloc(const xmpp_ctx_t *ctx, size_t size)
228 : {
229 710 : return ctx->mem->alloc(size, ctx->mem->userdata);
230 : }
231 :
232 : /** Free memory in a Strophe context.
233 : * All Strophe functions will use this to free allocated memory.
234 : *
235 : * @param ctx a Strophe context object
236 : * @param p a pointer referencing memory to be freed
237 : */
238 1687 : void strophe_free(const xmpp_ctx_t *ctx, void *p)
239 : {
240 1687 : ctx->mem->free(p, ctx->mem->userdata);
241 1687 : }
242 :
243 : /** Trampoline to \ref strophe_free
244 : *
245 : * @param ctx \ref strophe_free
246 : * @param p \ref strophe_free
247 : */
248 29 : void xmpp_free(const xmpp_ctx_t *ctx, void *p)
249 : {
250 29 : strophe_free(ctx, p);
251 29 : }
252 :
253 : /** Reallocate memory in a Strophe context.
254 : * All Strophe functions will use this to reallocate memory.
255 : *
256 : * @param ctx a Strophe context object
257 : * @param p a pointer to previously allocated memory
258 : * @param size the new size in bytes to allocate
259 : *
260 : * @return a pointer to the reallocated memory or NULL on an error
261 : */
262 5 : void *strophe_realloc(const xmpp_ctx_t *ctx, void *p, size_t size)
263 : {
264 5 : return ctx->mem->realloc(p, size, ctx->mem->userdata);
265 : }
266 :
267 : /** Write a log message to the logger.
268 : * Write a log message to the logger for the context for the specified
269 : * level and area. This function takes a printf-style format string and a
270 : * variable argument list (in va_list) format. This function is not meant
271 : * to be called directly, but is used via strophe_error, strophe_warn,
272 : * strophe_info, and strophe_debug.
273 : *
274 : * @param ctx a Strophe context object
275 : * @param level the level at which to log
276 : * @param area the area to log for
277 : * @param fmt a printf-style format string for the message
278 : * @param ap variable argument list supplied for the format string
279 : */
280 27 : static void _strophe_log(const xmpp_ctx_t *ctx,
281 : xmpp_log_level_t level,
282 : const char *area,
283 : const char *fmt,
284 : va_list ap)
285 : {
286 27 : int oldret, ret;
287 27 : char smbuf[1024];
288 27 : char *buf;
289 27 : va_list copy;
290 :
291 27 : if (!ctx->log->handler)
292 0 : return;
293 :
294 27 : if (ctx->log->handler == xmpp_default_logger &&
295 27 : level < *(xmpp_log_level_t *)ctx->log->userdata)
296 : return;
297 :
298 27 : va_copy(copy, ap);
299 27 : ret = strophe_vsnprintf(smbuf, sizeof(smbuf), fmt, ap);
300 27 : if (ret >= (int)sizeof(smbuf)) {
301 0 : buf = (char *)strophe_alloc(ctx, ret + 1);
302 0 : if (!buf) {
303 0 : buf = NULL;
304 0 : strophe_error(ctx, "log",
305 : "Failed allocating memory for log message.");
306 0 : va_end(copy);
307 0 : return;
308 : }
309 0 : oldret = ret;
310 0 : ret = strophe_vsnprintf(buf, ret + 1, fmt, copy);
311 0 : if (ret > oldret) {
312 0 : strophe_error(ctx, "log", "Unexpected error");
313 0 : strophe_free(ctx, buf);
314 0 : va_end(copy);
315 0 : return;
316 : }
317 : } else {
318 : buf = smbuf;
319 : }
320 27 : va_end(copy);
321 :
322 27 : ctx->log->handler(ctx->log->userdata, level, area, buf);
323 :
324 27 : if (buf != smbuf)
325 0 : strophe_free(ctx, buf);
326 : }
327 :
328 : /* Dummy trampoline, will be removed when deprecated.c is deleted */
329 0 : void strophe_log_internal(const xmpp_ctx_t *ctx,
330 : xmpp_log_level_t level,
331 : const char *area,
332 : const char *fmt,
333 : va_list ap)
334 : {
335 0 : _strophe_log(ctx, level, area, fmt, ap);
336 0 : }
337 :
338 : /** Write to the log at the ERROR level.
339 : * This is a convenience function for writing to the log at the
340 : * ERROR level. It takes a printf-style format string followed by a
341 : * variable list of arguments for formatting.
342 : *
343 : * @param ctx a Strophe context object
344 : * @param area the area to log for
345 : * @param fmt a printf-style format string followed by a variable list of
346 : * arguments to format
347 : */
348 0 : void strophe_error(const xmpp_ctx_t *ctx,
349 : const char *area,
350 : const char *fmt,
351 : ...)
352 : {
353 0 : va_list ap;
354 :
355 0 : va_start(ap, fmt);
356 0 : _strophe_log(ctx, XMPP_LEVEL_ERROR, area, fmt, ap);
357 0 : va_end(ap);
358 0 : }
359 :
360 : /** Write to the log at the WARN level.
361 : * This is a convenience function for writing to the log at the WARN level.
362 : * It takes a printf-style format string followed by a variable list of
363 : * arguments for formatting.
364 : *
365 : * @param ctx a Strophe context object
366 : * @param area the area to log for
367 : * @param fmt a printf-style format string followed by a variable list of
368 : * arguments to format
369 : */
370 3 : void strophe_warn(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
371 : {
372 3 : va_list ap;
373 :
374 3 : va_start(ap, fmt);
375 3 : _strophe_log(ctx, XMPP_LEVEL_WARN, area, fmt, ap);
376 3 : va_end(ap);
377 3 : }
378 :
379 : /** Write to the log at the INFO level.
380 : * This is a convenience function for writing to the log at the INFO level.
381 : * It takes a printf-style format string followed by a variable list of
382 : * arguments for formatting.
383 : *
384 : * @param ctx a Strophe context object
385 : * @param area the area to log for
386 : * @param fmt a printf-style format string followed by a variable list of
387 : * arguments to format
388 : */
389 0 : void strophe_info(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
390 : {
391 0 : va_list ap;
392 :
393 0 : va_start(ap, fmt);
394 0 : _strophe_log(ctx, XMPP_LEVEL_INFO, area, fmt, ap);
395 0 : va_end(ap);
396 0 : }
397 :
398 : /** Write to the log at the DEBUG level.
399 : * This is a convenience function for writing to the log at the DEBUG level.
400 : * It takes a printf-style format string followed by a variable list of
401 : * arguments for formatting.
402 : *
403 : * @param ctx a Strophe context object
404 : * @param area the area to log for
405 : * @param fmt a printf-style format string followed by a variable list of
406 : * arguments to format
407 : */
408 24 : void strophe_debug(const xmpp_ctx_t *ctx,
409 : const char *area,
410 : const char *fmt,
411 : ...)
412 : {
413 24 : va_list ap;
414 :
415 24 : va_start(ap, fmt);
416 24 : _strophe_log(ctx, XMPP_LEVEL_DEBUG, area, fmt, ap);
417 24 : va_end(ap);
418 24 : }
419 :
420 : /** Write to the log at the DEBUG level if verbosity is enabled.
421 : * This is a convenience function for writing to the log at the DEBUG level.
422 : * It takes a printf-style format string followed by a variable list of
423 : * arguments for formatting.
424 : *
425 : * @param level the verbosity level
426 : * @param ctx a Strophe context object
427 : * @param area the area to log for
428 : * @param fmt a printf-style format string followed by a variable list of
429 : * arguments to format
430 : */
431 0 : void strophe_debug_verbose(
432 : int level, const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
433 : {
434 0 : va_list ap;
435 :
436 0 : if (ctx->verbosity < level)
437 0 : return;
438 :
439 0 : va_start(ap, fmt);
440 0 : _strophe_log(ctx, XMPP_LEVEL_DEBUG, area, fmt, ap);
441 0 : va_end(ap);
442 : }
443 :
444 : /** Create and initialize a Strophe context object.
445 : * If mem is NULL, a default allocation setup will be used which
446 : * wraps malloc(), free(), and realloc() from the standard library.
447 : * If log is NULL, a default logger will be used which does no
448 : * logging. Basic filtered logging to stderr can be done with the
449 : * xmpp_get_default_logger() convenience function.
450 : *
451 : * @param mem a pointer to an xmpp_mem_t structure or NULL
452 : * @param log a pointer to an xmpp_log_t structure or NULL
453 : *
454 : * @return the allocated Strophe context object or NULL on an error
455 : *
456 : * @ingroup Context
457 : */
458 3 : xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t *mem, const xmpp_log_t *log)
459 : {
460 3 : xmpp_ctx_t *ctx = NULL;
461 :
462 3 : if (mem == NULL)
463 2 : ctx = xmpp_default_mem.alloc(sizeof(xmpp_ctx_t), NULL);
464 : else
465 1 : ctx = mem->alloc(sizeof(xmpp_ctx_t), mem->userdata);
466 :
467 3 : if (ctx != NULL) {
468 3 : memset(ctx, 0, sizeof(xmpp_ctx_t));
469 :
470 3 : if (mem != NULL)
471 1 : ctx->mem = mem;
472 : else
473 2 : ctx->mem = &xmpp_default_mem;
474 :
475 3 : if (log == NULL)
476 2 : ctx->log = &xmpp_default_log;
477 : else
478 1 : ctx->log = log;
479 :
480 3 : ctx->loop_status = XMPP_LOOP_NOTSTARTED;
481 3 : ctx->rand = xmpp_rand_new(ctx);
482 3 : ctx->timeout = EVENT_LOOP_DEFAULT_TIMEOUT;
483 3 : if (ctx->rand == NULL) {
484 0 : strophe_free(ctx, ctx);
485 0 : ctx = NULL;
486 : }
487 : }
488 :
489 3 : return ctx;
490 : }
491 :
492 : /** Free a Strophe context object that is no longer in use.
493 : *
494 : * @param ctx a Strophe context object
495 : *
496 : * @ingroup Context
497 : */
498 3 : void xmpp_ctx_free(xmpp_ctx_t *ctx)
499 : {
500 : /* mem and log are owned by their suppliers */
501 3 : xmpp_rand_free(ctx, ctx->rand);
502 3 : strophe_free(ctx, ctx); /* pull the hole in after us */
503 3 : }
504 :
505 : /** Set the verbosity level of a Strophe context.
506 : *
507 : * @param ctx a Strophe context object
508 : * @param level the verbosity level
509 : *
510 : * @ingroup Context
511 : */
512 0 : void xmpp_ctx_set_verbosity(xmpp_ctx_t *ctx, int level)
513 : {
514 0 : ctx->verbosity = level;
515 0 : }
|