00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
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
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
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
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
00072 static AST_RWLIST_HEAD_STATIC(presence_state_providers, presence_state_provider);
00073
00074
00075
00076 static AST_LIST_HEAD_STATIC(state_changes, state_change);
00077
00078
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
00291 static void *do_presence_changes(void *data)
00292 {
00293 struct state_change *next, *current;
00294
00295 for (;;) {
00296
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
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