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