Sat Apr 26 2014 22:01:39

Asterisk developer's documentation


presencestate.c
Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2011-2012, Digium, Inc.
00005  *
00006  * David Vossel <dvossel@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Presence state management
00022  */
00023 
00024 /*** MODULEINFO
00025    <support_level>core</support_level>
00026  ***/
00027 
00028 #include "asterisk.h"
00029 
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 381594 $")
00031 
00032 #include "asterisk/_private.h"
00033 #include "asterisk/utils.h"
00034 #include "asterisk/lock.h"
00035 #include "asterisk/linkedlists.h"
00036 #include "asterisk/presencestate.h"
00037 #include "asterisk/pbx.h"
00038 #include "asterisk/app.h"
00039 #include "asterisk/event.h"
00040 
00041 /*! \brief Device state strings for printing */
00042 static const struct {
00043    const char *string;
00044    enum ast_presence_state state;
00045 
00046 } state2string[] = {
00047    { "not_set", AST_PRESENCE_NOT_SET},
00048    { "unavailable", AST_PRESENCE_UNAVAILABLE },
00049    { "available", AST_PRESENCE_AVAILABLE},
00050    { "away", AST_PRESENCE_AWAY},
00051    { "xa", AST_PRESENCE_XA},
00052    { "chat", AST_PRESENCE_CHAT},
00053    { "dnd", AST_PRESENCE_DND},
00054 };
00055 
00056 /*! \brief Flag for the queue */
00057 static ast_cond_t change_pending;
00058 
00059 struct state_change {
00060    AST_LIST_ENTRY(state_change) list;
00061    char provider[1];
00062 };
00063 
00064 /*! \brief  A presence state provider */
00065 struct presence_state_provider {
00066    char label[40];
00067    ast_presence_state_prov_cb_type callback;
00068    AST_RWLIST_ENTRY(presence_state_provider) list;
00069 };
00070 
00071 /*! \brief A list of providers */
00072 static AST_RWLIST_HEAD_STATIC(presence_state_providers, presence_state_provider);
00073 
00074 /*! \brief The state change queue. State changes are queued
00075    for processing by a separate thread */
00076 static AST_LIST_HEAD_STATIC(state_changes, state_change);
00077 
00078 /*! \brief The presence state change notification thread */
00079 static pthread_t change_thread = AST_PTHREADT_NULL;
00080 
00081 const char *ast_presence_state2str(enum ast_presence_state state)
00082 {
00083    int i;
00084    for (i = 0; i < ARRAY_LEN(state2string); i++) {
00085       if (state == state2string[i].state) {
00086          return state2string[i].string;
00087       }
00088    }
00089    return "";
00090 }
00091 
00092 enum ast_presence_state ast_presence_state_val(const char *val)
00093 {
00094    int i;
00095    for (i = 0; i < ARRAY_LEN(state2string); i++) {
00096       if (!strcasecmp(val, state2string[i].string)) {
00097          return state2string[i].state;
00098       }
00099    }
00100    return AST_PRESENCE_INVALID;
00101 }
00102 
00103 static enum ast_presence_state presence_state_cached(const char *presence_provider, char **subtype, char **message)
00104 {
00105    enum ast_presence_state res = AST_PRESENCE_INVALID;
00106    struct ast_event *event;
00107    const char *_subtype;
00108    const char *_message;
00109 
00110    event = ast_event_get_cached(AST_EVENT_PRESENCE_STATE,
00111       AST_EVENT_IE_PRESENCE_PROVIDER, AST_EVENT_IE_PLTYPE_STR, presence_provider,
00112       AST_EVENT_IE_END);
00113 
00114    if (!event) {
00115       return res;
00116    }
00117 
00118    res = ast_event_get_ie_uint(event, AST_EVENT_IE_PRESENCE_STATE);
00119    _subtype = ast_event_get_ie_str(event, AST_EVENT_IE_PRESENCE_SUBTYPE);
00120    _message = ast_event_get_ie_str(event, AST_EVENT_IE_PRESENCE_MESSAGE);
00121 
00122    *subtype = !ast_strlen_zero(_subtype) ? ast_strdup(_subtype) : NULL;
00123    *message = !ast_strlen_zero(_message) ? ast_strdup(_message) : NULL;
00124    ast_event_destroy(event);
00125 
00126    return res;
00127 }
00128 
00129 static enum ast_presence_state ast_presence_state_helper(const char *presence_provider, char **subtype, char **message, int check_cache)
00130 {
00131    struct presence_state_provider *provider;
00132    char *address;
00133    char *label = ast_strdupa(presence_provider);
00134    int res = AST_PRESENCE_INVALID;
00135 
00136    if (check_cache) {
00137       res = presence_state_cached(presence_provider, subtype, message);
00138       if (res != AST_PRESENCE_INVALID) {
00139          return res;
00140       }
00141    }
00142 
00143    if ((address = strchr(label, ':'))) {
00144       *address = '\0';
00145       address++;
00146    } else {
00147       ast_log(LOG_WARNING, "No label found for presence state provider: %s\n", presence_provider);
00148       return res;
00149    }
00150 
00151    AST_RWLIST_RDLOCK(&presence_state_providers);
00152    AST_RWLIST_TRAVERSE(&presence_state_providers, provider, list) {
00153       ast_debug(5, "Checking provider %s with %s\n", provider->label, label);
00154 
00155       if (!strcasecmp(provider->label, label)) {
00156          res = provider->callback(address, subtype, message);
00157          break;
00158       }
00159    }
00160    AST_RWLIST_UNLOCK(&presence_state_providers);
00161 
00162    if (!provider) {
00163       ast_log(LOG_WARNING, "No provider found for label %s\n", label);
00164    }
00165 
00166    return res;
00167 }
00168 
00169 enum ast_presence_state ast_presence_state(const char *presence_provider, char **subtype, char **message)
00170 {
00171    return ast_presence_state_helper(presence_provider, subtype, message, 1);
00172 }
00173 
00174 enum ast_presence_state ast_presence_state_nocache(const char *presence_provider, char **subtype, char **message)
00175 {
00176    return ast_presence_state_helper(presence_provider, subtype, message, 0);
00177 }
00178 
00179 int ast_presence_state_prov_add(const char *label, ast_presence_state_prov_cb_type callback)
00180 {
00181    struct presence_state_provider *provider;
00182 
00183    if (!callback || !(provider = ast_calloc(1, sizeof(*provider)))) {
00184       return -1;
00185    }
00186 
00187    provider->callback = callback;
00188    ast_copy_string(provider->label, label, sizeof(provider->label));
00189 
00190    AST_RWLIST_WRLOCK(&presence_state_providers);
00191    AST_RWLIST_INSERT_HEAD(&presence_state_providers, provider, list);
00192    AST_RWLIST_UNLOCK(&presence_state_providers);
00193 
00194    return 0;
00195 }
00196 int ast_presence_state_prov_del(const char *label)
00197 {
00198    struct presence_state_provider *provider;
00199    int res = -1;
00200 
00201    AST_RWLIST_WRLOCK(&presence_state_providers);
00202    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&presence_state_providers, provider, list) {
00203       if (!strcasecmp(provider->label, label)) {
00204          AST_RWLIST_REMOVE_CURRENT(list);
00205          ast_free(provider);
00206          res = 0;
00207          break;
00208       }
00209    }
00210    AST_RWLIST_TRAVERSE_SAFE_END;
00211    AST_RWLIST_UNLOCK(&presence_state_providers);
00212 
00213    return res;
00214 }
00215 
00216 static void presence_state_event(const char *provider,
00217       enum ast_presence_state state,
00218       const char *subtype,
00219       const char *message)
00220 {
00221    struct ast_event *event;
00222 
00223    if (!(event = ast_event_new(AST_EVENT_PRESENCE_STATE,
00224          AST_EVENT_IE_PRESENCE_PROVIDER, AST_EVENT_IE_PLTYPE_STR, provider,
00225          AST_EVENT_IE_PRESENCE_STATE, AST_EVENT_IE_PLTYPE_UINT, state,
00226          AST_EVENT_IE_PRESENCE_SUBTYPE, AST_EVENT_IE_PLTYPE_STR, S_OR(subtype, ""),
00227          AST_EVENT_IE_PRESENCE_MESSAGE, AST_EVENT_IE_PLTYPE_STR, S_OR(message, ""),
00228          AST_EVENT_IE_END))) {
00229       return;
00230    }
00231 
00232    ast_event_queue_and_cache(event);
00233 }
00234 
00235 static void do_presence_state_change(const char *provider)
00236 {
00237    char *subtype = NULL;
00238    char *message = NULL;
00239    enum ast_presence_state state;
00240 
00241    state = ast_presence_state_helper(provider, &subtype, &message, 0);
00242 
00243    if (state < 0) {
00244       return;
00245    }
00246 
00247    presence_state_event(provider, state, subtype, message);
00248    ast_free(subtype);
00249    ast_free(message);
00250 }
00251 
00252 int ast_presence_state_changed_literal(enum ast_presence_state state,
00253       const char *subtype,
00254       const char *message,
00255       const char *presence_provider)
00256 {
00257    struct state_change *change;
00258 
00259    if (state != AST_PRESENCE_NOT_SET) {
00260       presence_state_event(presence_provider, state, subtype, message);
00261    } else if ((change_thread == AST_PTHREADT_NULL) ||
00262       !(change = ast_calloc(1, sizeof(*change) + strlen(presence_provider)))) {
00263       do_presence_state_change(presence_provider);
00264    } else {
00265       strcpy(change->provider, presence_provider);
00266       AST_LIST_LOCK(&state_changes);
00267       AST_LIST_INSERT_TAIL(&state_changes, change, list);
00268       ast_cond_signal(&change_pending);
00269       AST_LIST_UNLOCK(&state_changes);
00270    }
00271 
00272    return 0;
00273 }
00274 
00275 int ast_presence_state_changed(enum ast_presence_state state,
00276       const char *subtype,
00277       const char *message,
00278       const char *fmt, ...)
00279 {
00280    char buf[AST_MAX_EXTENSION];
00281    va_list ap;
00282 
00283    va_start(ap, fmt);
00284    vsnprintf(buf, sizeof(buf), fmt, ap);
00285    va_end(ap);
00286 
00287    return ast_presence_state_changed_literal(state, subtype, message, buf);
00288 }
00289 
00290 /*! \brief Go through the presence state change queue and update changes in the presence state thread */
00291 static void *do_presence_changes(void *data)
00292 {
00293    struct state_change *next, *current;
00294 
00295    for (;;) {
00296       /* This basically pops off any state change entries, resets the list back to NULL, unlocks, and processes each state change */
00297       AST_LIST_LOCK(&state_changes);
00298       if (AST_LIST_EMPTY(&state_changes))
00299          ast_cond_wait(&change_pending, &state_changes.lock);
00300       next = AST_LIST_FIRST(&state_changes);
00301       AST_LIST_HEAD_INIT_NOLOCK(&state_changes);
00302       AST_LIST_UNLOCK(&state_changes);
00303 
00304       /* Process each state change */
00305       while ((current = next)) {
00306          next = AST_LIST_NEXT(current, list);
00307          do_presence_state_change(current->provider);
00308          ast_free(current);
00309       }
00310    }
00311 
00312    return NULL;
00313 }
00314 
00315 int ast_presence_state_engine_init(void)
00316 {
00317    ast_cond_init(&change_pending, NULL);
00318    if (ast_pthread_create_background(&change_thread, NULL, do_presence_changes, NULL) < 0) {
00319       ast_log(LOG_ERROR, "Unable to start presence state change thread.\n");
00320       return -1;
00321    }
00322 
00323    return 0;
00324 }
00325