Sat Apr 26 2014 22:01:37

Asterisk developer's documentation


func_presencestate.c
Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2011, 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 Custom presence provider
00022  * \ingroup functions
00023  */
00024 
00025 /*** MODULEINFO
00026    <support_level>core</support_level>
00027  ***/
00028 
00029 #include "asterisk.h"
00030 
00031 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 409886 $")
00032 
00033 #include "asterisk/module.h"
00034 #include "asterisk/channel.h"
00035 #include "asterisk/pbx.h"
00036 #include "asterisk/utils.h"
00037 #include "asterisk/linkedlists.h"
00038 #include "asterisk/presencestate.h"
00039 #include "asterisk/cli.h"
00040 #include "asterisk/astdb.h"
00041 #include "asterisk/app.h"
00042 #ifdef TEST_FRAMEWORK
00043 #include "asterisk/test.h"
00044 #include "asterisk/event.h"
00045 #include <semaphore.h>
00046 #endif
00047 
00048 /*** DOCUMENTATION
00049    <function name="PRESENCE_STATE" language="en_US">
00050       <synopsis>
00051          Get or Set a presence state.
00052       </synopsis>
00053       <syntax>
00054          <parameter name="provider" required="true">
00055            <para>The provider of the presence, such as <literal>CustomPresence</literal></para>
00056          </parameter>
00057          <parameter name="field" required="true">
00058            <para>Which field of the presence state information is wanted.</para>
00059            <optionlist>
00060             <option name="value">
00061               <para>The current presence, such as <literal>away</literal></para>
00062             </option>
00063             <option name="subtype">
00064               <para>Further information about the current presence</para>
00065             </option>
00066              <option name="message">
00067               <para>A custom message that may indicate further details about the presence</para>
00068             </option>
00069            </optionlist>
00070          </parameter>
00071          <parameter name="options" required="false">
00072            <optionlist>
00073              <option name="e">
00074               <para>On Write - Use this option when the subtype and message provided are Base64
00075                encoded. On Read - Retrieves message/subtype in Base64 encoded form.</para>
00076             </option>
00077            </optionlist>
00078          </parameter>
00079       </syntax>
00080       <description>
00081          <para>The PRESENCE_STATE function can be used to retrieve the presence from any
00082          presence provider. For example:</para>
00083          <para>NoOp(SIP/mypeer has presence ${PRESENCE_STATE(SIP/mypeer,value)})</para>
00084          <para>NoOp(Conference number 1234 has presence message ${PRESENCE_STATE(MeetMe:1234,message)})</para>
00085          <para>The PRESENCE_STATE function can also be used to set custom presence state from
00086          the dialplan.  The <literal>CustomPresence:</literal> prefix must be used. For example:</para>
00087          <para>Set(PRESENCE_STATE(CustomPresence:lamp1)=away,temporary,Out to lunch)</para>
00088          <para>Set(PRESENCE_STATE(CustomPresence:lamp2)=dnd,,Trying to get work done)</para>
00089          <para>Set(PRESENCE_STATE(CustomPresence:lamp3)=xa,T24gdmFjYXRpb24=,,e)</para>
00090          <para>Set(BASE64_LAMP3_PRESENCE=${PRESENCE_STATE(CustomPresence:lamp3,subtype,e)})</para>
00091          <para>You can subscribe to the status of a custom presence state using a hint in
00092          the dialplan:</para>
00093          <para>exten => 1234,hint,,CustomPresence:lamp1</para>
00094          <para>The possible values for both uses of this function are:</para>
00095          <para>not_set | unavailable | available | away | xa | chat | dnd</para>
00096       </description>
00097    </function>
00098  ***/
00099 
00100 
00101 static const char astdb_family[] = "CustomPresence";
00102 
00103 static int presence_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00104 {
00105    int state;
00106    char *message = NULL;
00107    char *subtype = NULL;
00108    char *parse;
00109    int base64encode = 0;
00110    AST_DECLARE_APP_ARGS(args,
00111       AST_APP_ARG(provider);
00112       AST_APP_ARG(field);
00113       AST_APP_ARG(options);
00114    );
00115 
00116    if (ast_strlen_zero(data)) {
00117       ast_log(LOG_WARNING, "PRESENCE_STATE reading requires an argument \n");
00118       return -1;
00119    }
00120 
00121    parse = ast_strdupa(data);
00122 
00123    AST_STANDARD_APP_ARGS(args, parse);
00124 
00125    if (ast_strlen_zero(args.provider) || ast_strlen_zero(args.field)) {
00126       ast_log(LOG_WARNING, "PRESENCE_STATE reading requires both presence provider and presence field arguments. \n");
00127       return -1;
00128    }
00129 
00130    state = ast_presence_state_nocache(args.provider, &subtype, &message);
00131    if (state == AST_PRESENCE_INVALID) {
00132       ast_log(LOG_WARNING, "PRESENCE_STATE unknown \n");
00133       return -1;
00134    }
00135 
00136    if (!(ast_strlen_zero(args.options)) && (strchr(args.options, 'e'))) {
00137       base64encode = 1;
00138    }
00139 
00140    if (!ast_strlen_zero(subtype) && !strcasecmp(args.field, "subtype")) {
00141       if (base64encode) {
00142          ast_base64encode(buf, (unsigned char *) subtype, strlen(subtype), len);
00143       } else {
00144          ast_copy_string(buf, subtype, len);
00145       }
00146    } else if (!ast_strlen_zero(message) && !strcasecmp(args.field, "message")) {
00147       if (base64encode) {
00148          ast_base64encode(buf, (unsigned char *) message, strlen(message), len);
00149       } else {
00150          ast_copy_string(buf, message, len);
00151       }
00152 
00153    } else if (!strcasecmp(args.field, "value")) {
00154       ast_copy_string(buf, ast_presence_state2str(state), len);
00155    }
00156 
00157    ast_free(message);
00158    ast_free(subtype);
00159 
00160    return 0;
00161 }
00162 
00163 static int parse_data(char *data, enum ast_presence_state *state, char **subtype, char **message, char **options)
00164 {
00165    char *state_str;
00166 
00167    /* data syntax is state,subtype,message,options */
00168    *subtype = "";
00169    *message = "";
00170    *options = "";
00171 
00172    state_str = strsep(&data, ",");
00173    if (ast_strlen_zero(state_str)) {
00174       return -1; /* state is required */
00175    }
00176 
00177    *state = ast_presence_state_val(state_str);
00178 
00179    /* not a valid state */
00180    if (*state == AST_PRESENCE_INVALID) {
00181       ast_log(LOG_WARNING, "Unknown presence state value %s\n", state_str);
00182       return -1;
00183    }
00184 
00185    if (!(*subtype = strsep(&data,","))) {
00186       *subtype = "";
00187       return 0;
00188    }
00189 
00190    if (!(*message = strsep(&data, ","))) {
00191       *message = "";
00192       return 0;
00193    }
00194 
00195    if (!(*options = strsep(&data, ","))) {
00196       *options = "";
00197       return 0;
00198    }
00199 
00200    if (!ast_strlen_zero(*options) && !(strchr(*options, 'e'))) {
00201       ast_log(LOG_NOTICE, "Invalid options  '%s'\n", *options);
00202       return -1;
00203    }
00204 
00205    return 0;
00206 }
00207 
00208 static int presence_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
00209 {
00210    size_t len = strlen("CustomPresence:");
00211    char *tmp = data;
00212    char *args = ast_strdupa(value);
00213    enum ast_presence_state state;
00214    char *options, *message, *subtype;
00215 
00216    if (strncasecmp(data, "CustomPresence:", len)) {
00217       ast_log(LOG_WARNING, "The PRESENCE_STATE function can only set CustomPresence: presence providers.\n");
00218       return -1;
00219    }
00220    data += len;
00221    if (ast_strlen_zero(data)) {
00222       ast_log(LOG_WARNING, "PRESENCE_STATE function called with no custom device name!\n");
00223       return -1;
00224    }
00225 
00226    if (parse_data(args, &state, &subtype, &message, &options)) {
00227       ast_log(LOG_WARNING, "Invalid arguments to PRESENCE_STATE\n");
00228       return -1;
00229    }
00230 
00231    ast_db_put(astdb_family, data, value);
00232 
00233    ast_presence_state_changed_literal(state, subtype, message, tmp);
00234 
00235    return 0;
00236 }
00237 
00238 static enum ast_presence_state custom_presence_callback(const char *data, char **subtype, char **message)
00239 {
00240    char buf[1301] = "";
00241    enum ast_presence_state state;
00242    char *_options;
00243    char *_message;
00244    char *_subtype;
00245 
00246    ast_db_get(astdb_family, data, buf, sizeof(buf));
00247 
00248    if (parse_data(buf, &state, &_subtype, &_message, &_options)) {
00249       return AST_PRESENCE_INVALID;
00250    }
00251 
00252    if ((strchr(_options, 'e'))) {
00253       char tmp[1301];
00254       if (ast_strlen_zero(_subtype)) {
00255          *subtype = NULL;
00256       } else {
00257          memset(tmp, 0, sizeof(tmp));
00258          ast_base64decode((unsigned char *) tmp, _subtype, sizeof(tmp) - 1);
00259          *subtype = ast_strdup(tmp);
00260       }
00261 
00262       if (ast_strlen_zero(_message)) {
00263          *message = NULL;
00264       } else {
00265          memset(tmp, 0, sizeof(tmp));
00266          ast_base64decode((unsigned char *) tmp, _message, sizeof(tmp) - 1);
00267          *message = ast_strdup(tmp);
00268       }
00269    } else {
00270       *subtype = ast_strlen_zero(_subtype) ? NULL : ast_strdup(_subtype);
00271       *message = ast_strlen_zero(_message) ? NULL : ast_strdup(_message);
00272    }
00273    return state;
00274 }
00275 
00276 static struct ast_custom_function presence_function = {
00277    .name = "PRESENCE_STATE",
00278    .read = presence_read,
00279    .write = presence_write,
00280 };
00281 
00282 static char *handle_cli_presencestate_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00283 {
00284    struct ast_db_entry *db_entry, *db_tree;
00285 
00286    switch (cmd) {
00287    case CLI_INIT:
00288       e->command = "presencestate list";
00289       e->usage =
00290          "Usage: presencestate list\n"
00291          "       List all custom presence states that have been set by using\n"
00292          "       the PRESENCE_STATE dialplan function.\n";
00293       return NULL;
00294    case CLI_GENERATE:
00295       return NULL;
00296    }
00297 
00298    if (a->argc != e->args) {
00299       return CLI_SHOWUSAGE;
00300    }
00301 
00302    ast_cli(a->fd, "\n"
00303            "---------------------------------------------------------------------\n"
00304            "--- Custom Presence States ------------------------------------------\n"
00305            "---------------------------------------------------------------------\n"
00306            "---\n");
00307 
00308    db_entry = db_tree = ast_db_gettree(astdb_family, NULL);
00309    if (!db_entry) {
00310       ast_cli(a->fd, "No custom presence states defined\n");
00311       return CLI_SUCCESS;
00312    }
00313    for (; db_entry; db_entry = db_entry->next) {
00314       const char *object_name = strrchr(db_entry->key, '/') + 1;
00315       char state_info[1301];
00316       enum ast_presence_state state;
00317       char *subtype;
00318       char *message;
00319       char *options;
00320 
00321       ast_copy_string(state_info, db_entry->data, sizeof(state_info));
00322       if (parse_data(state_info, &state, &subtype, &message, &options)) {
00323          ast_log(LOG_WARNING, "Invalid CustomPresence entry %s encountered\n", db_entry->data);
00324          continue;
00325       }
00326 
00327       if (object_name <= (const char *) 1) {
00328          continue;
00329       }
00330       ast_cli(a->fd, "--- Name: 'CustomPresence:%s'\n"
00331                    "    --- State: '%s'\n"
00332                   "    --- Subtype: '%s'\n"
00333                   "    --- Message: '%s'\n"
00334                   "    --- Base64 Encoded: '%s'\n"
00335                      "---\n",
00336                   object_name,
00337                   ast_presence_state2str(state),
00338                   subtype,
00339                   message,
00340                   AST_CLI_YESNO(strchr(options, 'e')));
00341    }
00342    ast_db_freetree(db_tree);
00343    db_tree = NULL;
00344 
00345    ast_cli(a->fd,
00346            "---------------------------------------------------------------------\n"
00347            "---------------------------------------------------------------------\n"
00348            "\n");
00349 
00350    return CLI_SUCCESS;
00351 }
00352 
00353 static char *handle_cli_presencestate_change(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00354 {
00355     size_t len;
00356    const char *dev, *state, *full_dev;
00357    enum ast_presence_state state_val;
00358    char *message;
00359    char *subtype;
00360    char *options;
00361    char *args;
00362 
00363    switch (cmd) {
00364    case CLI_INIT:
00365       e->command = "presencestate change";
00366       e->usage =
00367          "Usage: presencestate change <entity> <state>[,<subtype>[,message[,options]]]\n"
00368          "       Change a custom presence to a new state.\n"
00369          "       The possible values for the state are:\n"
00370          "NOT_SET | UNAVAILABLE | AVAILABLE | AWAY | XA | CHAT | DND\n"
00371          "Optionally, a custom subtype and message may be provided, along with any options\n"
00372          "accepted by func_presencestate. If the subtype or message provided contain spaces,\n"
00373          "be sure to enclose the data in quotation marks (\"\")\n"
00374          "\n"
00375          "Examples:\n"
00376          "       presencestate change CustomPresence:mystate1 AWAY\n"
00377          "       presencestate change CustomPresence:mystate1 AVAILABLE\n"
00378          "       presencestate change CustomPresence:mystate1 \"Away,upstairs,eating lunch\"\n"
00379          "       \n";
00380       return NULL;
00381    case CLI_GENERATE:
00382    {
00383       static const char * const cmds[] = { "NOT_SET", "UNAVAILABLE", "AVAILABLE", "AWAY",
00384                        "XA", "CHAT", "DND", NULL };
00385 
00386       if (a->pos == e->args + 1) {
00387          return ast_cli_complete(a->word, cmds, a->n);
00388       }
00389 
00390       return NULL;
00391    }
00392    }
00393 
00394    if (a->argc != e->args + 2) {
00395       return CLI_SHOWUSAGE;
00396    }
00397 
00398    len = strlen("CustomPresence:");
00399    full_dev = dev = a->argv[e->args];
00400    state = a->argv[e->args + 1];
00401 
00402    if (strncasecmp(dev, "CustomPresence:", len)) {
00403       ast_cli(a->fd, "The presencestate command can only be used to set 'CustomPresence:' presence state!\n");
00404       return CLI_FAILURE;
00405    }
00406 
00407    dev += len;
00408    if (ast_strlen_zero(dev)) {
00409       return CLI_SHOWUSAGE;
00410    }
00411 
00412    args = ast_strdupa(state);
00413    if (parse_data(args, &state_val, &subtype, &message, &options)) {
00414       return CLI_SHOWUSAGE;
00415    }
00416 
00417    if (state_val == AST_PRESENCE_NOT_SET) {
00418       return CLI_SHOWUSAGE;
00419    }
00420 
00421    ast_cli(a->fd, "Changing %s to %s\n", dev, args);
00422 
00423    ast_db_put(astdb_family, dev, state);
00424 
00425    ast_presence_state_changed_literal(state_val, subtype, message, full_dev);
00426 
00427    return CLI_SUCCESS;
00428 }
00429 
00430 static struct ast_cli_entry cli_funcpresencestate[] = {
00431    AST_CLI_DEFINE(handle_cli_presencestate_list, "List currently know custom presence states"),
00432    AST_CLI_DEFINE(handle_cli_presencestate_change, "Change a custom presence state"),
00433 };
00434 
00435 #ifdef TEST_FRAMEWORK
00436 
00437 struct test_string {
00438    char *parse_string;
00439    struct {
00440       int value;
00441       const char *subtype;
00442       const char *message;
00443       const char *options; 
00444    } outputs;
00445 };
00446 
00447 AST_TEST_DEFINE(test_valid_parse_data)
00448 {
00449    int i;
00450    enum ast_presence_state state;
00451    char *subtype;
00452    char *message;
00453    char *options;
00454    enum ast_test_result_state res = AST_TEST_PASS;
00455    
00456    struct test_string tests [] = {
00457       { "away",
00458          { AST_PRESENCE_AWAY,
00459             "",
00460             "",
00461             ""
00462          }
00463       },
00464       { "not_set",
00465          { AST_PRESENCE_NOT_SET,
00466             "",
00467             "",
00468             ""
00469          }
00470       },
00471       { "unavailable",
00472          { AST_PRESENCE_UNAVAILABLE,
00473             "",
00474             "",
00475             ""
00476          }
00477       },
00478       { "available",
00479          { AST_PRESENCE_AVAILABLE,
00480             "",
00481             "",
00482             ""
00483          }
00484       },
00485       { "xa",
00486          { AST_PRESENCE_XA,
00487             "",
00488             "",
00489             ""
00490          }
00491       },
00492       { "chat",
00493          { AST_PRESENCE_CHAT,
00494             "",
00495             "",
00496             ""
00497          }
00498       },
00499       { "dnd",
00500          { AST_PRESENCE_DND,
00501             "",
00502             "",
00503             ""
00504          }
00505       },
00506       { "away,down the hall",
00507          { AST_PRESENCE_AWAY,
00508             "down the hall",
00509             "",
00510             ""
00511          }
00512       },
00513       { "away,down the hall,Quarterly financial meeting",
00514          { AST_PRESENCE_AWAY,
00515             "down the hall",
00516             "Quarterly financial meeting",
00517             ""
00518          }
00519       },
00520       { "away,,Quarterly financial meeting",
00521          { AST_PRESENCE_AWAY,
00522             "",
00523             "Quarterly financial meeting",
00524             ""
00525          }
00526       },
00527       { "away,,,e",
00528          { AST_PRESENCE_AWAY,
00529             "",
00530             "",
00531             "e",
00532          }
00533       },
00534       { "away,down the hall,,e",
00535          { AST_PRESENCE_AWAY,
00536             "down the hall",
00537             "",
00538             "e"
00539          }
00540       },
00541       { "away,down the hall,Quarterly financial meeting,e",
00542          { AST_PRESENCE_AWAY,
00543             "down the hall",
00544             "Quarterly financial meeting",
00545             "e"
00546          }
00547       },
00548       { "away,,Quarterly financial meeting,e",
00549          { AST_PRESENCE_AWAY,
00550             "",
00551             "Quarterly financial meeting",
00552             "e"
00553          }
00554       }
00555    };
00556 
00557    switch (cmd) {
00558    case TEST_INIT:
00559       info->name = "parse_valid_presence_data";
00560       info->category = "/funcs/func_presence/";
00561       info->summary = "PRESENCESTATE parsing test";
00562       info->description =
00563          "Ensure that parsing function accepts proper values, and gives proper outputs";
00564       return AST_TEST_NOT_RUN;
00565    case TEST_EXECUTE:
00566       break;
00567    }
00568 
00569    for (i = 0; i < ARRAY_LEN(tests); ++i) {
00570       int parse_result;
00571       char *parse_string = ast_strdup(tests[i].parse_string);
00572       if (!parse_string) {
00573          res = AST_TEST_FAIL;
00574          break;
00575       }
00576       parse_result = parse_data(parse_string, &state, &subtype, &message, &options);
00577       if (parse_result == -1) {
00578          res = AST_TEST_FAIL;
00579          ast_free(parse_string);
00580          break;
00581       }
00582       if (tests[i].outputs.value != state ||
00583             strcmp(tests[i].outputs.subtype, subtype) ||
00584             strcmp(tests[i].outputs.message, message) ||
00585             strcmp(tests[i].outputs.options, options)) {
00586          res = AST_TEST_FAIL;
00587          ast_free(parse_string);
00588          break;
00589       }
00590       ast_free(parse_string);
00591    }
00592 
00593    return res;
00594 }
00595 
00596 AST_TEST_DEFINE(test_invalid_parse_data)
00597 {
00598    int i;
00599    enum ast_presence_state state;
00600    char *subtype;
00601    char *message;
00602    char *options;
00603    enum ast_test_result_state res = AST_TEST_PASS;
00604 
00605    char *tests[] = {
00606       "",
00607       "bored",
00608       "away,,,i",
00609       /* XXX The following actually is parsed correctly. Should that
00610        * be changed?
00611        * "away,,,,e",
00612        */
00613    };
00614 
00615    switch (cmd) {
00616    case TEST_INIT:
00617       info->name = "parse_invalid_presence_data";
00618       info->category = "/funcs/func_presence/";
00619       info->summary = "PRESENCESTATE parsing test";
00620       info->description =
00621          "Ensure that parsing function rejects improper values";
00622       return AST_TEST_NOT_RUN;
00623    case TEST_EXECUTE:
00624       break;
00625    }
00626 
00627    for (i = 0; i < ARRAY_LEN(tests); ++i) {
00628       int parse_result;
00629       char *parse_string = ast_strdup(tests[i]);
00630       if (!parse_string) {
00631          res = AST_TEST_FAIL;
00632          break;
00633       }
00634       parse_result = parse_data(parse_string, &state, &subtype, &message, &options);
00635       if (parse_result == 0) {
00636          ast_log(LOG_WARNING, "Invalid string parsing failed on %s\n", tests[i]);
00637          res = AST_TEST_FAIL;
00638          ast_free(parse_string);
00639          break;
00640       }
00641       ast_free(parse_string);
00642    }
00643 
00644    return res;
00645 }
00646 
00647 struct test_cb_data {
00648    enum ast_presence_state presence;
00649    const char *provider;
00650    const char *subtype;
00651    const char *message;
00652    /* That's right. I'm using a semaphore */
00653    sem_t sem;
00654 };
00655 
00656 static void test_cb(const struct ast_event *event, void *userdata)
00657 {
00658    struct test_cb_data *cb_data = userdata;
00659    cb_data->presence = ast_event_get_ie_uint(event, AST_EVENT_IE_PRESENCE_STATE);
00660    cb_data->provider = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_PRESENCE_PROVIDER));
00661    cb_data->subtype = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_PRESENCE_SUBTYPE));
00662    cb_data->message = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_PRESENCE_MESSAGE));
00663    sem_post(&cb_data->sem);
00664 }
00665 
00666 /* XXX This test could probably stand to be moved since
00667  * it does not test func_presencestate but rather code in
00668  * presencestate.h and presencestate.c. However, the convenience
00669  * of presence_write() makes this a nice location for this test.
00670  */
00671 AST_TEST_DEFINE(test_presence_state_change)
00672 {
00673    struct ast_event_sub *test_sub;
00674    struct test_cb_data *cb_data;
00675 
00676    switch (cmd) {
00677    case TEST_INIT:
00678       info->name = "test_presence_state_change";
00679       info->category = "/funcs/func_presence/";
00680       info->summary = "presence state change subscription";
00681       info->description =
00682          "Ensure that presence state changes are communicated to subscribers";
00683       return AST_TEST_NOT_RUN;
00684    case TEST_EXECUTE:
00685       break;
00686    }
00687 
00688    cb_data = ast_calloc(1, sizeof(*cb_data));
00689    if (!cb_data) {
00690       return AST_TEST_FAIL;
00691    }
00692 
00693    if (!(test_sub = ast_event_subscribe(AST_EVENT_PRESENCE_STATE,
00694          test_cb, "Test presence state callbacks", cb_data, AST_EVENT_IE_END))) {
00695       return AST_TEST_FAIL;
00696    }
00697 
00698    if (sem_init(&cb_data->sem, 0, 0)) {
00699       return AST_TEST_FAIL;
00700    }
00701 
00702    presence_write(NULL, "PRESENCESTATE", "CustomPresence:TestPresenceStateChange", "away,down the hall,Quarterly financial meeting");
00703    sem_wait(&cb_data->sem);
00704    if (cb_data->presence != AST_PRESENCE_AWAY ||
00705          strcmp(cb_data->provider, "CustomPresence:TestPresenceStateChange") ||
00706          strcmp(cb_data->subtype, "down the hall") ||
00707          strcmp(cb_data->message, "Quarterly financial meeting")) {
00708       return AST_TEST_FAIL;
00709    }
00710 
00711    ast_free((char *)cb_data->provider);
00712    ast_free((char *)cb_data->subtype);
00713    ast_free((char *)cb_data->message);
00714    ast_free((char *)cb_data);
00715 
00716    ast_db_del("CustomPresence", "TestPresenceStateChange");
00717 
00718    return AST_TEST_PASS;
00719 }
00720 
00721 #endif
00722 
00723 static int unload_module(void)
00724 {
00725    int res = 0;
00726 
00727    res |= ast_custom_function_unregister(&presence_function);
00728    res |= ast_presence_state_prov_del("CustomPresence");
00729    res |= ast_cli_unregister_multiple(cli_funcpresencestate, ARRAY_LEN(cli_funcpresencestate));
00730 #ifdef TEST_FRAMEWORK
00731    AST_TEST_UNREGISTER(test_valid_parse_data);
00732    AST_TEST_UNREGISTER(test_invalid_parse_data);
00733    AST_TEST_UNREGISTER(test_presence_state_change);
00734 #endif
00735    return res;
00736 }
00737 
00738 static int load_module(void)
00739 {
00740    int res = 0;
00741    struct ast_db_entry *db_entry, *db_tree;
00742 
00743    /* Populate the presence state cache on the system with all of the currently
00744     * known custom presence states. */
00745    db_entry = db_tree = ast_db_gettree(astdb_family, NULL);
00746    for (; db_entry; db_entry = db_entry->next) {
00747       const char *dev_name = strrchr(db_entry->key, '/') + 1;
00748       char state_info[1301];
00749       enum ast_presence_state state;
00750       char *message;
00751       char *subtype;
00752       char *options;
00753       if (dev_name <= (const char *) 1) {
00754          continue;
00755       }
00756       ast_copy_string(state_info, db_entry->data, sizeof(state_info));
00757       if (parse_data(state_info, &state, &subtype, &message, &options)) {
00758          ast_log(LOG_WARNING, "Invalid CustomPresence entry %s encountered\n", db_entry->data);
00759          continue;
00760       }
00761       ast_presence_state_changed(state, subtype, message, "CustomPresence:%s", dev_name);
00762    }
00763    ast_db_freetree(db_tree);
00764    db_tree = NULL;
00765 
00766    res |= ast_custom_function_register(&presence_function);
00767    res |= ast_presence_state_prov_add("CustomPresence", custom_presence_callback);
00768    res |= ast_cli_register_multiple(cli_funcpresencestate, ARRAY_LEN(cli_funcpresencestate));
00769 #ifdef TEST_FRAMEWORK
00770    AST_TEST_REGISTER(test_valid_parse_data);
00771    AST_TEST_REGISTER(test_invalid_parse_data);
00772    AST_TEST_REGISTER(test_presence_state_change);
00773 #endif
00774 
00775    return res;
00776 }
00777 
00778 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Gets or sets a presence state in the dialplan",
00779    .load = load_module,
00780    .unload = unload_module,
00781    .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
00782 );
00783