agi.cc

00001 /* vim: set et sw=3 tw=0 fo=croqlaw cino=t0:
00002  * 
00003  * Astxx, the Asterisk C++ API and Utility Library.
00004  * Copyright (C) 2005-2007  Matthew A. Nicholson
00005  * Copyright (C) 2005-2007  Digium, Inc.
00006  * 
00007  * This library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public
00009  * License version 2.1 as published by the Free Software Foundation.
00010  * 
00011  * This library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Lesser General Public License for more details.
00015  * 
00016  * You should have received a copy of the GNU Lesser General Public
00017  * License along with this library; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  */
00020 
00021 #include <astxx/agi.h>
00022 #include <iostream>
00023 #include <sstream>
00024 #include <csignal>
00025 #include <cerrno>
00026 #include <cstring>
00027 
00028 using namespace std;
00029 using namespace astxx;
00030 
00031 // initialize and declare our state variables
00032 // XXX These variables are of type bool and not sig_atomic_t.  This may cause 
00033 // problems on certain platforms.  Hopefully the volatile keyword will help 
00034 // here.
00035 volatile bool agi::got_sighup = false;
00036 volatile bool agi::got_sigpipe = false;
00037 
00047 agi& agi::instance() {
00048    static agi instance;
00049    return instance;
00050 }
00051 
00061 agi::agi() {
00062    // block signals
00063    sigset_t new_set;
00064    sigset_t original_set;
00065 
00066    if (sigfillset(&new_set) && sigprocmask(SIG_BLOCK, &new_set, &original_set))
00067       throw agi::error("Error blocking signals during initilization: " + string(strerror(errno)));
00068 
00069    // set our signal handler
00070    if (SIG_ERR == signal(SIGHUP, agi::handle_sighup) || SIG_ERR == signal(SIGPIPE, agi::handle_sigpipe)) {
00071       sigprocmask(SIG_SETMASK, &original_set, NULL);
00072       throw agi::error("Error blocking signals during initilization: " + string(strerror(errno)));
00073    }
00074 
00075    // read our environment
00076    read_env();
00077 
00078    // restore the previous sigmask, unblocking signals
00079    if (sigprocmask(SIG_SETMASK, &original_set, NULL))
00080       throw agi::error("Error restoring original signal mask during initilization: " + string(strerror(errno)));
00081 }
00082 
00084 void agi::read_env() {
00085    // read our environment
00086    while (cin.peek() != '\n') {
00087       string key;
00088       string value;
00089 
00090       getline(cin, key, ':');
00091 
00092       if (cin.peek() == ' ')
00093          cin.ignore(1);
00094 
00095       getline(cin, value);
00096 
00097       env.insert(make_pair(key, value));
00098    }
00099 }
00100 
00113 string agi::operator [] (const std::string& key) {
00114    return env[key];
00115 }
00116 
00121 map<string, string>::const_iterator agi::begin() const {
00122    return env.begin();
00123 }
00124 
00129 map<string, string>::const_iterator agi::end() const {
00130    return env.end();
00131 }
00132 
00140 void agi::send_command(const std::string& command) const {
00141    cout << command << endl;
00142 }
00143 
00154 agi::result agi::execute(const std::string& command) const {
00155    // It is important to test for a hangup here because if we attempt to 
00156    // execute a command, after there was a hangup Asterisk is no longer around 
00157    // to service us.  If we recieve a hangup during execution of an 
00158    // application, it will return -1 or hangup as it's data.
00159    test_hangup();
00160 
00161    send_command(command);
00162    agi::result result = get_result();
00163 
00164    if (result.data == "hangup")
00165       throw agi::hangup_result();
00166 
00167    if (result.result == -1)
00168       throw agi::application_error("An error occured executing an agi command or the channel was hungup");
00169 
00170    if (got_sigpipe)
00171       throw agi::io_error("Received SIGPIPE");
00172 
00173    return result;
00174 }
00175 
00176 
00181 agi::result agi::get_result() const {
00182    agi::result result;
00183 
00184    if (!(cin >> result.code)) {
00185       cin.clear();
00186       throw agi::io_error("There was a error reading from the Asterisk server");
00187    }
00188 
00189    if (cin.peek() == ' ')
00190       cin.ignore(1);
00191 
00192    getline(cin, result.message);
00193 
00194    istringstream message(result.message);
00195 
00196    // Handle various result codes.  Different codes have different data 
00197    // associated with them.  Below we handle the various codes.
00198    switch (result.code) {
00199       case 200:
00200          message.ignore(32, '=');
00201 
00202          // make sure result= actually has a value after '='
00203          if (message.peek() != ' ') {
00204             // store the result= portion in a stirng and int
00205             message >> result.result_string;
00206             istringstream ss(result.result_string);
00207             if (!(ss >> result.result))
00208                result.result = 0;
00209          }
00210          else {
00211             result.result_string = "";
00212             result.result = 0;
00213             message.ignore();
00214          }
00215 
00216          // extract any data between ()
00217          {
00218             string end_str;
00219             if (result.message.find("(") != string::npos) {
00220                message.ignore(32, '(');
00221                getline(message, result.data);
00222 
00223                const string::size_type token = result.data.find_last_of(')');
00224                end_str = result.data.substr(token);
00225                result.data.erase(token);
00226 
00227                message.str(end_str);
00228             }
00229             else {
00230                getline(message, end_str);
00231                message.str(end_str);
00232             }
00233 
00234             // extract endpos if applicable
00235             if (end_str.find("endpos=") != string::npos) {
00236                message.ignore(32, '=');
00237                message >> result.endpos;
00238             }
00239          }
00240 
00241          break;
00242       case 510:
00243          throw agi::invalid_command(result.message + ".  This could be a bug in Astxx, or your Asterisk installation may be too old.");
00244 
00245       case 520:
00246          {
00247             string buffer;
00248             while (getline(cin, buffer)) {
00249                istringstream message(buffer);
00250                int end;
00251 
00252                if (message >> end) {
00253                   if (end == 520)
00254                      break;
00255                }
00256                else {
00257                   message.clear();
00258                   result.message += buffer;
00259                }
00260             }
00261 
00262             throw agi::usage_error(result.message);
00263          }
00264 
00265       default:
00266          throw agi::unknown_error(result.code, result.message);
00267    }
00268 
00269    return result;
00270 }
00271 
00273 void agi::handle_sighup(int) {
00274    got_sighup = true;
00275 }
00276 
00278 void agi::handle_sigpipe(int) {
00279    got_sigpipe = true;
00280 }
00281 
00289 void agi::test_hangup() const {
00290    if (got_sighup)
00291       throw agi::hangup_signal();
00292 }
00293 
00296 void agi::clear() {
00297    got_sighup = false;
00298    got_sigpipe = false;
00299 }
00300 
00312 agi& agi::answer() {
00313    agi::result result = execute("ANSWER");
00314    return *this;
00315 }
00316 
00338 int agi::channel_status(const std::string& channel) const {
00339    return execute("CHANNEL STATUS " + quote(channel)).result;
00340 }
00341 
00354 char agi::control_stream_file(const std::string& filename, const std::string& escape_digits, int skip_time, char fastforward, char rewind, char pause) const {
00355    ostringstream ss;
00356    ss << "STREAM FILE " << quote(filename) << " " << quote(escape_digits);
00357 
00358    /* The extra if checks below are just incase a user decides to pass one of 
00359     * our default values (0 in this case).  The checks make sure the extra 
00360     * values are passed in the correct positions for Asterisk to properly 
00361     * identify them.
00362     */
00363    if (skip_time != control_stream_file_skip_time || fastforward || rewind || pause)
00364       ss << " " << skip_time;
00365    if (fastforward || rewind || pause)
00366       ss << " " << quote(fastforward);
00367    if (rewind || pause)
00368       ss << " " << quote(rewind);
00369    if (pause)
00370       ss << " " << quote(pause);
00371 
00372    return execute(ss.str()).result;
00373 }
00374 
00385 agi& agi::database_del(const std::string& family, const std::string& key) {
00386    agi::result result = execute("DATABASE DEL " + quote(family) + " " + quote(key));
00387 
00388    // posibly pass key and value with the execption
00389    if (result.result == 0)
00390       throw agi::database_error("Error deleting database value");
00391    return *this;
00392 }
00393 
00403 agi& agi::database_deltree(const std::string& family, const std::string& keytree) {
00404    agi::result result = execute("DATABASE DELTREE " + quote(family) + " " + quote(keytree));
00405 
00406    // posibly pass key and value with the execption
00407    if (result.result == 0)
00408       throw agi::database_error("Error deleting database family/keytree");
00409    return *this;
00410 }
00411 
00420 string agi::database_get(const std::string& family, const std::string& key) const {
00421    return execute("DATABASE GET " + quote(family) + " " + quote(key)).data;
00422 }
00423 
00435 agi& agi::database_put(const std::string& family, const std::string& key, const std::string& value) {
00436    if (execute("DATABASE PUT " + quote(family) + " " + quote(key) + quote(value)).result == 0)
00437       throw agi::database_error("Error putting value in Asterisk database");
00438    return *this;
00439 }
00440 
00453 int agi::exec(const std::string& app, const std::string& options) const {
00454    return execute("EXEC " + quote(app) + " " + quote(options)).result;
00455 }
00456 
00465 string agi::get_data(const std::string& file, int timeout, int max_digits) const {
00466    ostringstream ss;
00467    ss << "GET DATA " << file;
00468 
00469    if (timeout != 0 || max_digits != get_data_max_digits)
00470       ss << " " << timeout;
00471 
00472    if (max_digits != get_data_max_digits)
00473       ss << " " << max_digits;
00474 
00475    return execute(ss.str()).result_string;
00476 }
00477 
00495 string agi::get_full_variable(const std::string& variable, const std::string& channel) const {
00496    agi::result result;
00497    try {
00498       ostringstream ss;
00499       ss << "GET FULL VARIABLE " << quote(variable);
00500 
00501       if (!channel.empty())
00502          ss << " " << quote(channel);
00503 
00504       result = execute(ss.str());
00505    }
00506    catch (agi::hangup_result& e) {
00507       result.data = "hangup"; 
00508    }
00509 
00510    if (result.result == 0); // possibly throw an exception here
00511 
00512    return result.data;
00513 }
00514 
00523 char agi::get_option(const std::string& file, const std::string& escape_digits, int timeout) const {
00524    ostringstream ss;
00525    ss << "GET OPTION " << quote(file) << " " << quote(escape_digits);
00526 
00527    if (timeout != 0)
00528       ss << " " << timeout;
00529 
00530    agi::result result = execute(ss.str());
00531 
00532    return result.result;
00533 }
00534 
00543 string agi::get_variable(const std::string& variable) const {
00544    agi::result result;
00545    try {
00546       result = execute("GET VARIABLE " + quote(variable));
00547    }
00548    catch (agi::hangup_result& e) {
00549       result.data = "hangup"; 
00550    }
00551 
00552    if (result.result == 0); // possibly throw an exception here
00553 
00554    return result.data;
00555 }
00556 
00564 agi& agi::hangup(const std::string& channel) {
00565    execute("HANGUP " + quote(channel));
00566    return *this;
00567 }
00568 
00572 agi& agi::noop() {
00573    execute("NOOP");
00574    return *this;
00575 }
00576 
00583 char agi::receive_char(int timeout) const {
00584    std::stringstream ss;
00585    ss << "RECEIVE CHAR " << timeout;
00586    return execute(ss.str()).result;
00587 }
00588 
00596 std::string agi::receive_text(int timeout) const {
00597    std::stringstream ss;
00598    ss << "RECEIVE TEXT " << timeout;
00599    return execute(ss.str()).data;
00600 }
00601 
00619 char agi::record_file(const std::string& filename, const std::string& format, const std::string& escape_digits, int timeout, int silence, bool beep, long offset) const {
00620    ostringstream ss;
00621    ss << "RECORD FILE " << quote(filename) << " " << quote(format) << " " << quote(escape_digits) << " " << timeout;
00622 
00623    if (offset)
00624       ss << " " << offset;
00625 
00626    if (beep)
00627       ss << " BEEP";
00628 
00629    if (silence)
00630       ss << " s=" << silence;
00631 
00632    return execute(ss.str()).result;
00633 }
00634 
00642 char agi::say_alpha(const std::string& alphanum, const std::string& escape_digits) const {
00643    return execute("SAY ALPHA " + quote(alphanum) + " " + quote(escape_digits)).result;
00644 }
00645 
00653 char agi::say_alpha(int number, const std::string& escape_digits) const {
00654    ostringstream ss;
00655    ss << "SAY ALPHA " << number << " " << quote(escape_digits);
00656    return execute(ss.str()).result;
00657 }
00658 
00666 char agi::say_date(time_t date, const std::string& escape_digits) const {
00667    ostringstream ss;
00668    ss << "SAY DATE " << date << " " << quote(escape_digits);
00669    return execute(ss.str()).result;
00670 }
00671 
00686 char agi::say_datetime(time_t date, const std::string& escape_digits, const std::string& format, const std::string& timezone) const {
00687    ostringstream ss;
00688    ss << "SAY DATETIME " << date << " " << quote(escape_digits);
00689 
00690    if (not format.empty() or not timezone.empty())
00691       ss << " " << quote(format);
00692 
00693    if (not timezone.empty())
00694       ss << " " << quote(timezone);
00695 
00696    return execute(ss.str()).result;
00697 }
00698 
00706 char agi::say_digits(int digits, const std::string& escape_digits) const {
00707    ostringstream ss;
00708    ss << "SAY DIGITS " << digits << " " << quote(escape_digits);
00709    return execute(ss.str()).result;
00710 }
00711 
00719 char agi::say_number(int number, const std::string& escape_digits) const {
00720    ostringstream ss;
00721    ss << "SAY NUMBER " << number << " " << quote(escape_digits);
00722    return execute(ss.str()).result;
00723 }
00724 
00732 char agi::say_phonetic(const std::string& characters, const std::string& escape_digits) const {
00733    return execute("SAY PHONETIC " + quote(characters) + " " + quote(escape_digits)).result;
00734 }
00735 
00743 char agi::say_time(time_t time, const std::string& escape_digits) const {
00744    ostringstream ss;
00745    ss << "SAY TIME " << time << " " << quote(escape_digits);
00746    return execute(ss.str()).result;
00747 }
00748 
00757 agi& agi::send_image(const std::string& image) {
00758    execute("SEND IMAGE " + quote(image));
00759    return *this;
00760 }
00761 
00770 agi& agi::send_text(const std::string& text) {
00771    execute("SEND TEXT " + quote(text));
00772    return *this;
00773 }
00774 
00782 agi& agi::set_autohangup(int delay) {
00783    ostringstream ss;
00784    ss << "SET AUTOHANGUP " << delay;
00785    execute(ss.str());
00786    return *this;
00787 }
00788 
00795 agi& agi::set_callerid(int number) {
00796    ostringstream ss;
00797    ss << "SET CALLERID " << number;
00798    execute(ss.str());
00799    return *this;
00800 }
00801 
00809 agi& agi::set_callerid(const std::string& cid) {
00810    execute("SET CALLERID " + quote(cid));
00811    return *this;
00812 }
00813 
00822 agi& agi::set_context(const std::string& context) {
00823    execute("SET CONTEXT " + quote(context));
00824    return *this;
00825 }
00826 
00835 agi& agi::set_extension(int extension) {
00836    ostringstream ss;
00837    ss << "SET EXTENSION " << extension;
00838    execute(ss.str());
00839    return *this;
00840 }
00841 
00850 agi& agi::set_extension(const std::string& extension) {
00851    execute("SET EXTENSION " + quote(extension));
00852    return *this;
00853 }
00854 
00862 agi& agi::set_music(bool enable, const std::string& music_class) {
00863    ostringstream ss;
00864    ss << "SET MUSIC ";
00865 
00866    if (enable)
00867       ss << "ON";
00868    else
00869       ss << "OFF";
00870 
00871    if (not music_class.empty())
00872       ss << " " << quote(music_class);
00873 
00874    execute(ss.str());
00875    return *this;
00876 }
00877 
00886 agi& agi::set_priority(int priority) {
00887    ostringstream ss;
00888    ss << "SET PRIORITY " << priority;
00889    execute(ss.str());
00890    return *this;
00891 }
00892 
00901 agi& agi::set_priority(const std::string& priority) {
00902    execute("SET PRIORITY " + quote(priority));
00903    return *this;
00904 }
00905 
00913 agi& agi::set_variable(const std::string& variable, const std::string& value) {
00914    execute("SET VARIABLE " + quote(variable) + " " + quote(value));
00915    return *this;
00916 }
00917 
00926 char agi::stream_file(const std::string& filename, const std::string& escape_digits, long offset) const {
00927    ostringstream ss;
00928    ss << "STREAM FILE " << quote(filename) << " " << quote(escape_digits);
00929 
00930    if (offset)
00931       ss << " " << offset;
00932 
00933    return execute(ss.str()).result;
00934 }
00935 
00942 agi& agi::tdd_mode(bool enable) {
00943    if (enable)
00944       execute("TDD MODE ON");
00945    else
00946       execute("TDD MODE OFF");
00947    return *this;
00948 }
00949 
00958 agi& agi::tdd_mode(const std::string& mode) {
00959    execute("TDD MODE " + quote(mode));
00960    return *this;
00961 }
00962 
00973 agi& agi::verbose(const std::string& text, int level) {
00974    string::size_type end_pos = 0;
00975    string::size_type start_pos = 0;
00976    while (end_pos != string::npos) {
00977       end_pos = text.find('\n', start_pos);
00978 
00979       ostringstream ss;
00980       ss << "VERBOSE "
00981          << quote(text.substr(start_pos, end_pos == string::npos ? string::npos : end_pos - start_pos))
00982          << " " << level;
00983       execute(ss.str());
00984 
00985       start_pos = end_pos + 1;
00986    }
00987    
00988    return *this;
00989 }
00990 
00998 char agi::wait_for_digit(long timeout) const {
00999    ostringstream ss;
01000    ss << "WAIT FOR DIGIT " << timeout;
01001    return execute(ss.str()).result;
01002 }
01003 
01004 /* @} end of agi Commands group */
01005 

Generated on Thu Jul 3 01:32:42 2008 for Astxx by  doxygen 1.5.6