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

Generated on Thu Apr 16 06:27:36 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6