Sat Apr 26 2014 22:01:33

Asterisk developer's documentation


chan_skinny.c
Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * chan_skinny was developed by Jeremy McNamara & Florian Overkamp
00007  * chan_skinny was heavily modified/fixed by North Antara
00008  *
00009  * See http://www.asterisk.org for more information about
00010  * the Asterisk project. Please do not directly contact
00011  * any of the maintainers of this project for assistance;
00012  * the project provides a web site, mailing lists and IRC
00013  * channels for your use.
00014  *
00015  * This program is free software, distributed under the terms of
00016  * the GNU General Public License Version 2. See the LICENSE file
00017  * at the top of the source tree.
00018  */
00019 
00020 /*! \file
00021  *
00022  * \brief Implementation of the Skinny protocol
00023  *
00024  * \author Jeremy McNamara & Florian Overkamp & North Antara
00025  * \ingroup channel_drivers
00026  */
00027 
00028 /*** MODULEINFO
00029    <support_level>extended</support_level>
00030  ***/
00031 
00032 #include "asterisk.h"
00033 
00034 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 380942 $")
00035 
00036 #include <sys/socket.h>
00037 #include <netinet/in.h>
00038 #include <netinet/tcp.h>
00039 #include <sys/ioctl.h>
00040 #include <net/if.h>
00041 #include <fcntl.h>
00042 #include <netdb.h>
00043 #include <arpa/inet.h>
00044 #include <sys/signal.h>
00045 #include <signal.h>
00046 #include <ctype.h>
00047 
00048 #include "asterisk/lock.h"
00049 #include "asterisk/channel.h"
00050 #include "asterisk/config.h"
00051 #include "asterisk/module.h"
00052 #include "asterisk/pbx.h"
00053 #include "asterisk/sched.h"
00054 #include "asterisk/io.h"
00055 #include "asterisk/rtp_engine.h"
00056 #include "asterisk/netsock2.h"
00057 #include "asterisk/acl.h"
00058 #include "asterisk/callerid.h"
00059 #include "asterisk/cli.h"
00060 #include "asterisk/manager.h"
00061 #include "asterisk/say.h"
00062 #include "asterisk/cdr.h"
00063 #include "asterisk/astdb.h"
00064 #include "asterisk/features.h"
00065 #include "asterisk/app.h"
00066 #include "asterisk/musiconhold.h"
00067 #include "asterisk/utils.h"
00068 #include "asterisk/dsp.h"
00069 #include "asterisk/stringfields.h"
00070 #include "asterisk/abstract_jb.h"
00071 #include "asterisk/threadstorage.h"
00072 #include "asterisk/devicestate.h"
00073 #include "asterisk/event.h"
00074 #include "asterisk/indications.h"
00075 #include "asterisk/linkedlists.h"
00076 
00077 /*** DOCUMENTATION
00078    <manager name="SKINNYdevices" language="en_US">
00079       <synopsis>
00080          List SKINNY devices (text format).
00081       </synopsis>
00082       <syntax>
00083          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00084       </syntax>
00085       <description>
00086          <para>Lists Skinny devices in text format with details on current status.
00087          Devicelist will follow as separate events, followed by a final event called
00088          DevicelistComplete.</para>
00089       </description>
00090    </manager>
00091    <manager name="SKINNYshowdevice" language="en_US">
00092       <synopsis>
00093          Show SKINNY device (text format).
00094       </synopsis>
00095       <syntax>
00096          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00097          <parameter name="Device" required="true">
00098             <para>The device name you want to check.</para>
00099          </parameter>
00100       </syntax>
00101       <description>
00102          <para>Show one SKINNY device with details on current status.</para>
00103       </description>
00104    </manager>
00105    <manager name="SKINNYlines" language="en_US">
00106       <synopsis>
00107          List SKINNY lines (text format).
00108       </synopsis>
00109       <syntax>
00110          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00111       </syntax>
00112       <description>
00113          <para>Lists Skinny lines in text format with details on current status.
00114          Linelist will follow as separate events, followed by a final event called
00115          LinelistComplete.</para>
00116       </description>
00117    </manager>
00118    <manager name="SKINNYshowline" language="en_US">
00119       <synopsis>
00120          Show SKINNY line (text format).
00121       </synopsis>
00122       <syntax>
00123          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00124          <parameter name="Line" required="true">
00125             <para>The line name you want to check.</para>
00126          </parameter>
00127       </syntax>
00128       <description>
00129          <para>Show one SKINNY line with details on current status.</para>
00130       </description>
00131    </manager>
00132  ***/
00133 
00134 /* Skinny debugging only available if asterisk configured with --enable-dev-mode */
00135 #ifdef AST_DEVMODE
00136 static int skinnydebug = 0;
00137 char dbgcli_buf[256];
00138 char dbgreg_buf[256];
00139 char dbgsub_buf[256];
00140 #define DEBUG_GENERAL   (1 << 1)
00141 #define DEBUG_SUB (1 << 2)
00142 #define DEBUG_PACKET (1 << 3)
00143 #define DEBUG_AUDIO  (1 << 4)
00144 #define DEBUG_LOCK   (1 << 5)
00145 #define DEBUG_TEMPLATE  (1 << 6)
00146 #define DEBUG_THREAD (1 << 7)
00147 #define DEBUG_HINT   (1 << 8)
00148 #define SKINNY_DEBUG(type, verb_level, text, ...)                 \
00149    do{                                 \
00150       if (skinnydebug & (type)) {                     \
00151          ast_verb(verb_level, "[%d] " text, ast_get_tid(), ##__VA_ARGS__); \
00152       }                             \
00153    }while(0)
00154 #else
00155 #define SKINNY_DEBUG(type, verb_level, text, ...)
00156 #endif
00157 
00158 /*************************************
00159  * Skinny/Asterisk Protocol Settings *
00160  *************************************/
00161 static const char tdesc[] = "Skinny Client Control Protocol (Skinny)";
00162 static const char config[] = "skinny.conf";
00163 
00164 static struct ast_format_cap *default_cap;
00165 static struct ast_codec_pref default_prefs;
00166 
00167 enum skinny_codecs {
00168    SKINNY_CODEC_ALAW = 2,
00169    SKINNY_CODEC_ULAW = 4,
00170    SKINNY_CODEC_G723_1 = 9,
00171    SKINNY_CODEC_G729A = 12,
00172    SKINNY_CODEC_G726_32 = 82, /* XXX Which packing order does this translate to? */
00173    SKINNY_CODEC_H261 = 100,
00174    SKINNY_CODEC_H263 = 101
00175 };
00176 
00177 #define DEFAULT_SKINNY_PORT 2000
00178 #define DEFAULT_SKINNY_BACKLOG 2
00179 #define SKINNY_MAX_PACKET 2000
00180 #define DEFAULT_AUTH_TIMEOUT 30
00181 #define DEFAULT_AUTH_LIMIT 50
00182 
00183 static struct {
00184    unsigned int tos;
00185    unsigned int tos_audio;
00186    unsigned int tos_video;
00187    unsigned int cos;
00188    unsigned int cos_audio;
00189    unsigned int cos_video;
00190 } qos = { 0, 0, 0, 0, 0, 0 };
00191 
00192 static int keep_alive = 120;
00193 static int auth_timeout = DEFAULT_AUTH_TIMEOUT;
00194 static int auth_limit = DEFAULT_AUTH_LIMIT;
00195 static int unauth_sessions = 0;
00196 static char vmexten[AST_MAX_EXTENSION];      /* Voicemail pilot number */
00197 static char used_context[AST_MAX_EXTENSION]; /* placeholder to check if context are already used in regcontext */
00198 static char regcontext[AST_MAX_CONTEXT];     /* Context for auto-extension */
00199 static char date_format[6] = "D-M-Y";
00200 static char version_id[16] = "P002F202";
00201 
00202 #if __BYTE_ORDER == __LITTLE_ENDIAN
00203 #define letohl(x) (x)
00204 #define letohs(x) (x)
00205 #define htolel(x) (x)
00206 #define htoles(x) (x)
00207 #else
00208 #if defined(HAVE_BYTESWAP_H)
00209 #include <byteswap.h>
00210 #define letohl(x) bswap_32(x)
00211 #define letohs(x) bswap_16(x)
00212 #define htolel(x) bswap_32(x)
00213 #define htoles(x) bswap_16(x)
00214 #elif defined(HAVE_SYS_ENDIAN_SWAP16)
00215 #include <sys/endian.h>
00216 #define letohl(x) __swap32(x)
00217 #define letohs(x) __swap16(x)
00218 #define htolel(x) __swap32(x)
00219 #define htoles(x) __swap16(x)
00220 #elif defined(HAVE_SYS_ENDIAN_BSWAP16)
00221 #include <sys/endian.h>
00222 #define letohl(x) bswap32(x)
00223 #define letohs(x) bswap16(x)
00224 #define htolel(x) bswap32(x)
00225 #define htoles(x) bswap16(x)
00226 #else
00227 #define __bswap_16(x) \
00228    ((((x) & 0xff00) >> 8) | \
00229     (((x) & 0x00ff) << 8))
00230 #define __bswap_32(x) \
00231    ((((x) & 0xff000000) >> 24) | \
00232     (((x) & 0x00ff0000) >>  8) | \
00233     (((x) & 0x0000ff00) <<  8) | \
00234     (((x) & 0x000000ff) << 24))
00235 #define letohl(x) __bswap_32(x)
00236 #define letohs(x) __bswap_16(x)
00237 #define htolel(x) __bswap_32(x)
00238 #define htoles(x) __bswap_16(x)
00239 #endif
00240 #endif
00241 
00242 /*! Global jitterbuffer configuration - by default, jb is disabled
00243  *  \note Values shown here match the defaults shown in skinny.conf.sample */
00244 static struct ast_jb_conf default_jbconf =
00245 {
00246    .flags = 0,
00247    .max_size = 200,
00248    .resync_threshold = 1000,
00249    .impl = "fixed",
00250    .target_extra = 40,
00251 };
00252 static struct ast_jb_conf global_jbconf;
00253 
00254 #ifdef AST_DEVMODE
00255 AST_THREADSTORAGE(message2str_threadbuf);
00256 #define MESSAGE2STR_BUFSIZE   35
00257 #endif
00258 
00259 AST_THREADSTORAGE(device2str_threadbuf);
00260 #define DEVICE2STR_BUFSIZE   15
00261 
00262 AST_THREADSTORAGE(control2str_threadbuf);
00263 #define CONTROL2STR_BUFSIZE   100
00264 
00265 AST_THREADSTORAGE(substate2str_threadbuf);
00266 #define SUBSTATE2STR_BUFSIZE   15
00267 
00268 AST_THREADSTORAGE(callstate2str_threadbuf);
00269 #define CALLSTATE2STR_BUFSIZE   15
00270 
00271 /*********************
00272  * Protocol Messages *
00273  *********************/
00274 /* message types */
00275 #define KEEP_ALIVE_MESSAGE 0x0000
00276 /* no additional struct */
00277 
00278 #define REGISTER_MESSAGE 0x0001
00279 struct register_message {
00280    char name[16];
00281    uint32_t userId;
00282    uint32_t instance;
00283    uint32_t ip;
00284    uint32_t type;
00285    uint32_t maxStreams;
00286    uint32_t space;
00287    uint8_t protocolVersion;
00288    /*! \brief space2 is used for newer version of skinny */
00289    char space2[3];
00290 };
00291 
00292 #define IP_PORT_MESSAGE 0x0002
00293 
00294 #define KEYPAD_BUTTON_MESSAGE 0x0003
00295 struct keypad_button_message {
00296    uint32_t button;
00297    uint32_t lineInstance;
00298    uint32_t callReference;
00299 };
00300 
00301 
00302 #define ENBLOC_CALL_MESSAGE 0x0004
00303 struct enbloc_call_message {
00304    char calledParty[24];
00305 };
00306 
00307 #define STIMULUS_MESSAGE 0x0005
00308 struct stimulus_message {
00309    uint32_t stimulus;
00310    uint32_t stimulusInstance;
00311    uint32_t callreference;
00312 };
00313 
00314 #define OFFHOOK_MESSAGE 0x0006
00315 struct offhook_message {
00316    uint32_t instance;
00317    uint32_t reference;
00318 };
00319 
00320 #define ONHOOK_MESSAGE 0x0007
00321 struct onhook_message {
00322    uint32_t instance;
00323    uint32_t reference;
00324 };
00325 
00326 #define CAPABILITIES_RES_MESSAGE 0x0010
00327 struct station_capabilities {
00328    uint32_t codec; /* skinny codec, not ast codec */
00329    uint32_t frames;
00330    union {
00331       char res[8];
00332       uint32_t rate;
00333    } payloads;
00334 };
00335 
00336 #define SKINNY_MAX_CAPABILITIES 18
00337 
00338 struct capabilities_res_message {
00339    uint32_t count;
00340    struct station_capabilities caps[SKINNY_MAX_CAPABILITIES];
00341 };
00342 
00343 #define SPEED_DIAL_STAT_REQ_MESSAGE 0x000A
00344 struct speed_dial_stat_req_message {
00345    uint32_t speedDialNumber;
00346 };
00347 
00348 #define LINE_STATE_REQ_MESSAGE 0x000B
00349 struct line_state_req_message {
00350    uint32_t lineNumber;
00351 };
00352 
00353 #define TIME_DATE_REQ_MESSAGE 0x000D
00354 #define BUTTON_TEMPLATE_REQ_MESSAGE 0x000E
00355 #define VERSION_REQ_MESSAGE 0x000F
00356 #define SERVER_REQUEST_MESSAGE 0x0012
00357 
00358 #define ALARM_MESSAGE 0x0020
00359 struct alarm_message {
00360    uint32_t alarmSeverity;
00361    char displayMessage[80];
00362    uint32_t alarmParam1;
00363    uint32_t alarmParam2;
00364 };
00365 
00366 #define OPEN_RECEIVE_CHANNEL_ACK_MESSAGE 0x0022
00367 struct open_receive_channel_ack_message_ip4 {
00368    uint32_t status;
00369    uint32_t ipAddr;
00370    uint32_t port;
00371    uint32_t callReference;
00372 };
00373 struct open_receive_channel_ack_message_ip6 {
00374    uint32_t status;
00375    uint32_t space;
00376    char ipAddr[16];
00377    uint32_t port;
00378    uint32_t callReference;
00379 };
00380 
00381 #define SOFT_KEY_SET_REQ_MESSAGE 0x0025
00382 
00383 #define SOFT_KEY_EVENT_MESSAGE 0x0026
00384 struct soft_key_event_message {
00385    uint32_t softKeyEvent;
00386    uint32_t instance;
00387    uint32_t callreference;
00388 };
00389 
00390 #define UNREGISTER_MESSAGE 0x0027
00391 #define SOFT_KEY_TEMPLATE_REQ_MESSAGE 0x0028
00392 #define HEADSET_STATUS_MESSAGE 0x002B
00393 #define REGISTER_AVAILABLE_LINES_MESSAGE 0x002D
00394 
00395 #define REGISTER_ACK_MESSAGE 0x0081
00396 struct register_ack_message {
00397    uint32_t keepAlive;
00398    char dateTemplate[6];
00399    char res[2];
00400    uint32_t secondaryKeepAlive;
00401    char res2[4];
00402 };
00403 
00404 #define START_TONE_MESSAGE 0x0082
00405 struct start_tone_message {
00406    uint32_t tone;
00407    uint32_t space;
00408    uint32_t instance;
00409    uint32_t reference;
00410 };
00411 
00412 #define STOP_TONE_MESSAGE 0x0083
00413 struct stop_tone_message {
00414    uint32_t instance;
00415    uint32_t reference;
00416    uint32_t space;
00417 };
00418 
00419 #define SET_RINGER_MESSAGE 0x0085
00420 struct set_ringer_message {
00421    uint32_t ringerMode;
00422    uint32_t unknown1; /* See notes in transmit_ringer_mode */
00423    uint32_t unknown2;
00424    uint32_t space[2];
00425 };
00426 
00427 #define SET_LAMP_MESSAGE 0x0086
00428 struct set_lamp_message {
00429    uint32_t stimulus;
00430    uint32_t stimulusInstance;
00431    uint32_t deviceStimulus;
00432 };
00433 
00434 #define SET_SPEAKER_MESSAGE 0x0088
00435 struct set_speaker_message {
00436    uint32_t mode;
00437 };
00438 
00439 /* XXX When do we need to use this? */
00440 #define SET_MICROPHONE_MESSAGE 0x0089
00441 struct set_microphone_message {
00442    uint32_t mode;
00443 };
00444 
00445 #define START_MEDIA_TRANSMISSION_MESSAGE 0x008A
00446 struct media_qualifier {
00447    uint32_t precedence;
00448    uint32_t vad;
00449    uint32_t packets;
00450    uint32_t bitRate;
00451 };
00452 
00453 struct start_media_transmission_message_ip4 {
00454    uint32_t conferenceId;
00455    uint32_t passThruPartyId;
00456    uint32_t remoteIp;
00457    uint32_t remotePort;
00458    uint32_t packetSize;
00459    uint32_t payloadType;
00460    struct media_qualifier qualifier;
00461    uint32_t space[19];
00462 };
00463 
00464 struct start_media_transmission_message_ip6 {
00465    uint32_t conferenceId;
00466    uint32_t passThruPartyId;
00467    uint32_t space;
00468    char remoteIp[16];
00469    uint32_t remotePort;
00470    uint32_t packetSize;
00471    uint32_t payloadType;
00472    struct media_qualifier qualifier;
00473    /*! \brief space2 is used for newer version of skinny */
00474    uint32_t space2[19];
00475 };
00476 
00477 #define STOP_MEDIA_TRANSMISSION_MESSAGE 0x008B
00478 struct stop_media_transmission_message {
00479    uint32_t conferenceId;
00480    uint32_t passThruPartyId;
00481    uint32_t space[3];
00482 };
00483 
00484 #define CALL_INFO_MESSAGE 0x008F
00485 struct call_info_message {
00486    char callingPartyName[40];
00487    char callingParty[24];
00488    char calledPartyName[40];
00489    char calledParty[24];
00490    uint32_t instance;
00491    uint32_t reference;
00492    uint32_t type;
00493    char originalCalledPartyName[40];
00494    char originalCalledParty[24];
00495    char lastRedirectingPartyName[40];
00496    char lastRedirectingParty[24];
00497    uint32_t originalCalledPartyRedirectReason;
00498    uint32_t lastRedirectingReason;
00499    char callingPartyVoiceMailbox[24];
00500    char calledPartyVoiceMailbox[24];
00501    char originalCalledPartyVoiceMailbox[24];
00502    char lastRedirectingVoiceMailbox[24];
00503    uint32_t space[3];
00504 };
00505 
00506 #define FORWARD_STAT_MESSAGE 0x0090
00507 struct forward_stat_message {
00508    uint32_t activeforward;
00509    uint32_t lineNumber;
00510    uint32_t fwdall;
00511    char fwdallnum[24];
00512    uint32_t fwdbusy;
00513    char fwdbusynum[24];
00514    uint32_t fwdnoanswer;
00515    char fwdnoanswernum[24];
00516 };
00517 
00518 #define SPEED_DIAL_STAT_RES_MESSAGE 0x0091
00519 struct speed_dial_stat_res_message {
00520    uint32_t speedDialNumber;
00521    char speedDialDirNumber[24];
00522    char speedDialDisplayName[40];
00523 };
00524 
00525 #define LINE_STAT_RES_MESSAGE 0x0092
00526 struct line_stat_res_message {
00527    uint32_t lineNumber;
00528    char lineDirNumber[24];
00529    char lineDisplayName[24];
00530    uint32_t space[15];
00531 };
00532 
00533 #define DEFINETIMEDATE_MESSAGE 0x0094
00534 struct definetimedate_message {
00535    uint32_t year; /* since 1900 */
00536    uint32_t month;
00537    uint32_t dayofweek; /* monday = 1 */
00538    uint32_t day;
00539    uint32_t hour;
00540    uint32_t minute;
00541    uint32_t seconds;
00542    uint32_t milliseconds;
00543    uint32_t timestamp;
00544 };
00545 
00546 #define BUTTON_TEMPLATE_RES_MESSAGE 0x0097
00547 struct button_definition {
00548    uint8_t instanceNumber;
00549    uint8_t buttonDefinition;
00550 };
00551 
00552 struct button_definition_template {
00553    uint8_t buttonDefinition;
00554    /* for now, anything between 0xB0 and 0xCF is custom */
00555    /*int custom;*/
00556 };
00557 
00558 #define STIMULUS_REDIAL 0x01
00559 #define STIMULUS_SPEEDDIAL 0x02
00560 #define STIMULUS_HOLD 0x03
00561 #define STIMULUS_TRANSFER 0x04
00562 #define STIMULUS_FORWARDALL 0x05
00563 #define STIMULUS_FORWARDBUSY 0x06
00564 #define STIMULUS_FORWARDNOANSWER 0x07
00565 #define STIMULUS_DISPLAY 0x08
00566 #define STIMULUS_LINE 0x09
00567 #define STIMULUS_VOICEMAIL 0x0F
00568 #define STIMULUS_AUTOANSWER 0x11
00569 #define STIMULUS_DND 0x3F
00570 #define STIMULUS_CONFERENCE 0x7D
00571 #define STIMULUS_CALLPARK 0x7E
00572 #define STIMULUS_CALLPICKUP 0x7F
00573 #define STIMULUS_NONE 0xFF
00574 
00575 /* Button types */
00576 #define BT_REDIAL STIMULUS_REDIAL
00577 #define BT_SPEEDDIAL STIMULUS_SPEEDDIAL
00578 #define BT_HOLD STIMULUS_HOLD
00579 #define BT_TRANSFER STIMULUS_TRANSFER
00580 #define BT_FORWARDALL STIMULUS_FORWARDALL
00581 #define BT_FORWARDBUSY STIMULUS_FORWARDBUSY
00582 #define BT_FORWARDNOANSWER STIMULUS_FORWARDNOANSWER
00583 #define BT_DISPLAY STIMULUS_DISPLAY
00584 #define BT_LINE STIMULUS_LINE
00585 #define BT_VOICEMAIL STIMULUS_VOICEMAIL
00586 #define BT_AUTOANSWER STIMULUS_AUTOANSWER
00587 #define BT_DND STIMULUS_DND
00588 #define BT_CONFERENCE STIMULUS_CONFERENCE
00589 #define BT_CALLPARK STIMULUS_CALLPARK
00590 #define BT_CALLPICKUP STIMULUS_CALLPICKUP
00591 #define BT_NONE 0x00
00592 
00593 /* Custom button types - add our own between 0xB0 and 0xCF.
00594    This may need to be revised in the future,
00595    if stimuluses are ever added in this range. */
00596 #define BT_CUST_LINESPEEDDIAL 0xB0 /* line or speeddial with/without hint */
00597 #define BT_CUST_LINE 0xB1          /* line or speeddial with hint only */
00598 
00599 struct button_template_res_message {
00600    uint32_t buttonOffset;
00601    uint32_t buttonCount;
00602    uint32_t totalButtonCount;
00603    struct button_definition definition[42];
00604 };
00605 
00606 #define VERSION_RES_MESSAGE 0x0098
00607 struct version_res_message {
00608    char version[16];
00609 };
00610 
00611 #define DISPLAYTEXT_MESSAGE 0x0099
00612 struct displaytext_message {
00613    char text[40];
00614 };
00615 
00616 #define CLEAR_NOTIFY_MESSAGE  0x0115
00617 #define CLEAR_DISPLAY_MESSAGE 0x009A
00618 struct clear_display_message {
00619    uint32_t space;
00620 };
00621 
00622 #define CAPABILITIES_REQ_MESSAGE 0x009B
00623 
00624 #define REGISTER_REJ_MESSAGE 0x009D
00625 struct register_rej_message {
00626    char errMsg[33];
00627 };
00628 
00629 #define SERVER_RES_MESSAGE 0x009E
00630 struct server_identifier {
00631    char serverName[48];
00632 };
00633 
00634 struct server_res_message {
00635    struct server_identifier server[5];
00636    uint32_t serverListenPort[5];
00637    uint32_t serverIpAddr[5];
00638 };
00639 
00640 #define RESET_MESSAGE 0x009F
00641 struct reset_message {
00642    uint32_t resetType;
00643 };
00644 
00645 #define KEEP_ALIVE_ACK_MESSAGE 0x0100
00646 
00647 #define OPEN_RECEIVE_CHANNEL_MESSAGE 0x0105
00648 struct open_receive_channel_message {
00649    uint32_t conferenceId;
00650    uint32_t partyId;
00651    uint32_t packets;
00652    uint32_t capability;
00653    uint32_t echo;
00654    uint32_t bitrate;
00655    uint32_t space[36];
00656 };
00657 
00658 #define CLOSE_RECEIVE_CHANNEL_MESSAGE 0x0106
00659 struct close_receive_channel_message {
00660    uint32_t conferenceId;
00661    uint32_t partyId;
00662    uint32_t space[2];
00663 };
00664 
00665 #define SOFT_KEY_TEMPLATE_RES_MESSAGE 0x0108
00666 struct soft_key_template_definition {
00667    char softKeyLabel[16];
00668    uint32_t softKeyEvent;
00669 };
00670 
00671 #define BKSP_REQ_MESSAGE 0x0119
00672 struct bksp_req_message {
00673    uint32_t instance;
00674    uint32_t callreference;
00675 };
00676 
00677 #define KEYDEF_ONHOOK 0
00678 #define KEYDEF_CONNECTED 1
00679 #define KEYDEF_ONHOLD 2
00680 #define KEYDEF_RINGIN 3
00681 #define KEYDEF_OFFHOOK 4
00682 #define KEYDEF_CONNWITHTRANS 5
00683 #define KEYDEF_DADFD 6 /* Digits After Dialing First Digit */
00684 #define KEYDEF_CONNWITHCONF 7
00685 #define KEYDEF_RINGOUT 8
00686 #define KEYDEF_OFFHOOKWITHFEAT 9
00687 #define KEYDEF_UNKNOWN 10
00688 #define KEYDEF_SLAHOLD 11
00689 #define KEYDEF_SLACONNECTEDNOTACTIVE 12
00690 
00691 #define SOFTKEY_NONE 0x00
00692 #define SOFTKEY_REDIAL 0x01
00693 #define SOFTKEY_NEWCALL 0x02
00694 #define SOFTKEY_HOLD 0x03
00695 #define SOFTKEY_TRNSFER 0x04
00696 #define SOFTKEY_CFWDALL 0x05
00697 #define SOFTKEY_CFWDBUSY 0x06
00698 #define SOFTKEY_CFWDNOANSWER 0x07
00699 #define SOFTKEY_BKSPC 0x08
00700 #define SOFTKEY_ENDCALL 0x09
00701 #define SOFTKEY_RESUME 0x0A
00702 #define SOFTKEY_ANSWER 0x0B
00703 #define SOFTKEY_INFO 0x0C
00704 #define SOFTKEY_CONFRN 0x0D
00705 #define SOFTKEY_PARK 0x0E
00706 #define SOFTKEY_JOIN 0x0F
00707 #define SOFTKEY_MEETME 0x10
00708 #define SOFTKEY_PICKUP 0x11
00709 #define SOFTKEY_GPICKUP 0x12
00710 #define SOFTKEY_DND 0x13
00711 #define SOFTKEY_IDIVERT 0x14
00712 
00713 static struct soft_key_template_definition soft_key_template_default[] = {
00714    { "\200\001", SOFTKEY_REDIAL },
00715    { "\200\002", SOFTKEY_NEWCALL },
00716    { "\200\003", SOFTKEY_HOLD },
00717    { "\200\004", SOFTKEY_TRNSFER },
00718    { "\200\005", SOFTKEY_CFWDALL },
00719    { "\200\006", SOFTKEY_CFWDBUSY },
00720    { "\200\007", SOFTKEY_CFWDNOANSWER },
00721    { "\200\010", SOFTKEY_BKSPC },
00722    { "\200\011", SOFTKEY_ENDCALL },
00723    { "\200\012", SOFTKEY_RESUME },
00724    { "\200\013", SOFTKEY_ANSWER },
00725    { "\200\014", SOFTKEY_INFO },
00726    { "\200\015", SOFTKEY_CONFRN },
00727    { "\200\016", SOFTKEY_PARK },
00728    { "\200\017", SOFTKEY_JOIN },
00729    { "\200\020", SOFTKEY_MEETME },
00730    { "\200\021", SOFTKEY_PICKUP },
00731    { "\200\022", SOFTKEY_GPICKUP },
00732    { "\200\077", SOFTKEY_DND },
00733    { "\200\120", SOFTKEY_IDIVERT },
00734 };
00735 
00736 /* Localized message "codes" (in octal)
00737    Below is en_US (taken from a 7970)
00738 
00739    \200\xxx
00740        \000: ???
00741        \001: Redial
00742        \002: New Call
00743        \003: Hold
00744        \004: Transfer
00745        \005: CFwdALL
00746        \006: CFwdBusy
00747        \007: CFwdNoAnswer
00748        \010: <<
00749        \011: EndCall
00750        \012: Resume
00751        \013: Answer
00752        \014: Info
00753        \015: Confrn
00754        \016: Park
00755        \017: Join
00756        \020: MeetMe
00757        \021: PickUp
00758        \022: GPickUp
00759        \023: Your current options
00760        \024: Off Hook
00761        \025: On Hook
00762        \026: Ring out
00763        \027: From
00764        \030: Connected
00765        \031: Busy
00766        \032: Line In Use
00767        \033: Call Waiting
00768        \034: Call Transfer
00769        \035: Call Park
00770        \036: Call Proceed
00771        \037: In Use Remote
00772        \040: Enter number
00773        \041: Call park At
00774        \042: Primary Only
00775        \043: Temp Fail
00776        \044: You Have VoiceMail
00777        \045: Forwarded to
00778        \046: Can Not Complete Conference
00779        \047: No Conference Bridge
00780        \050: Can Not Hold Primary Control
00781        \051: Invalid Conference Participant
00782        \052: In Conference Already
00783        \053: No Participant Info
00784        \054: Exceed Maximum Parties
00785        \055: Key Is Not Active
00786        \056: Error No License
00787        \057: Error DBConfig
00788        \060: Error Database
00789        \061: Error Pass Limit
00790        \062: Error Unknown
00791        \063: Error Mismatch
00792        \064: Conference
00793        \065: Park Number
00794        \066: Private
00795        \067: Not Enough Bandwidth
00796        \070: Unknown Number
00797        \071: RmLstC
00798        \072: Voicemail
00799        \073: ImmDiv
00800        \074: Intrcpt
00801        \075: SetWtch
00802        \076: TrnsfVM
00803        \077: DND
00804        \100: DivAll
00805        \101: CallBack
00806        \102: Network congestion,rerouting
00807        \103: Barge
00808        \104: Failed to setup Barge
00809        \105: Another Barge exists
00810        \106: Incompatible device type
00811        \107: No Park Number Available
00812        \110: CallPark Reversion
00813        \111: Service is not Active
00814        \112: High Traffic Try Again Later
00815        \113: QRT
00816        \114: MCID
00817        \115: DirTrfr
00818        \116: Select
00819        \117: ConfList
00820        \120: iDivert
00821        \121: cBarge
00822        \122: Can Not Complete Transfer
00823        \123: Can Not Join Calls
00824        \124: Mcid Successful
00825        \125: Number Not Configured
00826        \126: Security Error
00827        \127: Video Bandwidth Unavailable
00828        \130: VidMode
00829        \131: Max Call Duration Timeout
00830        \132: Max Hold Duration Timeout
00831        \133: OPickUp
00832        \134: ???
00833        \135: ???
00834        \136: ???
00835        \137: ???
00836        \140: ???
00837        \141: External Transfer Restricted
00838        \142: ???
00839        \143: ???
00840        \144: ???
00841        \145: Mac Address
00842        \146: Host Name
00843        \147: Domain Name
00844        \150: IP Address
00845        \151: Subnet Mask
00846        \152: TFTP Server 1
00847        \153: Default Router 1
00848        \154: Default Router 2
00849        \155: Default Router 3
00850        \156: Default Router 4
00851        \157: Default Router 5
00852        \160: DNS Server 1
00853        \161: DNS Server 2
00854        \162: DNS Server 3
00855        \163: DNS Server 4
00856        \164: DNS Server 5
00857        \165: Operational VLAN Id
00858        \166: Admin. VLAN Id
00859        \167: CallManager 1
00860        \170: CallManager 2
00861        \171: CallManager 3
00862        \172: CallManager 4
00863        \173: CallManager 5
00864        \174: Information URL
00865        \175: Directories URL
00866        \176: Messages URL
00867        \177: Services URL
00868  */
00869 
00870 struct soft_key_definitions {
00871    const uint8_t mode;
00872    const uint8_t *defaults;
00873    const int count;
00874 };
00875 
00876 static const uint8_t soft_key_default_onhook[] = {
00877    SOFTKEY_REDIAL,
00878    SOFTKEY_NEWCALL,
00879    SOFTKEY_CFWDALL,
00880    SOFTKEY_CFWDBUSY,
00881    SOFTKEY_DND,
00882    /*SOFTKEY_GPICKUP,
00883    SOFTKEY_CONFRN,*/
00884 };
00885 
00886 static const uint8_t soft_key_default_connected[] = {
00887    SOFTKEY_HOLD,
00888    SOFTKEY_ENDCALL,
00889    SOFTKEY_TRNSFER,
00890    SOFTKEY_PARK,
00891    SOFTKEY_CFWDALL,
00892    SOFTKEY_CFWDBUSY,
00893 };
00894 
00895 static const uint8_t soft_key_default_onhold[] = {
00896    SOFTKEY_RESUME,
00897    SOFTKEY_NEWCALL,
00898    SOFTKEY_ENDCALL,
00899    SOFTKEY_TRNSFER,
00900 };
00901 
00902 static const uint8_t soft_key_default_ringin[] = {
00903    SOFTKEY_ANSWER,
00904    SOFTKEY_ENDCALL,
00905    SOFTKEY_TRNSFER,
00906 };
00907 
00908 static const uint8_t soft_key_default_offhook[] = {
00909    SOFTKEY_REDIAL,
00910    SOFTKEY_ENDCALL,
00911    SOFTKEY_CFWDALL,
00912    SOFTKEY_CFWDBUSY,
00913    /*SOFTKEY_GPICKUP,*/
00914 };
00915 
00916 static const uint8_t soft_key_default_connwithtrans[] = {
00917    SOFTKEY_HOLD,
00918    SOFTKEY_ENDCALL,
00919    SOFTKEY_TRNSFER,
00920    SOFTKEY_PARK,
00921    SOFTKEY_CFWDALL,
00922    SOFTKEY_CFWDBUSY,
00923 };
00924 
00925 static const uint8_t soft_key_default_dadfd[] = {
00926    SOFTKEY_BKSPC,
00927    SOFTKEY_ENDCALL,
00928 };
00929 
00930 static const uint8_t soft_key_default_connwithconf[] = {
00931    SOFTKEY_NONE,
00932 };
00933 
00934 static const uint8_t soft_key_default_ringout[] = {
00935    SOFTKEY_NONE,
00936    SOFTKEY_ENDCALL,
00937 };
00938 
00939 static const uint8_t soft_key_default_offhookwithfeat[] = {
00940    SOFTKEY_REDIAL,
00941    SOFTKEY_ENDCALL,
00942    SOFTKEY_TRNSFER,
00943 };
00944 
00945 static const uint8_t soft_key_default_unknown[] = {
00946    SOFTKEY_NONE,
00947 };
00948 
00949 static const uint8_t soft_key_default_SLAhold[] = {
00950    SOFTKEY_REDIAL,
00951    SOFTKEY_NEWCALL,
00952    SOFTKEY_RESUME,
00953 };
00954 
00955 static const uint8_t soft_key_default_SLAconnectednotactive[] = {
00956    SOFTKEY_REDIAL,
00957    SOFTKEY_NEWCALL,
00958    SOFTKEY_JOIN,
00959 };
00960 
00961 static const struct soft_key_definitions soft_key_default_definitions[] = {
00962    {KEYDEF_ONHOOK, soft_key_default_onhook, sizeof(soft_key_default_onhook) / sizeof(uint8_t)},
00963    {KEYDEF_CONNECTED, soft_key_default_connected, sizeof(soft_key_default_connected) / sizeof(uint8_t)},
00964    {KEYDEF_ONHOLD, soft_key_default_onhold, sizeof(soft_key_default_onhold) / sizeof(uint8_t)},
00965    {KEYDEF_RINGIN, soft_key_default_ringin, sizeof(soft_key_default_ringin) / sizeof(uint8_t)},
00966    {KEYDEF_OFFHOOK, soft_key_default_offhook, sizeof(soft_key_default_offhook) / sizeof(uint8_t)},
00967    {KEYDEF_CONNWITHTRANS, soft_key_default_connwithtrans, sizeof(soft_key_default_connwithtrans) / sizeof(uint8_t)},
00968    {KEYDEF_DADFD, soft_key_default_dadfd, sizeof(soft_key_default_dadfd) / sizeof(uint8_t)},
00969    {KEYDEF_CONNWITHCONF, soft_key_default_connwithconf, sizeof(soft_key_default_connwithconf) / sizeof(uint8_t)},
00970    {KEYDEF_RINGOUT, soft_key_default_ringout, sizeof(soft_key_default_ringout) / sizeof(uint8_t)},
00971    {KEYDEF_OFFHOOKWITHFEAT, soft_key_default_offhookwithfeat, sizeof(soft_key_default_offhookwithfeat) / sizeof(uint8_t)},
00972    {KEYDEF_UNKNOWN, soft_key_default_unknown, sizeof(soft_key_default_unknown) / sizeof(uint8_t)},
00973    {KEYDEF_SLAHOLD, soft_key_default_SLAhold, sizeof(soft_key_default_SLAhold) / sizeof(uint8_t)},
00974    {KEYDEF_SLACONNECTEDNOTACTIVE, soft_key_default_SLAconnectednotactive, sizeof(soft_key_default_SLAconnectednotactive) / sizeof(uint8_t)}
00975 };
00976 
00977 struct soft_key_template_res_message {
00978    uint32_t softKeyOffset;
00979    uint32_t softKeyCount;
00980    uint32_t totalSoftKeyCount;
00981    struct soft_key_template_definition softKeyTemplateDefinition[32];
00982 };
00983 
00984 #define SOFT_KEY_SET_RES_MESSAGE 0x0109
00985 
00986 struct soft_key_set_definition {
00987    uint8_t softKeyTemplateIndex[16];
00988    uint16_t softKeyInfoIndex[16];
00989 };
00990 
00991 struct soft_key_set_res_message {
00992    uint32_t softKeySetOffset;
00993    uint32_t softKeySetCount;
00994    uint32_t totalSoftKeySetCount;
00995    struct soft_key_set_definition softKeySetDefinition[16];
00996    uint32_t res;
00997 };
00998 
00999 #define SELECT_SOFT_KEYS_MESSAGE 0x0110
01000 struct select_soft_keys_message {
01001    uint32_t instance;
01002    uint32_t reference;
01003    uint32_t softKeySetIndex;
01004    uint32_t validKeyMask;
01005 };
01006 
01007 #define CALL_STATE_MESSAGE 0x0111
01008 struct call_state_message {
01009    uint32_t callState;
01010    uint32_t lineInstance;
01011    uint32_t callReference;
01012    uint32_t space[3];
01013 };
01014 
01015 #define DISPLAY_PROMPT_STATUS_MESSAGE 0x0112
01016 struct display_prompt_status_message {
01017    uint32_t messageTimeout;
01018    char promptMessage[32];
01019    uint32_t lineInstance;
01020    uint32_t callReference;
01021    uint32_t space[3];
01022 };
01023 
01024 #define CLEAR_PROMPT_MESSAGE  0x0113
01025 struct clear_prompt_message {
01026    uint32_t lineInstance;
01027    uint32_t callReference;
01028 };
01029 
01030 #define DISPLAY_NOTIFY_MESSAGE 0x0114
01031 struct display_notify_message {
01032    uint32_t displayTimeout;
01033    char displayMessage[100];
01034 };
01035 
01036 #define ACTIVATE_CALL_PLANE_MESSAGE 0x0116
01037 struct activate_call_plane_message {
01038    uint32_t lineInstance;
01039 };
01040 
01041 #define DIALED_NUMBER_MESSAGE 0x011D
01042 struct dialed_number_message {
01043    char dialedNumber[24];
01044    uint32_t lineInstance;
01045    uint32_t callReference;
01046 };
01047 
01048 union skinny_data {
01049    struct alarm_message alarm;
01050    struct speed_dial_stat_req_message speeddialreq;
01051    struct register_message reg;
01052    struct register_ack_message regack;
01053    struct register_rej_message regrej;
01054    struct capabilities_res_message caps;
01055    struct version_res_message version;
01056    struct button_template_res_message buttontemplate;
01057    struct displaytext_message displaytext;
01058    struct clear_display_message cleardisplay;
01059    struct display_prompt_status_message displaypromptstatus;
01060    struct clear_prompt_message clearpromptstatus;
01061    struct definetimedate_message definetimedate;
01062    struct start_tone_message starttone;
01063    struct stop_tone_message stoptone;
01064    struct speed_dial_stat_res_message speeddial;
01065    struct line_state_req_message line;
01066    struct line_stat_res_message linestat;
01067    struct soft_key_set_res_message softkeysets;
01068    struct soft_key_template_res_message softkeytemplate;
01069    struct server_res_message serverres;
01070    struct reset_message reset;
01071    struct set_lamp_message setlamp;
01072    struct set_ringer_message setringer;
01073    struct call_state_message callstate;
01074    struct keypad_button_message keypad;
01075    struct select_soft_keys_message selectsoftkey;
01076    struct activate_call_plane_message activatecallplane;
01077    struct stimulus_message stimulus;
01078    struct offhook_message offhook;
01079    struct onhook_message onhook;
01080    struct set_speaker_message setspeaker;
01081    struct set_microphone_message setmicrophone;
01082    struct call_info_message callinfo;
01083    struct start_media_transmission_message_ip4 startmedia_ip4;
01084    struct start_media_transmission_message_ip6 startmedia_ip6;
01085    struct stop_media_transmission_message stopmedia;
01086    struct open_receive_channel_message openreceivechannel;
01087    struct open_receive_channel_ack_message_ip4 openreceivechannelack_ip4;
01088    struct open_receive_channel_ack_message_ip6 openreceivechannelack_ip6;
01089    struct close_receive_channel_message closereceivechannel;
01090    struct display_notify_message displaynotify;
01091    struct dialed_number_message dialednumber;
01092    struct soft_key_event_message softkeyeventmessage;
01093    struct enbloc_call_message enbloccallmessage;
01094    struct forward_stat_message forwardstat;
01095    struct bksp_req_message bkspmessage;
01096 };
01097 
01098 /* packet composition */
01099 struct skinny_req {
01100    int len;
01101    int res;
01102    int e;
01103    union skinny_data data;
01104 };
01105 
01106 /* XXX This is the combined size of the variables above.  (len, res, e)
01107    If more are added, this MUST change.
01108    (sizeof(skinny_req) - sizeof(skinny_data)) DOES NOT WORK on all systems (amd64?). */
01109 static int skinny_header_size = 12;
01110 
01111 /*****************************
01112  * Asterisk specific globals *
01113  *****************************/
01114 
01115 static int skinnyreload = 0;
01116 
01117 /* a hostname, portnumber, socket and such is usefull for VoIP protocols */
01118 static struct sockaddr_in bindaddr;
01119 static char ourhost[256];
01120 static int ourport;
01121 static struct in_addr __ourip;
01122 static struct ast_hostent ahp;
01123 static struct hostent *hp;
01124 static int skinnysock = -1;
01125 static pthread_t accept_t;
01126 static int callnums = 1;
01127 
01128 #define SKINNY_DEVICE_UNKNOWN -1
01129 #define SKINNY_DEVICE_NONE 0
01130 #define SKINNY_DEVICE_30SPPLUS 1
01131 #define SKINNY_DEVICE_12SPPLUS 2
01132 #define SKINNY_DEVICE_12SP 3
01133 #define SKINNY_DEVICE_12 4
01134 #define SKINNY_DEVICE_30VIP 5
01135 #define SKINNY_DEVICE_7910 6
01136 #define SKINNY_DEVICE_7960 7
01137 #define SKINNY_DEVICE_7940 8
01138 #define SKINNY_DEVICE_7935 9
01139 #define SKINNY_DEVICE_ATA186 12 /* Cisco ATA-186 */
01140 #define SKINNY_DEVICE_7941 115
01141 #define SKINNY_DEVICE_7971 119
01142 #define SKINNY_DEVICE_7914 124 /* Expansion module */
01143 #define SKINNY_DEVICE_7985 302
01144 #define SKINNY_DEVICE_7911 307
01145 #define SKINNY_DEVICE_7961GE 308
01146 #define SKINNY_DEVICE_7941GE 309
01147 #define SKINNY_DEVICE_7931 348
01148 #define SKINNY_DEVICE_7921 365
01149 #define SKINNY_DEVICE_7906 369
01150 #define SKINNY_DEVICE_7962 404 /* Not found */
01151 #define SKINNY_DEVICE_7937 431
01152 #define SKINNY_DEVICE_7942 434
01153 #define SKINNY_DEVICE_7945 435
01154 #define SKINNY_DEVICE_7965 436
01155 #define SKINNY_DEVICE_7975 437
01156 #define SKINNY_DEVICE_7905 20000
01157 #define SKINNY_DEVICE_7920 30002
01158 #define SKINNY_DEVICE_7970 30006
01159 #define SKINNY_DEVICE_7912 30007
01160 #define SKINNY_DEVICE_7902 30008
01161 #define SKINNY_DEVICE_CIPC 30016 /* Cisco IP Communicator */
01162 #define SKINNY_DEVICE_7961 30018
01163 #define SKINNY_DEVICE_7936 30019
01164 #define SKINNY_DEVICE_SCCPGATEWAY_AN 30027 /* Analog gateway */
01165 #define SKINNY_DEVICE_SCCPGATEWAY_BRI 30028 /* BRI gateway */
01166 
01167 #define SKINNY_SPEAKERON 1
01168 #define SKINNY_SPEAKEROFF 2
01169 
01170 #define SKINNY_MICON 1
01171 #define SKINNY_MICOFF 2
01172 
01173 #define SKINNY_OFFHOOK 1
01174 #define SKINNY_ONHOOK 2
01175 #define SKINNY_RINGOUT 3
01176 #define SKINNY_RINGIN 4
01177 #define SKINNY_CONNECTED 5
01178 #define SKINNY_BUSY 6
01179 #define SKINNY_CONGESTION 7
01180 #define SKINNY_HOLD 8
01181 #define SKINNY_CALLWAIT 9
01182 #define SKINNY_TRANSFER 10
01183 #define SKINNY_PARK 11
01184 #define SKINNY_PROGRESS 12
01185 #define SKINNY_CALLREMOTEMULTILINE 13
01186 #define SKINNY_INVALID 14
01187 
01188 #define SKINNY_INCOMING 1
01189 #define SKINNY_OUTGOING 2
01190 
01191 #define SKINNY_SILENCE 0x00      /* Note sure this is part of the protocol, remove? */
01192 #define SKINNY_DIALTONE 0x21
01193 #define SKINNY_BUSYTONE 0x23
01194 #define SKINNY_ALERT 0x24
01195 #define SKINNY_REORDER 0x25
01196 #define SKINNY_CALLWAITTONE 0x2D
01197 #define SKINNY_ZIPZIP 0x31
01198 #define SKINNY_ZIP 0x32
01199 #define SKINNY_BEEPBONK 0x33
01200 #define SKINNY_BARGIN 0x43
01201 #define SKINNY_NOTONE 0x7F
01202 
01203 #define SKINNY_LAMP_OFF 1
01204 #define SKINNY_LAMP_ON 2
01205 #define SKINNY_LAMP_WINK 3
01206 #define SKINNY_LAMP_FLASH 4
01207 #define SKINNY_LAMP_BLINK 5
01208 
01209 #define SKINNY_RING_OFF 1
01210 #define SKINNY_RING_INSIDE 2
01211 #define SKINNY_RING_OUTSIDE 3
01212 #define SKINNY_RING_FEATURE 4
01213 
01214 #define SKINNY_CFWD_ALL       (1 << 0)
01215 #define SKINNY_CFWD_BUSY      (1 << 1)
01216 #define SKINNY_CFWD_NOANSWER  (1 << 2)
01217 
01218 /* Skinny rtp stream modes. Do we really need this? */
01219 #define SKINNY_CX_SENDONLY 0
01220 #define SKINNY_CX_RECVONLY 1
01221 #define SKINNY_CX_SENDRECV 2
01222 #define SKINNY_CX_CONF 3
01223 #define SKINNY_CX_CONFERENCE 3
01224 #define SKINNY_CX_MUTE 4
01225 #define SKINNY_CX_INACTIVE 4
01226 
01227 #if 0
01228 static const char * const skinny_cxmodes[] = {
01229    "sendonly",
01230    "recvonly",
01231    "sendrecv",
01232    "confrnce",
01233    "inactive"
01234 };
01235 #endif
01236 
01237 /* driver scheduler */
01238 static struct ast_sched_context *sched = NULL;
01239 
01240 /* Protect the network socket */
01241 AST_MUTEX_DEFINE_STATIC(netlock);
01242 
01243 /* Wait up to 16 seconds for first digit */
01244 static int firstdigittimeout = 16000;
01245 
01246 /* How long to wait for following digits */
01247 static int gendigittimeout = 8000;
01248 
01249 /* How long to wait for an extra digit, if there is an ambiguous match */
01250 static int matchdigittimeout = 3000;
01251 
01252 #define SUBSTATE_UNSET 0
01253 #define SUBSTATE_OFFHOOK 1
01254 #define SUBSTATE_ONHOOK 2
01255 #define SUBSTATE_RINGOUT 3
01256 #define SUBSTATE_RINGIN 4
01257 #define SUBSTATE_CONNECTED 5
01258 #define SUBSTATE_BUSY 6
01259 #define SUBSTATE_CONGESTION 7
01260 #define SUBSTATE_HOLD 8
01261 #define SUBSTATE_CALLWAIT 9
01262 #define SUBSTATE_PROGRESS 12
01263 #define SUBSTATE_DIALING 101
01264 
01265 struct skinny_subchannel {
01266    ast_mutex_t lock;
01267    struct ast_channel *owner;
01268    struct ast_rtp_instance *rtp;
01269    struct ast_rtp_instance *vrtp;
01270    unsigned int callid;
01271    char exten[AST_MAX_EXTENSION];
01272    /* time_t lastouttime; */ /* Unused */
01273    int progress;
01274    int ringing;
01275    /* int lastout; */ /* Unused */
01276    int cxmode;
01277    int nat;
01278    int calldirection;
01279    int blindxfer;
01280    int xferor;
01281    int substate;
01282    int aa_sched;
01283    int aa_beep;
01284    int aa_mute;
01285    int dialer_sched;
01286 
01287    AST_LIST_ENTRY(skinny_subchannel) list;
01288    struct skinny_subchannel *related;
01289    struct skinny_line *line;
01290    struct skinny_subline *subline;
01291 };
01292 
01293 #define SKINNY_LINE_OPTIONS            \
01294    char name[80];             \
01295    char label[24];               \
01296    char accountcode[AST_MAX_ACCOUNT_CODE];      \
01297    char exten[AST_MAX_EXTENSION];         \
01298    char context[AST_MAX_CONTEXT];         \
01299    char language[MAX_LANGUAGE];        \
01300    char cid_num[AST_MAX_EXTENSION];       \
01301    char cid_name[AST_MAX_EXTENSION];      \
01302    char lastcallerid[AST_MAX_EXTENSION];     \
01303    int cfwdtype;              \
01304    char call_forward_all[AST_MAX_EXTENSION]; \
01305    char call_forward_busy[AST_MAX_EXTENSION];   \
01306    char call_forward_noanswer[AST_MAX_EXTENSION];  \
01307    char mailbox[AST_MAX_EXTENSION];    \
01308    char vmexten[AST_MAX_EXTENSION];    \
01309    char regexten[AST_MAX_EXTENSION];      \
01310    char regcontext[AST_MAX_CONTEXT];      \
01311    char parkinglot[AST_MAX_CONTEXT];      \
01312    char mohinterpret[MAX_MUSICCLASS];     \
01313    char mohsuggest[MAX_MUSICCLASS];    \
01314    char lastnumberdialed[AST_MAX_EXTENSION]; \
01315    char dialoutexten[AST_MAX_EXTENSION];     \
01316    char dialoutcontext[AST_MAX_CONTEXT];     \
01317    ast_group_t callgroup;           \
01318    ast_group_t pickupgroup;         \
01319    int callwaiting;           \
01320    int transfer;              \
01321    int threewaycalling;          \
01322    int mwiblink;              \
01323    int cancallforward;           \
01324    int getforward;               \
01325    int dnd;             \
01326    int hidecallerid;          \
01327    int amaflags;              \
01328    int instance;              \
01329    int group;              \
01330    struct ast_codec_pref confprefs;    \
01331    struct ast_codec_pref prefs;        \
01332    int nonCodecCapability;          \
01333    int immediate;             \
01334    int nat;             \
01335    int directmedia;           \
01336    int prune;
01337 
01338 struct skinny_line {
01339    SKINNY_LINE_OPTIONS
01340    ast_mutex_t lock;
01341    struct skinny_container *container;
01342    struct ast_event_sub *mwi_event_sub; /* Event based MWI */
01343    struct skinny_subchannel *activesub;
01344    AST_LIST_HEAD(, skinny_subchannel) sub;
01345    AST_LIST_HEAD(, skinny_subline) sublines;
01346    AST_LIST_ENTRY(skinny_line) list;
01347    AST_LIST_ENTRY(skinny_line) all;
01348    struct skinny_device *device;
01349    struct ast_format_cap *cap;
01350    struct ast_format_cap *confcap;
01351    struct ast_variable *chanvars; /*!< Channel variables to set for inbound call */
01352    int newmsgs;
01353 };
01354 
01355 static struct skinny_line_options{
01356    SKINNY_LINE_OPTIONS
01357 } default_line_struct = {
01358    .callwaiting = 1,
01359    .transfer = 1,
01360    .mwiblink = 0,
01361    .dnd = 0,
01362    .hidecallerid = 0,
01363    .amaflags = 0,
01364    .instance = 0,
01365    .directmedia = 0,
01366    .nat = 0,
01367    .getforward = 0,
01368    .prune = 0,
01369 };
01370 static struct skinny_line_options *default_line = &default_line_struct;
01371 
01372 static AST_LIST_HEAD_STATIC(lines, skinny_line);
01373 
01374 struct skinny_subline {
01375    struct skinny_container *container;
01376    struct skinny_line *line;
01377    struct skinny_subchannel *sub;
01378    AST_LIST_ENTRY(skinny_subline) list;
01379    char name[80];
01380    char context[AST_MAX_CONTEXT];
01381    char exten[AST_MAX_EXTENSION];
01382    char stname[AST_MAX_EXTENSION];
01383    char lnname[AST_MAX_EXTENSION];
01384    char ourName[40];
01385    char ourNum[24];
01386    char theirName[40];
01387    char theirNum[24];
01388    int calldirection;
01389    int substate;
01390    int extenstate;
01391    unsigned int callid;
01392 };
01393 
01394 struct skinny_speeddial {
01395    ast_mutex_t lock;
01396    struct skinny_container *container;
01397    char label[42];
01398    char context[AST_MAX_CONTEXT];
01399    char exten[AST_MAX_EXTENSION];
01400    int instance;
01401    int stateid;
01402    int laststate;
01403    int isHint;
01404 
01405    AST_LIST_ENTRY(skinny_speeddial) list;
01406    struct skinny_device *parent;
01407 };
01408 
01409 #define SKINNY_DEVICECONTAINER 1
01410 #define SKINNY_LINECONTAINER 2
01411 #define SKINNY_SUBLINECONTAINER 3
01412 #define SKINNY_SDCONTAINER 4
01413 
01414 struct skinny_container {
01415    int type;
01416    void *data;
01417 };
01418 
01419 struct skinny_addon {
01420    ast_mutex_t lock;
01421    char type[10];
01422    AST_LIST_ENTRY(skinny_addon) list;
01423    struct skinny_device *parent;
01424 };
01425 
01426 #define SKINNY_DEVICE_OPTIONS             \
01427    char name[80];                \
01428    char id[16];                  \
01429    char version_id[16];             \
01430    char vmexten[AST_MAX_EXTENSION];       \
01431    int type;                  \
01432    int protocolversion;          \
01433    int hookstate;             \
01434    int lastlineinstance;               \
01435    int lastcallreference;              \
01436    struct ast_codec_pref confprefs;       \
01437    int earlyrtp;                 \
01438    int transfer;                 \
01439    int callwaiting;              \
01440    int mwiblink;                 \
01441    int dnd;                \
01442    int prune;
01443 
01444 struct skinny_device {
01445    SKINNY_DEVICE_OPTIONS
01446    struct type *first;
01447    struct type *last;
01448    ast_mutex_t lock;
01449    struct sockaddr_in addr;
01450    struct in_addr ourip;
01451    struct ast_ha *ha;
01452    struct skinnysession *session;
01453    struct skinny_line *activeline;
01454    struct ast_format_cap *cap;
01455    struct ast_format_cap *confcap;
01456    AST_LIST_HEAD(, skinny_line) lines;
01457    AST_LIST_HEAD(, skinny_speeddial) speeddials;
01458    AST_LIST_HEAD(, skinny_addon) addons;
01459    AST_LIST_ENTRY(skinny_device) list;
01460 };
01461 
01462 static struct skinny_device_options {
01463    SKINNY_DEVICE_OPTIONS
01464 } default_device_struct = {
01465    .transfer = 1,
01466    .earlyrtp = 1,
01467    .callwaiting = 1,
01468    .mwiblink = 0,
01469    .dnd = 0,
01470    .prune = 0,
01471    .hookstate = SKINNY_ONHOOK,
01472 };
01473 static struct skinny_device_options *default_device = &default_device_struct;
01474    
01475 static AST_LIST_HEAD_STATIC(devices, skinny_device);
01476 
01477 struct skinnysession {
01478    pthread_t t;
01479    ast_mutex_t lock;
01480    time_t start;
01481    struct sockaddr_in sin;
01482    int fd;
01483    char inbuf[SKINNY_MAX_PACKET];
01484    char outbuf[SKINNY_MAX_PACKET];
01485    struct skinny_device *device;
01486    AST_LIST_ENTRY(skinnysession) list;
01487 };
01488 
01489 static struct ast_channel *skinny_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *dest, int *cause);
01490 static AST_LIST_HEAD_STATIC(sessions, skinnysession);
01491 
01492 static int skinny_devicestate(const char *data);
01493 static int skinny_call(struct ast_channel *ast, const char *dest, int timeout);
01494 static int skinny_hangup(struct ast_channel *ast);
01495 static int skinny_answer(struct ast_channel *ast);
01496 static struct ast_frame *skinny_read(struct ast_channel *ast);
01497 static int skinny_write(struct ast_channel *ast, struct ast_frame *frame);
01498 static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, size_t datalen);
01499 static int skinny_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
01500 static int skinny_senddigit_begin(struct ast_channel *ast, char digit);
01501 static int skinny_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration);
01502 static void mwi_event_cb(const struct ast_event *event, void *userdata);
01503 static int skinny_dialer_cb(const void *data);
01504 static int skinny_reload(void);
01505 
01506 static void setsubstate(struct skinny_subchannel *sub, int state);
01507 static void dumpsub(struct skinny_subchannel *sub, int forcehangup);
01508 static void activatesub(struct skinny_subchannel *sub, int state);
01509 static void dialandactivatesub(struct skinny_subchannel *sub, char exten[AST_MAX_EXTENSION]);
01510 
01511 static struct ast_channel_tech skinny_tech = {
01512    .type = "Skinny",
01513    .description = tdesc,
01514    .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER,
01515    .requester = skinny_request,
01516    .devicestate = skinny_devicestate,
01517    .call = skinny_call,
01518    .hangup = skinny_hangup,
01519    .answer = skinny_answer,
01520    .read = skinny_read,
01521    .write = skinny_write,
01522    .indicate = skinny_indicate,
01523    .fixup = skinny_fixup,
01524    .send_digit_begin = skinny_senddigit_begin,
01525    .send_digit_end = skinny_senddigit_end,
01526    .bridge = ast_rtp_instance_bridge, 
01527 };
01528 
01529 static int skinny_extensionstate_cb(char *context, char *id, struct ast_state_cb_info *info, void *data);
01530 static int skinny_transfer(struct skinny_subchannel *sub);
01531 
01532 static struct skinny_line *skinny_line_alloc(void)
01533 {
01534    struct skinny_line *l;
01535    if (!(l = ast_calloc(1, sizeof(*l)))) {
01536       return NULL;
01537    }
01538 
01539    l->cap = ast_format_cap_alloc_nolock();
01540    l->confcap = ast_format_cap_alloc_nolock();
01541    if (!l->cap || !l->confcap) {
01542       l->cap = ast_format_cap_destroy(l->cap);
01543       l->confcap = ast_format_cap_destroy(l->confcap);
01544       ast_free(l);
01545       return NULL;
01546    }
01547    return l;
01548 }
01549 static struct skinny_line *skinny_line_destroy(struct skinny_line *l)
01550 {
01551    l->cap = ast_format_cap_destroy(l->cap);
01552    l->confcap = ast_format_cap_destroy(l->confcap);
01553    ast_free(l->container);
01554    ast_free(l);
01555    return NULL;
01556 }
01557 static struct skinny_device *skinny_device_alloc(void)
01558 {
01559    struct skinny_device *d;
01560    if (!(d = ast_calloc(1, sizeof(*d)))) {
01561       return NULL;
01562    }
01563 
01564    d->cap = ast_format_cap_alloc_nolock();
01565    d->confcap = ast_format_cap_alloc_nolock();
01566    if (!d->cap || !d->confcap) {
01567       d->cap = ast_format_cap_destroy(d->cap);
01568       d->confcap = ast_format_cap_destroy(d->confcap);
01569       ast_free(d);
01570       return NULL;
01571    }
01572    return d;
01573 }
01574 static struct skinny_device *skinny_device_destroy(struct skinny_device *d)
01575 {
01576    d->cap = ast_format_cap_destroy(d->cap);
01577    d->confcap = ast_format_cap_destroy(d->confcap);
01578    ast_free(d);
01579    return NULL;
01580 }
01581 
01582 static void *get_button_template(struct skinnysession *s, struct button_definition_template *btn)
01583 {
01584    struct skinny_device *d = s->device;
01585    struct skinny_addon *a;
01586    int i;
01587 
01588    switch (d->type) {
01589       case SKINNY_DEVICE_30SPPLUS:
01590       case SKINNY_DEVICE_30VIP:
01591          /* 13 rows, 2 columns */
01592          for (i = 0; i < 4; i++)
01593             (btn++)->buttonDefinition = BT_CUST_LINE;
01594          (btn++)->buttonDefinition = BT_REDIAL;
01595          (btn++)->buttonDefinition = BT_VOICEMAIL;
01596          (btn++)->buttonDefinition = BT_CALLPARK;
01597          (btn++)->buttonDefinition = BT_FORWARDALL;
01598          (btn++)->buttonDefinition = BT_CONFERENCE;
01599          for (i = 0; i < 4; i++)
01600             (btn++)->buttonDefinition = BT_NONE;
01601          for (i = 0; i < 13; i++)
01602             (btn++)->buttonDefinition = BT_SPEEDDIAL;
01603          
01604          break;
01605       case SKINNY_DEVICE_12SPPLUS:
01606       case SKINNY_DEVICE_12SP:
01607       case SKINNY_DEVICE_12:
01608          /* 6 rows, 2 columns */
01609          for (i = 0; i < 2; i++)
01610             (btn++)->buttonDefinition = BT_CUST_LINE;
01611          for (i = 0; i < 4; i++)
01612             (btn++)->buttonDefinition = BT_SPEEDDIAL;
01613          (btn++)->buttonDefinition = BT_HOLD;
01614          (btn++)->buttonDefinition = BT_REDIAL;
01615          (btn++)->buttonDefinition = BT_TRANSFER;
01616          (btn++)->buttonDefinition = BT_FORWARDALL;
01617          (btn++)->buttonDefinition = BT_CALLPARK;
01618          (btn++)->buttonDefinition = BT_VOICEMAIL;
01619          break;
01620       case SKINNY_DEVICE_7910:
01621          (btn++)->buttonDefinition = BT_LINE;
01622          (btn++)->buttonDefinition = BT_HOLD;
01623          (btn++)->buttonDefinition = BT_TRANSFER;
01624          (btn++)->buttonDefinition = BT_DISPLAY;
01625          (btn++)->buttonDefinition = BT_VOICEMAIL;
01626          (btn++)->buttonDefinition = BT_CONFERENCE;
01627          (btn++)->buttonDefinition = BT_FORWARDALL;
01628          for (i = 0; i < 2; i++)
01629             (btn++)->buttonDefinition = BT_SPEEDDIAL;
01630          (btn++)->buttonDefinition = BT_REDIAL;
01631          break;
01632       case SKINNY_DEVICE_7960:
01633       case SKINNY_DEVICE_7961:
01634       case SKINNY_DEVICE_7961GE:
01635       case SKINNY_DEVICE_7962:
01636       case SKINNY_DEVICE_7965:
01637          for (i = 0; i < 6; i++)
01638             (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL;
01639          break;
01640       case SKINNY_DEVICE_7940:
01641       case SKINNY_DEVICE_7941:
01642       case SKINNY_DEVICE_7941GE:
01643       case SKINNY_DEVICE_7942:
01644       case SKINNY_DEVICE_7945:
01645          for (i = 0; i < 2; i++)
01646             (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL;
01647          break;
01648       case SKINNY_DEVICE_7935:
01649       case SKINNY_DEVICE_7936:
01650          for (i = 0; i < 2; i++)
01651             (btn++)->buttonDefinition = BT_LINE;
01652          break;
01653       case SKINNY_DEVICE_ATA186:
01654          (btn++)->buttonDefinition = BT_LINE;
01655          break;
01656       case SKINNY_DEVICE_7970:
01657       case SKINNY_DEVICE_7971:
01658       case SKINNY_DEVICE_7975:
01659       case SKINNY_DEVICE_CIPC:
01660          for (i = 0; i < 8; i++)
01661             (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL;
01662          break;
01663       case SKINNY_DEVICE_7985:
01664          /* XXX I have no idea what the buttons look like on these. */
01665          ast_log(LOG_WARNING, "Unsupported device type '%d (7985)' found.\n", d->type);
01666          break;
01667       case SKINNY_DEVICE_7912:
01668       case SKINNY_DEVICE_7911:
01669       case SKINNY_DEVICE_7905:
01670          (btn++)->buttonDefinition = BT_LINE;
01671          (btn++)->buttonDefinition = BT_HOLD;
01672          break;
01673       case SKINNY_DEVICE_7920:
01674          /* XXX I don't know if this is right. */
01675          for (i = 0; i < 4; i++)
01676             (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL;
01677          break;
01678       case SKINNY_DEVICE_7921:
01679          for (i = 0; i < 6; i++)
01680             (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL;
01681          break;
01682       case SKINNY_DEVICE_7902:
01683          ast_log(LOG_WARNING, "Unsupported device type '%d (7902)' found.\n", d->type);
01684          break;
01685       case SKINNY_DEVICE_7906:
01686          ast_log(LOG_WARNING, "Unsupported device type '%d (7906)' found.\n", d->type);
01687          break;
01688       case SKINNY_DEVICE_7931:
01689          ast_log(LOG_WARNING, "Unsupported device type '%d (7931)' found.\n", d->type);
01690          break;
01691       case SKINNY_DEVICE_7937:
01692          ast_log(LOG_WARNING, "Unsupported device type '%d (7937)' found.\n", d->type);
01693          break;
01694       case SKINNY_DEVICE_7914:
01695          ast_log(LOG_WARNING, "Unsupported device type '%d (7914)' found.  Expansion module registered by itself?\n", d->type);
01696          break;
01697       case SKINNY_DEVICE_SCCPGATEWAY_AN:
01698       case SKINNY_DEVICE_SCCPGATEWAY_BRI:
01699          ast_log(LOG_WARNING, "Unsupported device type '%d (SCCP gateway)' found.\n", d->type);
01700          break;
01701       default:
01702          ast_log(LOG_WARNING, "Unknown device type '%d' found.\n", d->type);
01703          break;
01704    }
01705 
01706    AST_LIST_LOCK(&d->addons);
01707    AST_LIST_TRAVERSE(&d->addons, a, list) {
01708       if (!strcasecmp(a->type, "7914")) {
01709          for (i = 0; i < 14; i++)
01710             (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL;
01711       } else {
01712          ast_log(LOG_WARNING, "Unknown addon type '%s' found.  Skipping.\n", a->type);
01713       }
01714    }
01715    AST_LIST_UNLOCK(&d->addons);
01716 
01717    return btn;
01718 }
01719 
01720 static struct skinny_req *req_alloc(size_t size, int response_message)
01721 {
01722    struct skinny_req *req;
01723 
01724    if (!(req = ast_calloc(1, skinny_header_size + size + 4)))
01725       return NULL;
01726 
01727    req->len = htolel(size+4);
01728    req->e = htolel(response_message);
01729 
01730    return req;
01731 }
01732 
01733 static struct skinny_line *find_line_by_instance(struct skinny_device *d, int instance)
01734 {
01735    struct skinny_line *l;
01736 
01737    /*Dialing from on hook or on a 7920 uses instance 0 in requests
01738      but we need to start looking at instance 1 */
01739 
01740    if (!instance)
01741       instance = 1;
01742 
01743    AST_LIST_TRAVERSE(&d->lines, l, list){
01744       if (l->instance == instance)
01745          break;
01746    }
01747 
01748    if (!l) {
01749       ast_log(LOG_WARNING, "Could not find line with instance '%d' on device '%s'\n", instance, d->name);
01750    }
01751    return l;
01752 }
01753 
01754 static struct skinny_line *find_line_by_name(const char *dest)
01755 {
01756    struct skinny_line *l;
01757    struct skinny_line *tmpl = NULL;
01758    struct skinny_device *d;
01759    char line[256];
01760    char *at;
01761    char *device;
01762    int checkdevice = 0;
01763 
01764    ast_copy_string(line, dest, sizeof(line));
01765    at = strchr(line, '@');
01766    if (at)
01767       *at++ = '\0';
01768    device = at;
01769 
01770    if (!ast_strlen_zero(device))
01771       checkdevice = 1;
01772 
01773    AST_LIST_LOCK(&devices);
01774    AST_LIST_TRAVERSE(&devices, d, list){
01775       if (checkdevice && tmpl)
01776          break;
01777       else if (!checkdevice) {
01778          /* This is a match, since we're checking for line on every device. */
01779       } else if (!strcasecmp(d->name, device)) {
01780       } else
01781          continue;
01782 
01783       /* Found the device (or we don't care which device) */
01784       AST_LIST_TRAVERSE(&d->lines, l, list){
01785          /* Search for the right line */
01786          if (!strcasecmp(l->name, line)) {
01787             if (tmpl) {
01788                ast_log(LOG_WARNING, "Ambiguous line name: %s\n", line);
01789                AST_LIST_UNLOCK(&devices);
01790                return NULL;
01791             } else
01792                tmpl = l;
01793          }
01794       }
01795    }
01796    AST_LIST_UNLOCK(&devices);
01797    return tmpl;
01798 }
01799 
01800 static struct skinny_subline *find_subline_by_name(const char *dest)
01801 {
01802    struct skinny_line *l;
01803    struct skinny_subline *subline;
01804    struct skinny_subline *tmpsubline = NULL;
01805    struct skinny_device *d;
01806 
01807    AST_LIST_LOCK(&devices);
01808    AST_LIST_TRAVERSE(&devices, d, list){
01809       AST_LIST_TRAVERSE(&d->lines, l, list){
01810          AST_LIST_TRAVERSE(&l->sublines, subline, list){
01811             if (!strcasecmp(subline->name, dest)) {
01812                if (tmpsubline) {
01813                   ast_verb(2, "Ambiguous subline name: %s\n", dest);
01814                   AST_LIST_UNLOCK(&devices);
01815                   return NULL;
01816                } else
01817                   tmpsubline = subline;
01818             }
01819          }
01820       }
01821    }
01822    AST_LIST_UNLOCK(&devices);
01823    return tmpsubline;
01824 }
01825 
01826 static struct skinny_subline *find_subline_by_callid(struct skinny_device *d, int callid)
01827 {
01828    struct skinny_subline *subline;
01829    struct skinny_line *l;
01830    
01831    AST_LIST_TRAVERSE(&d->lines, l, list){
01832       AST_LIST_TRAVERSE(&l->sublines, subline, list){
01833          if (subline->callid == callid) {
01834             return subline;
01835          }
01836       }
01837    }
01838    return NULL;
01839 }
01840 
01841 /*!
01842  * implement the setvar config line
01843  */
01844 static struct ast_variable *add_var(const char *buf, struct ast_variable *list)
01845 {
01846    struct ast_variable *tmpvar = NULL;
01847    char *varname = ast_strdupa(buf), *varval = NULL;
01848 
01849    if ((varval = strchr(varname,'='))) {
01850       *varval++ = '\0';
01851       if ((tmpvar = ast_variable_new(varname, varval, ""))) {
01852          tmpvar->next = list;
01853          list = tmpvar;
01854       }
01855    }
01856    return list;
01857 }
01858 
01859 static int skinny_sched_del(int sched_id, struct skinny_subchannel *sub)
01860 {
01861    SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - Deleting SCHED %d\n",
01862       sub->callid, sched_id);
01863    return ast_sched_del(sched, sched_id);
01864 }
01865 
01866 static int skinny_sched_add(int when, ast_sched_cb callback, struct skinny_subchannel *sub)
01867 {
01868    int ret;
01869    ret = ast_sched_add(sched, when, callback, sub);
01870    SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - Added SCHED %d\n",
01871       sub->callid, ret);
01872    return ret;
01873 }
01874 
01875 /* It's quicker/easier to find the subchannel when we know the instance number too */
01876 static struct skinny_subchannel *find_subchannel_by_instance_reference(struct skinny_device *d, int instance, int reference)
01877 {
01878    struct skinny_line *l = find_line_by_instance(d, instance);
01879    struct skinny_subchannel *sub;
01880 
01881    if (!l) {
01882       return NULL;
01883    }
01884 
01885    /* 7920 phones set call reference to 0, so use the first
01886       sub-channel on the list.
01887            This MIGHT need more love to be right */
01888    if (!reference)
01889       sub = AST_LIST_FIRST(&l->sub);
01890    else {
01891       AST_LIST_TRAVERSE(&l->sub, sub, list) {
01892          if (sub->callid == reference)
01893             break;
01894       }
01895    }
01896    if (!sub) {
01897       ast_log(LOG_WARNING, "Could not find subchannel with reference '%d' on '%s'\n", reference, d->name);
01898    }
01899    return sub;
01900 }
01901 
01902 /* Find the subchannel when we only have the callid - this shouldn't happen often */
01903 static struct skinny_subchannel *find_subchannel_by_reference(struct skinny_device *d, int reference)
01904 {
01905    struct skinny_line *l;
01906    struct skinny_subchannel *sub = NULL;
01907 
01908    AST_LIST_TRAVERSE(&d->lines, l, list){
01909       AST_LIST_TRAVERSE(&l->sub, sub, list){
01910          if (sub->callid == reference)
01911             break;
01912       }
01913       if (sub)
01914          break;
01915    }
01916 
01917    if (!l) {
01918       ast_log(LOG_WARNING, "Could not find any lines that contained a subchannel with reference '%d' on device '%s'\n", reference, d->name);
01919    } else {
01920       if (!sub) {
01921          ast_log(LOG_WARNING, "Could not find subchannel with reference '%d' on '%s@%s'\n", reference, l->name, d->name);
01922       }
01923    }
01924    return sub;
01925 }
01926 
01927 static struct skinny_speeddial *find_speeddial_by_instance(struct skinny_device *d, int instance, int isHint)
01928 {
01929    struct skinny_speeddial *sd;
01930 
01931    AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
01932       if (sd->isHint == isHint && sd->instance == instance)
01933          break;
01934    }
01935 
01936    if (!sd) {
01937       ast_log(LOG_WARNING, "Could not find speeddial with instance '%d' on device '%s'\n", instance, d->name);
01938    }
01939    return sd;
01940 }
01941 
01942 static struct ast_format *codec_skinny2ast(enum skinny_codecs skinnycodec, struct ast_format *result)
01943 {
01944    switch (skinnycodec) {
01945    case SKINNY_CODEC_ALAW:
01946       return ast_format_set(result, AST_FORMAT_ALAW, 0);
01947    case SKINNY_CODEC_ULAW:
01948       return ast_format_set(result, AST_FORMAT_ULAW, 0);
01949    case SKINNY_CODEC_G723_1:
01950       return ast_format_set(result, AST_FORMAT_G723_1, 0);
01951    case SKINNY_CODEC_G729A:
01952       return ast_format_set(result, AST_FORMAT_G729A, 0);
01953    case SKINNY_CODEC_G726_32:
01954       return ast_format_set(result, AST_FORMAT_G726_AAL2, 0); /* XXX Is this right? */
01955    case SKINNY_CODEC_H261:
01956       return ast_format_set(result, AST_FORMAT_H261, 0);
01957    case SKINNY_CODEC_H263:
01958       return ast_format_set(result, AST_FORMAT_H263 ,0);
01959    default:
01960       ast_format_clear(result);
01961       return result;
01962    }
01963 }
01964 
01965 static int codec_ast2skinny(const struct ast_format *astcodec)
01966 {
01967    switch (astcodec->id) {
01968    case AST_FORMAT_ALAW:
01969       return SKINNY_CODEC_ALAW;
01970    case AST_FORMAT_ULAW:
01971       return SKINNY_CODEC_ULAW;
01972    case AST_FORMAT_G723_1:
01973       return SKINNY_CODEC_G723_1;
01974    case AST_FORMAT_G729A:
01975       return SKINNY_CODEC_G729A;
01976    case AST_FORMAT_G726_AAL2: /* XXX Is this right? */
01977       return SKINNY_CODEC_G726_32;
01978    case AST_FORMAT_H261:
01979       return SKINNY_CODEC_H261;
01980    case AST_FORMAT_H263:
01981       return SKINNY_CODEC_H263;
01982    default:
01983       return 0;
01984    }
01985 }
01986 
01987 static int set_callforwards(struct skinny_line *l, const char *cfwd, int cfwdtype)
01988 {
01989    if (!l)
01990       return 0;
01991 
01992    if (!ast_strlen_zero(cfwd)) {
01993       if (cfwdtype & SKINNY_CFWD_ALL) {
01994          l->cfwdtype |= SKINNY_CFWD_ALL;
01995          ast_copy_string(l->call_forward_all, cfwd, sizeof(l->call_forward_all));
01996       }
01997       if (cfwdtype & SKINNY_CFWD_BUSY) {
01998          l->cfwdtype |= SKINNY_CFWD_BUSY;
01999          ast_copy_string(l->call_forward_busy, cfwd, sizeof(l->call_forward_busy));
02000       }
02001       if (cfwdtype & SKINNY_CFWD_NOANSWER) {
02002          l->cfwdtype |= SKINNY_CFWD_NOANSWER;
02003          ast_copy_string(l->call_forward_noanswer, cfwd, sizeof(l->call_forward_noanswer));
02004       }
02005    } else {
02006       if (cfwdtype & SKINNY_CFWD_ALL) {
02007          l->cfwdtype &= ~SKINNY_CFWD_ALL;
02008          memset(l->call_forward_all, 0, sizeof(l->call_forward_all));
02009       }
02010       if (cfwdtype & SKINNY_CFWD_BUSY) {
02011          l->cfwdtype &= ~SKINNY_CFWD_BUSY;
02012          memset(l->call_forward_busy, 0, sizeof(l->call_forward_busy));
02013       }
02014       if (cfwdtype & SKINNY_CFWD_NOANSWER) {
02015          l->cfwdtype &= ~SKINNY_CFWD_NOANSWER;
02016          memset(l->call_forward_noanswer, 0, sizeof(l->call_forward_noanswer));
02017       }
02018    }
02019    return l->cfwdtype;
02020 }
02021 
02022 static void cleanup_stale_contexts(char *new, char *old)
02023 {
02024    char *oldcontext, *newcontext, *stalecontext, *stringp, newlist[AST_MAX_CONTEXT];
02025 
02026    while ((oldcontext = strsep(&old, "&"))) {
02027       stalecontext = '\0';
02028       ast_copy_string(newlist, new, sizeof(newlist));
02029       stringp = newlist;
02030       while ((newcontext = strsep(&stringp, "&"))) {
02031          if (strcmp(newcontext, oldcontext) == 0) {
02032             /* This is not the context you're looking for */
02033             stalecontext = '\0';
02034             break;
02035          } else if (strcmp(newcontext, oldcontext)) {
02036             stalecontext = oldcontext;
02037          }
02038          
02039       }
02040       if (stalecontext)
02041          ast_context_destroy(ast_context_find(stalecontext), "Skinny");
02042    }
02043 }
02044 
02045 static void register_exten(struct skinny_line *l)
02046 {
02047    char multi[256];
02048    char *stringp, *ext, *context;
02049 
02050    if (ast_strlen_zero(regcontext))
02051       return;
02052 
02053    ast_copy_string(multi, S_OR(l->regexten, l->name), sizeof(multi));
02054    stringp = multi;
02055    while ((ext = strsep(&stringp, "&"))) {
02056       if ((context = strchr(ext, '@'))) {
02057          *context++ = '\0'; /* split ext@context */
02058          if (!ast_context_find(context)) {
02059             ast_log(LOG_WARNING, "Context %s must exist in regcontext= in skinny.conf!\n", context);
02060             continue;
02061          }
02062       } else {
02063          context = regcontext;
02064       }
02065       ast_add_extension(context, 1, ext, 1, NULL, NULL, "Noop",
02066           ast_strdup(l->name), ast_free_ptr, "Skinny");
02067    }
02068 }
02069 
02070 static void unregister_exten(struct skinny_line *l)
02071 {
02072    char multi[256];
02073    char *stringp, *ext, *context;
02074 
02075    if (ast_strlen_zero(regcontext))
02076       return;
02077 
02078    ast_copy_string(multi, S_OR(l->regexten, l->name), sizeof(multi));
02079    stringp = multi;
02080    while ((ext = strsep(&stringp, "&"))) {
02081       if ((context = strchr(ext, '@'))) {
02082          *context++ = '\0'; /* split ext@context */
02083          if (!ast_context_find(context)) {
02084             ast_log(LOG_WARNING, "Context %s must exist in regcontext= in skinny.conf!\n", context);
02085             continue;
02086          }
02087       } else {
02088          context = regcontext;
02089       }
02090       ast_context_remove_extension(context, ext, 1, NULL);
02091    }
02092 }
02093 
02094 static int skinny_register(struct skinny_req *req, struct skinnysession *s)
02095 {
02096    struct skinny_device *d;
02097    struct skinny_line *l;
02098    struct skinny_subline *subline;
02099    struct skinny_speeddial *sd;
02100    struct sockaddr_in sin;
02101    socklen_t slen;
02102    int instance;
02103 
02104    AST_LIST_LOCK(&devices);
02105    AST_LIST_TRAVERSE(&devices, d, list){
02106       struct ast_sockaddr addr;
02107       ast_sockaddr_from_sin(&addr, &s->sin);
02108       if (!d->session && !strcasecmp(req->data.reg.name, d->id)
02109             && ast_apply_ha(d->ha, &addr)) {
02110          s->device = d;
02111          d->type = letohl(req->data.reg.type);
02112          d->protocolversion = letohl(req->data.reg.protocolVersion);
02113          if (ast_strlen_zero(d->version_id)) {
02114             ast_copy_string(d->version_id, version_id, sizeof(d->version_id));
02115          }
02116          d->session = s;
02117 
02118          slen = sizeof(sin);
02119          if (getsockname(s->fd, (struct sockaddr *)&sin, &slen)) {
02120             ast_log(LOG_WARNING, "Cannot get socket name\n");
02121             sin.sin_addr = __ourip;
02122          }
02123          d->ourip = sin.sin_addr;
02124 
02125          AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
02126             sd->stateid = ast_extension_state_add(sd->context, sd->exten, skinny_extensionstate_cb, sd->container);
02127          }
02128          instance = 0;
02129          AST_LIST_TRAVERSE(&d->lines, l, list) {
02130             instance++;
02131          }
02132          AST_LIST_TRAVERSE(&d->lines, l, list) {
02133             ast_format_cap_joint_copy(l->confcap, d->cap, l->cap);
02134             l->prefs = l->confprefs;
02135             if (!l->prefs.order[0]) {
02136                l->prefs = d->confprefs;
02137             }
02138             /* l->capability = d->capability;
02139             l->prefs = d->prefs; */
02140             l->instance = instance;
02141             l->newmsgs = ast_app_has_voicemail(l->mailbox, NULL);
02142             set_callforwards(l, NULL, 0);
02143             manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: Skinny\r\nPeer: Skinny/%s@%s\r\nPeerStatus: Registered\r\n", l->name, d->name);
02144             register_exten(l);
02145             /* initialize MWI on line and device */
02146             mwi_event_cb(0, l);
02147             AST_LIST_TRAVERSE(&l->sublines, subline, list) {
02148                ast_extension_state_add(subline->context, subline->exten, skinny_extensionstate_cb, subline->container);
02149             }
02150             ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Skinny/%s", l->name);
02151             --instance;
02152          }
02153          break;
02154       }
02155    }
02156    AST_LIST_UNLOCK(&devices);
02157    if (!d) {
02158       return 0;
02159    }
02160    return 1;
02161 }
02162 
02163 static int skinny_unregister(struct skinny_req *req, struct skinnysession *s)
02164 {
02165    struct skinny_device *d;
02166    struct skinny_line *l;
02167    struct skinny_speeddial *sd;
02168 
02169    d = s->device;
02170 
02171    if (d) {
02172       d->session = NULL;
02173 
02174       AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
02175          if (sd->stateid > -1)
02176             ast_extension_state_del(sd->stateid, NULL);
02177       }
02178       AST_LIST_TRAVERSE(&d->lines, l, list) {
02179          if (l->device == d) {
02180             ast_format_cap_remove_all(l->cap);
02181             ast_parse_allow_disallow(&l->prefs, l->cap, "all", 0);
02182             l->instance = 0;
02183             manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: Skinny\r\nPeer: Skinny/%s@%s\r\nPeerStatus: Unregistered\r\n", l->name, d->name);
02184             unregister_exten(l);
02185             ast_devstate_changed(AST_DEVICE_UNAVAILABLE, AST_DEVSTATE_CACHABLE, "Skinny/%s", l->name);
02186          }
02187       }
02188    }
02189 
02190    return -1; /* main loop will destroy the session */
02191 }
02192 
02193 #ifdef AST_DEVMODE
02194 static char *callstate2str(int ind)
02195 {
02196    char *tmp;
02197 
02198    switch (ind) {
02199    case SKINNY_OFFHOOK:
02200       return "SKINNY_OFFHOOK";
02201    case SKINNY_ONHOOK:
02202       return "SKINNY_ONHOOK";
02203    case SKINNY_RINGOUT:
02204       return "SKINNY_RINGOUT";
02205    case SKINNY_RINGIN:
02206       return "SKINNY_RINGIN";
02207    case SKINNY_CONNECTED:
02208       return "SKINNY_CONNECTED";
02209    case SKINNY_BUSY:
02210       return "SKINNY_BUSY";
02211    case SKINNY_CONGESTION:
02212       return "SKINNY_CONGESTION";
02213    case SKINNY_PROGRESS:
02214       return "SKINNY_PROGRESS";
02215    case SKINNY_HOLD:
02216       return "SKINNY_HOLD";
02217    case SKINNY_CALLWAIT:
02218       return "SKINNY_CALLWAIT";
02219    default:
02220       if (!(tmp = ast_threadstorage_get(&callstate2str_threadbuf, CALLSTATE2STR_BUFSIZE)))
02221                         return "Unknown";
02222       snprintf(tmp, CALLSTATE2STR_BUFSIZE, "UNKNOWN-%d", ind);
02223       return tmp;
02224    }
02225 }
02226 
02227 #endif
02228 
02229 static int transmit_response_bysession(struct skinnysession *s, struct skinny_req *req)
02230 {
02231    int res = 0;
02232 
02233    if (!s) {
02234       ast_log(LOG_WARNING, "Asked to transmit to a non-existent session!\n");
02235       return -1;
02236    }
02237 
02238    ast_mutex_lock(&s->lock);
02239 
02240    if ((letohl(req->len) > SKINNY_MAX_PACKET) || (letohl(req->len) < 0)) {
02241       ast_log(LOG_WARNING, "transmit_response: the length of the request (%d) is out of bounds (%d)\n", letohl(req->len), SKINNY_MAX_PACKET);
02242       ast_mutex_unlock(&s->lock);
02243       return -1;
02244    }
02245 
02246    memset(s->outbuf, 0, sizeof(s->outbuf));
02247    memcpy(s->outbuf, req, skinny_header_size);
02248    memcpy(s->outbuf+skinny_header_size, &req->data, letohl(req->len));
02249 
02250    res = write(s->fd, s->outbuf, letohl(req->len)+8);
02251 
02252    if (res != letohl(req->len)+8) {
02253       ast_log(LOG_WARNING, "Transmit: write only sent %d out of %d bytes: %s\n", res, letohl(req->len)+8, strerror(errno));
02254       if (res == -1) {
02255          ast_log(LOG_WARNING, "Transmit: Skinny Client was lost, unregistering\n");
02256          skinny_unregister(NULL, s);
02257       }
02258 
02259    }
02260 
02261    ast_free(req);
02262    ast_mutex_unlock(&s->lock);
02263    return 1;
02264 }
02265 
02266 static void transmit_response(struct skinny_device *d, struct skinny_req *req)
02267 {
02268    transmit_response_bysession(d->session, req);
02269 }
02270 
02271 static void transmit_registerrej(struct skinnysession *s)
02272 {
02273    struct skinny_req *req;
02274    char name[16];
02275 
02276    if (!(req = req_alloc(sizeof(struct register_rej_message), REGISTER_REJ_MESSAGE)))
02277       return;
02278 
02279    memcpy(&name, req->data.reg.name, sizeof(name));
02280    snprintf(req->data.regrej.errMsg, sizeof(req->data.regrej.errMsg), "No Authority: %s", name);
02281 
02282    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting REGISTER_REJ_MESSAGE to UNKNOWN_DEVICE\n");
02283    transmit_response_bysession(s, req);
02284 }
02285 
02286 static void transmit_speaker_mode(struct skinny_device *d, int mode)
02287 {
02288    struct skinny_req *req;
02289 
02290    if (!(req = req_alloc(sizeof(struct set_speaker_message), SET_SPEAKER_MESSAGE)))
02291       return;
02292 
02293    req->data.setspeaker.mode = htolel(mode);
02294 
02295    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting SET_SPEAKER_MESSAGE to %s, mode %d\n", d->name, mode);
02296    transmit_response(d, req);
02297 }
02298 
02299 static void transmit_microphone_mode(struct skinny_device *d, int mode)
02300 {
02301    struct skinny_req *req;
02302 
02303    if (!(req = req_alloc(sizeof(struct set_microphone_message), SET_MICROPHONE_MESSAGE)))
02304       return;
02305 
02306    req->data.setmicrophone.mode = htolel(mode);
02307 
02308    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting SET_MICROPHONE_MESSAGE to %s, mode %d\n", d->name, mode);
02309    transmit_response(d, req);
02310 }
02311 
02312 //static void transmit_callinfo(struct skinny_subchannel *sub)
02313 static void transmit_callinfo(struct skinny_device *d, int instance, int callid, char *fromname, char *fromnum, char *toname, char *tonum, int calldirection)
02314 {
02315    struct skinny_req *req;
02316 
02317    if (!(req = req_alloc(sizeof(struct call_info_message), CALL_INFO_MESSAGE)))
02318       return;
02319 
02320    ast_copy_string(req->data.callinfo.callingPartyName, fromname, sizeof(req->data.callinfo.callingPartyName));
02321    ast_copy_string(req->data.callinfo.callingParty, fromnum, sizeof(req->data.callinfo.callingParty));
02322    ast_copy_string(req->data.callinfo.calledPartyName, toname, sizeof(req->data.callinfo.calledPartyName));
02323    ast_copy_string(req->data.callinfo.calledParty, tonum, sizeof(req->data.callinfo.calledParty));
02324    req->data.callinfo.instance = htolel(instance);
02325    req->data.callinfo.reference = htolel(callid);
02326    req->data.callinfo.type = htolel(calldirection);
02327 
02328    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting CALL_INFO_MESSAGE to %s, to %s(%s) from %s(%s) (dir=%d) on %s(%d)\n",
02329       d->name, toname, tonum, fromname, fromnum, calldirection, d->name, instance);
02330    transmit_response(d, req);
02331 }
02332 
02333 static void send_callinfo(struct skinny_subchannel *sub)
02334 {
02335    struct ast_channel *ast;
02336    struct skinny_device *d;
02337    struct skinny_line *l;
02338    char *fromname;
02339    char *fromnum;
02340    char *toname;
02341    char *tonum;
02342 
02343    if (!sub || !sub->owner || !sub->line || !sub->line->device) {
02344       return;
02345    }
02346    
02347    ast = sub->owner;
02348    l = sub->line;
02349    d = l->device;
02350    
02351    if (sub->calldirection == SKINNY_INCOMING) {
02352       fromname = S_COR(ast_channel_connected(ast)->id.name.valid, ast_channel_connected(ast)->id.name.str, "");
02353       fromnum = S_COR(ast_channel_connected(ast)->id.number.valid, ast_channel_connected(ast)->id.number.str, "");
02354       toname = S_COR(ast_channel_caller(ast)->id.name.valid, ast_channel_caller(ast)->id.name.str, "");
02355       tonum = S_COR(ast_channel_caller(ast)->id.number.valid, ast_channel_caller(ast)->id.number.str, "");
02356    } else if (sub->calldirection == SKINNY_OUTGOING) {
02357       fromname = S_COR(ast_channel_caller(ast)->id.name.valid, ast_channel_caller(ast)->id.name.str, "");
02358       fromnum = S_COR(ast_channel_caller(ast)->id.number.valid, ast_channel_caller(ast)->id.number.str, "");
02359       toname = S_COR(ast_channel_connected(ast)->id.name.valid, ast_channel_connected(ast)->id.name.str, l->lastnumberdialed);
02360       tonum = S_COR(ast_channel_connected(ast)->id.number.valid, ast_channel_connected(ast)->id.number.str, l->lastnumberdialed);
02361    } else {
02362       ast_verb(1, "Error sending Callinfo to %s(%d) - No call direction in sub\n", d->name, l->instance);
02363       return;
02364    }
02365    transmit_callinfo(d, l->instance, sub->callid, fromname, fromnum, toname, tonum, sub->calldirection);
02366 }
02367 
02368 static void push_callinfo(struct skinny_subline *subline, struct skinny_subchannel *sub)
02369 {
02370    struct ast_channel *ast;
02371    struct skinny_device *d;
02372    struct skinny_line *l;
02373    char *fromname;
02374    char *fromnum;
02375    char *toname;
02376    char *tonum;
02377 
02378    if (!sub || !sub->owner || !sub->line || !sub->line->device) {
02379       return;
02380    }
02381    
02382    ast = sub->owner;
02383    l = sub->line;
02384    d = l->device;
02385    
02386    if (sub->calldirection == SKINNY_INCOMING) {
02387       fromname = S_COR(ast_channel_connected(ast)->id.name.valid, ast_channel_connected(ast)->id.name.str, "");
02388       fromnum = S_COR(ast_channel_connected(ast)->id.number.valid, ast_channel_connected(ast)->id.number.str, "");
02389       toname = S_COR(ast_channel_caller(ast)->id.name.valid, ast_channel_caller(ast)->id.name.str, "");
02390       tonum = S_COR(ast_channel_caller(ast)->id.number.valid, ast_channel_caller(ast)->id.number.str, "");
02391    } else if (sub->calldirection == SKINNY_OUTGOING) {
02392       fromname = S_COR(ast_channel_caller(ast)->id.name.valid, ast_channel_caller(ast)->id.name.str, "");
02393       fromnum = S_COR(ast_channel_caller(ast)->id.number.valid, ast_channel_caller(ast)->id.number.str, "");
02394       toname = S_COR(ast_channel_connected(ast)->id.name.valid, ast_channel_connected(ast)->id.name.str, l->lastnumberdialed);
02395       tonum = S_COR(ast_channel_connected(ast)->id.number.valid, ast_channel_connected(ast)->id.number.str, l->lastnumberdialed);
02396    } else {
02397       ast_verb(1, "Error sending Callinfo to %s(%d) - No call direction in sub\n", d->name, l->instance);
02398       return;
02399    }
02400    transmit_callinfo(subline->line->device, subline->line->instance, subline->callid, fromname, fromnum, toname, tonum, sub->calldirection);
02401 }
02402 
02403 static void transmit_connect(struct skinny_device *d, struct skinny_subchannel *sub)
02404 {
02405    struct skinny_req *req;
02406    struct skinny_line *l = sub->line;
02407    struct ast_format_list fmt;
02408    struct ast_format tmpfmt;
02409 
02410    if (!(req = req_alloc(sizeof(struct open_receive_channel_message), OPEN_RECEIVE_CHANNEL_MESSAGE)))
02411       return;
02412    ast_best_codec(l->cap, &tmpfmt);
02413    fmt = ast_codec_pref_getsize(&l->prefs, &tmpfmt);
02414 
02415    req->data.openreceivechannel.conferenceId = htolel(sub->callid);
02416    req->data.openreceivechannel.partyId = htolel(sub->callid);
02417    req->data.openreceivechannel.packets = htolel(fmt.cur_ms);
02418    req->data.openreceivechannel.capability = htolel(codec_ast2skinny(&fmt.format));
02419    req->data.openreceivechannel.echo = htolel(0);
02420    req->data.openreceivechannel.bitrate = htolel(0);
02421 
02422    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting OPEN_RECEIVE_CHANNEL_MESSAGE to %s, confid %d, partyid %d, ms %d, fmt %d, echo %d, brate %d\n",
02423       d->name, sub->callid, sub->callid, fmt.cur_ms, codec_ast2skinny(&fmt.format), 0, 0);
02424    transmit_response(d, req);
02425 }
02426 
02427 static void transmit_start_tone(struct skinny_device *d, int tone, int instance, int reference)
02428 {
02429    struct skinny_req *req;
02430    if (!(req = req_alloc(sizeof(struct start_tone_message), START_TONE_MESSAGE)))
02431       return;
02432    req->data.starttone.tone = htolel(tone);
02433    req->data.starttone.instance = htolel(instance);
02434    req->data.starttone.reference = htolel(reference);
02435 
02436    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting START_TONE_MESSAGE to %s, tone %d, inst %d, ref %d\n",
02437       d->name, tone, instance, reference);
02438    transmit_response(d, req);
02439 }
02440 
02441 static void transmit_stop_tone(struct skinny_device *d, int instance, int reference)
02442 {
02443    struct skinny_req *req;
02444    if (!(req = req_alloc(sizeof(struct stop_tone_message), STOP_TONE_MESSAGE)))
02445       return;
02446    req->data.stoptone.instance = htolel(instance);
02447    req->data.stoptone.reference = htolel(reference);
02448 
02449    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting STOP_TONE_MESSAGE to %s, inst %d, ref %d\n",
02450       d->name, instance, reference);
02451    transmit_response(d, req);
02452 }
02453 
02454 static void transmit_selectsoftkeys(struct skinny_device *d, int instance, int callid, int softkey)
02455 {
02456    struct skinny_req *req;
02457 
02458    if (!(req = req_alloc(sizeof(struct select_soft_keys_message), SELECT_SOFT_KEYS_MESSAGE)))
02459       return;
02460 
02461    req->data.selectsoftkey.instance = htolel(instance);
02462    req->data.selectsoftkey.reference = htolel(callid);
02463    req->data.selectsoftkey.softKeySetIndex = htolel(softkey);
02464    req->data.selectsoftkey.validKeyMask = htolel(0xFFFFFFFF);
02465 
02466    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting SELECT_SOFT_KEYS_MESSAGE to %s, inst %d, callid %d, softkey %d, mask 0xFFFFFFFF\n",
02467       d->name, instance, callid, softkey);
02468    transmit_response(d, req);
02469 }
02470 
02471 static void transmit_lamp_indication(struct skinny_device *d, int stimulus, int instance, int indication)
02472 {
02473    struct skinny_req *req;
02474 
02475    if (!(req = req_alloc(sizeof(struct set_lamp_message), SET_LAMP_MESSAGE)))
02476       return;
02477 
02478    req->data.setlamp.stimulus = htolel(stimulus);
02479    req->data.setlamp.stimulusInstance = htolel(instance);
02480    req->data.setlamp.deviceStimulus = htolel(indication);
02481 
02482    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting SET_LAMP_MESSAGE to %s, stim %d, inst %d, ind %d\n",
02483       d->name, stimulus, instance, indication);
02484    transmit_response(d, req);
02485 }
02486 
02487 static void transmit_ringer_mode(struct skinny_device *d, int mode)
02488 {
02489    struct skinny_req *req;
02490 
02491    if (!(req = req_alloc(sizeof(struct set_ringer_message), SET_RINGER_MESSAGE)))
02492       return;
02493 
02494    req->data.setringer.ringerMode = htolel(mode);
02495    /* XXX okay, I don't quite know what this is, but here's what happens (on a 7960).
02496       Note: The phone will always show as ringing on the display.
02497 
02498       1: phone will audibly ring over and over
02499       2: phone will audibly ring only once
02500       any other value, will NOT cause the phone to audibly ring
02501    */
02502    req->data.setringer.unknown1 = htolel(1);
02503    /* XXX the value here doesn't seem to change anything.  Must be higher than 0.
02504       Perhaps a packet capture can shed some light on this. */
02505    req->data.setringer.unknown2 = htolel(1);
02506 
02507    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting SET_RINGER_MESSAGE to %s, mode %d, unk1 1, unk2 1\n",
02508       d->name, mode);
02509    transmit_response(d, req);
02510 }
02511 
02512 static void transmit_clear_display_message(struct skinny_device *d, int instance, int reference)
02513 {
02514    struct skinny_req *req;
02515    if (!(req = req_alloc(sizeof(struct clear_display_message), CLEAR_DISPLAY_MESSAGE)))
02516       return;
02517 
02518    //what do we want hear CLEAR_DISPLAY_MESSAGE or CLEAR_PROMPT_STATUS???
02519    //if we are clearing the display, it appears there is no instance and refernece info (size 0)
02520    //req->data.clearpromptstatus.lineInstance = instance;
02521    //req->data.clearpromptstatus.callReference = reference;
02522 
02523    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting CLEAR_DISPLAY_MESSAGE to %s\n", d->name);
02524    transmit_response(d, req);
02525 }
02526 
02527 /* This function is not currently used, but will be (wedhorn)*/
02528 /* static void transmit_display_message(struct skinny_device *d, const char *text, int instance, int reference)
02529 {
02530    struct skinny_req *req;
02531 
02532    if (text == 0) {
02533       ast_verb(1, "Bug, Asked to display empty message\n");
02534       return;
02535    }
02536 
02537    if (!(req = req_alloc(sizeof(struct displaytext_message), DISPLAYTEXT_MESSAGE)))
02538       return;
02539 
02540    ast_copy_string(req->data.displaytext.text, text, sizeof(req->data.displaytext.text));
02541    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting DISPLAYTEXT_MESSAGE to %s, text %s\n", d->name, text);
02542    transmit_response(d, req);
02543 } */
02544 
02545 static void transmit_displaynotify(struct skinny_device *d, const char *text, int t)
02546 {
02547    struct skinny_req *req;
02548 
02549    if (!(req = req_alloc(sizeof(struct display_notify_message), DISPLAY_NOTIFY_MESSAGE)))
02550       return;
02551 
02552    ast_copy_string(req->data.displaynotify.displayMessage, text, sizeof(req->data.displaynotify.displayMessage));
02553    req->data.displaynotify.displayTimeout = htolel(t);
02554 
02555    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting DISPLAY_NOTIFY_MESSAGE to %s, text %s\n", d->name, text);
02556    transmit_response(d, req);
02557 }
02558 
02559 static void transmit_displaypromptstatus(struct skinny_device *d, const char *text, int t, int instance, int callid)
02560 {
02561    struct skinny_req *req;
02562 
02563    if (!(req = req_alloc(sizeof(struct display_prompt_status_message), DISPLAY_PROMPT_STATUS_MESSAGE)))
02564       return;
02565 
02566    ast_copy_string(req->data.displaypromptstatus.promptMessage, text, sizeof(req->data.displaypromptstatus.promptMessage));
02567    req->data.displaypromptstatus.messageTimeout = htolel(t);
02568    req->data.displaypromptstatus.lineInstance = htolel(instance);
02569    req->data.displaypromptstatus.callReference = htolel(callid);
02570 
02571    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting DISPLAY_PROMPT_STATUS_MESSAGE to %s, text %s\n", d->name, text);
02572    transmit_response(d, req);
02573 }
02574 
02575 static void transmit_clearpromptmessage(struct skinny_device *d, int instance, int callid)
02576 {
02577    struct skinny_req *req;
02578 
02579    if (!(req = req_alloc(sizeof(struct clear_prompt_message), CLEAR_PROMPT_MESSAGE)))
02580       return;
02581 
02582    req->data.clearpromptstatus.lineInstance = htolel(instance);
02583    req->data.clearpromptstatus.callReference = htolel(callid);
02584 
02585    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting CLEAR_PROMPT_MESSAGE to %s, inst %d, callid %d\n",
02586       d->name, instance, callid);
02587    transmit_response(d, req);
02588 }
02589 
02590 static void transmit_dialednumber(struct skinny_device *d, const char *text, int instance, int callid)
02591 {
02592    struct skinny_req *req;
02593 
02594    if (!(req = req_alloc(sizeof(struct dialed_number_message), DIALED_NUMBER_MESSAGE)))
02595       return;
02596 
02597    ast_copy_string(req->data.dialednumber.dialedNumber, text, sizeof(req->data.dialednumber.dialedNumber));
02598    req->data.dialednumber.lineInstance = htolel(instance);
02599    req->data.dialednumber.callReference = htolel(callid);
02600 
02601    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting DIALED_NUMBER_MESSAGE to %s, num %s, inst %d, callid %d\n",
02602       d->name, text, instance, callid);
02603    transmit_response(d, req);
02604 }
02605 
02606 static void transmit_closereceivechannel(struct skinny_device *d, struct skinny_subchannel *sub)
02607 {
02608    struct skinny_req *req;
02609 
02610    if (!(req = req_alloc(sizeof(struct close_receive_channel_message), CLOSE_RECEIVE_CHANNEL_MESSAGE)))
02611       return;
02612 
02613    req->data.closereceivechannel.conferenceId = htolel(0);
02614    req->data.closereceivechannel.partyId = htolel(sub->callid);
02615 
02616    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting CLOSE_RECEIVE_CHANNEL_MESSAGE to %s, confid %d, callid %d\n",
02617       d->name, 0, sub->callid);
02618    transmit_response(d, req);
02619 }
02620 
02621 static void transmit_stopmediatransmission(struct skinny_device *d, struct skinny_subchannel *sub)
02622 {
02623    struct skinny_req *req;
02624 
02625    if (!(req = req_alloc(sizeof(struct stop_media_transmission_message), STOP_MEDIA_TRANSMISSION_MESSAGE)))
02626       return;
02627 
02628    req->data.stopmedia.conferenceId = htolel(0);
02629    req->data.stopmedia.passThruPartyId = htolel(sub->callid);
02630 
02631    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting STOP_MEDIA_TRANSMISSION_MESSAGE to %s, confid %d, passthrupartyid %d\n",
02632       d->name, 0, sub->callid);
02633    transmit_response(d, req);
02634 }
02635 
02636 static void transmit_startmediatransmission(struct skinny_device *d, struct skinny_subchannel *sub, struct sockaddr_in dest, struct ast_format_list fmt)
02637 {
02638    struct skinny_req *req;
02639 
02640    if (d->protocolversion < 17) {
02641       if (!(req = req_alloc(sizeof(struct start_media_transmission_message_ip4), START_MEDIA_TRANSMISSION_MESSAGE)))
02642          return;
02643       req->data.startmedia_ip4.conferenceId = htolel(sub->callid);
02644       req->data.startmedia_ip4.passThruPartyId = htolel(sub->callid);
02645       req->data.startmedia_ip4.remoteIp = dest.sin_addr.s_addr;
02646       req->data.startmedia_ip4.remotePort = htolel(ntohs(dest.sin_port));
02647       req->data.startmedia_ip4.packetSize = htolel(fmt.cur_ms);
02648       req->data.startmedia_ip4.payloadType = htolel(codec_ast2skinny(&fmt.format));
02649       req->data.startmedia_ip4.qualifier.precedence = htolel(127);
02650       req->data.startmedia_ip4.qualifier.vad = htolel(0);
02651       req->data.startmedia_ip4.qualifier.packets = htolel(0);
02652       req->data.startmedia_ip4.qualifier.bitRate = htolel(0);
02653    } else {
02654       if (!(req = req_alloc(sizeof(struct start_media_transmission_message_ip6), START_MEDIA_TRANSMISSION_MESSAGE)))
02655          return;
02656       req->data.startmedia_ip6.conferenceId = htolel(sub->callid);
02657       req->data.startmedia_ip6.passThruPartyId = htolel(sub->callid);
02658       memcpy(req->data.startmedia_ip6.remoteIp, &dest.sin_addr.s_addr, sizeof(dest.sin_addr.s_addr));
02659       req->data.startmedia_ip6.remotePort = htolel(ntohs(dest.sin_port));
02660       req->data.startmedia_ip6.packetSize = htolel(fmt.cur_ms);
02661       req->data.startmedia_ip6.payloadType = htolel(codec_ast2skinny(&fmt.format));
02662       req->data.startmedia_ip6.qualifier.precedence = htolel(127);
02663       req->data.startmedia_ip6.qualifier.vad = htolel(0);
02664       req->data.startmedia_ip6.qualifier.packets = htolel(0);
02665       req->data.startmedia_ip6.qualifier.bitRate = htolel(0);
02666    }
02667 
02668    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting START_MEDIA_TRANSMISSION_MESSAGE to %s, callid %d, passthrupartyid %d, ip %s:%d, ms %d, fmt %d, prec 127\n",
02669       d->name, sub->callid, sub->callid, ast_inet_ntoa(dest.sin_addr), dest.sin_port, fmt.cur_ms, codec_ast2skinny(&fmt.format));
02670    transmit_response(d, req);
02671 }
02672 
02673 static void transmit_activatecallplane(struct skinny_device *d, struct skinny_line *l)
02674 {
02675    struct skinny_req *req;
02676 
02677    if (!(req = req_alloc(sizeof(struct activate_call_plane_message), ACTIVATE_CALL_PLANE_MESSAGE)))
02678       return;
02679 
02680    req->data.activatecallplane.lineInstance = htolel(l->instance);
02681 
02682    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting ACTIVATE_CALL_PLANE_MESSAGE to %s, inst %d\n",
02683       d->name, l->instance);
02684    transmit_response(d, req);
02685 }
02686 
02687 static void transmit_callstate(struct skinny_device *d, int buttonInstance, unsigned callid, int state)
02688 {
02689    struct skinny_req *req;
02690 
02691    if (!(req = req_alloc(sizeof(struct call_state_message), CALL_STATE_MESSAGE)))
02692       return;
02693 
02694    req->data.callstate.callState = htolel(state);
02695    req->data.callstate.lineInstance = htolel(buttonInstance);
02696    req->data.callstate.callReference = htolel(callid);
02697 
02698    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting CALL_STATE_MESSAGE to %s, state %s, inst %d, callid %d\n",
02699       d->name, callstate2str(state), buttonInstance, callid);
02700    transmit_response(d, req);
02701 }
02702 
02703 static void transmit_cfwdstate(struct skinny_device *d, struct skinny_line *l)
02704 {
02705    struct skinny_req *req;
02706    int anyon = 0;
02707 
02708    if (!(req = req_alloc(sizeof(struct forward_stat_message), FORWARD_STAT_MESSAGE)))
02709       return;
02710 
02711    if (l->cfwdtype & SKINNY_CFWD_ALL) {
02712       if (!ast_strlen_zero(l->call_forward_all)) {
02713          ast_copy_string(req->data.forwardstat.fwdallnum, l->call_forward_all, sizeof(req->data.forwardstat.fwdallnum));
02714          req->data.forwardstat.fwdall = htolel(1);
02715          anyon++;
02716       } else {
02717          req->data.forwardstat.fwdall = htolel(0);
02718       }
02719    }
02720    if (l->cfwdtype & SKINNY_CFWD_BUSY) {
02721       if (!ast_strlen_zero(l->call_forward_busy)) {
02722          ast_copy_string(req->data.forwardstat.fwdbusynum, l->call_forward_busy, sizeof(req->data.forwardstat.fwdbusynum));
02723          req->data.forwardstat.fwdbusy = htolel(1);
02724          anyon++;
02725       } else {
02726          req->data.forwardstat.fwdbusy = htolel(0);
02727       }
02728    }
02729    if (l->cfwdtype & SKINNY_CFWD_NOANSWER) {
02730       if (!ast_strlen_zero(l->call_forward_noanswer)) {
02731          ast_copy_string(req->data.forwardstat.fwdnoanswernum, l->call_forward_noanswer, sizeof(req->data.forwardstat.fwdnoanswernum));
02732          req->data.forwardstat.fwdnoanswer = htolel(1);
02733          anyon++;
02734       } else {
02735          req->data.forwardstat.fwdnoanswer = htolel(0);
02736       }
02737    }
02738    req->data.forwardstat.lineNumber = htolel(l->instance);
02739    if (anyon)
02740       req->data.forwardstat.activeforward = htolel(7);
02741    else
02742       req->data.forwardstat.activeforward = htolel(0);
02743 
02744    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting FORWARD_STAT_MESSAGE to %s, inst %d, all %s, busy %s, noans %s, acitve %d\n",
02745       d->name, l->instance, l->call_forward_all, l->call_forward_busy, l->call_forward_noanswer, anyon ? 7 : 0);
02746    transmit_response(d, req);
02747 }
02748 
02749 static void transmit_speeddialstatres(struct skinny_device *d, struct skinny_speeddial *sd)
02750 {
02751    struct skinny_req *req;
02752 
02753    if (!(req = req_alloc(sizeof(struct speed_dial_stat_res_message), SPEED_DIAL_STAT_RES_MESSAGE)))
02754       return;
02755 
02756    req->data.speeddialreq.speedDialNumber = htolel(sd->instance);
02757    ast_copy_string(req->data.speeddial.speedDialDirNumber, sd->exten, sizeof(req->data.speeddial.speedDialDirNumber));
02758    ast_copy_string(req->data.speeddial.speedDialDisplayName, sd->label, sizeof(req->data.speeddial.speedDialDisplayName));
02759 
02760    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting SPEED_DIAL_STAT_RES_MESSAGE to %s, inst %d, dir %s, display %s\n",
02761       d->name, sd->instance, sd->exten, sd->label);
02762    transmit_response(d, req);
02763 }
02764 
02765 //static void transmit_linestatres(struct skinny_device *d, struct skinny_line *l)
02766 static void transmit_linestatres(struct skinny_device *d, int instance)
02767 {
02768    struct skinny_req *req;
02769    struct skinny_line *l;
02770    struct skinny_speeddial *sd;
02771 
02772    if (!(req = req_alloc(sizeof(struct line_stat_res_message), LINE_STAT_RES_MESSAGE)))
02773       return;
02774 
02775    if ((l = find_line_by_instance(d, instance))) {
02776       req->data.linestat.lineNumber = letohl(l->instance);
02777       memcpy(req->data.linestat.lineDirNumber, l->name, sizeof(req->data.linestat.lineDirNumber));
02778       memcpy(req->data.linestat.lineDisplayName, l->label, sizeof(req->data.linestat.lineDisplayName));
02779    } else if ((sd = find_speeddial_by_instance(d, instance, 1))) {
02780       req->data.linestat.lineNumber = letohl(sd->instance);
02781       memcpy(req->data.linestat.lineDirNumber, sd->label, sizeof(req->data.linestat.lineDirNumber));
02782       memcpy(req->data.linestat.lineDisplayName, sd->label, sizeof(req->data.linestat.lineDisplayName));
02783    }
02784 
02785    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting LINE_STAT_RES_MESSAGE to %s, inst %d, num %s, label %s\n",
02786       d->name, l->instance, req->data.linestat.lineDirNumber, req->data.linestat.lineDisplayName);
02787    transmit_response(d, req);
02788 }
02789 
02790 static void transmit_definetimedate(struct skinny_device *d)
02791 {
02792    struct skinny_req *req;
02793    struct timeval now = ast_tvnow();
02794    struct ast_tm cmtime;
02795 
02796    if (!(req = req_alloc(sizeof(struct definetimedate_message), DEFINETIMEDATE_MESSAGE)))
02797       return;
02798 
02799    ast_localtime(&now, &cmtime, NULL);
02800    req->data.definetimedate.year = htolel(cmtime.tm_year+1900);
02801    req->data.definetimedate.month = htolel(cmtime.tm_mon+1);
02802    req->data.definetimedate.dayofweek = htolel(cmtime.tm_wday);
02803    req->data.definetimedate.day = htolel(cmtime.tm_mday);
02804    req->data.definetimedate.hour = htolel(cmtime.tm_hour);
02805    req->data.definetimedate.minute = htolel(cmtime.tm_min);
02806    req->data.definetimedate.seconds = htolel(cmtime.tm_sec);
02807    req->data.definetimedate.milliseconds = htolel(cmtime.tm_usec / 1000);
02808    req->data.definetimedate.timestamp = htolel(now.tv_sec);
02809 
02810    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting DEFINETIMEDATE_MESSAGE to %s, date %d %d %d dow %d time %d:%d:%d.%d\n",
02811       d->name, req->data.definetimedate.year, req->data.definetimedate.month, req->data.definetimedate.day, req->data.definetimedate.dayofweek,
02812       req->data.definetimedate.hour, req->data.definetimedate.minute, req->data.definetimedate.seconds, req->data.definetimedate.milliseconds);
02813    transmit_response(d, req);
02814 }
02815 
02816 static void transmit_versionres(struct skinny_device *d)
02817 {
02818    struct skinny_req *req;
02819    if (!(req = req_alloc(sizeof(struct version_res_message), VERSION_RES_MESSAGE)))
02820       return;
02821 
02822    ast_copy_string(req->data.version.version, d->version_id, sizeof(req->data.version.version));
02823 
02824    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting VERSION_RES_MESSAGE to %s, version %s\n", d->name, d->version_id);
02825    transmit_response(d, req);
02826 }
02827 
02828 static void transmit_serverres(struct skinny_device *d)
02829 {
02830    struct skinny_req *req;
02831    if (!(req = req_alloc(sizeof(struct server_res_message), SERVER_RES_MESSAGE)))
02832       return;
02833 
02834    memcpy(req->data.serverres.server[0].serverName, ourhost,
02835          sizeof(req->data.serverres.server[0].serverName));
02836    req->data.serverres.serverListenPort[0] = htolel(ourport);
02837    req->data.serverres.serverIpAddr[0] = htolel(d->ourip.s_addr);
02838 
02839    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting SERVER_RES_MESSAGE to %s, srvname %s %s:%d\n",
02840       d->name, ourhost, ast_inet_ntoa(d->ourip), ourport);
02841    transmit_response(d, req);
02842 }
02843 
02844 static void transmit_softkeysetres(struct skinny_device *d)
02845 {
02846    struct skinny_req *req;
02847    int i;
02848    int x;
02849    int y;
02850    const struct soft_key_definitions *softkeymode = soft_key_default_definitions;
02851 
02852    if (!(req = req_alloc(sizeof(struct soft_key_set_res_message), SOFT_KEY_SET_RES_MESSAGE)))
02853       return;
02854 
02855    SKINNY_DEBUG(DEBUG_TEMPLATE, 3, "Creating Softkey Template\n");
02856 
02857    req->data.softkeysets.softKeySetOffset = htolel(0);
02858    req->data.softkeysets.softKeySetCount = htolel(13);
02859    req->data.softkeysets.totalSoftKeySetCount = htolel(13);
02860    for (x = 0; x < sizeof(soft_key_default_definitions) / sizeof(struct soft_key_definitions); x++) {
02861       const uint8_t *defaults = softkeymode->defaults;
02862       /* XXX I wanted to get the size of the array dynamically, but that wasn't wanting to work.
02863          This will have to do for now. */
02864       for (y = 0; y < softkeymode->count; y++) {
02865          for (i = 0; i < (sizeof(soft_key_template_default) / sizeof(struct soft_key_template_definition)); i++) {
02866             if (defaults[y] == i+1) {
02867                req->data.softkeysets.softKeySetDefinition[softkeymode->mode].softKeyTemplateIndex[y] = (i+1);
02868                req->data.softkeysets.softKeySetDefinition[softkeymode->mode].softKeyInfoIndex[y] = htoles(i+301);
02869                SKINNY_DEBUG(DEBUG_TEMPLATE, 4, "softKeySetDefinition : softKeyTemplateIndex: %d softKeyInfoIndex: %d\n",
02870                   i+1, i+301);
02871             }
02872          }
02873       }
02874       softkeymode++;
02875    }
02876 
02877    SKINNY_DEBUG(DEBUG_PACKET | DEBUG_TEMPLATE, 3, "Transmitting SOFT_KEY_SET_RES_MESSAGE to %s, template data\n",
02878       d->name);
02879    transmit_response(d, req);
02880 }
02881 
02882 static void transmit_softkeytemplateres(struct skinny_device *d)
02883 {
02884    struct skinny_req *req;
02885    if (!(req = req_alloc(sizeof(struct soft_key_template_res_message), SOFT_KEY_TEMPLATE_RES_MESSAGE)))
02886       return;
02887 
02888    req->data.softkeytemplate.softKeyOffset = htolel(0);
02889    req->data.softkeytemplate.softKeyCount = htolel(sizeof(soft_key_template_default) / sizeof(struct soft_key_template_definition));
02890    req->data.softkeytemplate.totalSoftKeyCount = htolel(sizeof(soft_key_template_default) / sizeof(struct soft_key_template_definition));
02891    memcpy(req->data.softkeytemplate.softKeyTemplateDefinition,
02892       soft_key_template_default,
02893       sizeof(soft_key_template_default));
02894 
02895    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting SOFT_KEY_TEMPLATE_RES_MESSAGE to %s, offset 0, keycnt %d, totalkeycnt %d, template data\n",
02896       d->name, req->data.softkeytemplate.softKeyCount, req->data.softkeytemplate.totalSoftKeyCount);
02897    transmit_response(d, req);
02898 }
02899 
02900 static void transmit_reset(struct skinny_device *d, int fullrestart)
02901 {
02902    struct skinny_req *req;
02903   
02904    if (!(req = req_alloc(sizeof(struct reset_message), RESET_MESSAGE)))
02905       return;
02906   
02907    if (fullrestart)
02908       req->data.reset.resetType = 2;
02909    else
02910       req->data.reset.resetType = 1;
02911 
02912    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting RESET_MESSAGE to %s, type %s\n",
02913       d->name, (fullrestart) ? "Restarting" : "Resetting");
02914    transmit_response(d, req);
02915 }
02916 
02917 static void transmit_keepaliveack(struct skinny_device *d)
02918 {
02919    struct skinny_req *req;
02920 
02921    if (!(req = req_alloc(0, KEEP_ALIVE_ACK_MESSAGE)))
02922       return;
02923 
02924    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting KEEP_ALIVE_ACK_MESSAGE to %s\n", d->name);
02925    transmit_response(d, req);
02926 }
02927 
02928 static void transmit_registerack(struct skinny_device *d)
02929 {
02930    struct skinny_req *req;
02931 
02932    if (!(req = req_alloc(sizeof(struct register_ack_message), REGISTER_ACK_MESSAGE)))
02933       return;
02934 
02935    req->data.regack.res[0] = '0';
02936    req->data.regack.res[1] = '\0';
02937    req->data.regack.keepAlive = htolel(keep_alive);
02938    memcpy(req->data.regack.dateTemplate, date_format, sizeof(req->data.regack.dateTemplate));
02939    req->data.regack.res2[0] = '0';
02940    req->data.regack.res2[1] = '\0';
02941    req->data.regack.secondaryKeepAlive = htolel(keep_alive);
02942 
02943 #ifdef AST_DEVMODE
02944    {
02945    short res = req->data.regack.res[0] << 8 | req->data.regack.res[1];
02946    int res2 = req->data.regack.res2[0] << 24 | req->data.regack.res2[1] << 16 | req->data.regack.res2[2] << 8 | req->data.regack.res2[3];
02947    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting REGISTER_ACK_MESSAGE to %s, keepalive %d, datetemplate %s, seckeepalive %d, res 0x%04x, res2 0x%08x\n",
02948       d->name, keep_alive, date_format, keep_alive, res, res2);
02949    }
02950 #endif
02951 
02952    transmit_response(d, req);
02953 }
02954 
02955 static void transmit_capabilitiesreq(struct skinny_device *d)
02956 {
02957    struct skinny_req *req;
02958 
02959    if (!(req = req_alloc(0, CAPABILITIES_REQ_MESSAGE)))
02960       return;
02961 
02962    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting CAPABILITIES_REQ_MESSAGE to %s\n", d->name);
02963    transmit_response(d, req);
02964 }
02965 
02966 static void transmit_backspace(struct skinny_device *d, int instance, unsigned callid)
02967 {
02968    struct skinny_req *req;
02969 
02970    if (!(req = req_alloc(sizeof(struct bksp_req_message), BKSP_REQ_MESSAGE)))
02971       return;
02972 
02973    req->data.bkspmessage.instance = htolel(instance);
02974    req->data.bkspmessage.callreference = htolel(callid);
02975 
02976    SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting BKSP_REQ_MESSAGE to %s, inst %d, callid %d \n",
02977       d->name, instance, callid);
02978    transmit_response(d, req);
02979 }
02980 
02981 static int skinny_extensionstate_cb(char *context, char *exten, struct ast_state_cb_info *info, void *data)
02982 {
02983    struct skinny_container *container = data;
02984    struct skinny_device *d = NULL;
02985    char hint[AST_MAX_EXTENSION];
02986    int state = info->exten_state;
02987 
02988    /* only interested in device state here */
02989    if (info->reason != AST_HINT_UPDATE_DEVICE) {
02990       return 0;
02991    }
02992 
02993    if (container->type == SKINNY_SDCONTAINER) {
02994       struct skinny_speeddial *sd = container->data;
02995       d = sd->parent;
02996 
02997       SKINNY_DEBUG(DEBUG_HINT, 3, "Got hint %s on speeddial %s\n", ast_extension_state2str(state), sd->label);
02998 
02999       if (ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, sd->context, sd->exten)) {
03000          /* If they are not registered, we will override notification and show no availability */
03001          if (ast_device_state(hint) == AST_DEVICE_UNAVAILABLE) {
03002             transmit_lamp_indication(d, STIMULUS_LINE, sd->instance, SKINNY_LAMP_FLASH);
03003             transmit_callstate(d, sd->instance, 0, SKINNY_ONHOOK);
03004             return 0;
03005          }
03006          switch (state) {
03007          case AST_EXTENSION_DEACTIVATED: /* Retry after a while */
03008          case AST_EXTENSION_REMOVED:     /* Extension is gone */
03009             SKINNY_DEBUG(DEBUG_HINT, 3, "Extension state: Watcher for hint %s %s. Notify Device %s\n",
03010                exten, state == AST_EXTENSION_DEACTIVATED ? "deactivated" : "removed", d->name);
03011             sd->stateid = -1;
03012             transmit_lamp_indication(d, STIMULUS_LINE, sd->instance, SKINNY_LAMP_OFF);
03013             transmit_callstate(d, sd->instance, 0, SKINNY_ONHOOK);
03014             break;
03015          case AST_EXTENSION_RINGING:
03016          case AST_EXTENSION_UNAVAILABLE:
03017             transmit_lamp_indication(d, STIMULUS_LINE, sd->instance, SKINNY_LAMP_BLINK);
03018             transmit_callstate(d, sd->instance, 0, SKINNY_RINGIN);
03019             break;
03020          case AST_EXTENSION_BUSY: /* callstate = SKINNY_BUSY wasn't wanting to work - I'll settle for this */
03021          case AST_EXTENSION_INUSE:
03022             transmit_lamp_indication(d, STIMULUS_LINE, sd->instance, SKINNY_LAMP_ON);
03023             transmit_callstate(d, sd->instance, 0, SKINNY_CALLREMOTEMULTILINE);
03024             break;
03025          case AST_EXTENSION_ONHOLD:
03026             transmit_lamp_indication(d, STIMULUS_LINE, sd->instance, SKINNY_LAMP_WINK);
03027             transmit_callstate(d, sd->instance, 0, SKINNY_HOLD);
03028             break;
03029          case AST_EXTENSION_NOT_INUSE:
03030          default:
03031             transmit_lamp_indication(d, STIMULUS_LINE, sd->instance, SKINNY_LAMP_OFF);
03032             transmit_callstate(d, sd->instance, 0, SKINNY_ONHOOK);
03033             break;
03034          }
03035       }
03036       sd->laststate = state;
03037    } else if (container->type == SKINNY_SUBLINECONTAINER) {
03038       struct skinny_subline *subline = container->data;
03039       struct skinny_line *l = subline->line;
03040       d = l->device;
03041 
03042       SKINNY_DEBUG(DEBUG_HINT, 3, "Got hint %s on subline %s (%s@%s)\n", ast_extension_state2str(state), subline->name, exten, context);
03043 
03044       subline->extenstate = state;
03045 
03046       if (subline->callid == 0) {
03047          return 0;
03048       }
03049 
03050       switch (state) {
03051       case AST_EXTENSION_RINGING: /* Handled by normal ringin */
03052          break;
03053       case AST_EXTENSION_INUSE:
03054          if (subline->sub && (subline->sub->substate == SKINNY_CONNECTED)) { /* Device has a real call */
03055             transmit_callstate(d, l->instance, subline->callid, SKINNY_CONNECTED);
03056             transmit_selectsoftkeys(d, l->instance, subline->callid, KEYDEF_CONNECTED);
03057             transmit_displaypromptstatus(d, "Connected", 0, l->instance, subline->callid);
03058          } else { /* Some other device has active call */
03059             transmit_callstate(d, l->instance, subline->callid, SKINNY_CALLREMOTEMULTILINE);
03060             transmit_selectsoftkeys(d, l->instance, subline->callid, KEYDEF_SLACONNECTEDNOTACTIVE);
03061             transmit_displaypromptstatus(d, "In Use", 0, l->instance, subline->callid);
03062          }
03063          transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
03064          transmit_ringer_mode(d, SKINNY_RING_OFF);
03065          transmit_activatecallplane(d, l);
03066          break;
03067       case AST_EXTENSION_ONHOLD:
03068          transmit_callstate(d, l->instance, subline->callid, SKINNY_HOLD);
03069          transmit_selectsoftkeys(d, l->instance, subline->callid, KEYDEF_SLAHOLD);
03070          transmit_displaypromptstatus(d, "Hold", 0, l->instance, subline->callid);
03071          transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK);
03072          transmit_activatecallplane(d, l);
03073          break;
03074       case AST_EXTENSION_NOT_INUSE:
03075          transmit_callstate(d, l->instance, subline->callid, SKINNY_ONHOOK);
03076          transmit_selectsoftkeys(d, l->instance, subline->callid, KEYDEF_ONHOOK);
03077          transmit_clearpromptmessage(d, l->instance, subline->callid);
03078          transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF);
03079          transmit_activatecallplane(d, l);
03080          subline->callid = 0;
03081          break;
03082       default:
03083          ast_log(LOG_WARNING, "AST_EXTENSION_STATE %s not configured\n", ast_extension_state2str(state));
03084       }
03085    } else {
03086       ast_log(LOG_WARNING, "Invalid data supplied to skinny_extensionstate_cb\n");
03087    }
03088 
03089    return 0;
03090 }
03091 
03092 static void update_connectedline(struct skinny_subchannel *sub, const void *data, size_t datalen)
03093 {
03094    struct ast_channel *c = sub->owner;
03095    struct skinny_line *l = sub->line;
03096    struct skinny_device *d = l->device;
03097 
03098    if (!d->session) {
03099       return;
03100    }
03101 
03102    if (!ast_channel_caller(c)->id.number.valid
03103       || ast_strlen_zero(ast_channel_caller(c)->id.number.str)
03104       || !ast_channel_connected(c)->id.number.valid
03105       || ast_strlen_zero(ast_channel_connected(c)->id.number.str))
03106       return;
03107 
03108    SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - Updating\n", sub->callid);
03109 
03110    send_callinfo(sub);
03111 }
03112 
03113 static void mwi_event_cb(const struct ast_event *event, void *userdata)
03114 {
03115    struct skinny_line *l = userdata;
03116    struct skinny_device *d = l->device;
03117    struct skinny_line *l2;
03118    int dev_msgs = 0;
03119    
03120    if (!d || !d->session) {
03121       return;
03122    }
03123 
03124    if (event) {
03125       l->newmsgs = ast_event_get_ie_uint(event, AST_EVENT_IE_NEWMSGS);
03126    }
03127 
03128    if (l->newmsgs) {
03129       transmit_lamp_indication(d, STIMULUS_VOICEMAIL, l->instance, l->mwiblink?SKINNY_LAMP_BLINK:SKINNY_LAMP_ON);
03130    } else {
03131       transmit_lamp_indication(d, STIMULUS_VOICEMAIL, l->instance, SKINNY_LAMP_OFF);
03132    }
03133 
03134    /* find out wether the device lamp should be on or off */
03135    AST_LIST_TRAVERSE(&d->lines, l2, list) {
03136       if (l2->newmsgs) {
03137          dev_msgs++;
03138       }
03139    }
03140 
03141    if (dev_msgs) {
03142       transmit_lamp_indication(d, STIMULUS_VOICEMAIL, 0, d->mwiblink?SKINNY_LAMP_BLINK:SKINNY_LAMP_ON);
03143    } else {
03144       transmit_lamp_indication(d, STIMULUS_VOICEMAIL, 0, SKINNY_LAMP_OFF);
03145    }
03146    ast_verb(3, "Skinny mwi_event_cb found %d new messages\n", l->newmsgs);
03147 }
03148 
03149 /* I do not believe skinny can deal with video.
03150    Anyone know differently? */
03151 /* Yes, it can.  Currently 7985 and Cisco VT Advantage do video. */
03152 static enum ast_rtp_glue_result skinny_get_vrtp_peer(struct ast_channel *c, struct ast_rtp_instance **instance)
03153 {
03154    struct skinny_subchannel *sub = NULL;
03155 
03156    if (!(sub = ast_channel_tech_pvt(c)) || !(sub->vrtp))
03157       return AST_RTP_GLUE_RESULT_FORBID;
03158 
03159    ao2_ref(sub->vrtp, +1);
03160    *instance = sub->vrtp;
03161 
03162    return AST_RTP_GLUE_RESULT_REMOTE;
03163 }
03164 
03165 static enum ast_rtp_glue_result skinny_get_rtp_peer(struct ast_channel *c, struct ast_rtp_instance **instance)
03166 {
03167    struct skinny_subchannel *sub = NULL;
03168    struct skinny_line *l;
03169    enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_REMOTE;
03170 
03171    SKINNY_DEBUG(DEBUG_AUDIO, 4, "skinny_get_rtp_peer() Channel = %s\n", ast_channel_name(c));
03172 
03173    if (!(sub = ast_channel_tech_pvt(c)))
03174       return AST_RTP_GLUE_RESULT_FORBID;
03175 
03176    ast_mutex_lock(&sub->lock);
03177 
03178    if (!(sub->rtp)){
03179       ast_mutex_unlock(&sub->lock);
03180       return AST_RTP_GLUE_RESULT_FORBID;
03181    }
03182 
03183    ao2_ref(sub->rtp, +1);
03184    *instance = sub->rtp;
03185 
03186    l = sub->line;
03187 
03188    if (!l->directmedia || l->nat){
03189       res = AST_RTP_GLUE_RESULT_LOCAL;
03190       SKINNY_DEBUG(DEBUG_AUDIO, 4, "skinny_get_rtp_peer() Using AST_RTP_GLUE_RESULT_LOCAL \n");
03191    }
03192 
03193    ast_mutex_unlock(&sub->lock);
03194 
03195    return res;
03196 
03197 }
03198 
03199 static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp_instance *rtp, struct ast_rtp_instance *vrtp, struct ast_rtp_instance *trtp, const struct ast_format_cap *codecs, int nat_active)
03200 {
03201    struct skinny_subchannel *sub;
03202    struct skinny_line *l;
03203    struct skinny_device *d;
03204    struct ast_format_list fmt;
03205    struct sockaddr_in us = { 0, };
03206    struct sockaddr_in them = { 0, };
03207    struct ast_sockaddr them_tmp;
03208    struct ast_sockaddr us_tmp;
03209    
03210    sub = ast_channel_tech_pvt(c);
03211 
03212    if (ast_channel_state(c) != AST_STATE_UP)
03213       return 0;
03214 
03215    if (!sub) {
03216       return -1;
03217    }
03218 
03219    l = sub->line;
03220    d = l->device;
03221 
03222    if (rtp){
03223       struct ast_format tmpfmt;
03224       ast_rtp_instance_get_remote_address(rtp, &them_tmp);
03225       ast_sockaddr_to_sin(&them_tmp, &them);
03226 
03227       /* Shutdown any early-media or previous media on re-invite */
03228       transmit_stopmediatransmission(d, sub);
03229 
03230       SKINNY_DEBUG(DEBUG_AUDIO, 4, "Peerip = %s:%d\n", ast_inet_ntoa(them.sin_addr), ntohs(them.sin_port));
03231 
03232       ast_best_codec(l->cap, &tmpfmt);
03233       fmt = ast_codec_pref_getsize(&l->prefs, &tmpfmt);
03234 
03235       SKINNY_DEBUG(DEBUG_AUDIO, 4, "Setting payloadType to '%s' (%d ms)\n", ast_getformatname(&fmt.format), fmt.cur_ms);
03236 
03237       if (!(l->directmedia) || (l->nat)){
03238          ast_rtp_instance_get_local_address(rtp, &us_tmp);
03239          ast_sockaddr_to_sin(&us_tmp, &us);
03240          us.sin_addr.s_addr = us.sin_addr.s_addr ? us.sin_addr.s_addr : d->ourip.s_addr;
03241          transmit_startmediatransmission(d, sub, us, fmt);
03242       } else {
03243          transmit_startmediatransmission(d, sub, them, fmt);
03244       }
03245 
03246       return 0;
03247    }
03248    /* Need a return here to break the bridge */
03249    return 0;
03250 }
03251 
03252 static struct ast_rtp_glue skinny_rtp_glue = {
03253    .type = "Skinny",
03254    .get_rtp_info = skinny_get_rtp_peer,
03255    .get_vrtp_info = skinny_get_vrtp_peer,
03256    .update_peer = skinny_set_rtp_peer,
03257 };
03258 
03259 #ifdef AST_DEVMODE
03260 static char *skinny_debugs(void)
03261 {
03262    char *ptr;
03263    int posn = 0;
03264 
03265    ptr = dbgcli_buf;
03266    strncpy(ptr, "\0", 1);
03267    if (skinnydebug & DEBUG_GENERAL) {
03268       strncpy(ptr, "general ", 8);
03269       posn += 8;
03270       ptr += 8;
03271    }
03272    if (skinnydebug & DEBUG_SUB) {
03273       strncpy(ptr, "sub ", 4);
03274       posn += 4;
03275       ptr += 4;
03276    }
03277    if (skinnydebug & DEBUG_AUDIO) {
03278       strncpy(ptr, "audio ", 6);
03279       posn += 6;
03280       ptr += 6;
03281    }
03282    if (skinnydebug & DEBUG_PACKET) {
03283       strncpy(ptr, "packet ", 7);
03284       posn += 7;
03285       ptr += 7;
03286    }
03287    if (skinnydebug & DEBUG_LOCK) {
03288       strncpy(ptr, "lock ", 5);
03289       posn += 5;
03290       ptr += 5;
03291    }
03292    if (skinnydebug & DEBUG_TEMPLATE) {
03293       strncpy(ptr, "template ", 9);
03294       posn += 9;
03295       ptr += 9;
03296    }
03297    if (skinnydebug & DEBUG_THREAD) {
03298       strncpy(ptr, "thread ", 7);
03299       posn += 7;
03300       ptr += 7;
03301    }
03302    if (skinnydebug & DEBUG_HINT) {
03303       strncpy(ptr, "hint ", 5);
03304       posn += 5;
03305       ptr += 5;
03306    }
03307    if (posn > 0) {
03308       strncpy(--ptr, "\0", 1);
03309    }
03310    return dbgcli_buf;
03311 }
03312 
03313 static char *handle_skinny_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03314 {
03315    int i;
03316    int result = 0;
03317    const char *arg;
03318    int bitmask;
03319    int negate;
03320 
03321    switch (cmd) {
03322    case CLI_INIT:
03323       e->command = "skinny debug {audio|hint|lock|off|packet|show|sub|template|thread}";
03324       e->usage =
03325          "Usage: skinny debug {audio|hint|lock|off|packet|show|sub|template|thread}\n"
03326          "       Enables/Disables various Skinny debugging messages\n";
03327       return NULL;
03328    case CLI_GENERATE:
03329       return NULL;
03330    }
03331 
03332    if (a->argc < 3)
03333       return CLI_SHOWUSAGE;
03334 
03335    if (a->argc == 3 && !strncasecmp(a->argv[e->args-1], "show", 4)) {
03336       ast_cli(a->fd, "Skinny Debugging - %s\n", skinny_debugs());
03337       return CLI_SUCCESS;
03338    }
03339 
03340    for(i = e->args-1; i < a->argc; i++) {
03341       result++;
03342       arg = a->argv[i];
03343 
03344       if (!strncasecmp(arg, "off", 3)) {
03345          skinnydebug = 0;
03346          continue;
03347       }
03348 
03349       if (!strncasecmp(arg, "-", 1) || !strncasecmp(arg, "!", 1)) {
03350          negate = 1;
03351          arg++;
03352       } else if (!strncasecmp(arg, "+", 1)) {
03353          negate = 0;
03354          arg++;
03355       } else {
03356          negate = 0;
03357       }
03358 
03359       if (!strncasecmp(arg, "general", 7)) {
03360          bitmask = DEBUG_GENERAL;
03361       } else if (!strncasecmp(arg, "sub", 3)) {
03362          bitmask = DEBUG_SUB;
03363       } else if (!strncasecmp(arg, "packet", 6)) {
03364          bitmask = DEBUG_PACKET;
03365       } else if (!strncasecmp(arg, "audio", 5)) {
03366          bitmask = DEBUG_AUDIO;
03367       } else if (!strncasecmp(arg, "lock", 4)) {
03368          bitmask = DEBUG_LOCK;
03369       } else if (!strncasecmp(arg, "template", 8)) {
03370          bitmask = DEBUG_TEMPLATE;
03371       } else if (!strncasecmp(arg, "thread", 6)) {
03372          bitmask = DEBUG_THREAD;
03373       } else if (!strncasecmp(arg, "hint", 4)) {
03374          bitmask = DEBUG_HINT;
03375       } else {
03376          ast_cli(a->fd, "Skinny Debugging - option '%s' unknown\n", a->argv[i]);
03377          result--;
03378          continue;
03379       }
03380 
03381       if (negate) {
03382          skinnydebug &= ~bitmask;
03383       } else {
03384          skinnydebug |= bitmask;
03385       }
03386    }
03387    if (result) {
03388       ast_cli(a->fd, "Skinny Debugging - %s\n", skinnydebug ? skinny_debugs() : "off");
03389       return CLI_SUCCESS;
03390    } else {
03391       return CLI_SHOWUSAGE;
03392    }
03393 }
03394 #endif
03395 
03396 static char *handle_skinny_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03397 {
03398    switch (cmd) {
03399    case CLI_INIT:
03400       e->command = "skinny reload";
03401       e->usage =
03402          "Usage: skinny reload\n"
03403          "       Reloads the chan_skinny configuration\n";
03404       return NULL;
03405    case CLI_GENERATE:
03406       return NULL;
03407    }
03408    
03409    if (a->argc != e->args)
03410       return CLI_SHOWUSAGE;
03411 
03412    skinny_reload();
03413    return CLI_SUCCESS;
03414 
03415 }
03416 
03417 static char *complete_skinny_devices(const char *word, int state)
03418 {
03419    struct skinny_device *d;
03420    char *result = NULL;
03421    int wordlen = strlen(word), which = 0;
03422 
03423    AST_LIST_TRAVERSE(&devices, d, list) {
03424       if (!strncasecmp(word, d->id, wordlen) && ++which > state)
03425          result = ast_strdup(d->id);
03426    }
03427 
03428    return result;
03429 }
03430 
03431 static char *complete_skinny_show_device(const char *line, const char *word, int pos, int state)
03432 {
03433    return (pos == 3 ? ast_strdup(complete_skinny_devices(word, state)) : NULL);
03434 }
03435 
03436 static char *complete_skinny_reset(const char *line, const char *word, int pos, int state)
03437 {
03438    return (pos == 2 ? ast_strdup(complete_skinny_devices(word, state)) : NULL);
03439 }
03440 
03441 static char *complete_skinny_show_line(const char *line, const char *word, int pos, int state)
03442 {
03443    struct skinny_device *d;
03444    struct skinny_line *l;
03445    char *result = NULL;
03446    int wordlen = strlen(word), which = 0;
03447 
03448    if (pos != 3)
03449       return NULL;
03450    
03451    AST_LIST_TRAVERSE(&devices, d, list) {
03452       AST_LIST_TRAVERSE(&d->lines, l, list) {
03453          if (!strncasecmp(word, l->name, wordlen) && ++which > state)
03454             result = ast_strdup(l->name);
03455       }
03456    }
03457 
03458    return result;
03459 }
03460 
03461 static char *handle_skinny_reset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03462 {
03463    struct skinny_device *d;
03464 
03465    switch (cmd) {
03466    case CLI_INIT:
03467       e->command = "skinny reset";
03468       e->usage =
03469          "Usage: skinny reset <DeviceId|DeviceName|all> [restart]\n"
03470          "       Causes a Skinny device to reset itself, optionally with a full restart\n";
03471       return NULL;
03472    case CLI_GENERATE:
03473       return complete_skinny_reset(a->line, a->word, a->pos, a->n);
03474    }
03475 
03476    if (a->argc < 3 || a->argc > 4)
03477       return CLI_SHOWUSAGE;
03478 
03479    AST_LIST_LOCK(&devices);
03480    AST_LIST_TRAVERSE(&devices, d, list) {
03481       int fullrestart = 0;
03482       if (!strcasecmp(a->argv[2], d->id) || !strcasecmp(a->argv[2], d->name) || !strcasecmp(a->argv[2], "all")) {
03483          if (!(d->session))
03484             continue;
03485 
03486          if (a->argc == 4 && !strcasecmp(a->argv[3], "restart"))
03487             fullrestart = 1;
03488          
03489          transmit_reset(d, fullrestart);
03490       }
03491    }
03492    AST_LIST_UNLOCK(&devices);
03493    return CLI_SUCCESS;
03494 }
03495 
03496 static char *device2str(int type)
03497 {
03498    char *tmp;
03499 
03500    switch (type) {
03501    case SKINNY_DEVICE_NONE:
03502       return "No Device";
03503    case SKINNY_DEVICE_30SPPLUS:
03504       return "30SP Plus";
03505    case SKINNY_DEVICE_12SPPLUS:
03506       return "12SP Plus";
03507    case SKINNY_DEVICE_12SP:
03508       return "12SP";
03509    case SKINNY_DEVICE_12:
03510       return "12";
03511    case SKINNY_DEVICE_30VIP:
03512       return "30VIP";
03513    case SKINNY_DEVICE_7910:
03514       return "7910";
03515    case SKINNY_DEVICE_7960:
03516       return "7960";
03517    case SKINNY_DEVICE_7940:
03518       return "7940";
03519    case SKINNY_DEVICE_7935:
03520       return "7935";
03521    case SKINNY_DEVICE_ATA186:
03522       return "ATA186";
03523    case SKINNY_DEVICE_7941:
03524       return "7941";
03525    case SKINNY_DEVICE_7971:
03526       return "7971";
03527    case SKINNY_DEVICE_7914:
03528       return "7914";
03529    case SKINNY_DEVICE_7985:
03530       return "7985";
03531    case SKINNY_DEVICE_7911:
03532       return "7911";
03533    case SKINNY_DEVICE_7961GE:
03534       return "7961GE";
03535    case SKINNY_DEVICE_7941GE:
03536       return "7941GE";
03537    case SKINNY_DEVICE_7931:
03538       return "7931";
03539    case SKINNY_DEVICE_7921:
03540       return "7921";
03541    case SKINNY_DEVICE_7906:
03542       return "7906";
03543    case SKINNY_DEVICE_7962:
03544       return "7962";
03545    case SKINNY_DEVICE_7937:
03546       return "7937";
03547    case SKINNY_DEVICE_7942:
03548       return "7942";
03549    case SKINNY_DEVICE_7945:
03550       return "7945";
03551    case SKINNY_DEVICE_7965:
03552       return "7965";
03553    case SKINNY_DEVICE_7975:
03554       return "7975";
03555    case SKINNY_DEVICE_7905:
03556       return "7905";
03557    case SKINNY_DEVICE_7920:
03558       return "7920";
03559    case SKINNY_DEVICE_7970:
03560       return "7970";
03561    case SKINNY_DEVICE_7912:
03562       return "7912";
03563    case SKINNY_DEVICE_7902:
03564       return "7902";
03565    case SKINNY_DEVICE_CIPC:
03566       return "IP Communicator";
03567    case SKINNY_DEVICE_7961:
03568       return "7961";
03569    case SKINNY_DEVICE_7936:
03570       return "7936";
03571    case SKINNY_DEVICE_SCCPGATEWAY_AN:
03572       return "SCCPGATEWAY_AN";
03573    case SKINNY_DEVICE_SCCPGATEWAY_BRI:
03574       return "SCCPGATEWAY_BRI";
03575    case SKINNY_DEVICE_UNKNOWN:
03576       return "Unknown";
03577    default:
03578       if (!(tmp = ast_threadstorage_get(&device2str_threadbuf, DEVICE2STR_BUFSIZE)))
03579          return "Unknown";
03580       snprintf(tmp, DEVICE2STR_BUFSIZE, "UNKNOWN-%d", type);
03581       return tmp;
03582    }
03583 }
03584 
03585 /*! \brief Print codec list from preference to CLI/manager */
03586 static void print_codec_to_cli(int fd, struct ast_codec_pref *pref)
03587 {
03588    int x;
03589    struct ast_format tmpfmt;
03590 
03591    for(x = 0; x < 32 ; x++) {
03592       ast_codec_pref_index(pref, x, &tmpfmt);
03593       if (!tmpfmt.id)
03594          break;
03595       ast_cli(fd, "%s", ast_getformatname(&tmpfmt));
03596       ast_cli(fd, ":%d", pref->framing[x]);
03597       if (x < 31 && ast_codec_pref_index(pref, x + 1, &tmpfmt))
03598          ast_cli(fd, ",");
03599    }
03600    if (!x)
03601       ast_cli(fd, "none");
03602 }
03603 
03604 static char *_skinny_show_devices(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[])
03605 {
03606    struct skinny_device *d;
03607    struct skinny_line *l;
03608    const char *id;
03609    char idtext[256] = "";
03610    int total_devices = 0;
03611 
03612    if (s) { /* Manager - get ActionID */
03613       id = astman_get_header(m, "ActionID");
03614       if (!ast_strlen_zero(id))
03615          snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
03616    }
03617 
03618    switch (argc) {
03619    case 3:
03620       break;
03621    default:
03622       return CLI_SHOWUSAGE;
03623    }
03624 
03625    if (!s) {
03626       ast_cli(fd, "Name                 DeviceId         IP              Type            R NL\n");
03627       ast_cli(fd, "-------------------- ---------------- --------------- --------------- - --\n");
03628    }
03629 
03630    AST_LIST_LOCK(&devices);
03631    AST_LIST_TRAVERSE(&devices, d, list) {
03632       int numlines = 0;
03633       total_devices++;
03634       AST_LIST_TRAVERSE(&d->lines, l, list) {
03635          numlines++;
03636       }
03637       if (!s) {
03638          ast_cli(fd, "%-20s %-16s %-15s %-15s %c %2d\n",
03639             d->name,
03640             d->id,
03641             d->session?ast_inet_ntoa(d->session->sin.sin_addr):"",
03642             device2str(d->type),
03643             d->session?'Y':'N',
03644             numlines);
03645       } else {
03646          astman_append(s,
03647             "Event: DeviceEntry\r\n%s"
03648             "Channeltype: SKINNY\r\n"
03649             "ObjectName: %s\r\n"
03650             "ChannelObjectType: device\r\n"
03651             "DeviceId: %s\r\n"
03652             "IPaddress: %s\r\n"
03653             "Type: %s\r\n"
03654             "Devicestatus: %s\r\n"
03655             "NumberOfLines: %d\r\n",
03656             idtext,
03657             d->name,
03658             d->id,
03659             d->session?ast_inet_ntoa(d->session->sin.sin_addr):"-none-",
03660             device2str(d->type),
03661             d->session?"registered":"unregistered",
03662             numlines);
03663       }
03664    }
03665    AST_LIST_UNLOCK(&devices);
03666 
03667    if (total)
03668       *total = total_devices;
03669    
03670    return CLI_SUCCESS;
03671 }
03672 
03673 /*! \brief  Show SKINNY devices in the manager API */
03674 /*    Inspired from chan_sip */
03675 static int manager_skinny_show_devices(struct mansession *s, const struct message *m)
03676 {
03677    const char *id = astman_get_header(m, "ActionID");
03678    const char *a[] = {"skinny", "show", "devices"};
03679    char idtext[256] = "";
03680    int total = 0;
03681 
03682    if (!ast_strlen_zero(id))
03683       snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
03684 
03685    astman_send_listack(s, m, "Device status list will follow", "start");
03686    /* List the devices in separate manager events */
03687    _skinny_show_devices(-1, &total, s, m, 3, a);
03688    /* Send final confirmation */
03689    astman_append(s,
03690    "Event: DevicelistComplete\r\n"
03691    "EventList: Complete\r\n"
03692    "ListItems: %d\r\n"
03693    "%s"
03694    "\r\n", total, idtext);
03695    return 0;
03696 }
03697 
03698 static char *handle_skinny_show_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03699 {
03700 
03701    switch (cmd) {
03702    case CLI_INIT:
03703       e->command = "skinny show devices";
03704       e->usage =
03705          "Usage: skinny show devices\n"
03706          "       Lists all devices known to the Skinny subsystem.\n";
03707       return NULL;
03708    case CLI_GENERATE:
03709       return NULL;
03710    }
03711 
03712    return _skinny_show_devices(a->fd, NULL, NULL, NULL, a->argc, (const char **) a->argv);
03713 }
03714 
03715 static char *_skinny_show_device(int type, int fd, struct mansession *s, const struct message *m, int argc, const char *argv[])
03716 {
03717    struct skinny_device *d;
03718    struct skinny_line *l;
03719    struct skinny_speeddial *sd;
03720    struct skinny_addon *sa;
03721    char codec_buf[512];
03722 
03723    if (argc < 4) {
03724       return CLI_SHOWUSAGE;
03725    }
03726 
03727    AST_LIST_LOCK(&devices);
03728    AST_LIST_TRAVERSE(&devices, d, list) {
03729       if (!strcasecmp(argv[3], d->id) || !strcasecmp(argv[3], d->name)) {
03730          int numlines = 0, numaddons = 0, numspeeddials = 0;
03731 
03732          AST_LIST_TRAVERSE(&d->lines, l, list){
03733             numlines++;
03734          }
03735 
03736          AST_LIST_TRAVERSE(&d->addons, sa, list) {
03737             numaddons++;
03738          }
03739 
03740          AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
03741             numspeeddials++;
03742          }
03743 
03744          if (type == 0) { /* CLI */
03745             ast_cli(fd, "Name:        %s\n", d->name);
03746             ast_cli(fd, "Id:          %s\n", d->id);
03747             ast_cli(fd, "version:     %s\n", S_OR(d->version_id, "Unknown"));
03748             ast_cli(fd, "Ip address:  %s\n", (d->session ? ast_inet_ntoa(d->session->sin.sin_addr) : "Unknown"));
03749             ast_cli(fd, "Port:        %d\n", (d->session ? ntohs(d->session->sin.sin_port) : 0));
03750             ast_cli(fd, "Device Type: %s\n", device2str(d->type));
03751             ast_cli(fd, "Conf Codecs:");
03752             ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, d->confcap);
03753             ast_cli(fd, "%s\n", codec_buf);
03754             ast_cli(fd, "Neg Codecs: ");
03755             ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, d->cap);
03756             ast_cli(fd, "%s\n", codec_buf);
03757             ast_cli(fd, "Registered:  %s\n", (d->session ? "Yes" : "No"));
03758             ast_cli(fd, "Lines:       %d\n", numlines);
03759             AST_LIST_TRAVERSE(&d->lines, l, list) {
03760                ast_cli(fd, "  %s (%s)\n", l->name, l->label);
03761             }
03762             AST_LIST_TRAVERSE(&d->addons, sa, list) {
03763                numaddons++;
03764             }  
03765             ast_cli(fd, "Addons:      %d\n", numaddons);
03766             AST_LIST_TRAVERSE(&d->addons, sa, list) {
03767                ast_cli(fd, "  %s\n", sa->type);
03768             }
03769             AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
03770                numspeeddials++;
03771             }
03772             ast_cli(fd, "Speeddials:  %d\n", numspeeddials);
03773             AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
03774                ast_cli(fd, "  %s (%s) ishint: %d\n", sd->exten, sd->label, sd->isHint);
03775             }
03776          } else { /* manager */
03777             astman_append(s, "Channeltype: SKINNY\r\n");
03778             astman_append(s, "ObjectName: %s\r\n", d->name);
03779             astman_append(s, "ChannelObjectType: device\r\n");
03780             astman_append(s, "Id: %s\r\n", d->id);
03781             astman_append(s, "version: %s\r\n", S_OR(d->version_id, "Unknown"));
03782             astman_append(s, "Ipaddress: %s\r\n", (d->session ? ast_inet_ntoa(d->session->sin.sin_addr) : "Unknown"));
03783             astman_append(s, "Port: %d\r\n", (d->session ? ntohs(d->session->sin.sin_port) : 0));
03784             astman_append(s, "DeviceType: %s\r\n", device2str(d->type));
03785             astman_append(s, "Codecs: ");
03786             ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, d->confcap);
03787             astman_append(s, "%s\r\n", codec_buf);
03788             astman_append(s, "CodecOrder: ");
03789             ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, d->cap);
03790             astman_append(s, "%s\r\n", codec_buf);
03791             astman_append(s, "Devicestatus: %s\r\n", (d->session?"registered":"unregistered"));
03792             astman_append(s, "NumberOfLines: %d\r\n", numlines);
03793             AST_LIST_TRAVERSE(&d->lines, l, list) {
03794                astman_append(s, "Line: %s (%s)\r\n", l->name, l->label);
03795             }
03796             astman_append(s, "NumberOfAddons: %d\r\n", numaddons);
03797             AST_LIST_TRAVERSE(&d->addons, sa, list) {
03798                astman_append(s, "Addon: %s\r\n", sa->type);
03799             }
03800             astman_append(s, "NumberOfSpeeddials: %d\r\n", numspeeddials);
03801             AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
03802                astman_append(s, "Speeddial: %s (%s) ishint: %d\r\n", sd->exten, sd->label, sd->isHint);
03803             }
03804          }
03805       }
03806    }
03807    AST_LIST_UNLOCK(&devices);
03808    return CLI_SUCCESS;
03809 }
03810 
03811 static int manager_skinny_show_device(struct mansession *s, const struct message *m)
03812 {
03813    const char *a[4];
03814    const char *device;
03815 
03816    device = astman_get_header(m, "Device");
03817    if (ast_strlen_zero(device)) {
03818       astman_send_error(s, m, "Device: <name> missing.");
03819       return 0;
03820    }
03821    a[0] = "skinny";
03822    a[1] = "show";
03823    a[2] = "device";
03824    a[3] = device;
03825 
03826    _skinny_show_device(1, -1, s, m, 4, a);
03827    astman_append(s, "\r\n\r\n" );
03828    return 0;
03829 }
03830 
03831 /*! \brief Show device information */
03832 static char *handle_skinny_show_device(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03833 {
03834    switch (cmd) {
03835    case CLI_INIT:
03836       e->command = "skinny show device";
03837       e->usage =
03838          "Usage: skinny show device <DeviceId|DeviceName>\n"
03839          "       Lists all deviceinformation of a specific device known to the Skinny subsystem.\n";
03840       return NULL;
03841    case CLI_GENERATE:
03842       return complete_skinny_show_device(a->line, a->word, a->pos, a->n);
03843    }
03844 
03845    return _skinny_show_device(0, a->fd, NULL, NULL, a->argc, (const char **) a->argv);
03846 }
03847 
03848 static char *_skinny_show_lines(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[])
03849 {
03850    struct skinny_line *l;
03851    struct skinny_subchannel *sub;
03852    int total_lines = 0;
03853    int verbose = 0;
03854    const char *id;
03855    char idtext[256] = "";
03856 
03857    if (s) { /* Manager - get ActionID */
03858       id = astman_get_header(m, "ActionID");
03859       if (!ast_strlen_zero(id))
03860          snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
03861    }
03862 
03863    switch (argc) {
03864    case 4:
03865       verbose = 1;
03866       break;
03867    case 3:
03868       verbose = 0;
03869       break;
03870    default:
03871       return CLI_SHOWUSAGE;
03872    }
03873 
03874    if (!s) {
03875       ast_cli(fd, "Name                 Device Name          Instance Label               \n");
03876       ast_cli(fd, "-------------------- -------------------- -------- --------------------\n");
03877    }
03878    AST_LIST_LOCK(&lines);
03879    AST_LIST_TRAVERSE(&lines, l, all) {
03880       total_lines++;
03881       if (!s) {
03882          ast_cli(fd, "%-20s %-20s %8d %-20s\n",
03883             l->name,
03884             (l->device ? l->device->name : "Not connected"),
03885             l->instance,
03886             l->label);
03887          if (verbose) {
03888             AST_LIST_TRAVERSE(&l->sub, sub, list) {
03889                ast_cli(fd, "  %s> %s to %s\n",
03890                   (sub == l->activesub?"Active  ":"Inactive"),
03891                   ast_channel_name(sub->owner),
03892                   (ast_bridged_channel(sub->owner)?ast_channel_name(ast_bridged_channel(sub->owner)):"")
03893                );
03894             }
03895          }
03896       } else {
03897          astman_append(s,
03898             "Event: LineEntry\r\n%s"
03899             "Channeltype: SKINNY\r\n"
03900             "ObjectName: %s\r\n"
03901             "ChannelObjectType: line\r\n"
03902             "Device: %s\r\n"
03903             "Instance: %d\r\n"
03904             "Label: %s\r\n",
03905             idtext,
03906             l->name,
03907             (l->device ? l->device->name : "None"),
03908             l->instance,
03909             l->label);
03910       }
03911    }
03912    AST_LIST_UNLOCK(&lines);
03913 
03914    if (total) {
03915       *total = total_lines;
03916    }
03917 
03918    return CLI_SUCCESS;
03919 }
03920 
03921 /*! \brief  Show Skinny lines in the manager API */
03922 /*    Inspired from chan_sip */
03923 static int manager_skinny_show_lines(struct mansession *s, const struct message *m)
03924 {
03925    const char *id = astman_get_header(m, "ActionID");
03926    const char *a[] = {"skinny", "show", "lines"};
03927    char idtext[256] = "";
03928    int total = 0;
03929 
03930    if (!ast_strlen_zero(id))
03931       snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
03932 
03933    astman_send_listack(s, m, "Line status list will follow", "start");
03934    /* List the lines in separate manager events */
03935    _skinny_show_lines(-1, &total, s, m, 3, a);
03936    /* Send final confirmation */
03937    astman_append(s,
03938    "Event: LinelistComplete\r\n"
03939    "EventList: Complete\r\n"
03940    "ListItems: %d\r\n"
03941    "%s"
03942    "\r\n", total, idtext);
03943    return 0;
03944 }
03945 
03946 static char *handle_skinny_show_lines(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03947 {
03948    switch (cmd) {
03949    case CLI_INIT:
03950       e->command = "skinny show lines [verbose]";
03951       e->usage =
03952          "Usage: skinny show lines\n"
03953          "       Lists all lines known to the Skinny subsystem.\n"
03954          "       If 'verbose' is specified, the output includes\n"
03955          "       information about subs for each line.\n";
03956       return NULL;
03957    case CLI_GENERATE:
03958       return NULL;
03959    }
03960 
03961    if (a->argc == e->args) {
03962       if (strcasecmp(a->argv[e->args-1], "verbose")) {
03963          return CLI_SHOWUSAGE;
03964       }
03965    } else if (a->argc != e->args - 1) {
03966       return CLI_SHOWUSAGE;
03967    }
03968 
03969    return _skinny_show_lines(a->fd, NULL, NULL, NULL, a->argc, (const char **) a->argv);
03970 }
03971 
03972 static char *_skinny_show_line(int type, int fd, struct mansession *s, const struct message *m, int argc, const char *argv[])
03973 {
03974    struct skinny_device *d;
03975    struct skinny_line *l;
03976    struct skinny_subline *subline;
03977    struct ast_codec_pref *pref;
03978    int x = 0;
03979    char codec_buf[512];
03980    char group_buf[256];
03981    char cbuf[256];
03982 
03983    switch (argc) {
03984    case 4:
03985       break;
03986    case 6:
03987       break;
03988    default:
03989       return CLI_SHOWUSAGE;
03990    }
03991 
03992    AST_LIST_LOCK(&devices);
03993 
03994    /* Show all lines matching the one supplied */
03995    AST_LIST_TRAVERSE(&devices, d, list) {
03996       if (argc == 6 && (strcasecmp(argv[5], d->id) && strcasecmp(argv[5], d->name))) {
03997          continue;
03998       }
03999       AST_LIST_TRAVERSE(&d->lines, l, list) {
04000          if (strcasecmp(argv[3], l->name)) {
04001             continue;
04002          }
04003          if (type == 0) { /* CLI */
04004             ast_cli(fd, "Line:             %s\n", l->name);
04005             ast_cli(fd, "On Device:        %s\n", d->name);
04006             ast_cli(fd, "Line Label:       %s\n", l->label);
04007             ast_cli(fd, "Extension:        %s\n", S_OR(l->exten, "<not set>"));
04008             ast_cli(fd, "Context:          %s\n", l->context);
04009             ast_cli(fd, "CallGroup:        %s\n", ast_print_group(group_buf, sizeof(group_buf), l->callgroup));
04010             ast_cli(fd, "PickupGroup:      %s\n", ast_print_group(group_buf, sizeof(group_buf), l->pickupgroup));
04011             ast_cli(fd, "Language:         %s\n", S_OR(l->language, "<not set>"));
04012             ast_cli(fd, "Accountcode:      %s\n", S_OR(l->accountcode, "<not set>"));
04013             ast_cli(fd, "AmaFlag:          %s\n", ast_cdr_flags2str(l->amaflags));
04014             ast_cli(fd, "CallerId Number:  %s\n", S_OR(l->cid_num, "<not set>"));
04015             ast_cli(fd, "CallerId Name:    %s\n", S_OR(l->cid_name, "<not set>"));
04016             ast_cli(fd, "Hide CallerId:    %s\n", (l->hidecallerid ? "Yes" : "No"));
04017             ast_cli(fd, "CFwdAll:          %s\n", S_COR((l->cfwdtype & SKINNY_CFWD_ALL), l->call_forward_all, "<not set>"));
04018             ast_cli(fd, "CFwdBusy:         %s\n", S_COR((l->cfwdtype & SKINNY_CFWD_BUSY), l->call_forward_busy, "<not set>"));
04019             ast_cli(fd, "CFwdNoAnswer:     %s\n", S_COR((l->cfwdtype & SKINNY_CFWD_NOANSWER), l->call_forward_noanswer, "<not set>"));
04020             ast_cli(fd, "VoicemailBox:     %s\n", S_OR(l->mailbox, "<not set>"));
04021             ast_cli(fd, "VoicemailNumber:  %s\n", S_OR(l->vmexten, "<not set>"));
04022             ast_cli(fd, "MWIblink:         %d\n", l->mwiblink);
04023             ast_cli(fd, "Regextension:     %s\n", S_OR(l->regexten, "<not set>"));
04024             ast_cli(fd, "Regcontext:       %s\n", S_OR(l->regcontext, "<not set>"));
04025             ast_cli(fd, "MoHInterpret:     %s\n", S_OR(l->mohinterpret, "<not set>"));
04026             ast_cli(fd, "MoHSuggest:       %s\n", S_OR(l->mohsuggest, "<not set>"));
04027             ast_cli(fd, "Last dialed nr:   %s\n", S_OR(l->lastnumberdialed, "<no calls made yet>"));
04028             ast_cli(fd, "Last CallerID:    %s\n", S_OR(l->lastcallerid, "<not set>"));
04029             ast_cli(fd, "Transfer enabled: %s\n", (l->transfer ? "Yes" : "No"));
04030             ast_cli(fd, "Callwaiting:      %s\n", (l->callwaiting ? "Yes" : "No"));
04031             ast_cli(fd, "3Way Calling:     %s\n", (l->threewaycalling ? "Yes" : "No"));
04032             ast_cli(fd, "Can forward:      %s\n", (l->cancallforward ? "Yes" : "No"));
04033             ast_cli(fd, "Do Not Disturb:   %s\n", (l->dnd ? "Yes" : "No"));
04034             ast_cli(fd, "NAT:              %s\n", (l->nat ? "Yes" : "No"));
04035             ast_cli(fd, "immediate:        %s\n", (l->immediate ? "Yes" : "No"));
04036             ast_cli(fd, "Group:            %d\n", l->group);
04037             ast_cli(fd, "Parkinglot:       %s\n", S_OR(l->parkinglot, "<not set>"));
04038             ast_cli(fd, "Conf Codecs:      ");
04039             ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, l->confcap);
04040             ast_cli(fd, "%s\n", codec_buf);
04041             ast_cli(fd, "Neg Codecs:       ");
04042             ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, l->cap);
04043             ast_cli(fd, "%s\n", codec_buf);
04044             ast_cli(fd, "Codec Order:      (");
04045             print_codec_to_cli(fd, &l->prefs);
04046             ast_cli(fd, ")\n");
04047             if  (AST_LIST_FIRST(&l->sublines)) {
04048                ast_cli(fd, "Sublines:\n");
04049                AST_LIST_TRAVERSE(&l->sublines, subline, list) {
04050                   ast_cli(fd, "     %s, %s@%s\n", subline->name, subline->exten, subline->context);
04051                }
04052             }
04053             ast_cli(fd, "\n");
04054          } else { /* manager */
04055             astman_append(s, "Channeltype: SKINNY\r\n");
04056             astman_append(s, "ObjectName: %s\r\n", l->name);
04057             astman_append(s, "ChannelObjectType: line\r\n");
04058             astman_append(s, "Device: %s\r\n", d->name);
04059             astman_append(s, "LineLabel: %s\r\n", l->label);
04060             astman_append(s, "Extension: %s\r\n", S_OR(l->exten, "<not set>"));
04061             astman_append(s, "Context: %s\r\n", l->context);
04062             astman_append(s, "CallGroup: %s\r\n", ast_print_group(group_buf, sizeof(group_buf), l->callgroup));
04063             astman_append(s, "PickupGroup: %s\r\n", ast_print_group(group_buf, sizeof(group_buf), l->pickupgroup));
04064             astman_append(s, "Language: %s\r\n", S_OR(l->language, "<not set>"));
04065             astman_append(s, "Accountcode: %s\r\n", S_OR(l->accountcode, "<not set>"));
04066             astman_append(s, "AMAflags: %s\r\n", ast_cdr_flags2str(l->amaflags));
04067             astman_append(s, "Callerid: %s\r\n", ast_callerid_merge(cbuf, sizeof(cbuf), l->cid_name, l->cid_num, ""));
04068             astman_append(s, "HideCallerId: %s\r\n", (l->hidecallerid ? "Yes" : "No"));
04069             astman_append(s, "CFwdAll: %s\r\n", S_COR((l->cfwdtype & SKINNY_CFWD_ALL), l->call_forward_all, "<not set>"));
04070             astman_append(s, "CFwdBusy: %s\r\n", S_COR((l->cfwdtype & SKINNY_CFWD_BUSY), l->call_forward_busy, "<not set>"));
04071             astman_append(s, "CFwdNoAnswer: %s\r\n", S_COR((l->cfwdtype & SKINNY_CFWD_NOANSWER), l->call_forward_noanswer, "<not set>"));
04072             astman_append(s, "VoicemailBox: %s\r\n", S_OR(l->mailbox, "<not set>"));
04073             astman_append(s, "VoicemailNumber: %s\r\n", S_OR(l->vmexten, "<not set>"));
04074             astman_append(s, "MWIblink: %d\r\n", l->mwiblink);
04075             astman_append(s, "RegExtension: %s\r\n", S_OR(l->regexten, "<not set>"));
04076             astman_append(s, "Regcontext: %s\r\n", S_OR(l->regcontext, "<not set>"));
04077             astman_append(s, "MoHInterpret: %s\r\n", S_OR(l->mohinterpret, "<not set>"));
04078             astman_append(s, "MoHSuggest: %s\r\n", S_OR(l->mohsuggest, "<not set>"));
04079             astman_append(s, "LastDialedNr: %s\r\n", S_OR(l->lastnumberdialed, "<no calls made yet>"));
04080             astman_append(s, "LastCallerID: %s\r\n", S_OR(l->lastcallerid, "<not set>"));
04081             astman_append(s, "Transfer: %s\r\n", (l->transfer ? "Yes" : "No"));
04082             astman_append(s, "Callwaiting: %s\r\n", (l->callwaiting ? "Yes" : "No"));
04083             astman_append(s, "3WayCalling: %s\r\n", (l->threewaycalling ? "Yes" : "No"));
04084             astman_append(s, "CanForward: %s\r\n", (l->cancallforward ? "Yes" : "No"));
04085             astman_append(s, "DoNotDisturb: %s\r\n", (l->dnd ? "Yes" : "No"));
04086             astman_append(s, "NAT: %s\r\n", (l->nat ? "Yes" : "No"));
04087             astman_append(s, "immediate: %s\r\n", (l->immediate ? "Yes" : "No"));
04088             astman_append(s, "Group: %d\r\n", l->group);
04089             astman_append(s, "Parkinglot: %s\r\n", S_OR(l->parkinglot, "<not set>"));
04090             ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, l->confcap);
04091             astman_append(s, "Codecs: %s\r\n", codec_buf);
04092             astman_append(s, "CodecOrder: ");
04093             pref = &l->prefs;
04094             for(x = 0; x < 32 ; x++) {
04095                struct ast_format tmpfmt;
04096                ast_codec_pref_index(pref, x, &tmpfmt);
04097                if (!tmpfmt.id)
04098                   break;
04099                astman_append(s, "%s", ast_getformatname(&tmpfmt));
04100                if (x < 31 && ast_codec_pref_index(pref, x+1, &tmpfmt))
04101                   astman_append(s, ",");
04102             }
04103             astman_append(s, "\r\n");
04104          }
04105       }
04106    }
04107    
04108    AST_LIST_UNLOCK(&devices);
04109    return CLI_SUCCESS;
04110 }
04111 
04112 static int manager_skinny_show_line(struct mansession *s, const struct message *m)
04113 {
04114    const char *a[4];
04115    const char *line;
04116 
04117    line = astman_get_header(m, "Line");
04118    if (ast_strlen_zero(line)) {
04119       astman_send_error(s, m, "Line: <name> missing.");
04120       return 0;
04121    }
04122    a[0] = "skinny";
04123    a[1] = "show";
04124    a[2] = "line";
04125    a[3] = line;
04126 
04127    _skinny_show_line(1, -1, s, m, 4, a);
04128    astman_append(s, "\r\n\r\n" );
04129    return 0;
04130 }
04131 
04132 /*! \brief List line information. */
04133 static char *handle_skinny_show_line(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
04134 {
04135    switch (cmd) {
04136    case CLI_INIT:
04137       e->command = "skinny show line";
04138       e->usage =
04139          "Usage: skinny show line <Line> [ on <DeviceID|DeviceName> ]\n"
04140          "       List all lineinformation of a specific line known to the Skinny subsystem.\n";
04141       return NULL;
04142    case CLI_GENERATE:
04143       return complete_skinny_show_line(a->line, a->word, a->pos, a->n);
04144    }
04145 
04146    return _skinny_show_line(0, a->fd, NULL, NULL, a->argc, (const char **) a->argv);
04147 }
04148 
04149 /*! \brief List global settings for the Skinny subsystem. */
04150 static char *handle_skinny_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
04151 {
04152    switch (cmd) {
04153    case CLI_INIT:
04154       e->command = "skinny show settings";
04155       e->usage =
04156          "Usage: skinny show settings\n"
04157          "       Lists all global configuration settings of the Skinny subsystem.\n";
04158       return NULL;
04159    case CLI_GENERATE:
04160       return NULL;
04161    }  
04162 
04163    if (a->argc != 3)
04164       return CLI_SHOWUSAGE;
04165 
04166    ast_cli(a->fd, "\nGlobal Settings:\n");
04167    ast_cli(a->fd, "  Skinny Port:            %d\n", ntohs(bindaddr.sin_port));
04168    ast_cli(a->fd, "  Bindaddress:            %s\n", ast_inet_ntoa(bindaddr.sin_addr));
04169    ast_cli(a->fd, "  KeepAlive:              %d\n", keep_alive);
04170    ast_cli(a->fd, "  Date Format:            %s\n", date_format);
04171    ast_cli(a->fd, "  Voice Mail Extension:   %s\n", S_OR(vmexten, "(not set)"));
04172    ast_cli(a->fd, "  Reg. context:           %s\n", S_OR(regcontext, "(not set)"));
04173    ast_cli(a->fd, "  Jitterbuffer enabled:   %s\n", AST_CLI_YESNO(ast_test_flag(&global_jbconf, AST_JB_ENABLED)));
04174     if (ast_test_flag(&global_jbconf, AST_JB_ENABLED)) {
04175       ast_cli(a->fd, "  Jitterbuffer forced:    %s\n", AST_CLI_YESNO(ast_test_flag(&global_jbconf, AST_JB_FORCED)));
04176       ast_cli(a->fd, "  Jitterbuffer max size:  %ld\n", global_jbconf.max_size);
04177       ast_cli(a->fd, "  Jitterbuffer resync:    %ld\n", global_jbconf.resync_threshold);
04178       ast_cli(a->fd, "  Jitterbuffer impl:      %s\n", global_jbconf.impl);
04179       if (!strcasecmp(global_jbconf.impl, "adaptive")) {
04180          ast_cli(a->fd, "  Jitterbuffer tgt extra: %ld\n", global_jbconf.target_extra);
04181       }
04182       ast_cli(a->fd, "  Jitterbuffer log:       %s\n", AST_CLI_YESNO(ast_test_flag(&global_jbconf, AST_JB_LOG)));
04183    }
04184 
04185    return CLI_SUCCESS;
04186 }
04187 
04188 static struct ast_cli_entry cli_skinny[] = {
04189    AST_CLI_DEFINE(handle_skinny_show_devices, "List defined Skinny devices"),
04190    AST_CLI_DEFINE(handle_skinny_show_device, "List Skinny device information"),
04191    AST_CLI_DEFINE(handle_skinny_show_lines, "List defined Skinny lines per device"),
04192    AST_CLI_DEFINE(handle_skinny_show_line, "List Skinny line information"),
04193    AST_CLI_DEFINE(handle_skinny_show_settings, "List global Skinny settings"),
04194 #ifdef AST_DEVMODE
04195    AST_CLI_DEFINE(handle_skinny_set_debug, "Enable/Disable Skinny debugging"),
04196 #endif
04197    AST_CLI_DEFINE(handle_skinny_reset, "Reset Skinny device(s)"),
04198    AST_CLI_DEFINE(handle_skinny_reload, "Reload Skinny config"),
04199 };
04200 
04201 static void start_rtp(struct skinny_subchannel *sub)
04202 {
04203    struct skinny_line *l = sub->line;
04204    struct skinny_device *d = l->device;
04205    int hasvideo = 0;
04206    struct ast_sockaddr bindaddr_tmp;
04207 
04208    ast_mutex_lock(&sub->lock);
04209    /* Allocate the RTP */
04210    ast_sockaddr_from_sin(&bindaddr_tmp, &bindaddr);
04211    sub->rtp = ast_rtp_instance_new("asterisk", sched, &bindaddr_tmp, NULL);
04212    if (hasvideo)
04213       sub->vrtp = ast_rtp_instance_new("asterisk", sched, &bindaddr_tmp, NULL);
04214 
04215    if (sub->rtp) {
04216       ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_RTCP, 1);
04217    }
04218    if (sub->vrtp) {
04219       ast_rtp_instance_set_prop(sub->vrtp, AST_RTP_PROPERTY_RTCP, 1);
04220    }
04221 
04222    if (sub->rtp && sub->owner) {
04223       ast_channel_set_fd(sub->owner, 0, ast_rtp_instance_fd(sub->rtp, 0));
04224       ast_channel_set_fd(sub->owner, 1, ast_rtp_instance_fd(sub->rtp, 1));
04225    }
04226    if (hasvideo && sub->vrtp && sub->owner) {
04227       ast_channel_set_fd(sub->owner, 2, ast_rtp_instance_fd(sub->vrtp, 0));
04228       ast_channel_set_fd(sub->owner, 3, ast_rtp_instance_fd(sub->vrtp, 1));
04229    }
04230    if (sub->rtp) {
04231       ast_rtp_instance_set_qos(sub->rtp, qos.tos_audio, qos.cos_audio, "Skinny RTP");
04232       ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_NAT, l->nat);
04233    }
04234    if (sub->vrtp) {
04235       ast_rtp_instance_set_qos(sub->vrtp, qos.tos_video, qos.cos_video, "Skinny VRTP");
04236       ast_rtp_instance_set_prop(sub->vrtp, AST_RTP_PROPERTY_NAT, l->nat);
04237    }
04238    /* Set Frame packetization */
04239    if (sub->rtp)
04240       ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(sub->rtp), sub->rtp, &l->prefs);
04241 
04242    /* Create the RTP connection */
04243    transmit_connect(d, sub);
04244    ast_mutex_unlock(&sub->lock);
04245 }
04246 
04247 static void destroy_rtp(struct skinny_subchannel *sub)
04248 {
04249    if (sub->rtp) {
04250       SKINNY_DEBUG(DEBUG_AUDIO, 3, "Sub %d - Destroying RTP\n", sub->callid);
04251       ast_rtp_instance_stop(sub->rtp);
04252       ast_rtp_instance_destroy(sub->rtp);
04253       sub->rtp = NULL;
04254    }
04255    if (sub->vrtp) {
04256       SKINNY_DEBUG(DEBUG_AUDIO, 3, "Sub %d - Destroying VRTP\n", sub->callid);
04257       ast_rtp_instance_stop(sub->vrtp);
04258       ast_rtp_instance_destroy(sub->vrtp);
04259       sub->vrtp = NULL;
04260    }
04261 }
04262 
04263 static void *skinny_newcall(void *data)
04264 {
04265    struct ast_channel *c = data;
04266    struct skinny_subchannel *sub = ast_channel_tech_pvt(c);
04267    struct skinny_line *l = sub->line;
04268    struct skinny_device *d = l->device;
04269    int res = 0;
04270 
04271    ast_set_callerid(c,
04272       l->hidecallerid ? "" : l->cid_num,
04273       l->hidecallerid ? "" : l->cid_name,
04274       ast_channel_caller(c)->ani.number.valid ? NULL : l->cid_num);
04275 #if 1 /* XXX This code is probably not necessary */
04276    ast_party_number_free(&ast_channel_connected(c)->id.number);
04277    ast_party_number_init(&ast_channel_connected(c)->id.number);
04278    ast_channel_connected(c)->id.number.valid = 1;
04279    ast_channel_connected(c)->id.number.str = ast_strdup(ast_channel_exten(c));
04280    ast_party_name_free(&ast_channel_connected(c)->id.name);
04281    ast_party_name_init(&ast_channel_connected(c)->id.name);
04282 #endif
04283    ast_setstate(c, AST_STATE_RING);
04284    if (!sub->rtp) {
04285       start_rtp(sub);
04286    }
04287    ast_verb(3, "Sub %d - Calling %s@%s\n", sub->callid, ast_channel_exten(c), ast_channel_context(c));
04288    res = ast_pbx_run(c);
04289    if (res) {
04290       ast_log(LOG_WARNING, "PBX exited non-zero\n");
04291       transmit_start_tone(d, SKINNY_REORDER, l->instance, sub->callid);
04292    }
04293    return NULL;
04294 }
04295 
04296 static void skinny_dialer(struct skinny_subchannel *sub, int timedout)
04297 {
04298    struct ast_channel *c = sub->owner;
04299    struct skinny_line *l = sub->line;
04300    struct skinny_device *d = l->device;
04301 
04302    if (timedout || !ast_matchmore_extension(c, ast_channel_context(c), sub->exten, 1, l->cid_num)) {
04303       SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - Force dialing '%s'\n", sub->callid, sub->exten);
04304       if (ast_exists_extension(c, ast_channel_context(c), sub->exten, 1, l->cid_num)) {
04305          if (sub->substate == SUBSTATE_OFFHOOK) {
04306             dialandactivatesub(sub, sub->exten);
04307          }
04308       } else {
04309          if (d->hookstate == SKINNY_OFFHOOK) {
04310             // FIXME: redundant because below will onhook before the sound plays, but it correct to send it.
04311             transmit_start_tone(d, SKINNY_REORDER, l->instance, sub->callid);
04312          }
04313          dumpsub(sub, 0);
04314       }
04315    } else {
04316       SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - Wait for more digits\n", sub->callid);
04317       if (ast_exists_extension(c, ast_channel_context(c), sub->exten, 1, l->cid_num)) {
04318          sub->dialer_sched = skinny_sched_add(matchdigittimeout, skinny_dialer_cb, sub);
04319       } else {
04320          sub->dialer_sched = skinny_sched_add(gendigittimeout, skinny_dialer_cb, sub);
04321       }
04322    }
04323 }
04324 
04325 static int skinny_dialer_cb(const void *data)
04326 {
04327    struct skinny_subchannel *sub = (struct skinny_subchannel *)data;
04328    SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - Dialer called from SCHED %d\n", sub->callid, sub->dialer_sched);
04329    sub->dialer_sched = 0;
04330    skinny_dialer(sub, 1);
04331    return 0;
04332 }
04333 
04334 static int skinny_autoanswer_cb(const void *data)
04335 {
04336    struct skinny_subchannel *sub = (struct skinny_subchannel *)data;
04337    sub->aa_sched = 0;
04338    setsubstate(sub, SKINNY_CONNECTED);
04339    return 0;
04340 }
04341 
04342 static int skinny_call(struct ast_channel *ast, const char *dest, int timeout)
04343 {
04344    int res = 0;
04345    struct skinny_subchannel *sub = ast_channel_tech_pvt(ast);
04346    struct skinny_line *l = sub->line;
04347    struct skinny_device *d = l->device;
04348    struct ast_var_t *current;
04349    int doautoanswer = 0;
04350 
04351    if (!d || !d->session) {
04352       ast_log(LOG_WARNING, "Device not registered, cannot call %s\n", dest);
04353       return -1;
04354    }
04355 
04356    if ((ast_channel_state(ast) != AST_STATE_DOWN) && (ast_channel_state(ast) != AST_STATE_RESERVED)) {
04357       ast_log(LOG_WARNING, "skinny_call called on %s, neither down nor reserved\n", ast_channel_name(ast));
04358       return -1;
04359    }
04360 
04361    SKINNY_DEBUG(DEBUG_SUB, 3, "Skinny Call (%s) - Sub %d\n",
04362       ast_channel_name(ast), sub->callid);
04363 
04364    if (l->dnd) {
04365       ast_queue_control(ast, AST_CONTROL_BUSY);
04366       return -1;
04367    }
04368 
04369    if (AST_LIST_NEXT(sub,list) && !l->callwaiting) {
04370       ast_queue_control(ast, AST_CONTROL_BUSY);
04371       return -1;
04372    }
04373 
04374    AST_LIST_TRAVERSE(ast_channel_varshead(ast), current, entries) {
04375       if (!(strcasecmp(ast_var_name(current),"SKINNY_AUTOANSWER"))) {
04376          if (d->hookstate == SKINNY_ONHOOK && !sub->aa_sched) {
04377             char buf[24];
04378             int aatime;
04379             char *stringp = buf, *curstr;
04380             ast_copy_string(buf, ast_var_value(current), sizeof(buf));
04381             curstr = strsep(&stringp, ":");
04382             aatime = atoi(curstr);
04383             while ((curstr = strsep(&stringp, ":"))) {
04384                if (!(strcasecmp(curstr,"BEEP"))) {
04385                   sub->aa_beep = 1;
04386                } else if (!(strcasecmp(curstr,"MUTE"))) {
04387                   sub->aa_mute = 1;
04388                }
04389             }
04390             SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - setting autoanswer time=%dms %s%s\n",
04391                sub->callid, aatime, sub->aa_beep ? "BEEP " : "", sub->aa_mute ? "MUTE" : "");
04392             if (aatime) {
04393                //sub->aa_sched = ast_sched_add(sched, aatime, skinny_autoanswer_cb, sub);
04394                sub->aa_sched = skinny_sched_add(aatime, skinny_autoanswer_cb, sub);
04395             } else {
04396                doautoanswer = 1;
04397             }
04398          }
04399       }
04400    }
04401 
04402    setsubstate(sub, SUBSTATE_RINGIN);
04403    if (doautoanswer) {
04404       setsubstate(sub, SUBSTATE_CONNECTED);
04405    }
04406    return res;
04407 }
04408 
04409 static int skinny_hangup(struct ast_channel *ast)
04410 {
04411    struct skinny_subchannel *sub = ast_channel_tech_pvt(ast);
04412 
04413    if (!sub) {
04414       ast_debug(1, "Asked to hangup channel not connected\n");
04415       return 0;
04416    }
04417 
04418    dumpsub(sub, 1);
04419 
04420    SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - Destroying\n", sub->callid);
04421 
04422    ast_mutex_lock(&sub->lock);
04423    sub->owner = NULL;
04424    ast_channel_tech_pvt_set(ast, NULL);
04425    destroy_rtp(sub);
04426    ast_mutex_unlock(&sub->lock);
04427    ast_free(sub);
04428    ast_module_unref(ast_module_info->self);
04429    return 0;
04430 }
04431 
04432 static int skinny_answer(struct ast_channel *ast)
04433 {
04434    int res = 0;
04435    struct skinny_subchannel *sub = ast_channel_tech_pvt(ast);
04436 
04437    if (sub->blindxfer) {
04438       SKINNY_DEBUG(DEBUG_SUB, 3, "skinny_answer(%s) on %s@%s-%d with BlindXFER, transferring\n",
04439          ast_channel_name(ast), sub->line->name, sub->line->device->name, sub->callid);
04440       ast_setstate(ast, AST_STATE_UP);
04441       skinny_transfer(sub);
04442       return 0;
04443    }
04444 
04445    sub->cxmode = SKINNY_CX_SENDRECV;
04446 
04447    SKINNY_DEBUG(DEBUG_SUB, 3, "skinny_answer(%s) on %s@%s-%d\n",
04448       ast_channel_name(ast), sub->line->name, sub->line->device->name, sub->callid);
04449 
04450    setsubstate(sub, SUBSTATE_CONNECTED);
04451 
04452    return res;
04453 }
04454 
04455 /* Retrieve audio/etc from channel.  Assumes sub->lock is already held. */
04456 static struct ast_frame *skinny_rtp_read(struct skinny_subchannel *sub)
04457 {
04458    struct ast_channel *ast = sub->owner;
04459    struct ast_frame *f;
04460 
04461    if (!sub->rtp) {
04462       /* We have no RTP allocated for this channel */
04463       return &ast_null_frame;
04464    }
04465 
04466    switch(ast_channel_fdno(ast)) {
04467    case 0:
04468       f = ast_rtp_instance_read(sub->rtp, 0); /* RTP Audio */
04469       break;
04470    case 1:
04471       f = ast_rtp_instance_read(sub->rtp, 1); /* RTCP Control Channel */
04472       break;
04473    case 2:
04474       f = ast_rtp_instance_read(sub->vrtp, 0); /* RTP Video */
04475       break;
04476    case 3:
04477       f = ast_rtp_instance_read(sub->vrtp, 1); /* RTCP Control Channel for video */
04478       break;
04479 #if 0
04480    case 5:
04481       /* Not yet supported */
04482       f = ast_udptl_read(sub->udptl); /* UDPTL for T.38 */
04483       break;
04484 #endif
04485    default:
04486       f = &ast_null_frame;
04487    }
04488 
04489    if (ast) {
04490       /* We already hold the channel lock */
04491       if (f->frametype == AST_FRAME_VOICE) {
04492          if (!(ast_format_cap_iscompatible(ast_channel_nativeformats(ast), &f->subclass.format))) {
04493             ast_debug(1, "Oooh, format changed to %s\n", ast_getformatname(&f->subclass.format));
04494             ast_format_cap_set(ast_channel_nativeformats(ast), &f->subclass.format);
04495             ast_set_read_format(ast, ast_channel_readformat(ast));
04496             ast_set_write_format(ast, ast_channel_writeformat(ast));
04497          }
04498       }
04499    }
04500    return f;
04501 }
04502 
04503 static struct ast_frame *skinny_read(struct ast_channel *ast)
04504 {
04505    struct ast_frame *fr;
04506    struct skinny_subchannel *sub = ast_channel_tech_pvt(ast);
04507    ast_mutex_lock(&sub->lock);
04508    fr = skinny_rtp_read(sub);
04509    ast_mutex_unlock(&sub->lock);
04510    return fr;
04511 }
04512 
04513 static int skinny_write(struct ast_channel *ast, struct ast_frame *frame)
04514 {
04515    struct skinny_subchannel *sub = ast_channel_tech_pvt(ast);
04516    int res = 0;
04517    if (frame->frametype != AST_FRAME_VOICE) {
04518       if (frame->frametype == AST_FRAME_IMAGE) {
04519          return 0;
04520       } else {
04521          ast_log(LOG_WARNING, "Can't send %d type frames with skinny_write\n", frame->frametype);
04522          return 0;
04523       }
04524    } else {
04525       if (!(ast_format_cap_iscompatible(ast_channel_nativeformats(ast), &frame->subclass.format))) {
04526          char buf[256];
04527          ast_log(LOG_WARNING, "Asked to transmit frame type %s, while native formats is %s (read/write = %s/%s)\n",
04528             ast_getformatname(&frame->subclass.format),
04529             ast_getformatname_multiple(buf, sizeof(buf), ast_channel_nativeformats(ast)),
04530             ast_getformatname(ast_channel_readformat(ast)),
04531             ast_getformatname(ast_channel_writeformat(ast)));
04532          return -1;
04533       }
04534    }
04535    if (sub) {
04536       ast_mutex_lock(&sub->lock);
04537       if (sub->rtp) {
04538          res = ast_rtp_instance_write(sub->rtp, frame);
04539       }
04540       ast_mutex_unlock(&sub->lock);
04541    }
04542    return res;
04543 }
04544 
04545 static int skinny_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
04546 {
04547    struct skinny_subchannel *sub = ast_channel_tech_pvt(newchan);
04548    ast_log(LOG_NOTICE, "skinny_fixup(%s, %s)\n", ast_channel_name(oldchan), ast_channel_name(newchan));
04549    if (sub->owner != oldchan) {
04550       ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, sub->owner);
04551       return -1;
04552    }
04553    sub->owner = newchan;
04554    return 0;
04555 }
04556 
04557 static int skinny_senddigit_begin(struct ast_channel *ast, char digit)
04558 {
04559    return -1; /* Start inband indications */
04560 }
04561 
04562 static int skinny_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration)
04563 {
04564 #if 0
04565    struct skinny_subchannel *sub = ast->tech_pvt;
04566    struct skinny_line *l = sub->line;
04567    struct skinny_device *d = l->device;
04568    int tmp;
04569    /* not right */
04570    sprintf(tmp, "%d", digit);
04571    //transmit_tone(d, digit, l->instance, sub->callid);
04572 #endif
04573    return -1; /* Stop inband indications */
04574 }
04575 
04576 static int get_devicestate(struct skinny_line *l)
04577 {
04578    struct skinny_subchannel *sub;
04579    int res = AST_DEVICE_UNKNOWN;
04580 
04581    if (!l)
04582       res = AST_DEVICE_INVALID;
04583    else if (!l->device || !l->device->session)
04584       res = AST_DEVICE_UNAVAILABLE;
04585    else if (l->dnd)
04586       res = AST_DEVICE_BUSY;
04587    else {
04588       if (l->device->hookstate == SKINNY_ONHOOK) {
04589          res = AST_DEVICE_NOT_INUSE;
04590       } else {
04591          res = AST_DEVICE_INUSE;
04592       }
04593 
04594       AST_LIST_TRAVERSE(&l->sub, sub, list) {
04595          if (sub->substate == SUBSTATE_HOLD) {
04596             res = AST_DEVICE_ONHOLD;
04597             break;
04598          }
04599       }
04600    }
04601 
04602    return res;
04603 }
04604 
04605 static char *control2str(int ind) {
04606    char *tmp;
04607 
04608    switch (ind) {
04609    case AST_CONTROL_HANGUP:
04610       return "Other end has hungup";
04611    case AST_CONTROL_RING:
04612       return "Local ring";
04613    case AST_CONTROL_RINGING:
04614       return "Remote end is ringing";
04615    case AST_CONTROL_ANSWER:
04616       return "Remote end has answered";
04617    case AST_CONTROL_BUSY:
04618       return "Remote end is busy";
04619    case AST_CONTROL_TAKEOFFHOOK:
04620       return "Make it go off hook";
04621    case AST_CONTROL_OFFHOOK:
04622       return "Line is off hook";
04623    case AST_CONTROL_CONGESTION:
04624       return "Congestion (circuits busy)";
04625    case AST_CONTROL_FLASH:
04626       return "Flash hook";
04627    case AST_CONTROL_WINK:
04628       return "Wink";
04629    case AST_CONTROL_OPTION:
04630       return "Set a low-level option";
04631    case AST_CONTROL_RADIO_KEY:
04632       return "Key Radio";
04633    case AST_CONTROL_RADIO_UNKEY:
04634       return "Un-Key Radio";
04635    case AST_CONTROL_PROGRESS:
04636       return "Remote end is making Progress";
04637    case AST_CONTROL_PROCEEDING:
04638       return "Remote end is proceeding";
04639    case AST_CONTROL_HOLD:
04640       return "Hold";
04641    case AST_CONTROL_UNHOLD:
04642       return "Unhold";
04643    case AST_CONTROL_VIDUPDATE:
04644       return "VidUpdate";
04645    case AST_CONTROL_SRCUPDATE:
04646       return "Media Source Update";
04647    case AST_CONTROL_TRANSFER:
04648       return "Transfer";
04649    case AST_CONTROL_CONNECTED_LINE:
04650       return "Connected Line";
04651    case AST_CONTROL_REDIRECTING:
04652       return "Redirecting";
04653    case AST_CONTROL_T38_PARAMETERS:
04654       return "T38_Parameters";
04655    case AST_CONTROL_CC:
04656       return "CC Not Possible";
04657    case AST_CONTROL_SRCCHANGE:
04658       return "Media Source Change";
04659    case AST_CONTROL_INCOMPLETE:
04660       return "Incomplete";
04661    case -1:
04662       return "Stop tone";
04663    default:
04664       if (!(tmp = ast_threadstorage_get(&control2str_threadbuf, CONTROL2STR_BUFSIZE)))
04665                         return "Unknown";
04666       snprintf(tmp, CONTROL2STR_BUFSIZE, "UNKNOWN-%d", ind);
04667       return tmp;
04668    }
04669 }
04670 
04671 static int skinny_transfer(struct skinny_subchannel *sub)
04672 {
04673    struct skinny_subchannel *xferor; /* the sub doing the transferring */
04674    struct skinny_subchannel *xferee; /* the sub being transferred */
04675    struct ast_tone_zone_sound *ts = NULL;
04676 
04677    if (ast_bridged_channel(sub->owner) || ast_bridged_channel(sub->related->owner)) {
04678       if (sub->xferor) {
04679          xferor = sub;
04680          xferee = sub->related;
04681       } else {
04682          xferor = sub;
04683          xferee = sub->related;
04684       }
04685 
04686       SKINNY_DEBUG(DEBUG_SUB, 3, "Transferee channels (local/remote): %s and %s\n",
04687          ast_channel_name(xferee->owner), ast_bridged_channel(xferee->owner) ? ast_channel_name(ast_bridged_channel(xferee->owner)) : "");
04688       SKINNY_DEBUG(DEBUG_SUB, 3, "Transferor channels (local/remote): %s and %s\n",
04689          ast_channel_name(xferor->owner), ast_bridged_channel(xferor->owner) ? ast_channel_name(ast_bridged_channel(xferor->owner)) : "");
04690 
04691       if (ast_bridged_channel(xferor->owner)) {
04692          if (ast_bridged_channel(xferee->owner)) {
04693             ast_queue_control(xferee->owner, AST_CONTROL_UNHOLD);
04694          }
04695          if (ast_channel_state(xferor->owner) == AST_STATE_RING) {
04696             /* play ringing inband */
04697             if ((ts = ast_get_indication_tone(ast_channel_zone(xferor->owner), "ring"))) {
04698                ast_playtones_start(xferor->owner, 0, ts->data, 1);
04699                ts = ast_tone_zone_sound_unref(ts);
04700             }
04701          }
04702          SKINNY_DEBUG(DEBUG_SUB, 3, "Transfer Masquerading %s to %s\n",
04703             ast_channel_name(xferee->owner), ast_bridged_channel(xferor->owner) ? ast_channel_name(ast_bridged_channel(xferor->owner)) : "");
04704          if (ast_channel_masquerade(xferee->owner, ast_bridged_channel(xferor->owner))) {
04705             ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
04706                ast_channel_name(ast_bridged_channel(xferor->owner)), ast_channel_name(xferee->owner));
04707             return -1;
04708          }
04709       } else if (ast_bridged_channel(xferee->owner)) {
04710          ast_queue_control(xferee->owner, AST_CONTROL_UNHOLD);
04711          if (ast_channel_state(xferor->owner) == AST_STATE_RING) {
04712             /* play ringing inband */
04713             if ((ts = ast_get_indication_tone(ast_channel_zone(xferor->owner), "ring"))) {
04714                ast_playtones_start(xferor->owner, 0, ts->data, 1);
04715                ts = ast_tone_zone_sound_unref(ts);
04716             }
04717          }
04718          SKINNY_DEBUG(DEBUG_SUB, 3, "Transfer Masquerading %s to %s\n",
04719             ast_channel_name(xferor->owner), ast_bridged_channel(xferee->owner) ? ast_channel_name(ast_bridged_channel(xferee->owner)) : "");
04720          if (ast_channel_masquerade(xferor->owner, ast_bridged_channel(xferee->owner))) {
04721             ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
04722                ast_channel_name(ast_bridged_channel(xferee->owner)), ast_channel_name(xferor->owner));
04723             return -1;
04724          }
04725          return 0;
04726       } else {
04727          ast_debug(1, "Neither %s nor %s are in a bridge, nothing to transfer\n",
04728             ast_channel_name(xferor->owner), ast_channel_name(xferee->owner));
04729       }
04730    }
04731    return 0;
04732 }
04733 
04734 static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, size_t datalen)
04735 {
04736    struct skinny_subchannel *sub = ast_channel_tech_pvt(ast);
04737    struct skinny_line *l = sub->line;
04738    struct skinny_device *d = l->device;
04739    struct skinnysession *s = d->session;
04740 
04741    if (!s) {
04742       ast_log(LOG_NOTICE, "Asked to indicate '%s' condition on channel %s, but session does not exist.\n", control2str(ind), ast_channel_name(ast));
04743       return -1;
04744    }
04745 
04746    SKINNY_DEBUG(DEBUG_SUB, 3, "Asked to indicate '%s' condition on channel %s (Sub %d)\n",
04747       control2str(ind), ast_channel_name(ast), sub->callid);
04748    switch(ind) {
04749    case AST_CONTROL_RINGING:
04750       if (sub->blindxfer) {
04751          SKINNY_DEBUG(DEBUG_SUB, 3, "Channel %s (Sub %d) set up for Blind Xfer, so Xfer rather than ring device\n",
04752             ast_channel_name(ast), sub->callid);
04753          skinny_transfer(sub);
04754          break;
04755       }
04756       setsubstate(sub, SUBSTATE_RINGOUT);
04757       return (d->earlyrtp ? -1 : 0); /* Tell asterisk to provide inband signalling if rtp started */
04758    case AST_CONTROL_BUSY:
04759       setsubstate(sub, SUBSTATE_BUSY);
04760       return (d->earlyrtp ? -1 : 0); /* Tell asterisk to provide inband signalling if rtp started */
04761    case AST_CONTROL_INCOMPLETE:
04762       /* Support for incomplete not supported for chan_skinny; treat as congestion */
04763    case AST_CONTROL_CONGESTION:
04764       setsubstate(sub, SUBSTATE_CONGESTION);
04765       return (d->earlyrtp ? -1 : 0); /* Tell asterisk to provide inband signalling if rtp started */
04766    case AST_CONTROL_PROGRESS:
04767       setsubstate(sub, SUBSTATE_PROGRESS);
04768       return (d->earlyrtp ? -1 : 0); /* Tell asterisk to provide inband signalling if rtp started */
04769    case -1:  /* STOP_TONE */
04770       transmit_stop_tone(d, l->instance, sub->callid);
04771       break;
04772    case AST_CONTROL_HOLD:
04773       ast_moh_start(ast, data, l->mohinterpret);
04774       break;
04775    case AST_CONTROL_UNHOLD:
04776       ast_moh_stop(ast);
04777       break;
04778    case AST_CONTROL_PROCEEDING:
04779       break;
04780    case AST_CONTROL_SRCUPDATE:
04781       if (sub->rtp) {
04782          ast_rtp_instance_update_source(sub->rtp);
04783       }
04784       break;
04785    case AST_CONTROL_SRCCHANGE:
04786       if (sub->rtp) {
04787          ast_rtp_instance_change_source(sub->rtp);
04788       }
04789       break;
04790    case AST_CONTROL_CONNECTED_LINE:
04791       update_connectedline(sub, data, datalen);
04792       break;
04793    default:
04794       ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", ind);
04795       /* fallthrough */
04796    case AST_CONTROL_PVT_CAUSE_CODE:
04797       return -1; /* Tell asterisk to provide inband signalling */
04798    }
04799    return 0;
04800 }
04801 
04802 static struct ast_channel *skinny_new(struct skinny_line *l, struct skinny_subline *subline, int state, const char *linkedid, int direction)
04803 {
04804    struct ast_channel *tmp;
04805    struct skinny_subchannel *sub;
04806    struct skinny_device *d = l->device;
04807    struct ast_variable *v = NULL;
04808    struct ast_format tmpfmt;
04809 
04810    if (!l->device || !l->device->session) {
04811       ast_log(LOG_WARNING, "Device for line %s is not registered.\n", l->name);
04812       return NULL;
04813    }
04814 
04815    tmp = ast_channel_alloc(1, state, l->cid_num, l->cid_name, l->accountcode, l->exten, l->context, linkedid, l->amaflags, "Skinny/%s@%s-%d", l->name, d->name, callnums);
04816    if (!tmp) {
04817       ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
04818       return NULL;
04819    } else {
04820       sub = ast_calloc(1, sizeof(*sub));
04821       if (!sub) {
04822          ast_log(LOG_WARNING, "Unable to allocate Skinny subchannel\n");
04823          return NULL;
04824       } else {
04825          ast_mutex_init(&sub->lock);
04826 
04827          sub->owner = tmp;
04828          sub->callid = callnums++;
04829          d->lastlineinstance = l->instance;
04830          d->lastcallreference = sub->callid;
04831          sub->cxmode = SKINNY_CX_INACTIVE;
04832          sub->nat = l->nat;
04833          sub->line = l;
04834          sub->blindxfer = 0;
04835          sub->xferor = 0;
04836          sub->related = NULL;
04837          sub->calldirection = direction;
04838          sub->aa_sched = 0;
04839          sub->dialer_sched = 0;
04840 
04841          if (subline) {
04842             sub->subline = subline;
04843             subline->sub = sub;
04844          } else {
04845             sub->subline = NULL;
04846          }
04847          
04848          AST_LIST_INSERT_HEAD(&l->sub, sub, list);
04849          //l->activesub = sub;
04850       }
04851       ast_channel_tech_set(tmp, &skinny_tech);
04852       ast_channel_tech_pvt_set(tmp, sub);
04853       ast_format_cap_copy(ast_channel_nativeformats(tmp), l->cap);
04854       if (ast_format_cap_is_empty(ast_channel_nativeformats(tmp))) {
04855          // Should throw an error
04856          ast_format_cap_copy(ast_channel_nativeformats(tmp), default_cap);
04857       }
04858       ast_best_codec(ast_channel_nativeformats(tmp), &tmpfmt);
04859       SKINNY_DEBUG(DEBUG_SUB, 3, "skinny_new: tmp->nativeformats=%s fmt=%s\n",
04860          ast_getformatname_multiple(dbgsub_buf, sizeof(dbgsub_buf), ast_channel_nativeformats(tmp)),
04861          ast_getformatname(&tmpfmt));
04862       if (sub->rtp) {
04863          ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(sub->rtp, 0));
04864       }
04865       if (state == AST_STATE_RING) {
04866          ast_channel_rings_set(tmp, 1);
04867       }
04868       ast_format_copy(ast_channel_writeformat(tmp), &tmpfmt);
04869       ast_format_copy(ast_channel_rawwriteformat(tmp), &tmpfmt);
04870       ast_format_copy(ast_channel_readformat(tmp), &tmpfmt);
04871       ast_format_copy(ast_channel_rawreadformat(tmp), &tmpfmt);
04872 
04873       if (!ast_strlen_zero(l->language))
04874          ast_channel_language_set(tmp, l->language);
04875       if (!ast_strlen_zero(l->accountcode))
04876          ast_channel_accountcode_set(tmp, l->accountcode);
04877       if (!ast_strlen_zero(l->parkinglot))
04878          ast_channel_parkinglot_set(tmp, l->parkinglot);
04879       if (l->amaflags)
04880          ast_channel_amaflags_set(tmp, l->amaflags);
04881 
04882       ast_module_ref(ast_module_info->self);
04883       ast_channel_callgroup_set(tmp, l->callgroup);
04884       ast_channel_pickupgroup_set(tmp, l->pickupgroup);
04885 
04886       /* XXX Need to figure out how to handle CFwdNoAnswer */
04887       if (l->cfwdtype & SKINNY_CFWD_ALL) {
04888          ast_channel_call_forward_set(tmp, l->call_forward_all);
04889       } else if (l->cfwdtype & SKINNY_CFWD_BUSY) {
04890          if (get_devicestate(l) != AST_DEVICE_NOT_INUSE) {
04891             ast_channel_call_forward_set(tmp, l->call_forward_busy);
04892          }
04893       }
04894 
04895       if (subline) {
04896          ast_channel_context_set(tmp, subline->context);
04897       } else {
04898          ast_channel_context_set(tmp, l->context);
04899       }
04900       ast_channel_exten_set(tmp, l->exten);
04901 
04902       /* Don't use ast_set_callerid() here because it will
04903        * generate a needless NewCallerID event */
04904       if (!ast_strlen_zero(l->cid_num)) {
04905          ast_channel_caller(tmp)->ani.number.valid = 1;
04906          ast_channel_caller(tmp)->ani.number.str = ast_strdup(l->cid_num);
04907       }
04908 
04909       ast_channel_priority_set(tmp, 1);
04910       ast_channel_adsicpe_set(tmp, AST_ADSI_UNAVAILABLE);
04911 
04912       if (sub->rtp)
04913          ast_jb_configure(tmp, &global_jbconf);
04914 
04915       /* Set channel variables for this call from configuration */
04916       for (v = l->chanvars ; v ; v = v->next)
04917          pbx_builtin_setvar_helper(tmp, v->name, v->value);
04918 
04919       if (state != AST_STATE_DOWN) {
04920          if (ast_pbx_start(tmp)) {
04921             ast_log(LOG_WARNING, "Unable to start PBX on %s\n", ast_channel_name(tmp));
04922             ast_hangup(tmp);
04923             tmp = NULL;
04924          }
04925       }
04926    }
04927    return tmp;
04928 }
04929 static char *substate2str(int ind) {
04930    char *tmp;
04931 
04932    switch (ind) {
04933    case SUBSTATE_UNSET:
04934       return "SUBSTATE_UNSET";
04935    case SUBSTATE_OFFHOOK:
04936       return "SUBSTATE_OFFHOOK";
04937    case SUBSTATE_ONHOOK:
04938       return "SUBSTATE_ONHOOK";
04939    case SUBSTATE_RINGOUT:
04940       return "SUBSTATE_RINGOUT";
04941    case SUBSTATE_RINGIN:
04942       return "SUBSTATE_RINGIN";
04943    case SUBSTATE_CONNECTED:
04944       return "SUBSTATE_CONNECTED";
04945    case SUBSTATE_BUSY:
04946       return "SUBSTATE_BUSY";
04947    case SUBSTATE_CONGESTION:
04948       return "SUBSTATE_CONGESTION";
04949    case SUBSTATE_PROGRESS:
04950       return "SUBSTATE_PROGRESS";
04951    case SUBSTATE_HOLD:
04952       return "SUBSTATE_HOLD";
04953    case SUBSTATE_CALLWAIT:
04954       return "SUBSTATE_CALLWAIT";
04955    case SUBSTATE_DIALING:
04956       return "SUBSTATE_DIALING";
04957    default:
04958       if (!(tmp = ast_threadstorage_get(&substate2str_threadbuf, SUBSTATE2STR_BUFSIZE)))
04959                         return "Unknown";
04960       snprintf(tmp, SUBSTATE2STR_BUFSIZE, "UNKNOWN-%d", ind);
04961       return tmp;
04962    }
04963 }
04964 
04965 static void setsubstate(struct skinny_subchannel *sub, int state)
04966 {
04967    struct skinny_line *l = sub->line;
04968    struct skinny_subline *subline = sub->subline;
04969    struct skinny_device *d = l->device;
04970    struct ast_channel *c = sub->owner;
04971    pthread_t t;
04972    int actualstate = state;
04973 
04974    if (sub->substate == SUBSTATE_ONHOOK) {
04975       return;
04976    }
04977 
04978    if (sub->dialer_sched) {
04979       skinny_sched_del(sub->dialer_sched, sub);
04980       sub->dialer_sched = 0;
04981    }
04982 
04983    if (state != SUBSTATE_RINGIN && sub->aa_sched) {
04984       skinny_sched_del(sub->aa_sched, sub);
04985       sub->aa_sched = 0;
04986       sub->aa_beep = 0;
04987       sub->aa_mute = 0;
04988    }
04989 
04990    if ((state == SUBSTATE_RINGIN) && ((d->hookstate == SKINNY_OFFHOOK) || (AST_LIST_NEXT(AST_LIST_FIRST(&l->sub), list)))) {
04991       actualstate = SUBSTATE_CALLWAIT;
04992    }
04993 
04994    if ((state == SUBSTATE_CONNECTED) && (!subline) && (AST_LIST_FIRST(&l->sublines))) {
04995       const char *slastation;
04996       struct skinny_subline *tmpsubline;
04997       slastation = pbx_builtin_getvar_helper(c, "SLASTATION");
04998       ast_verb(3, "Connecting %s to subline\n", slastation);
04999       if (slastation) {
05000          AST_LIST_TRAVERSE(&l->sublines, tmpsubline, list) {
05001             if (!strcasecmp(tmpsubline->stname, slastation)) {
05002                subline = tmpsubline;
05003                break;
05004             }
05005          }
05006          if (subline) {
05007             struct skinny_line *tmpline;
05008             subline->sub = sub;
05009             sub->subline = subline;
05010             subline->callid = sub->callid;
05011             send_callinfo(sub);
05012             AST_LIST_TRAVERSE(&lines, tmpline, all) {
05013                AST_LIST_TRAVERSE(&tmpline->sublines, tmpsubline, list) {
05014                   if (!(subline == tmpsubline)) {
05015                      if (!strcasecmp(subline->lnname, tmpsubline->lnname)) {
05016                         struct ast_state_cb_info info = {
05017                            .exten_state = tmpsubline->extenstate,
05018                         };
05019                         tmpsubline->callid = callnums++;
05020                         transmit_callstate(tmpsubline->line->device, tmpsubline->line->instance, tmpsubline->callid, SKINNY_OFFHOOK);
05021                         push_callinfo(tmpsubline, sub);
05022                         skinny_extensionstate_cb(NULL, NULL, &info, tmpsubline->container);
05023                      }
05024                   }
05025                }
05026             }
05027          }
05028       }
05029    }
05030 
05031    if (subline) { /* Different handling for subs under a subline, indications come through hints */
05032       switch (actualstate) {
05033       case SUBSTATE_ONHOOK:
05034          AST_LIST_REMOVE(&l->sub, sub, list);
05035          if (sub->related) {
05036             sub->related->related = NULL;
05037          }
05038 
05039          if (sub == l->activesub) {
05040             l->activesub = NULL;
05041             transmit_closereceivechannel(d, sub);
05042             transmit_stopmediatransmission(d, sub);
05043          }
05044 
05045          if (subline->callid) {
05046             transmit_stop_tone(d, l->instance, sub->callid);
05047             transmit_callstate(d, l->instance, subline->callid, SKINNY_CALLREMOTEMULTILINE);
05048             transmit_selectsoftkeys(d, l->instance, subline->callid, KEYDEF_SLACONNECTEDNOTACTIVE);
05049             transmit_displaypromptstatus(d, "In Use", 0, l->instance, subline->callid);
05050          }
05051 
05052          sub->cxmode = SKINNY_CX_RECVONLY;
05053          sub->substate = SUBSTATE_ONHOOK;
05054          destroy_rtp(sub);
05055          sub->substate = SUBSTATE_ONHOOK;
05056          if (sub->owner) {
05057             ast_queue_hangup(sub->owner);
05058          }
05059          return;
05060       case SUBSTATE_CONNECTED:
05061          transmit_activatecallplane(d, l);
05062          transmit_stop_tone(d, l->instance, sub->callid);
05063          transmit_selectsoftkeys(d, l->instance, subline->callid, KEYDEF_CONNECTED);
05064          transmit_callstate(d, l->instance, subline->callid, SKINNY_CONNECTED);
05065          if (!sub->rtp) {
05066             start_rtp(sub);
05067          }
05068          if (sub->substate == SUBSTATE_RINGIN || sub->substate == SUBSTATE_CALLWAIT) {
05069             ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
05070          }
05071          if (sub->substate == SUBSTATE_DIALING || sub->substate == SUBSTATE_RINGOUT) {
05072             transmit_dialednumber(d, l->lastnumberdialed, l->instance, sub->callid);
05073          }
05074          if (ast_channel_state(sub->owner) != AST_STATE_UP) {
05075             ast_setstate(sub->owner, AST_STATE_UP);
05076          }
05077          sub->substate = SUBSTATE_CONNECTED;
05078          l->activesub = sub;
05079          return; 
05080       case SUBSTATE_HOLD:
05081          if (sub->substate != SUBSTATE_CONNECTED) {
05082             ast_log(LOG_WARNING, "Cannot set substate to SUBSTATE_HOLD from %s (on call-%d)\n", substate2str(sub->substate), sub->callid);
05083             return;
05084          }
05085          transmit_activatecallplane(d, l);
05086          transmit_closereceivechannel(d, sub);
05087          transmit_stopmediatransmission(d, sub);
05088 
05089          transmit_callstate(d, l->instance, subline->callid, SKINNY_CALLREMOTEMULTILINE);
05090          transmit_selectsoftkeys(d, l->instance, subline->callid, KEYDEF_SLACONNECTEDNOTACTIVE);
05091          transmit_displaypromptstatus(d, "In Use", 0, l->instance, subline->callid);
05092          
05093          sub->substate = SUBSTATE_HOLD;
05094 
05095          ast_queue_control_data(sub->owner, AST_CONTROL_HOLD,
05096             S_OR(l->mohsuggest, NULL),
05097             !ast_strlen_zero(l->mohsuggest) ? strlen(l->mohsuggest) + 1 : 0);
05098 
05099          return;
05100       default:
05101          ast_log(LOG_WARNING, "Substate handling under subline for state %d not implemented on Sub-%d\n", state, sub->callid);
05102       }
05103    }
05104 
05105    if ((d->hookstate == SKINNY_ONHOOK) && ((actualstate == SUBSTATE_OFFHOOK) || (actualstate == SUBSTATE_DIALING)
05106       || (actualstate == SUBSTATE_RINGOUT) || (actualstate == SUBSTATE_CONNECTED) || (actualstate == SUBSTATE_BUSY)
05107       || (actualstate == SUBSTATE_CONGESTION) || (actualstate == SUBSTATE_PROGRESS))) {
05108          d->hookstate = SKINNY_OFFHOOK;
05109          transmit_speaker_mode(d, SKINNY_SPEAKERON);
05110    }
05111 
05112    SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - change state from %s to %s\n",
05113       sub->callid, substate2str(sub->substate), substate2str(actualstate));
05114 
05115    if (actualstate == sub->substate) {
05116       send_callinfo(sub);
05117       transmit_callstate(d, l->instance, sub->callid, SKINNY_HOLD);
05118       return;
05119    }
05120 
05121    switch (actualstate) {
05122    case SUBSTATE_OFFHOOK:
05123       ast_verb(1, "Call-id: %d\n", sub->callid);
05124       l->activesub = sub;
05125       transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
05126       transmit_activatecallplane(d, l);
05127       transmit_clear_display_message(d, l->instance, sub->callid);
05128       transmit_start_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05129       transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_OFFHOOK);
05130       transmit_displaypromptstatus(d, "Enter number", 0, l->instance, sub->callid);
05131 
05132       sub->substate = SUBSTATE_OFFHOOK;
05133       sub->dialer_sched = skinny_sched_add(firstdigittimeout, skinny_dialer_cb, sub);
05134       break;
05135    case SUBSTATE_ONHOOK:
05136       AST_LIST_REMOVE(&l->sub, sub, list);
05137       if (sub->related) {
05138          sub->related->related = NULL;
05139       }
05140 
05141       if (sub == l->activesub) {
05142          l->activesub = NULL;
05143          transmit_closereceivechannel(d, sub);
05144          transmit_stopmediatransmission(d, sub);
05145          transmit_stop_tone(d, l->instance, sub->callid);
05146          transmit_callstate(d, l->instance, sub->callid, SKINNY_ONHOOK);
05147          transmit_clearpromptmessage(d, l->instance, sub->callid);
05148          transmit_ringer_mode(d, SKINNY_RING_OFF);
05149          transmit_definetimedate(d); 
05150          transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF);
05151       } else {
05152          transmit_stop_tone(d, l->instance, sub->callid);
05153          transmit_callstate(d, l->instance, sub->callid, SKINNY_ONHOOK);
05154          transmit_clearpromptmessage(d, l->instance, sub->callid);
05155       }
05156 
05157       sub->cxmode = SKINNY_CX_RECVONLY;
05158       destroy_rtp(sub);
05159       if (sub->owner) {
05160          if (sub->substate == SUBSTATE_OFFHOOK) {
05161             sub->substate = SUBSTATE_ONHOOK;
05162             ast_hangup(sub->owner);
05163          } else {
05164             sub->substate = SUBSTATE_ONHOOK;
05165             ast_queue_hangup(sub->owner);
05166          }
05167       } else {
05168          sub->substate = SUBSTATE_ONHOOK;
05169       }
05170       break;
05171    case SUBSTATE_DIALING:
05172       if (ast_strlen_zero(sub->exten) || !ast_exists_extension(c, ast_channel_context(c), sub->exten, 1, l->cid_num)) {
05173          ast_log(LOG_WARNING, "Exten (%s)@(%s) does not exist, unable to set substate DIALING on sub %d\n", sub->exten, ast_channel_context(c), sub->callid);
05174          return;
05175       }
05176 
05177       if (d->hookstate == SKINNY_ONHOOK) {
05178          d->hookstate = SKINNY_OFFHOOK;
05179          transmit_speaker_mode(d, SKINNY_SPEAKERON);
05180          transmit_activatecallplane(d, l);
05181       }
05182 
05183       if (!sub->subline) {
05184          transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
05185          transmit_stop_tone(d, l->instance, sub->callid);
05186          transmit_clear_display_message(d, l->instance, sub->callid);
05187          transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
05188          transmit_displaypromptstatus(d, "Dialing", 0, l->instance, sub->callid);
05189       }
05190 
05191       if  (AST_LIST_FIRST(&l->sublines)) {
05192          if (subline) {
05193             ast_channel_exten_set(c, subline->exten);
05194             ast_channel_context_set(c, "sla_stations");
05195          } else {
05196             pbx_builtin_setvar_helper(c, "_DESTEXTEN", sub->exten);
05197             pbx_builtin_setvar_helper(c, "_DESTCONTEXT", ast_channel_context(c));
05198             ast_channel_exten_set(c, l->dialoutexten);
05199             ast_channel_context_set(c, l->dialoutcontext);
05200             ast_copy_string(l->lastnumberdialed, sub->exten, sizeof(l->lastnumberdialed));
05201          }
05202       } else {
05203          ast_channel_exten_set(c, sub->exten);
05204          ast_copy_string(l->lastnumberdialed, sub->exten, sizeof(l->lastnumberdialed));
05205       }
05206       
05207       sub->substate = SUBSTATE_DIALING;
05208    
05209       if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
05210          ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
05211          ast_hangup(c);
05212       }
05213       break;
05214    case SUBSTATE_RINGOUT:
05215       if (!(sub->substate == SUBSTATE_DIALING || sub->substate == SUBSTATE_PROGRESS)) {
05216          ast_log(LOG_WARNING, "Cannot set substate to SUBSTATE_RINGOUT from %s (on call-%d)\n", substate2str(sub->substate), sub->callid);
05217          return;
05218       }
05219       if (sub->substate != SUBSTATE_PROGRESS) {
05220          transmit_callstate(d, l->instance, sub->callid, SKINNY_PROGRESS);
05221       }
05222    
05223       if (!d->earlyrtp) {
05224          transmit_start_tone(d, SKINNY_ALERT, l->instance, sub->callid);
05225       }
05226       transmit_callstate(d, l->instance, sub->callid, SKINNY_RINGOUT);
05227       transmit_dialednumber(d, l->lastnumberdialed, l->instance, sub->callid);
05228       transmit_displaypromptstatus(d, "Ring Out", 0, l->instance, sub->callid);
05229       send_callinfo(sub);
05230       sub->substate = SUBSTATE_RINGOUT;
05231       break;
05232    case SUBSTATE_RINGIN:
05233       transmit_callstate(d, l->instance, sub->callid, SKINNY_RINGIN);
05234       transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGIN);
05235       transmit_displaypromptstatus(d, "Ring-In", 0, l->instance, sub->callid);
05236       send_callinfo(sub);
05237       transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK);
05238       transmit_ringer_mode(d, SKINNY_RING_INSIDE);
05239       transmit_activatecallplane(d, l);
05240 
05241       if (d->hookstate == SKINNY_ONHOOK) {
05242          l->activesub = sub;
05243       }
05244    
05245       if (sub->substate != SUBSTATE_RINGIN || sub->substate != SUBSTATE_CALLWAIT) {
05246          ast_setstate(c, AST_STATE_RINGING);
05247          ast_queue_control(c, AST_CONTROL_RINGING);
05248       }
05249       sub->substate = SUBSTATE_RINGIN;
05250       break;
05251    case SUBSTATE_CALLWAIT:
05252       transmit_callstate(d, l->instance, sub->callid, SKINNY_RINGIN);
05253       transmit_callstate(d, l->instance, sub->callid, SKINNY_CALLWAIT);
05254       transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGIN);
05255       transmit_displaypromptstatus(d, "Callwaiting", 0, l->instance, sub->callid);
05256       send_callinfo(sub);
05257       transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK);
05258       transmit_start_tone(d, SKINNY_CALLWAITTONE, l->instance, sub->callid);
05259    
05260       ast_setstate(c, AST_STATE_RINGING);
05261       ast_queue_control(c, AST_CONTROL_RINGING);
05262       sub->substate = SUBSTATE_CALLWAIT;
05263       break;
05264    case SUBSTATE_CONNECTED:
05265       if (sub->substate == SUBSTATE_RINGIN) {
05266          transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
05267       }
05268       if (sub->substate == SUBSTATE_HOLD) {
05269          ast_queue_control(sub->owner, AST_CONTROL_UNHOLD);
05270          transmit_connect(d, sub);
05271       }
05272       transmit_ringer_mode(d, SKINNY_RING_OFF);
05273       transmit_activatecallplane(d, l);
05274       transmit_stop_tone(d, l->instance, sub->callid);
05275       send_callinfo(sub);
05276       transmit_callstate(d, l->instance, sub->callid, SKINNY_CONNECTED);
05277       transmit_displaypromptstatus(d, "Connected", 0, l->instance, sub->callid);
05278       transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
05279       if (!sub->rtp) {
05280          start_rtp(sub);
05281       }
05282       if (sub->aa_beep) {
05283          transmit_start_tone(d, SKINNY_ZIP, l->instance, sub->callid);
05284       }
05285       if (sub->aa_mute) {
05286          transmit_microphone_mode(d, SKINNY_MICOFF);
05287       }
05288       if (sub->substate == SUBSTATE_RINGIN || sub->substate == SUBSTATE_CALLWAIT) {
05289          ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
05290       }
05291       if (sub->substate == SUBSTATE_DIALING || sub->substate == SUBSTATE_RINGOUT) {
05292          transmit_dialednumber(d, l->lastnumberdialed, l->instance, sub->callid);
05293       }
05294       if (ast_channel_state(sub->owner) != AST_STATE_UP) {
05295          ast_setstate(sub->owner, AST_STATE_UP);
05296       }
05297       sub->substate = SUBSTATE_CONNECTED;
05298       l->activesub = sub;
05299       break;
05300    case SUBSTATE_BUSY:
05301       if (!(sub->substate == SUBSTATE_DIALING || sub->substate == SUBSTATE_PROGRESS || sub->substate == SUBSTATE_RINGOUT)) {
05302          ast_log(LOG_WARNING, "Cannot set substate to SUBSTATE_BUSY from %s (on call-%d)\n", substate2str(sub->substate), sub->callid);
05303          return;
05304       }
05305 
05306       if (!d->earlyrtp) {
05307          transmit_start_tone(d, SKINNY_BUSYTONE, l->instance, sub->callid);
05308       }
05309       send_callinfo(sub);
05310       transmit_callstate(d, l->instance, sub->callid, SKINNY_BUSY);
05311       transmit_displaypromptstatus(d, "Busy", 0, l->instance, sub->callid);
05312       sub->substate = SUBSTATE_BUSY;
05313       break;
05314    case SUBSTATE_CONGESTION:
05315       if (!(sub->substate == SUBSTATE_DIALING || sub->substate == SUBSTATE_PROGRESS || sub->substate == SUBSTATE_RINGOUT)) {
05316          ast_log(LOG_WARNING, "Cannot set substate to SUBSTATE_CONGESTION from %s (on call-%d)\n", substate2str(sub->substate), sub->callid);
05317          return;
05318       }
05319 
05320       if (!d->earlyrtp) {
05321          transmit_start_tone(d, SKINNY_REORDER, l->instance, sub->callid);
05322       }
05323       send_callinfo(sub);
05324       transmit_callstate(d, l->instance, sub->callid, SKINNY_CONGESTION);
05325       transmit_displaypromptstatus(d, "Congestion", 0, l->instance, sub->callid);
05326       sub->substate = SUBSTATE_CONGESTION;
05327       break;
05328    case SUBSTATE_PROGRESS:
05329       if (sub->substate != SUBSTATE_DIALING) {
05330          ast_log(LOG_WARNING, "Cannot set substate to SUBSTATE_PROGRESS from %s (on call-%d)\n", substate2str(sub->substate), sub->callid);
05331          return;
05332       }
05333 
05334       if (!d->earlyrtp) {
05335          transmit_start_tone(d, SKINNY_ALERT, l->instance, sub->callid);
05336       }
05337       send_callinfo(sub);
05338       transmit_callstate(d, l->instance, sub->callid, SKINNY_PROGRESS);
05339       transmit_displaypromptstatus(d, "Call Progress", 0, l->instance, sub->callid);
05340       sub->substate = SUBSTATE_PROGRESS;
05341       break;
05342    case SUBSTATE_HOLD:
05343       if (sub->substate != SUBSTATE_CONNECTED) {
05344          ast_log(LOG_WARNING, "Cannot set substate to SUBSTATE_HOLD from %s (on call-%d)\n", substate2str(sub->substate), sub->callid);
05345          return;
05346       }
05347       ast_queue_control_data(sub->owner, AST_CONTROL_HOLD,
05348          S_OR(l->mohsuggest, NULL),
05349          !ast_strlen_zero(l->mohsuggest) ? strlen(l->mohsuggest) + 1 : 0);
05350 
05351       transmit_activatecallplane(d, l);
05352       transmit_closereceivechannel(d, sub);
05353       transmit_stopmediatransmission(d, sub);
05354 
05355       transmit_callstate(d, l->instance, sub->callid, SKINNY_HOLD);
05356       transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_WINK);
05357       transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_ONHOLD);
05358       sub->substate = SUBSTATE_HOLD;
05359       break;
05360    default:
05361       ast_log(LOG_WARNING, "Was asked to change to nonexistant substate %d on Sub-%d\n", state, sub->callid);
05362    }
05363 }
05364 
05365 static void dumpsub(struct skinny_subchannel *sub, int forcehangup)
05366 {
05367    struct skinny_line *l = sub->line;
05368    struct skinny_device *d = l->device;
05369    struct skinny_subchannel *activate_sub = NULL;
05370    struct skinny_subchannel *tsub;
05371 
05372    SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - Dumping\n", sub->callid);
05373 
05374    if (!forcehangup && sub->substate == SUBSTATE_HOLD) {
05375       l->activesub = NULL;
05376       return;
05377    }
05378 
05379    if (sub == l->activesub) {
05380       d->hookstate = SKINNY_ONHOOK;
05381       transmit_speaker_mode(d, SKINNY_SPEAKEROFF); 
05382       if (sub->related) {
05383          activate_sub = sub->related;
05384          setsubstate(sub, SUBSTATE_ONHOOK);
05385          l->activesub = activate_sub;
05386          if (l->activesub->substate != SUBSTATE_HOLD) {
05387             ast_log(LOG_WARNING, "Sub-%d was related but not at SUBSTATE_HOLD\n", sub->callid);
05388             return;
05389          }
05390          setsubstate(l->activesub, SUBSTATE_HOLD);
05391       } else {
05392          setsubstate(sub, SUBSTATE_ONHOOK);
05393          AST_LIST_TRAVERSE(&l->sub, tsub, list) {
05394             if (tsub->substate == SUBSTATE_CALLWAIT) {
05395                activate_sub = tsub;
05396             }
05397          }
05398          if (activate_sub) {
05399             setsubstate(activate_sub, SUBSTATE_RINGIN);
05400             return;
05401          }
05402          AST_LIST_TRAVERSE(&l->sub, tsub, list) {
05403             if (tsub->substate == SUBSTATE_HOLD) {
05404                activate_sub = tsub;
05405             }
05406          }
05407          if (activate_sub) {
05408             setsubstate(activate_sub, SUBSTATE_HOLD);
05409             return;
05410          }
05411       }
05412    } else {
05413       setsubstate(sub, SUBSTATE_ONHOOK);
05414    }
05415 }
05416 
05417 static void activatesub(struct skinny_subchannel *sub, int state)
05418 {
05419    struct skinny_line *l = sub->line;
05420 
05421    SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - Activating, and deactivating sub %d\n",
05422       sub->callid, l->activesub ? l->activesub->callid : 0);
05423 
05424    ast_channel_lock(sub->owner);
05425 
05426    if (sub == l->activesub) {
05427       setsubstate(sub, state);
05428    } else {
05429       if (l->activesub) {
05430          if (l->activesub->substate == SUBSTATE_RINGIN) {
05431             setsubstate(l->activesub, SUBSTATE_CALLWAIT);
05432          } else if (l->activesub->substate != SUBSTATE_HOLD) {
05433             setsubstate(l->activesub, SUBSTATE_ONHOOK);
05434          }
05435       }
05436       l->activesub = sub;
05437       setsubstate(sub, state);
05438    }
05439 
05440    ast_channel_unlock(sub->owner);
05441 }
05442 
05443 static void dialandactivatesub(struct skinny_subchannel *sub, char exten[AST_MAX_EXTENSION])
05444 {
05445    if (sub->line->getforward) {
05446       struct skinny_line *l = sub->line;
05447       struct skinny_device *d = l->device;
05448 
05449       // FIXME: needs some love and remove sleeps
05450       SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - Set callforward to %s\n", sub->callid, exten);
05451       set_callforwards(l, sub->exten, l->getforward);
05452       transmit_start_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05453       transmit_lamp_indication(d, STIMULUS_FORWARDALL, 1, SKINNY_LAMP_ON);
05454       transmit_displaynotify(d, "CFwd enabled", 10);
05455       transmit_cfwdstate(d, l);
05456       ast_safe_sleep(sub->owner, 500);
05457       ast_indicate(sub->owner, -1);
05458       ast_safe_sleep(sub->owner, 1000);
05459       l->getforward = 0;
05460       dumpsub(sub, 0);
05461    } else {
05462       SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - Dial %s and Activate\n", sub->callid, exten);
05463       ast_copy_string(sub->exten, exten, sizeof(sub->exten));
05464       activatesub(sub, SUBSTATE_DIALING);
05465    }
05466 }
05467 
05468 static int handle_hold_button(struct skinny_subchannel *sub)
05469 {
05470    if (!sub)
05471       return -1;
05472    if (sub->related) {
05473       setsubstate(sub, SUBSTATE_HOLD);
05474       activatesub(sub->related, SUBSTATE_CONNECTED);
05475    } else {
05476       if (sub->substate == SUBSTATE_HOLD) {
05477          activatesub(sub, SUBSTATE_CONNECTED);
05478       } else {
05479          setsubstate(sub, SUBSTATE_HOLD);
05480       }
05481    }
05482    return 1;
05483 }
05484 
05485 static int handle_transfer_button(struct skinny_subchannel *sub)
05486 {
05487    struct skinny_line *l;
05488    struct skinny_device *d;
05489    struct skinny_subchannel *newsub;
05490    struct ast_channel *c;
05491 
05492    if (!sub) {
05493       ast_verbose("Transfer: No subchannel to transfer\n");
05494       return -1;
05495    }
05496 
05497    l = sub->line;
05498    d = l->device;
05499 
05500    if (!d->session) {
05501       ast_log(LOG_WARNING, "Device for line %s is not registered.\n", l->name);
05502       return -1;
05503    }
05504 
05505    if (!sub->related) {
05506       /* Another sub has not been created so this must be first XFER press */
05507       if (!(sub->substate == SUBSTATE_HOLD)) {
05508          setsubstate(sub, SUBSTATE_HOLD);
05509       }
05510       c = skinny_new(l, NULL, AST_STATE_DOWN, NULL, SKINNY_OUTGOING);
05511       if (c) {
05512          newsub = ast_channel_tech_pvt(c);
05513          /* point the sub and newsub at each other so we know they are related */
05514          newsub->related = sub;
05515          sub->related = newsub;
05516          newsub->xferor = 1;
05517          setsubstate(newsub, SUBSTATE_OFFHOOK);
05518       } else {
05519          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05520       }
05521    } else {
05522       /* We already have a related sub so we can either complete XFER or go into BLINDXFER (or cancel BLINDXFER */
05523       if (sub->blindxfer) {
05524          /* toggle blindxfer off */
05525          sub->blindxfer = 0;
05526          sub->related->blindxfer = 0;
05527          /* we really need some indications */
05528       } else {
05529          /* We were doing attended transfer */
05530          if (ast_channel_state(sub->owner) == AST_STATE_DOWN || ast_channel_state(sub->related->owner) == AST_STATE_DOWN) {
05531             /* one of the subs so we cant transfer yet, toggle blindxfer on */
05532             sub->blindxfer = 1;
05533             sub->related->blindxfer = 1;
05534          } else {
05535             /* big assumption we have two channels, lets transfer */
05536             skinny_transfer(sub);
05537          }
05538       }
05539    }
05540    return 0;
05541 }
05542 
05543 static int handle_callforward_button(struct skinny_subchannel *sub, int cfwdtype)
05544 {
05545    struct skinny_line *l = sub->line;
05546    struct skinny_device *d = l->device;
05547    struct ast_channel *c = sub->owner;
05548 
05549    if (!d->session) {
05550       ast_log(LOG_WARNING, "Device for line %s is not registered.\n", l->name);
05551       return 0;
05552    }
05553 
05554    if (d->hookstate == SKINNY_ONHOOK) {
05555       d->hookstate = SKINNY_OFFHOOK;
05556       transmit_speaker_mode(d, SKINNY_SPEAKERON);
05557       transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
05558       transmit_activatecallplane(d, l);
05559    }
05560    transmit_clear_display_message(d, l->instance, sub->callid);
05561 
05562    if (l->cfwdtype & cfwdtype) {
05563       set_callforwards(l, NULL, cfwdtype);
05564       ast_safe_sleep(c, 500);
05565       transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
05566       transmit_closereceivechannel(d, sub);
05567       transmit_stopmediatransmission(d, sub);
05568       transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
05569       transmit_clearpromptmessage(d, l->instance, sub->callid);
05570       transmit_callstate(d, l->instance, sub->callid, SKINNY_ONHOOK);
05571       transmit_selectsoftkeys(d, 0, 0, KEYDEF_ONHOOK);
05572       transmit_activatecallplane(d, l);
05573       transmit_displaynotify(d, "CFwd disabled", 10);
05574       if (sub->owner && ast_channel_state(sub->owner) != AST_STATE_UP) {
05575          ast_indicate(c, -1);
05576          ast_hangup(c);
05577       }
05578       transmit_cfwdstate(d, l);
05579    } else {
05580       l->getforward = cfwdtype;
05581       setsubstate(sub, SUBSTATE_OFFHOOK);
05582    }
05583    return 0;
05584 }
05585 static int handle_ip_port_message(struct skinny_req *req, struct skinnysession *s)
05586 {
05587    /* no response necessary */
05588    return 1;
05589 }
05590 
05591 static int handle_keypad_button_message(struct skinny_req *req, struct skinnysession *s)
05592 {
05593    struct skinny_subchannel *sub = NULL;
05594    struct skinny_line *l;
05595    struct skinny_device *d = s->device;
05596    struct ast_frame f = { 0, };
05597    char dgt;
05598    int digit;
05599    int lineInstance;
05600    int callReference;
05601    size_t len;
05602 
05603    digit = letohl(req->data.keypad.button);
05604    lineInstance = letohl(req->data.keypad.lineInstance);
05605    callReference = letohl(req->data.keypad.callReference);
05606 
05607    if (lineInstance && callReference) {
05608       sub = find_subchannel_by_instance_reference(d, lineInstance, callReference);
05609    } else {
05610       sub = d->activeline->activesub;
05611    }
05612 
05613    if (!sub)
05614       return 0;
05615 
05616    l = sub->line;
05617 
05618    if (digit == 14) {
05619       dgt = '*';
05620    } else if (digit == 15) {
05621       dgt = '#';
05622    } else if (digit >= 0 && digit <= 9) {
05623       dgt = '0' + digit;
05624    } else {
05625       /* digit=10-13 (A,B,C,D ?), or
05626        * digit is bad value
05627        *
05628        * probably should not end up here, but set
05629        * value for backward compatibility, and log
05630        * a warning.
05631        */
05632       dgt = '0' + digit;
05633       ast_log(LOG_WARNING, "Unsupported digit %d\n", digit);
05634    }
05635 
05636    if ((sub->owner && ast_channel_state(sub->owner) <  AST_STATE_UP)) {
05637       if (sub->dialer_sched &&   !skinny_sched_del(sub->dialer_sched, sub)) {
05638          SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - Got a digit and not timed out, so try dialing\n", sub->callid);
05639          sub->dialer_sched = 0;
05640          len = strlen(sub->exten);
05641          if (len == 0) {
05642             transmit_stop_tone(d, l->instance, sub->callid);
05643             transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_DADFD);
05644          }
05645          if (len < sizeof(sub->exten) - 1) {
05646             sub->exten[len] = dgt;
05647             sub->exten[len + 1] = '\0';
05648          }
05649          if (len == sizeof(sub->exten) - 1) {
05650             skinny_dialer(sub, 1);
05651          } else {
05652             skinny_dialer(sub, 0);
05653          }
05654       } else {
05655          SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d  Got a digit already timedout, ignore\n", sub->callid);
05656          /* Timed out so the call is being progressed elsewhere, to late for digits */
05657          return 0;
05658       }
05659    } else {
05660       SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - Got a digit and sending as DTMF\n", sub->callid);
05661       f.subclass.integer = dgt;
05662       f.src = "skinny";
05663       if (sub->owner) {
05664          if (ast_channel_state(sub->owner) == 0) {
05665             f.frametype = AST_FRAME_DTMF_BEGIN;
05666             ast_queue_frame(sub->owner, &f);
05667          }
05668          /* XXX MUST queue this frame to all lines in threeway call if threeway call is active */
05669          f.frametype = AST_FRAME_DTMF_END;
05670          ast_queue_frame(sub->owner, &f);
05671          /* XXX This seriously needs to be fixed */
05672          if (AST_LIST_NEXT(sub, list) && AST_LIST_NEXT(sub, list)->owner) {
05673             if (ast_channel_state(sub->owner) == 0) {
05674                f.frametype = AST_FRAME_DTMF_BEGIN;
05675                ast_queue_frame(AST_LIST_NEXT(sub, list)->owner, &f);
05676             }
05677             f.frametype = AST_FRAME_DTMF_END;
05678             ast_queue_frame(AST_LIST_NEXT(sub, list)->owner, &f);
05679          }
05680       } else {
05681          ast_log(LOG_WARNING, "Got digit on %s, but not associated with channel\n", l->name);
05682       }
05683    }
05684    return 1;
05685 }
05686 
05687 static int handle_stimulus_message(struct skinny_req *req, struct skinnysession *s)
05688 {
05689    struct skinny_device *d = s->device;
05690    struct skinny_line *l;
05691    struct skinny_subchannel *sub;
05692    /*struct skinny_speeddial *sd;*/
05693    struct ast_channel *c;
05694    int event;
05695    int instance;
05696    int callreference;
05697    /*int res = 0;*/
05698 
05699    event = letohl(req->data.stimulus.stimulus);
05700    instance = letohl(req->data.stimulus.stimulusInstance);
05701    callreference = letohl(req->data.stimulus.callreference);
05702 
05703    /*  Note that this call should be using the passed in instance and callreference */
05704    sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
05705 
05706    if (!sub) {
05707       l = find_line_by_instance(d, d->lastlineinstance);
05708       if (!l) {
05709          return 0;
05710       }
05711       sub = l->activesub;
05712    } else {
05713       l = sub->line;
05714    }
05715 
05716    switch(event) {
05717    case STIMULUS_REDIAL:
05718       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received STIMULUS_REDIAL from %s, inst %d, callref %d\n",
05719          d->name, instance, callreference);
05720 
05721       if (ast_strlen_zero(l->lastnumberdialed)) {
05722          ast_log(LOG_WARNING, "Attempted redial, but no previously dialed number found. Ignoring button.\n");
05723          break;
05724       }
05725 
05726       c = skinny_new(l, NULL, AST_STATE_DOWN, NULL, SKINNY_OUTGOING);
05727       if (!c) {
05728          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05729       } else {
05730          sub = ast_channel_tech_pvt(c);
05731          l = sub->line;
05732          dialandactivatesub(sub, l->lastnumberdialed);
05733       }
05734       break;
05735    case STIMULUS_SPEEDDIAL:
05736        {
05737       struct skinny_speeddial *sd;
05738 
05739       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received STIMULUS_SPEEDDIAL from %s, inst %d, callref %d\n",
05740          d->name, instance, callreference);
05741 
05742       if (!(sd = find_speeddial_by_instance(d, instance, 0))) {
05743          return 0;
05744       }
05745 
05746       if (!sub || !sub->owner)
05747          c = skinny_new(l, NULL, AST_STATE_DOWN, NULL, SKINNY_OUTGOING);
05748       else
05749          c = sub->owner;
05750 
05751       if (!c) {
05752          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05753       } else {
05754          sub = ast_channel_tech_pvt(c);
05755          dialandactivatesub(sub, sd->exten);
05756       }
05757        }
05758       break;
05759    case STIMULUS_HOLD:
05760       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received STIMULUS_HOLD from %s, inst %d, callref %d\n",
05761          d->name, instance, callreference);
05762       handle_hold_button(sub);
05763       break;
05764    case STIMULUS_TRANSFER:
05765       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received STIMULUS_TRANSFER from %s, inst %d, callref %d\n",
05766          d->name, instance, callreference);
05767       if (l->transfer)
05768          handle_transfer_button(sub);
05769       else
05770          transmit_displaynotify(d, "Transfer disabled", 10);
05771       break;
05772    case STIMULUS_CONFERENCE:
05773       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received STIMULUS_CONFERENCE from %s, inst %d, callref %d\n",
05774          d->name, instance, callreference);
05775       /* XXX determine the best way to pull off a conference.  Meetme? */
05776       break;
05777    case STIMULUS_VOICEMAIL:
05778       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received STIMULUS_VOICEMAIL from %s, inst %d, callref %d\n",
05779          d->name, instance, callreference);
05780 
05781       if (!sub || !sub->owner) {
05782          c = skinny_new(l, NULL, AST_STATE_DOWN, NULL, SKINNY_OUTGOING);
05783       } else {
05784          c = sub->owner;
05785       }
05786       
05787       if (!c) {
05788          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05789          break;
05790       }
05791       
05792       sub = ast_channel_tech_pvt(c);
05793       if (sub->substate == SUBSTATE_UNSET || sub->substate == SUBSTATE_OFFHOOK){
05794          dialandactivatesub(sub, l->vmexten);
05795       }
05796       break;
05797    case STIMULUS_CALLPARK:
05798       {
05799       int extout;
05800       char message[32];
05801 
05802       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received STIMULUS_CALLPARK from %s, inst %d, callref %d\n",
05803          d->name, instance, callreference);
05804 
05805       if ((sub && sub->owner) && (ast_channel_state(sub->owner) ==  AST_STATE_UP)){
05806          c = sub->owner;
05807          if (ast_bridged_channel(c)) {
05808             if (!ast_masq_park_call(ast_bridged_channel(c), c, 0, &extout)) {
05809                snprintf(message, sizeof(message), "Call Parked at: %d", extout);
05810                transmit_displaynotify(d, message, 10);
05811             } else {
05812                transmit_displaynotify(d, "Call Park failed", 10);
05813             }
05814          } else {
05815             transmit_displaynotify(d, "Call Park not available", 10);
05816          }
05817       } else {
05818          transmit_displaynotify(d, "Call Park not available", 10);
05819       }
05820       break;
05821       }
05822    case STIMULUS_DND:
05823       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received STIMULUS_DND from %s, inst %d, callref %d\n",
05824          d->name, instance, callreference);
05825 
05826       /* Do not disturb */
05827       if (l->dnd != 0){
05828          ast_verb(3, "Disabling DND on %s@%s\n", l->name, d->name);
05829          l->dnd = 0;
05830          transmit_lamp_indication(d, STIMULUS_DND, 1, SKINNY_LAMP_ON);
05831          transmit_displaynotify(d, "DnD disabled", 10);
05832       } else {
05833          ast_verb(3, "Enabling DND on %s@%s\n", l->name, d->name);
05834          l->dnd = 1;
05835          transmit_lamp_indication(d, STIMULUS_DND, 1, SKINNY_LAMP_OFF);
05836          transmit_displaynotify(d, "DnD enabled", 10);
05837       }
05838       break;
05839    case STIMULUS_FORWARDALL:
05840       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received STIMULUS_FORWARDALL from %s, inst %d, callref %d\n",
05841          d->name, instance, callreference);
05842 
05843       if (!sub || !sub->owner) {
05844          c = skinny_new(l, NULL, AST_STATE_DOWN, NULL, SKINNY_OUTGOING);
05845       } else {
05846          c = sub->owner;
05847       }
05848 
05849       if (!c) {
05850          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05851       } else {
05852          sub = ast_channel_tech_pvt(c);
05853          handle_callforward_button(sub, SKINNY_CFWD_ALL);
05854       }
05855       break;
05856    case STIMULUS_FORWARDBUSY:
05857       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received STIMULUS_FORWARDBUSY from %s, inst %d, callref %d\n",
05858          d->name, instance, callreference);
05859 
05860       if (!sub || !sub->owner) {
05861          c = skinny_new(l, NULL, AST_STATE_DOWN, NULL, SKINNY_OUTGOING);
05862       } else {
05863          c = sub->owner;
05864       }
05865 
05866       if (!c) {
05867          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05868       } else {
05869          sub = ast_channel_tech_pvt(c);
05870          handle_callforward_button(sub, SKINNY_CFWD_BUSY);
05871       }
05872       break;
05873    case STIMULUS_FORWARDNOANSWER:
05874       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received STIMULUS_FORWARDNOANSWER from %s, inst %d, callref %d\n",
05875          d->name, instance, callreference);
05876 
05877 #if 0 /* Not sure how to handle this yet */
05878       if (!sub || !sub->owner) {
05879          c = skinny_new(l, NULL, AST_STATE_DOWN, NULL, SKINNY_OUTGOING);
05880       } else {
05881          c = sub->owner;
05882       }
05883 
05884       if (!c) {
05885          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05886       } else {
05887          sub = c->tech_pvt;
05888          handle_callforward_button(sub, SKINNY_CFWD_NOANSWER);
05889       }
05890 #endif
05891       break;
05892    case STIMULUS_DISPLAY:
05893       /* Not sure what this is */
05894       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received STIMULUS_DISPLAY from %s, inst %d, callref %d\n",
05895          d->name, instance, callreference);
05896       break;
05897    case STIMULUS_LINE:
05898       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received STIMULUS_LINE from %s, inst %d, callref %d\n",
05899          d->name, instance, callreference);
05900 
05901       l = find_line_by_instance(d, instance);
05902 
05903       if (!l) {
05904          return 0;
05905       }
05906 
05907       d->activeline = l;
05908 
05909       /* turn the speaker on */
05910       transmit_speaker_mode(d, SKINNY_SPEAKERON);
05911       transmit_ringer_mode(d, SKINNY_RING_OFF);
05912       transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
05913 
05914       d->hookstate = SKINNY_OFFHOOK;
05915 
05916       if (sub && sub->calldirection == SKINNY_INCOMING) {
05917          setsubstate(sub, SUBSTATE_CONNECTED);
05918       } else {
05919          if (sub && sub->owner) {
05920             ast_debug(1, "Current subchannel [%s] already has owner\n", ast_channel_name(sub->owner));
05921          } else {
05922             c = skinny_new(l, NULL, AST_STATE_DOWN, NULL, SKINNY_OUTGOING);
05923             if (c) {
05924                setsubstate(ast_channel_tech_pvt(c), SUBSTATE_OFFHOOK);
05925             } else {
05926                ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05927             }
05928          }
05929       }
05930       break;
05931    default:
05932       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received UNKNOWN_STIMULUS(%d) from %s, inst %d, callref %d\n",
05933          event, d->name, instance, callreference);
05934       break;
05935    }
05936    ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "Skinny/%s", l->name);
05937 
05938    return 1;
05939 }
05940 
05941 static int handle_offhook_message(struct skinny_req *req, struct skinnysession *s)
05942 {
05943    struct skinny_device *d = s->device;
05944    struct skinny_line *l = NULL;
05945    struct skinny_subchannel *sub = NULL;
05946    struct ast_channel *c;
05947    int instance;
05948    int reference;
05949 
05950    instance = letohl(req->data.offhook.instance);
05951    reference = letohl(req->data.offhook.reference);
05952 
05953    if (d->hookstate == SKINNY_OFFHOOK) {
05954       ast_verb(3, "Got offhook message when device (%s) already offhook\n", d->name);
05955       return 0;
05956    }
05957 
05958    if (reference) {
05959       sub = find_subchannel_by_instance_reference(d, instance, reference);
05960       if (sub) {
05961          l = sub->line;
05962       }
05963    }
05964    if (!sub) {
05965       if (instance) {
05966          l = find_line_by_instance(d, instance);
05967       } else {
05968          l = d->activeline;
05969       }
05970       sub = l->activesub;
05971    }
05972 
05973    transmit_ringer_mode(d, SKINNY_RING_OFF);
05974    d->hookstate = SKINNY_OFFHOOK;
05975 
05976    ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "Skinny/%s", l->name);
05977 
05978    if (sub && sub->substate == SUBSTATE_HOLD) {
05979       return 1;
05980    }
05981 
05982    transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
05983 
05984    if (sub && sub->calldirection == SKINNY_INCOMING) {
05985       setsubstate(sub, SUBSTATE_CONNECTED);
05986    } else {
05987       /* Not ideal, but let's send updated time at onhook and offhook, as it clears the display */
05988       transmit_definetimedate(d);
05989 
05990       if (sub && sub->owner) {
05991          ast_debug(1, "Current sub [%s] already has owner\n", ast_channel_name(sub->owner));
05992       } else {
05993          c = skinny_new(l, NULL, AST_STATE_DOWN, NULL, SKINNY_OUTGOING);
05994          if (c) {
05995             setsubstate(ast_channel_tech_pvt(c), SUBSTATE_OFFHOOK);
05996          } else {
05997             ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05998          }
05999       }
06000    }
06001    return 1;
06002 }
06003 
06004 static int handle_onhook_message(struct skinny_req *req, struct skinnysession *s)
06005 {
06006    struct skinny_device *d = s->device;
06007    struct skinny_line *l;
06008    struct skinny_subchannel *sub;
06009    int instance;
06010    int reference;
06011 
06012    instance = letohl(req->data.onhook.instance);
06013    reference = letohl(req->data.onhook.reference);
06014 
06015    if (instance && reference) {
06016       sub = find_subchannel_by_instance_reference(d, instance, reference);
06017       if (!sub) {
06018          return 0;
06019       }
06020       l = sub->line;
06021    } else {
06022       l = d->activeline;
06023       sub = l->activesub;
06024       if (!sub) {
06025          return 0;
06026       }
06027    }
06028 
06029    if (d->hookstate == SKINNY_ONHOOK) {
06030       /* Something else already put us back on hook */
06031       /* Not ideal, but let's send updated time anyway, as it clears the display */
06032       transmit_definetimedate(d);
06033       return 0;
06034    }
06035 
06036    if (l->transfer && sub->xferor && ast_channel_state(sub->owner) >= AST_STATE_RING) {
06037       /* We're allowed to transfer, we have two active calls and
06038          we made at least one of the calls.  Let's try and transfer */
06039       handle_transfer_button(sub);
06040       return 0;
06041    }
06042    
06043    ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Skinny/%s", l->name);
06044    
06045    dumpsub(sub, 0);
06046 
06047    d->hookstate = SKINNY_ONHOOK;
06048    
06049    /* Not ideal, but let's send updated time at onhook and offhook, as it clears the display */
06050    transmit_definetimedate(d);
06051 
06052    return 1;
06053 }
06054 
06055 static int handle_capabilities_res_message(struct skinny_req *req, struct skinnysession *s)
06056 {
06057    struct skinny_device *d = s->device;
06058    struct skinny_line *l;
06059    uint32_t count = 0;
06060    struct ast_format_cap *codecs = ast_format_cap_alloc();
06061    int i;
06062 
06063    if (!codecs) {
06064       return 0;
06065    }
06066 
06067    count = letohl(req->data.caps.count);
06068    if (count > SKINNY_MAX_CAPABILITIES) {
06069       count = SKINNY_MAX_CAPABILITIES;
06070       ast_log(LOG_WARNING, "Received more capabilities than we can handle (%d).  Ignoring the rest.\n", SKINNY_MAX_CAPABILITIES);
06071    }
06072 
06073    for (i = 0; i < count; i++) {
06074       struct ast_format acodec;
06075       int scodec = 0;
06076       scodec = letohl(req->data.caps.caps[i].codec);
06077       codec_skinny2ast(scodec, &acodec);
06078       SKINNY_DEBUG(DEBUG_AUDIO, 4, "Adding codec capability %s (%d)\n", ast_getformatname(&acodec), scodec);
06079       ast_format_cap_add(codecs, &acodec);
06080    }
06081 
06082    ast_format_cap_joint_copy(d->confcap, codecs, d->cap);
06083    SKINNY_DEBUG(DEBUG_AUDIO, 4, "Device capability set to '%s'\n", ast_getformatname_multiple(dbgreg_buf, sizeof(dbgreg_buf), d->cap));
06084    AST_LIST_TRAVERSE(&d->lines, l, list) {
06085       ast_mutex_lock(&l->lock);
06086       ast_format_cap_joint_copy(l->confcap, d->cap, l->cap);
06087       ast_mutex_unlock(&l->lock);
06088    }
06089 
06090    codecs = ast_format_cap_destroy(codecs);
06091    return 1;
06092 }
06093 
06094 static int handle_button_template_req_message(struct skinny_req *req, struct skinnysession *s)
06095 {
06096    struct skinny_device *d = s->device;
06097    struct skinny_line *l;
06098    int i;
06099 
06100    struct skinny_speeddial *sd;
06101    struct button_definition_template btn[42];
06102    int lineInstance = 1;
06103    int speeddialInstance = 1;
06104    int buttonCount = 0;
06105 
06106    if (!(req = req_alloc(sizeof(struct button_template_res_message), BUTTON_TEMPLATE_RES_MESSAGE)))
06107       return -1;
06108 
06109    SKINNY_DEBUG(DEBUG_TEMPLATE, 3, "Creating Button Template\n");
06110 
06111    memset(&btn, 0, sizeof(btn));
06112    get_button_template(s, btn);
06113 
06114    for (i=0; i<42; i++) {
06115       int btnSet = 0;
06116       switch (btn[i].buttonDefinition) {
06117          case BT_CUST_LINE:
06118             /* assume failure */
06119             req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE;
06120             req->data.buttontemplate.definition[i].instanceNumber = 0;
06121 
06122             AST_LIST_TRAVERSE(&d->lines, l, list) {
06123                if (l->instance == lineInstance) {
06124                   SKINNY_DEBUG(DEBUG_TEMPLATE, 4, "Adding button: %d, %d\n", BT_LINE, lineInstance);
06125                   req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
06126                   req->data.buttontemplate.definition[i].instanceNumber = lineInstance;
06127                   lineInstance++;
06128                   buttonCount++;
06129                   btnSet = 1;
06130                   break;
06131                }
06132             }
06133 
06134             if (!btnSet) {
06135                AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
06136                   if (sd->isHint && sd->instance == lineInstance) {
06137                      SKINNY_DEBUG(DEBUG_TEMPLATE, 4, "Adding button: %d, %d\n", BT_LINE, lineInstance);
06138                      req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
06139                      req->data.buttontemplate.definition[i].instanceNumber = lineInstance;
06140                      lineInstance++;
06141                      buttonCount++;
06142                      btnSet = 1;
06143                      break;
06144                   }
06145                }
06146             }
06147             break;
06148          case BT_CUST_LINESPEEDDIAL:
06149             /* assume failure */
06150             req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE;
06151             req->data.buttontemplate.definition[i].instanceNumber = 0;
06152 
06153             AST_LIST_TRAVERSE(&d->lines, l, list) {
06154                if (l->instance == lineInstance) {
06155                   SKINNY_DEBUG(DEBUG_TEMPLATE, 4, "Adding button: %d, %d\n", BT_LINE, lineInstance);
06156                   req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
06157                   req->data.buttontemplate.definition[i].instanceNumber = lineInstance;
06158                   lineInstance++;
06159                   buttonCount++;
06160                   btnSet = 1;
06161                   break;
06162                }
06163             }
06164 
06165             if (!btnSet) {
06166                AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
06167                   if (sd->isHint && sd->instance == lineInstance) {
06168                      SKINNY_DEBUG(DEBUG_TEMPLATE, 4, "Adding button: %d, %d\n", BT_LINE, lineInstance);
06169                      req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
06170                      req->data.buttontemplate.definition[i].instanceNumber = lineInstance;
06171                      lineInstance++;
06172                      buttonCount++;
06173                      btnSet = 1;
06174                      break;
06175                   } else if (!sd->isHint && sd->instance == speeddialInstance) {
06176                      SKINNY_DEBUG(DEBUG_TEMPLATE, 4, "Adding button: %d, %d\n", BT_SPEEDDIAL, speeddialInstance);
06177                      req->data.buttontemplate.definition[i].buttonDefinition = BT_SPEEDDIAL;
06178                      req->data.buttontemplate.definition[i].instanceNumber = speeddialInstance;
06179                      speeddialInstance++;
06180                      buttonCount++;
06181                      btnSet = 1;
06182                      break;
06183                   }
06184                }
06185             }
06186             break;
06187          case BT_LINE:
06188             req->data.buttontemplate.definition[i].buttonDefinition = htolel(BT_NONE);
06189             req->data.buttontemplate.definition[i].instanceNumber = htolel(0);
06190 
06191             AST_LIST_TRAVERSE(&d->lines, l, list) {
06192                if (l->instance == lineInstance) {
06193                   SKINNY_DEBUG(DEBUG_TEMPLATE, 4, "Adding button: %d, %d\n", BT_LINE, lineInstance);
06194                   req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
06195                   req->data.buttontemplate.definition[i].instanceNumber = lineInstance;
06196                   lineInstance++;
06197                   buttonCount++;
06198                   btnSet = 1;
06199                   break;
06200                }
06201             }
06202             break;
06203          case BT_SPEEDDIAL:
06204             req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE;
06205             req->data.buttontemplate.definition[i].instanceNumber = 0;
06206 
06207             AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
06208                if (!sd->isHint && sd->instance == speeddialInstance) {
06209                   SKINNY_DEBUG(DEBUG_TEMPLATE, 4, "Adding button: %d, %d\n", BT_SPEEDDIAL, speeddialInstance);
06210                   req->data.buttontemplate.definition[i].buttonDefinition = BT_SPEEDDIAL;
06211                   req->data.buttontemplate.definition[i].instanceNumber = speeddialInstance - 1;
06212                   speeddialInstance++;
06213                   buttonCount++;
06214                   btnSet = 1;
06215                   break;
06216                }
06217             }
06218             break;
06219          case BT_NONE:
06220             break;
06221          default:
06222             SKINNY_DEBUG(DEBUG_TEMPLATE, 4, "Adding button: %d, %d\n", btn[i].buttonDefinition, 0);
06223             req->data.buttontemplate.definition[i].buttonDefinition = htolel(btn[i].buttonDefinition);
06224             req->data.buttontemplate.definition[i].instanceNumber = 0;
06225             buttonCount++;
06226             btnSet = 1;
06227             break;
06228       }
06229    }
06230 
06231    req->data.buttontemplate.buttonOffset = 0;
06232    req->data.buttontemplate.buttonCount = htolel(buttonCount);
06233    req->data.buttontemplate.totalButtonCount = htolel(buttonCount);
06234 
06235    SKINNY_DEBUG(DEBUG_PACKET | DEBUG_TEMPLATE, 3, "Transmitting BUTTON_TEMPLATE_RES_MESSAGE to %s, type %d\n",
06236       d->name, d->type);
06237    transmit_response(d, req);
06238    return 1;
06239 }
06240 
06241 static int handle_open_receive_channel_ack_message(struct skinny_req *req, struct skinnysession *s)
06242 {
06243    struct skinny_device *d = s->device;
06244    struct skinny_line *l;
06245    struct skinny_subchannel *sub;
06246    struct ast_format_list fmt;
06247    struct sockaddr_in sin = { 0, };
06248    struct sockaddr_in us = { 0, };
06249    struct ast_sockaddr sin_tmp;
06250    struct ast_sockaddr us_tmp;
06251    struct ast_format tmpfmt;
06252    uint32_t addr;
06253    int port;
06254    int status;
06255    int callid;
06256 
06257    status = (d->protocolversion<17) ? letohl(req->data.openreceivechannelack_ip4.status) : letohl(req->data.openreceivechannelack_ip6.status);
06258 
06259    if (status) {
06260       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received OPEN_RECEIVE_CHANNEL_ACK_MESSAGE from %s, status %d\n",
06261          d->name, status);
06262       ast_log(LOG_ERROR, "Open Receive Channel Failure\n");
06263       return 0;
06264    }
06265 
06266    if (d->protocolversion<17) {
06267       addr = req->data.openreceivechannelack_ip4.ipAddr;
06268       port = letohl(req->data.openreceivechannelack_ip4.port);
06269       callid = letohl(req->data.openreceivechannelack_ip4.callReference);
06270    } else {
06271       memcpy(&addr, &req->data.openreceivechannelack_ip6.ipAddr, sizeof(addr));
06272       port = letohl(req->data.openreceivechannelack_ip6.port);
06273       callid = letohl(req->data.openreceivechannelack_ip6.callReference);
06274    }
06275 
06276    sin.sin_family = AF_INET;
06277    sin.sin_addr.s_addr = addr;
06278    sin.sin_port = htons(port);
06279 
06280    SKINNY_DEBUG(DEBUG_PACKET, 3, "Received OPEN_RECEIVE_CHANNEL_ACK_MESSAGE from %s, status %d, callid %d, ip %s:%d\n",
06281       d->name, status, callid, ast_inet_ntoa(sin.sin_addr), port);
06282 
06283    sub = find_subchannel_by_reference(d, callid);
06284 
06285    if (!sub) {
06286       ast_log(LOG_ERROR, "Open Receive Channel Failure - can't find sub for %d\n", callid);
06287       return 0;
06288    }
06289 
06290    l = sub->line;
06291 
06292    if (sub->rtp) {
06293       ast_sockaddr_from_sin(&sin_tmp, &sin);
06294       ast_rtp_instance_set_remote_address(sub->rtp, &sin_tmp);
06295       ast_rtp_instance_get_local_address(sub->rtp, &us_tmp);
06296       ast_sockaddr_to_sin(&us_tmp, &us);
06297       us.sin_addr.s_addr = us.sin_addr.s_addr ? us.sin_addr.s_addr : d->ourip.s_addr;
06298    } else {
06299       ast_log(LOG_ERROR, "No RTP structure, this is very bad\n");
06300       return 0;
06301    }
06302 
06303    SKINNY_DEBUG(DEBUG_PACKET, 4, "device ipaddr = %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
06304    SKINNY_DEBUG(DEBUG_PACKET, 4, "asterisk ipaddr = %s:%d\n", ast_inet_ntoa(us.sin_addr), ntohs(us.sin_port));
06305 
06306    ast_best_codec(l->cap, &tmpfmt);
06307    fmt = ast_codec_pref_getsize(&l->prefs, &tmpfmt);
06308 
06309    SKINNY_DEBUG(DEBUG_PACKET, 4, "Setting payloadType to '%s' (%d ms)\n", ast_getformatname(&fmt.format), fmt.cur_ms);
06310 
06311    transmit_startmediatransmission(d, sub, us, fmt);
06312 
06313    return 1;
06314 }
06315 
06316 static int handle_enbloc_call_message(struct skinny_req *req, struct skinnysession *s)
06317 {
06318    struct skinny_device *d = s->device;
06319    struct skinny_line *l;
06320    struct skinny_subchannel *sub = NULL;
06321    struct ast_channel *c;
06322 
06323    sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
06324 
06325    if (!sub) {
06326       l = find_line_by_instance(d, d->lastlineinstance);
06327       if (!l) {
06328          return 0;
06329       }
06330    } else {
06331       l = sub->line;
06332    }
06333 
06334    c = skinny_new(l, NULL, AST_STATE_DOWN, NULL, SKINNY_OUTGOING);
06335 
06336    if(!c) {
06337       ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
06338    } else {
06339       d->hookstate = SKINNY_OFFHOOK;
06340 
06341       sub = ast_channel_tech_pvt(c);
06342       dialandactivatesub(sub, req->data.enbloccallmessage.calledParty);
06343    }
06344    
06345    return 1;
06346 }
06347 
06348 
06349 static int handle_soft_key_event_message(struct skinny_req *req, struct skinnysession *s)
06350 {
06351    struct skinny_device *d = s->device;
06352    struct skinny_line *l;
06353    struct skinny_subchannel *sub = NULL;
06354    struct ast_channel *c;
06355    int event;
06356    int instance;
06357    int callreference;
06358 
06359    event = letohl(req->data.softkeyeventmessage.softKeyEvent);
06360    instance = letohl(req->data.softkeyeventmessage.instance);
06361    callreference = letohl(req->data.softkeyeventmessage.callreference);
06362 
06363    if (instance) {
06364       l = find_line_by_instance(d, instance);
06365       if (callreference) {
06366          sub = find_subchannel_by_instance_reference(d, instance, callreference);
06367       } else {
06368          sub = find_subchannel_by_instance_reference(d, instance, d->lastcallreference);
06369       }
06370    } else {
06371       l = find_line_by_instance(d, d->lastlineinstance);
06372    }
06373 
06374    if (!l) {
06375       ast_log(LOG_WARNING,  "Received Softkey Event: %d(%d/%d) but can't find line\n", event, instance, callreference);
06376       return 0;
06377    }
06378 
06379    ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "Skinny/%s", l->name);
06380 
06381    switch(event) {
06382    case SOFTKEY_NONE:
06383       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_NONE from %s, inst %d, callref %d\n",
06384          d->name, instance, callreference);
06385       break;
06386    case SOFTKEY_REDIAL:
06387       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_REDIAL from %s, inst %d, callref %d\n",
06388          d->name, instance, callreference);
06389 
06390       if (ast_strlen_zero(l->lastnumberdialed)) {
06391          ast_log(LOG_WARNING, "Attempted redial, but no previously dialed number found. Ignoring button.\n");
06392          break;
06393       }
06394 
06395       if (!sub || !sub->owner) {
06396          c = skinny_new(l, NULL, AST_STATE_DOWN, NULL, SKINNY_OUTGOING);
06397       } else {
06398          c = sub->owner;
06399       }
06400 
06401       if (!c) {
06402          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
06403       } else {
06404          sub = ast_channel_tech_pvt(c);
06405          dialandactivatesub(sub, l->lastnumberdialed);
06406       }
06407       break;
06408    case SOFTKEY_NEWCALL:  /* Actually the DIAL softkey */
06409       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_NEWCALL from %s, inst %d, callref %d\n",
06410          d->name, instance, callreference);
06411 
06412       /* New Call ALWAYS gets a new sub-channel */
06413       c = skinny_new(l, NULL, AST_STATE_DOWN, NULL, SKINNY_OUTGOING);
06414       sub = ast_channel_tech_pvt(c);
06415 
06416       if (!c) {
06417          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
06418       } else {
06419          activatesub(sub, SUBSTATE_OFFHOOK);
06420       }
06421       break;
06422    case SOFTKEY_HOLD:
06423       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_HOLD from %s, inst %d, callref %d\n",
06424          d->name, instance, callreference);
06425 
06426       if (sub) {
06427          setsubstate(sub, SUBSTATE_HOLD);
06428       } else { /* No sub, maybe an SLA call */
06429          struct skinny_subline *subline;
06430          if ((subline = find_subline_by_callid(d, callreference))) {
06431             setsubstate(subline->sub, SUBSTATE_HOLD);
06432          }
06433       }
06434 
06435       break;
06436    case SOFTKEY_TRNSFER:
06437       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_TRNSFER from %s, inst %d, callref %d\n",
06438          d->name, instance, callreference);
06439       if (l->transfer)
06440          handle_transfer_button(sub);
06441       else
06442          transmit_displaynotify(d, "Transfer disabled", 10);
06443 
06444       break;
06445    case SOFTKEY_DND:
06446       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_DND from %s, inst %d, callref %d\n",
06447          d->name, instance, callreference);
06448 
06449       /* Do not disturb */
06450       if (l->dnd != 0){
06451          ast_verb(3, "Disabling DND on %s@%s\n", l->name, d->name);
06452          l->dnd = 0;
06453          transmit_lamp_indication(d, STIMULUS_DND, 1, SKINNY_LAMP_ON);
06454          transmit_displaynotify(d, "DnD disabled", 10);
06455       } else {
06456          ast_verb(3, "Enabling DND on %s@%s\n", l->name, d->name);
06457          l->dnd = 1;
06458          transmit_lamp_indication(d, STIMULUS_DND, 1, SKINNY_LAMP_OFF);
06459          transmit_displaynotify(d, "DnD enabled", 10);
06460       }
06461       break;
06462    case SOFTKEY_CFWDALL:
06463       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_CFWDALL from %s, inst %d, callref %d\n",
06464          d->name, instance, callreference);
06465 
06466       if (!sub || !sub->owner) {
06467          c = skinny_new(l, NULL, AST_STATE_DOWN, NULL, SKINNY_OUTGOING);
06468       } else {
06469          c = sub->owner;
06470       }
06471 
06472       if (!c) {
06473          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
06474       } else {
06475          sub = ast_channel_tech_pvt(c);
06476          l->activesub = sub;
06477          handle_callforward_button(sub, SKINNY_CFWD_ALL);
06478       }
06479       break;
06480    case SOFTKEY_CFWDBUSY:
06481       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_CFWDBUSY from %s, inst %d, callref %d\n",
06482          d->name, instance, callreference);
06483 
06484       if (!sub || !sub->owner) {
06485          c = skinny_new(l, NULL, AST_STATE_DOWN, NULL, SKINNY_OUTGOING);
06486       } else {
06487          c = sub->owner;
06488       }
06489 
06490       if (!c) {
06491          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
06492       } else {
06493          sub = ast_channel_tech_pvt(c);
06494          l->activesub = sub;
06495          handle_callforward_button(sub, SKINNY_CFWD_BUSY);
06496       }
06497       break;
06498    case SOFTKEY_CFWDNOANSWER:
06499       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_CFWDNOANSWER from %s, inst %d, callref %d\n",
06500          d->name, instance, callreference);
06501 
06502 #if 0 /* Not sure how to handle this yet */
06503       if (!sub || !sub->owner) {
06504          c = skinny_new(l, NULL, AST_STATE_DOWN, NULL, SKINNY_OUTGOING);
06505       } else {
06506          c = sub->owner;
06507       }
06508 
06509       if (!c) {
06510          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
06511       } else {
06512          sub = c->tech_pvt;
06513          l->activesub = sub;
06514          handle_callforward_button(sub, SKINNY_CFWD_NOANSWER);
06515       }
06516 #endif
06517       break;
06518    case SOFTKEY_BKSPC:
06519       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_BKSPC from %s, inst %d, callref %d\n",
06520          d->name, instance, callreference);
06521       if (sub->dialer_sched && !skinny_sched_del(sub->dialer_sched, sub)) {
06522          size_t len;
06523          sub->dialer_sched = 0;
06524          len = strlen(sub->exten);
06525          if (len > 0) {
06526             sub->exten[len-1] = '\0';
06527             if (len == 1) {
06528                transmit_start_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
06529                transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_OFFHOOK);
06530             }
06531             transmit_backspace(d, l->instance, sub->callid);
06532          }
06533          sub->dialer_sched = skinny_sched_add(gendigittimeout, skinny_dialer_cb, sub);
06534       }
06535       break;
06536    case SOFTKEY_ENDCALL:
06537       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_ENDCALL from %s, inst %d, callref %d\n",
06538          d->name, instance, callreference);
06539 
06540       if (l->transfer && sub && sub->xferor && ast_channel_state(sub->owner) >= AST_STATE_RING) {
06541          /* We're allowed to transfer, we have two active calls and
06542              we made at least one of the calls.  Let's try and transfer */
06543          handle_transfer_button(sub);
06544          return 0;
06545       }
06546    
06547       ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Skinny/%s", l->name);
06548 
06549       if (sub) {
06550          dumpsub(sub, 1);
06551       } else { /* No sub, maybe an SLA call */
06552          struct skinny_subline *subline;
06553          if ((subline = find_subline_by_callid(d, callreference))) {
06554             dumpsub(subline->sub, 1);
06555          }
06556       }
06557 
06558       d->hookstate = SKINNY_ONHOOK;
06559    
06560       /* Not ideal, but let's send updated time at onhook and offhook, as it clears the display */
06561       transmit_definetimedate(d);
06562 
06563       break;
06564    case SOFTKEY_RESUME:
06565       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_RESUME from %s, inst %d, callref %d\n",
06566          d->name, instance, callreference);
06567 
06568       if (sub) {
06569          activatesub(sub, SUBSTATE_CONNECTED);
06570       } else { /* No sub, maybe an inactive SLA call */
06571          struct skinny_subline *subline;
06572          subline = find_subline_by_callid(d, callreference);
06573          c = skinny_new(l, subline, AST_STATE_DOWN, NULL, SKINNY_OUTGOING);
06574          if (!c) {
06575             ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
06576          } else {
06577             sub = ast_channel_tech_pvt(c);
06578             dialandactivatesub(sub, subline->exten);
06579          }
06580       }
06581       break;
06582    case SOFTKEY_ANSWER:
06583       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_ANSWER from %s, inst %d, callref %d\n",
06584          d->name, instance, callreference);
06585 
06586       transmit_ringer_mode(d, SKINNY_RING_OFF);
06587       transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
06588       if (d->hookstate == SKINNY_ONHOOK) {
06589          transmit_speaker_mode(d, SKINNY_SPEAKERON);
06590          d->hookstate = SKINNY_OFFHOOK;
06591       }
06592 
06593       if (sub && sub->calldirection == SKINNY_INCOMING) {
06594          activatesub(sub, SUBSTATE_CONNECTED);
06595       }
06596       break;
06597    case SOFTKEY_INFO:
06598       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_INFO from %s, inst %d, callref %d\n",
06599          d->name, instance, callreference);
06600       break;
06601    case SOFTKEY_CONFRN:
06602       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_CONFRN from %s, inst %d, callref %d\n",
06603          d->name, instance, callreference);
06604       /* XXX determine the best way to pull off a conference.  Meetme? */
06605       break;
06606    case SOFTKEY_PARK:
06607       {
06608       int extout;
06609       char message[32];
06610 
06611       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_PARK from %s, inst %d, callref %d\n",
06612          d->name, instance, callreference);
06613 
06614       if ((sub && sub->owner) && (ast_channel_state(sub->owner) ==  AST_STATE_UP)){
06615          c = sub->owner;
06616          if (ast_bridged_channel(c)) {
06617             if (!ast_masq_park_call(ast_bridged_channel(c), c, 0, &extout)) {
06618                snprintf(message, sizeof(message), "Call Parked at: %d", extout);
06619                transmit_displaynotify(d, message, 10);
06620             } else {
06621                transmit_displaynotify(d, "Call Park failed", 10);
06622             }
06623          } else {
06624             transmit_displaynotify(d, "Call Park not available", 10);
06625          }
06626       } else {
06627          transmit_displaynotify(d, "Call Park not available", 10);
06628       }
06629       break;
06630       }
06631    case SOFTKEY_JOIN:
06632       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_JOIN from %s, inst %d, callref %d\n",
06633          d->name, instance, callreference);
06634       /* this is SLA territory, should not get here unless there is a meetme at subline */
06635       {
06636          struct skinny_subline *subline;
06637          subline = find_subline_by_callid(d, callreference);
06638          c = skinny_new(l, subline, AST_STATE_DOWN, NULL, SKINNY_OUTGOING);
06639          if (!c) {
06640             ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
06641          } else {
06642             sub = ast_channel_tech_pvt(c);
06643             dialandactivatesub(sub, subline->exten);
06644          }
06645       }
06646       break;
06647    case SOFTKEY_MEETME:
06648       /* XXX How is this different from CONFRN? */
06649       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_MEETME from %s, inst %d, callref %d\n",
06650          d->name, instance, callreference);
06651       break;
06652    case SOFTKEY_PICKUP:
06653       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_PICKUP from %s, inst %d, callref %d\n",
06654          d->name, instance, callreference);
06655       break;
06656    case SOFTKEY_GPICKUP:
06657       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_GPICKUP from %s, inst %d, callref %d\n",
06658          d->name, instance, callreference);
06659       break;
06660    default:
06661       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_UNKNOWN(%d) from %s, inst %d, callref %d\n",
06662          event, d->name, instance, callreference);
06663       break;
06664    }
06665 
06666    return 1;
06667 }
06668 
06669 static int handle_message(struct skinny_req *req, struct skinnysession *s)
06670 {
06671    int res = 0;
06672    struct skinny_speeddial *sd;
06673    struct skinny_device *d = s->device;
06674 
06675    if ((!s->device) && (letohl(req->e) != REGISTER_MESSAGE && letohl(req->e) != ALARM_MESSAGE)) {
06676       ast_log(LOG_WARNING, "Client sent message #%d without first registering.\n", req->e);
06677       ast_free(req);
06678       return 0;
06679    }
06680 
06681    switch(letohl(req->e)) {
06682    case KEEP_ALIVE_MESSAGE:
06683       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received KEEP_ALIVE_MESSAGE from %s\n", d->name);
06684       transmit_keepaliveack(s->device);
06685       break;
06686    case REGISTER_MESSAGE:
06687       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received REGISTER_MESSAGE from %s, name %s, type %d, protovers %d\n",
06688          d->name, req->data.reg.name, letohl(req->data.reg.type), letohl(req->data.reg.protocolVersion));
06689       if (skinny_register(req, s)) {
06690          ast_atomic_fetchadd_int(&unauth_sessions, -1);
06691          ast_verb(3, "Device '%s' successfully registered (protoVers %d)\n", s->device->name, s->device->protocolversion);
06692          transmit_registerack(s->device);
06693          transmit_capabilitiesreq(s->device);
06694       } else {
06695          transmit_registerrej(s);
06696          ast_free(req);
06697          return -1;
06698       }
06699    case IP_PORT_MESSAGE:
06700       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received IP_PORT_MESSAGE from %s\n", d->name);
06701       res = handle_ip_port_message(req, s);
06702       break;
06703    case KEYPAD_BUTTON_MESSAGE:
06704       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received KEYPAD_BUTTON_MESSAGE from %s, digit %d, inst %d, callref %d\n",
06705          d->name, letohl(req->data.keypad.button), letohl(req->data.keypad.lineInstance), letohl(req->data.keypad.callReference));
06706       res = handle_keypad_button_message(req, s);
06707       break;
06708    case ENBLOC_CALL_MESSAGE:
06709       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received ENBLOC_CALL_MESSAGE from %s, calledParty %s\n",
06710          d->name, req->data.enbloccallmessage.calledParty);
06711       res = handle_enbloc_call_message(req, s);
06712       break;
06713    case STIMULUS_MESSAGE:
06714       /* SKINNY_PACKETDEBUG handled in handle_stimulus_message */
06715       res = handle_stimulus_message(req, s);
06716       break;
06717    case OFFHOOK_MESSAGE:
06718       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received OFFHOOK_MESSAGE from %s, inst %d, ref %d\n",
06719          d->name, letohl(req->data.offhook.instance), letohl(req->data.offhook.reference));
06720       res = handle_offhook_message(req, s);
06721       break;
06722    case ONHOOK_MESSAGE:
06723       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received ONHOOK_MESSAGE from %s, inst %d, ref %d\n",
06724          d->name, letohl(req->data.offhook.instance), letohl(req->data.offhook.reference));
06725       res = handle_onhook_message(req, s);
06726       break;
06727    case CAPABILITIES_RES_MESSAGE:
06728       SKINNY_DEBUG(DEBUG_PACKET | DEBUG_AUDIO, 3, "Received CAPABILITIES_RES_MESSAGE from %s, count %d, codec data\n",
06729          d->name, letohl(req->data.caps.count));
06730       res = handle_capabilities_res_message(req, s);
06731       break;
06732    case SPEED_DIAL_STAT_REQ_MESSAGE:
06733       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SPEED_DIAL_STAT_REQ_MESSAGE from %s, sdNum %d\n",
06734          d->name, letohl(req->data.speeddialreq.speedDialNumber));
06735       if ( (sd = find_speeddial_by_instance(s->device, letohl(req->data.speeddialreq.speedDialNumber), 0)) ) {
06736          transmit_speeddialstatres(d, sd);
06737       }
06738       break;
06739    case LINE_STATE_REQ_MESSAGE:
06740       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received LINE_STATE_REQ_MESSAGE from %s, lineNum %d\n",
06741          d->name, letohl(req->data.line.lineNumber));
06742       transmit_linestatres(d, letohl(req->data.line.lineNumber));
06743       break;
06744    case TIME_DATE_REQ_MESSAGE:
06745       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received TIME_DATE_REQ_MESSAGE from %s\n", d->name);
06746       transmit_definetimedate(d);
06747       break;
06748    case BUTTON_TEMPLATE_REQ_MESSAGE:
06749       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received BUTTON_TEMPLATE_REQ_MESSAGE from %s\n", d->name);
06750       res = handle_button_template_req_message(req, s);
06751       break;
06752    case VERSION_REQ_MESSAGE:
06753       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received VERSION_REQ_MESSAGE from %s\n", d->name);
06754       transmit_versionres(d);
06755       break;
06756    case SERVER_REQUEST_MESSAGE:
06757       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SERVER_REQUEST_MESSAGE from %s\n", d->name);
06758       transmit_serverres(d);
06759       break;
06760    case ALARM_MESSAGE:
06761       /* no response necessary */
06762       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received ALARM_MESSAGE from %s, alarm %s\n",
06763          d->name, req->data.alarm.displayMessage);
06764       break;
06765    case OPEN_RECEIVE_CHANNEL_ACK_MESSAGE:
06766       /* SKINNY_PACKETDEBUG handled in handle_open_receive_channel_ack_message */
06767       res = handle_open_receive_channel_ack_message(req, s);
06768       break;
06769    case SOFT_KEY_SET_REQ_MESSAGE:
06770       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFT_KEY_SET_REQ_MESSAGE from %s\n", d->name);
06771       transmit_softkeysetres(d);
06772       transmit_selectsoftkeys(d, 0, 0, KEYDEF_ONHOOK);
06773       break;
06774    case SOFT_KEY_EVENT_MESSAGE:
06775       /* SKINNY_PACKETDEBUG handled in handle_soft_key_event_message */
06776       res = handle_soft_key_event_message(req, s);
06777       break;
06778    case UNREGISTER_MESSAGE:
06779       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received UNREGISTER_MESSAGE from %s\n", d->name);
06780       res = skinny_unregister(req, s);
06781       break;
06782    case SOFT_KEY_TEMPLATE_REQ_MESSAGE:
06783       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFT_KEY_TEMPLATE_REQ_MESSAGE from %s\n", d->name);
06784       transmit_softkeytemplateres(d);
06785       break;
06786    case HEADSET_STATUS_MESSAGE:
06787       /* XXX umm...okay?  Why do I care? */
06788       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received HEADSET_STATUS_MESSAGE from %s\n", d->name);
06789       break;
06790    case REGISTER_AVAILABLE_LINES_MESSAGE:
06791       /* XXX I have no clue what this is for, but my phone was sending it, so... */
06792       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received REGISTER_AVAILABLE_LINES_MESSAGE from %s\n", d->name);
06793       break;
06794    default:
06795       SKINNY_DEBUG(DEBUG_PACKET, 3, "Received UNKNOWN_MESSAGE(%x) from %s\n", letohl(req->e), d->name);
06796       break;
06797    }
06798    if (res >= 0 && req)
06799       ast_free(req);
06800    return res;
06801 }
06802 
06803 static void destroy_session(struct skinnysession *s)
06804 {
06805    struct skinnysession *cur;
06806    AST_LIST_LOCK(&sessions);
06807    AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, cur, list) {
06808       if (cur == s) {
06809          AST_LIST_REMOVE_CURRENT(list);
06810          if (s->fd > -1) {
06811             close(s->fd);
06812          }
06813 
06814          if (s->device) {
06815             s->device->session = NULL;
06816          } else {
06817             ast_atomic_fetchadd_int(&unauth_sessions, -1);
06818          }
06819 
06820          ast_mutex_destroy(&s->lock);
06821 
06822          ast_free(s);
06823 
06824          break;
06825       }
06826    }
06827    AST_LIST_TRAVERSE_SAFE_END
06828    AST_LIST_UNLOCK(&sessions);
06829 }
06830 
06831 static int get_input(struct skinnysession *s)
06832 {
06833    int res;
06834    int dlen = 0;
06835    int timeout = keep_alive * 1100;
06836    time_t now;
06837    int *bufaddr;
06838    struct pollfd fds[1];
06839 
06840    if (!s->device) {
06841       if(time(&now) == -1) {
06842          ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
06843          return -1;
06844       }
06845 
06846       timeout = (auth_timeout - (now - s->start)) * 1000;
06847       if (timeout < 0) {
06848          /* we have timed out */
06849          ast_log(LOG_WARNING, "Skinny Client failed to authenticate in %d seconds\n", auth_timeout);
06850          return -1;
06851       }
06852    }
06853 
06854    fds[0].fd = s->fd;
06855    fds[0].events = POLLIN;
06856    fds[0].revents = 0;
06857    res = ast_poll(fds, 1, timeout); /* If nothing has happen, client is dead */
06858                    /* we add 10% to the keep_alive to deal */
06859                    /* with network delays, etc */
06860    if (res < 0) {
06861       if (errno != EINTR) {
06862          ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
06863          return res;
06864       }
06865    } else if (res == 0) {
06866       if (s->device) {
06867          ast_log(LOG_WARNING, "Skinny Client was lost, unregistering\n");
06868       } else {
06869          ast_log(LOG_WARNING, "Skinny Client failed to authenticate in %d seconds\n", auth_timeout);
06870       }
06871       skinny_unregister(NULL, s);
06872       return -1;
06873    }
06874 
06875    if (fds[0].revents) {
06876       ast_mutex_lock(&s->lock);
06877       memset(s->inbuf, 0, sizeof(s->inbuf));
06878       res = read(s->fd, s->inbuf, 4);
06879       if (res < 0) {
06880          ast_log(LOG_WARNING, "read() returned error: %s\n", strerror(errno));
06881 
06882          ast_log(LOG_WARNING, "Skinny Client was lost, unregistering\n");
06883 
06884          skinny_unregister(NULL, s);
06885          ast_mutex_unlock(&s->lock);
06886          return res;
06887       } else if (res != 4) {
06888          ast_log(LOG_WARNING, "Skinny Client sent less data than expected.  Expected 4 but got %d.\n", res);
06889          ast_mutex_unlock(&s->lock);
06890 
06891          if (res == 0) {
06892             ast_log(LOG_WARNING, "Skinny Client was lost, unregistering\n");
06893             skinny_unregister(NULL, s);
06894          }
06895 
06896          return -1;
06897       }
06898 
06899       bufaddr = (int *)s->inbuf;
06900       dlen = letohl(*bufaddr);
06901       if (dlen < 4) {
06902          ast_log(LOG_WARNING, "Skinny Client sent invalid data.\n");
06903          ast_mutex_unlock(&s->lock);
06904          return -1;
06905       }
06906       if (dlen+8 > sizeof(s->inbuf)) {
06907          ast_log(LOG_WARNING, "Skinny packet too large (%d bytes), max length(%d bytes)\n", dlen+8, SKINNY_MAX_PACKET);
06908          dlen = sizeof(s->inbuf) - 8;
06909       }
06910       *bufaddr = htolel(dlen);
06911 
06912       res = read(s->fd, s->inbuf+4, dlen+4);
06913       ast_mutex_unlock(&s->lock);
06914       if (res < 0) {
06915          ast_log(LOG_WARNING, "read() returned error: %s\n", strerror(errno));
06916          return res;
06917       } else if (res != (dlen+4)) {
06918          ast_log(LOG_WARNING, "Skinny Client sent less data than expected.\n");
06919          return -1;
06920       }
06921       return res;
06922    }
06923    return 0;
06924 }
06925 
06926 static struct skinny_req *skinny_req_parse(struct skinnysession *s)
06927 {
06928    struct skinny_req *req;
06929    int *bufaddr;
06930 
06931    if (!(req = ast_calloc(1, SKINNY_MAX_PACKET)))
06932       return NULL;
06933 
06934    ast_mutex_lock(&s->lock);
06935    memcpy(req, s->inbuf, skinny_header_size);
06936    bufaddr = (int *)(s->inbuf);
06937    memcpy(&req->data, s->inbuf+skinny_header_size, letohl(*bufaddr)-4);
06938 
06939    ast_mutex_unlock(&s->lock);
06940 
06941    if (letohl(req->e) < 0) {
06942       ast_log(LOG_ERROR, "Event Message is NULL from socket %d, This is bad\n", s->fd);
06943       ast_free(req);
06944       return NULL;
06945    }
06946 
06947    return req;
06948 }
06949 
06950 static void *skinny_session(void *data)
06951 {
06952    int res;
06953    struct skinny_req *req;
06954    struct skinnysession *s = data;
06955 
06956    ast_verb(3, "Starting Skinny session from %s\n", ast_inet_ntoa(s->sin.sin_addr));
06957 
06958    for (;;) {
06959       res = get_input(s);
06960       if (res < 0) {
06961          ast_verb(3, "Ending Skinny session from %s (bad input)\n", ast_inet_ntoa(s->sin.sin_addr));
06962          destroy_session(s);
06963          return NULL;
06964       }
06965 
06966       if (res > 0)
06967       {
06968          if (!(req = skinny_req_parse(s))) {
06969             ast_verb(3, "Ending Skinny session from %s (failed parse)\n", ast_inet_ntoa(s->sin.sin_addr));
06970             destroy_session(s);
06971             return NULL;
06972          }
06973 
06974          res = handle_message(req, s);
06975          if (res < 0) {
06976             ast_verb(3, "Ending Skinny session from %s\n", ast_inet_ntoa(s->sin.sin_addr));
06977             destroy_session(s);
06978             return NULL;
06979          }
06980       }
06981    }
06982    ast_debug(3, "Skinny Session returned: %s\n", strerror(errno));
06983 
06984    if (s)
06985       destroy_session(s);
06986 
06987    return 0;
06988 }
06989 
06990 static void *accept_thread(void *ignore)
06991 {
06992    int as;
06993    struct sockaddr_in sin;
06994    socklen_t sinlen;
06995    struct skinnysession *s;
06996    struct protoent *p;
06997    int arg = 1;
06998 
06999    for (;;) {
07000       sinlen = sizeof(sin);
07001       as = accept(skinnysock, (struct sockaddr *)&sin, &sinlen);
07002       if (as < 0) {
07003          ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
07004          continue;
07005       }
07006 
07007       if (ast_atomic_fetchadd_int(&unauth_sessions, +1) >= auth_limit) {
07008          close(as);
07009          ast_atomic_fetchadd_int(&unauth_sessions, -1);
07010          continue;
07011       }
07012 
07013       p = getprotobyname("tcp");
07014       if(p) {
07015          if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
07016             ast_log(LOG_WARNING, "Failed to set Skinny tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
07017          }
07018       }
07019       if (!(s = ast_calloc(1, sizeof(struct skinnysession)))) {
07020          close(as);
07021          ast_atomic_fetchadd_int(&unauth_sessions, -1);
07022          continue;
07023       }
07024 
07025       memcpy(&s->sin, &sin, sizeof(sin));
07026       ast_mutex_init(&s->lock);
07027       s->fd = as;
07028 
07029       if(time(&s->start) == -1) {
07030          ast_log(LOG_ERROR, "error executing time(): %s; disconnecting client\n", strerror(errno));
07031          destroy_session(s);
07032          continue;
07033       }
07034 
07035       AST_LIST_LOCK(&sessions);
07036       AST_LIST_INSERT_HEAD(&sessions, s, list);
07037       AST_LIST_UNLOCK(&sessions);
07038 
07039       if (ast_pthread_create(&s->t, NULL, skinny_session, s)) {
07040          destroy_session(s);
07041       }
07042    }
07043    SKINNY_DEBUG(DEBUG_THREAD, 3, "Killing accept thread\n");
07044    close(as);
07045    return 0;
07046 }
07047 
07048 static int skinny_devicestate(const char *data)
07049 {
07050    struct skinny_line *l;
07051    char *tmp;
07052 
07053    tmp = ast_strdupa(data);
07054 
07055    l = find_line_by_name(tmp);
07056 
07057    return get_devicestate(l);
07058 }
07059 
07060 static struct ast_channel *skinny_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *dest, int *cause)
07061 {
07062    struct skinny_line *l;
07063    struct skinny_subline *subline = NULL;
07064    struct ast_channel *tmpc = NULL;
07065    char tmp[256];
07066 
07067    if (!(ast_format_cap_has_type(cap, AST_FORMAT_TYPE_AUDIO))) {
07068       ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%s'\n", ast_getformatname_multiple(tmp, sizeof(tmp), cap));
07069       return NULL;
07070    }
07071 
07072    ast_copy_string(tmp, dest, sizeof(tmp));
07073    if (ast_strlen_zero(tmp)) {
07074       ast_log(LOG_NOTICE, "Skinny channels require a device\n");
07075       return NULL;
07076    }
07077    l = find_line_by_name(tmp);
07078    if (!l) {
07079       subline = find_subline_by_name(tmp);
07080       if (!subline) {
07081          ast_log(LOG_NOTICE, "No available lines on: %s\n", dest);
07082          return NULL;
07083       }
07084       l = subline->line;
07085    }
07086    ast_verb(3, "skinny_request(%s)\n", tmp);
07087    tmpc = skinny_new(l, subline, AST_STATE_DOWN, requestor ? ast_channel_linkedid(requestor) : NULL, SKINNY_INCOMING);
07088    if (!tmpc) {
07089       ast_log(LOG_WARNING, "Unable to make channel for '%s'\n", tmp);
07090    } else if (subline) {
07091       struct skinny_subchannel *sub = ast_channel_tech_pvt(tmpc);
07092       subline->sub = sub;
07093       subline->calldirection = SKINNY_INCOMING;
07094       subline->substate = SUBSTATE_UNSET;
07095       subline->callid = sub->callid;
07096       sub->subline = subline;
07097    }
07098    return tmpc;
07099 }
07100 
07101  #define TYPE_GENERAL   1
07102  #define TYPE_DEF_DEVICE 2
07103  #define TYPE_DEF_LINE  4
07104  #define TYPE_DEVICE    8
07105  #define TYPE_LINE   16
07106  
07107  #define CLINE_OPTS  ((struct skinny_line_options *)item)
07108  #define CLINE    ((struct skinny_line *)item)
07109  #define CDEV_OPTS   ((struct skinny_device_options *)item)
07110  #define CDEV     ((struct skinny_device *)item)
07111  
07112  static void config_parse_variables(int type, void *item, struct ast_variable *vptr)
07113  {
07114    struct ast_variable *v;
07115    int lineInstance = 1;
07116    int speeddialInstance = 1;
07117    
07118    while(vptr) {
07119       v = vptr;
07120       vptr = vptr->next;
07121  
07122       if (type & (TYPE_GENERAL)) {
07123          char newcontexts[AST_MAX_CONTEXT];
07124          char oldcontexts[AST_MAX_CONTEXT];
07125          char *stringp, *context, *oldregcontext;
07126          if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) {
07127             v = v->next;
07128             continue;
07129          }
07130          if (!strcasecmp(v->name, "bindaddr")) {
07131             if (!(hp = ast_gethostbyname(v->value, &ahp))) {
07132                ast_log(LOG_WARNING, "Invalid address: %s\n", v->value);
07133             } else {
07134                memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr));
07135             }
07136             continue;
07137          } else if (!strcasecmp(v->name, "keepalive")) {
07138             keep_alive = atoi(v->value);
07139             continue;
07140          } else if (!strcasecmp(v->name, "authtimeout")) {
07141             int timeout = atoi(v->value);
07142 
07143             if (timeout < 1) {
07144                ast_log(LOG_WARNING, "Invalid authtimeout value '%s', using default value\n", v->value);
07145                auth_timeout = DEFAULT_AUTH_TIMEOUT;
07146             } else {
07147                auth_timeout = timeout;
07148             }
07149             continue;
07150          } else if (!strcasecmp(v->name, "authlimit")) {
07151             int limit = atoi(v->value);
07152 
07153             if (limit < 1) {
07154                ast_log(LOG_WARNING, "Invalid authlimit value '%s', using default value\n", v->value);
07155                auth_limit = DEFAULT_AUTH_LIMIT;
07156             } else {
07157                auth_limit = limit;
07158             }
07159             continue;
07160          } else if (!strcasecmp(v->name, "regcontext")) {
07161             ast_copy_string(newcontexts, v->value, sizeof(newcontexts));
07162             stringp = newcontexts;
07163             /* Initialize copy of current global_regcontext for later use in removing stale contexts */
07164             ast_copy_string(oldcontexts, regcontext, sizeof(oldcontexts));
07165             oldregcontext = oldcontexts;
07166             /* Let's remove any contexts that are no longer defined in regcontext */
07167             cleanup_stale_contexts(stringp, oldregcontext);
07168             /* Create contexts if they don't exist already */
07169             while ((context = strsep(&stringp, "&"))) {
07170                ast_copy_string(used_context, context, sizeof(used_context));
07171                ast_context_find_or_create(NULL, NULL, context, "Skinny");
07172             }
07173             ast_copy_string(regcontext, v->value, sizeof(regcontext));
07174             continue;
07175          } else if (!strcasecmp(v->name, "vmexten")) {
07176             ast_copy_string(vmexten, v->value, sizeof(vmexten));
07177             continue;
07178          } else if (!strcasecmp(v->name, "dateformat")) {
07179             memcpy(date_format, v->value, sizeof(date_format));
07180             continue;
07181          } else if (!strcasecmp(v->name, "tos")) {
07182             if (ast_str2tos(v->value, &qos.tos))
07183                ast_log(LOG_WARNING, "Invalid tos value at line %d, refer to QoS documentation\n", v->lineno);
07184             continue;
07185          } else if (!strcasecmp(v->name, "tos_audio")) {
07186             if (ast_str2tos(v->value, &qos.tos_audio))
07187                ast_log(LOG_WARNING, "Invalid tos_audio value at line %d, refer to QoS documentation\n", v->lineno);
07188             continue;
07189          } else if (!strcasecmp(v->name, "tos_video")) {
07190             if (ast_str2tos(v->value, &qos.tos_video))
07191                ast_log(LOG_WARNING, "Invalid tos_video value at line %d, refer to QoS documentation\n", v->lineno);
07192             continue;
07193          } else if (!strcasecmp(v->name, "cos")) {
07194             if (ast_str2cos(v->value, &qos.cos))
07195                ast_log(LOG_WARNING, "Invalid cos value at line %d, refer to QoS documentation\n", v->lineno);
07196             continue;
07197          } else if (!strcasecmp(v->name, "cos_audio")) {
07198             if (ast_str2cos(v->value, &qos.cos_audio))
07199                ast_log(LOG_WARNING, "Invalid cos_audio value at line %d, refer to QoS documentation\n", v->lineno);
07200             continue;
07201          } else if (!strcasecmp(v->name, "cos_video")) {
07202             if (ast_str2cos(v->value, &qos.cos_video))
07203                ast_log(LOG_WARNING, "Invalid cos_video value at line %d, refer to QoS documentation\n", v->lineno);
07204             continue;
07205          } else if (!strcasecmp(v->name, "bindport")) {
07206             if (sscanf(v->value, "%5d", &ourport) == 1) {
07207                bindaddr.sin_port = htons(ourport);
07208             } else {
07209                ast_log(LOG_WARNING, "Invalid bindport '%s' at line %d of %s\n", v->value, v->lineno, config);
07210             }
07211             continue;
07212          } else if (!strcasecmp(v->name, "allow")) {
07213             ast_parse_allow_disallow(&default_prefs, default_cap, v->value, 1);
07214             continue;
07215          } else if (!strcasecmp(v->name, "disallow")) {
07216             ast_parse_allow_disallow(&default_prefs, default_cap, v->value, 0);
07217             continue;
07218          } 
07219       }
07220  
07221       if (!strcasecmp(v->name, "transfer")) {
07222          if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
07223             CDEV_OPTS->transfer = ast_true(v->value);
07224             continue;
07225          } else if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
07226             CLINE_OPTS->transfer = ast_true(v->value);
07227             continue;
07228          }
07229       } else if (!strcasecmp(v->name, "callwaiting")) {
07230          if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
07231             CDEV_OPTS->callwaiting = ast_true(v->value);
07232             continue;
07233          } else if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
07234             CLINE_OPTS->callwaiting = ast_true(v->value);
07235             continue;
07236          }
07237       } else if (!strcasecmp(v->name, "directmedia") || !strcasecmp(v->name, "canreinvite")) {
07238          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
07239             CLINE_OPTS->directmedia = ast_true(v->value);
07240             continue;
07241          }
07242       } else if (!strcasecmp(v->name, "nat")) {
07243          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
07244             CLINE_OPTS->nat = ast_true(v->value);
07245             continue;
07246          }
07247       } else if (!strcasecmp(v->name, "context")) {
07248          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
07249             ast_copy_string(CLINE_OPTS->context, v->value, sizeof(CLINE_OPTS->context));
07250             continue;
07251          }
07252       }else if (!strcasecmp(v->name, "vmexten")) {
07253          if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
07254             ast_copy_string(CDEV_OPTS->vmexten, v->value, sizeof(CDEV_OPTS->vmexten));
07255             continue;
07256          } else if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
07257             ast_copy_string(CLINE_OPTS->vmexten, v->value, sizeof(CLINE_OPTS->vmexten));
07258             continue;
07259          }
07260       } else if (!strcasecmp(v->name, "mwiblink")) {
07261          if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
07262             CDEV_OPTS->mwiblink = ast_true(v->value);
07263             continue;
07264          } else if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
07265             CLINE_OPTS->mwiblink = ast_true(v->value);
07266             continue;
07267          }
07268       } else if (!strcasecmp(v->name, "linelabel")) {
07269          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
07270             ast_copy_string(CLINE_OPTS->label, v->value, sizeof(CLINE_OPTS->label));
07271             continue;
07272          }
07273       } else if (!strcasecmp(v->name, "callerid")) {
07274          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
07275             if (!strcasecmp(v->value, "asreceived")) {
07276                CLINE_OPTS->cid_num[0] = '\0';
07277                CLINE_OPTS->cid_name[0] = '\0';
07278             } else {
07279                ast_callerid_split(v->value, CLINE_OPTS->cid_name, sizeof(CLINE_OPTS->cid_name), CLINE_OPTS->cid_num, sizeof(CLINE_OPTS->cid_num));
07280             }
07281             continue;
07282          }
07283       } else if (!strcasecmp(v->name, "amaflags")) {
07284          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
07285             int tempamaflags = ast_cdr_amaflags2int(v->value);
07286             if (tempamaflags < 0) {
07287                ast_log(LOG_WARNING, "Invalid AMA flags: %s at line %d\n", v->value, v->lineno);
07288             } else {
07289                CLINE_OPTS->amaflags = tempamaflags;
07290             }
07291             continue;
07292          }
07293       } else if (!strcasecmp(v->name, "regexten")) {
07294          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
07295             ast_copy_string(CLINE_OPTS->regexten, v->value, sizeof(CLINE_OPTS->regexten));
07296             continue;
07297          }
07298       } else if (!strcasecmp(v->name, "language")) {
07299          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
07300             ast_copy_string(CLINE_OPTS->language, v->value, sizeof(CLINE_OPTS->language));
07301             continue;
07302          }
07303       } else if (!strcasecmp(v->name, "accountcode")) {
07304          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
07305             ast_copy_string(CLINE_OPTS->accountcode, v->value, sizeof(CLINE_OPTS->accountcode));
07306             continue;
07307          }
07308       } else if (!strcasecmp(v->name, "mohinterpret") || !strcasecmp(v->name, "musiconhold")) {
07309          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
07310             ast_copy_string(CLINE_OPTS->mohinterpret, v->value, sizeof(CLINE_OPTS->mohinterpret));
07311             continue;
07312          }
07313       } else if (!strcasecmp(v->name, "mohsuggest")) {
07314          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
07315             ast_copy_string(CLINE_OPTS->mohsuggest, v->value, sizeof(CLINE_OPTS->mohsuggest));
07316             continue;
07317          }
07318       } else if (!strcasecmp(v->name, "callgroup")) {
07319          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
07320             CLINE_OPTS->callgroup = ast_get_group(v->value);
07321             continue;
07322          }
07323       } else if (!strcasecmp(v->name, "pickupgroup")) {
07324          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
07325             CLINE_OPTS->pickupgroup = ast_get_group(v->value);
07326             continue;
07327          }
07328       } else if (!strcasecmp(v->name, "immediate")) {
07329          if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE | TYPE_DEF_LINE | TYPE_LINE)) {
07330             CLINE_OPTS->immediate = ast_true(v->value);
07331             continue;
07332          }
07333       } else if (!strcasecmp(v->name, "cancallforward")) {
07334          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
07335             CLINE_OPTS->cancallforward = ast_true(v->value);
07336             continue;
07337          }
07338       } else if (!strcasecmp(v->name, "mailbox")) {
07339          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
07340             ast_copy_string(CLINE_OPTS->mailbox, v->value, sizeof(CLINE_OPTS->mailbox));
07341             continue;
07342          }
07343       } else if ( !strcasecmp(v->name, "parkinglot")) {
07344          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
07345             ast_copy_string(CLINE_OPTS->parkinglot, v->value, sizeof(CLINE_OPTS->parkinglot));
07346             continue;
07347          }
07348       } else if (!strcasecmp(v->name, "hasvoicemail")) {
07349          if (type & (TYPE_LINE)) {
07350             if (ast_true(v->value) && ast_strlen_zero(CLINE->mailbox)) {
07351                ast_copy_string(CLINE->mailbox, CLINE->name, sizeof(CLINE->mailbox));
07352             }
07353             continue;
07354          }
07355       } else if (!strcasecmp(v->name, "threewaycalling")) {
07356          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
07357             CLINE_OPTS->threewaycalling = ast_true(v->value);
07358             continue;
07359          }
07360       } else if (!strcasecmp(v->name, "setvar")) {
07361          if (type & (TYPE_LINE)) {
07362             CLINE->chanvars = add_var(v->value, CLINE->chanvars);
07363             continue;
07364          }
07365       } else if (!strcasecmp(v->name, "earlyrtp")) {
07366          if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
07367             CDEV_OPTS->earlyrtp = ast_true(v->value);
07368             continue;
07369          }
07370       } else if (!strcasecmp(v->name, "host")) {
07371          if (type & (TYPE_DEVICE)) {
07372             struct ast_sockaddr CDEV_addr_tmp;
07373 
07374             CDEV_addr_tmp.ss.ss_family = AF_INET;
07375             if (ast_get_ip(&CDEV_addr_tmp, v->value)) {
07376                ast_log(LOG_WARNING, "Bad IP '%s' at line %d.\n", v->value, v->lineno);
07377             }
07378             ast_sockaddr_to_sin(&CDEV_addr_tmp,
07379                       &CDEV->addr);
07380             continue;
07381          }
07382       } else if (!strcasecmp(v->name, "port")) {
07383          if (type & (TYPE_DEF_DEVICE)) {
07384             CDEV->addr.sin_port = htons(atoi(v->value));
07385             continue;
07386          }
07387       } else if (!strcasecmp(v->name, "device")) {
07388          if (type & (TYPE_DEVICE)) {
07389             ast_copy_string(CDEV_OPTS->id, v->value, sizeof(CDEV_OPTS->id));
07390             continue;
07391          }
07392       } else if (!strcasecmp(v->name, "permit") || !strcasecmp(v->name, "deny")) {
07393          if (type & (TYPE_DEVICE)) {
07394             CDEV->ha = ast_append_ha(v->name, v->value, CDEV->ha, NULL);
07395             continue;
07396          }
07397       } else if (!strcasecmp(v->name, "allow")) {
07398          if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
07399             ast_parse_allow_disallow(&CDEV->confprefs, CDEV->confcap, v->value, 1);
07400             continue;
07401          }
07402          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
07403             ast_parse_allow_disallow(&CLINE->confprefs, CLINE->confcap, v->value, 1);
07404             continue;
07405          }
07406       } else if (!strcasecmp(v->name, "disallow")) {
07407          if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
07408             ast_parse_allow_disallow(&CDEV->confprefs, CDEV->confcap, v->value, 0);
07409             continue;
07410          }
07411          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
07412             ast_parse_allow_disallow(&CLINE->confprefs, CLINE->confcap, v->value, 0);
07413             continue;
07414          }
07415       } else if (!strcasecmp(v->name, "version")) {
07416          if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
07417             ast_copy_string(CDEV_OPTS->version_id, v->value, sizeof(CDEV_OPTS->version_id));
07418             continue;
07419          }
07420       } else if (!strcasecmp(v->name, "line")) {
07421          if (type & (TYPE_DEVICE)) {
07422             struct skinny_line *l;
07423             AST_LIST_TRAVERSE(&lines, l, all) {
07424                if (!strcasecmp(v->value, l->name) && !l->prune) {
07425 
07426                   /* FIXME: temp solution about line conflicts */
07427                   struct skinny_device *d;
07428                   struct skinny_line *l2;
07429                   int lineinuse = 0;
07430                   AST_LIST_TRAVERSE(&devices, d, list) {
07431                      AST_LIST_TRAVERSE(&d->lines, l2, list) {
07432                         if (l2 == l && strcasecmp(d->id, CDEV->id)) {
07433                            ast_log(LOG_WARNING, "Line %s already used by %s. Not connecting to %s.\n", l->name, d->name, CDEV->name);
07434                            lineinuse++;
07435                         }
07436                      }
07437                   }
07438                   if (!lineinuse) {
07439                      if (!AST_LIST_FIRST(&CDEV->lines)) {
07440                         CDEV->activeline = l;
07441                      }
07442                      lineInstance++;
07443                      AST_LIST_INSERT_HEAD(&CDEV->lines, l, list);
07444                      l->device = CDEV;
07445                   }
07446                   break;
07447                }
07448             }
07449             continue;
07450          }
07451       } else if (!strcasecmp(v->name, "subline")) {
07452          if (type & (TYPE_LINE)) {
07453             struct skinny_subline *subline;
07454             struct skinny_container *container;
07455             char buf[256];
07456             char *stringp = buf, *exten, *stname, *context;
07457 
07458             if (!(subline = ast_calloc(1, sizeof(*subline)))) {
07459                ast_log(LOG_WARNING, "Unable to allocate memory for subline %s. Ignoring subline.\n", v->value);
07460                continue;
07461             }
07462             if (!(container = ast_calloc(1, sizeof(*container)))) {
07463                ast_log(LOG_WARNING, "Unable to allocate memory for subline %s container. Ignoring subline.\n", v->value);
07464                ast_free(subline);
07465                continue;
07466             }
07467             
07468             ast_copy_string(buf, v->value, sizeof(buf));
07469             exten = strsep(&stringp, "@");
07470             ast_copy_string(subline->exten, ast_strip(exten), sizeof(subline->exten));
07471             stname = strsep(&exten, "_");
07472             ast_copy_string(subline->stname, ast_strip(stname), sizeof(subline->stname));
07473             ast_copy_string(subline->lnname, ast_strip(exten), sizeof(subline->lnname));
07474             context = strsep(&stringp, ",");
07475             ast_copy_string(subline->name, ast_strip(stringp), sizeof(subline->name));
07476             ast_copy_string(subline->context, ast_strip(context), sizeof(subline->context));
07477 
07478             subline->line = CLINE;
07479             subline->sub = NULL;
07480 
07481             container->type = SKINNY_SUBLINECONTAINER;
07482             container->data = subline;
07483             subline->container = container;
07484             AST_LIST_INSERT_HEAD(&CLINE->sublines, subline, list);
07485             continue;
07486          }
07487       } else if (!strcasecmp(v->name, "dialoutcontext")) {
07488          if (type & (TYPE_LINE)) {
07489             ast_copy_string(CLINE_OPTS->dialoutcontext, v->value, sizeof(CLINE_OPTS->dialoutcontext));
07490             continue;
07491          }
07492       } else if (!strcasecmp(v->name, "dialoutexten")) {
07493          if (type & (TYPE_LINE)) {
07494             ast_copy_string(CLINE_OPTS->dialoutexten, v->value, sizeof(CLINE_OPTS->dialoutexten));
07495             continue;
07496          }
07497       } else if (!strcasecmp(v->name, "speeddial")) {
07498          if (type & (TYPE_DEVICE)) {
07499             struct skinny_speeddial *sd;
07500             struct skinny_container *container;
07501             char buf[256];
07502             char *stringp = buf, *exten, *context, *label;
07503 
07504             if (!(sd = ast_calloc(1, sizeof(*sd)))) {
07505                ast_log(LOG_WARNING, "Unable to allocate memory for speeddial %s. Ignoring speeddial.\n", v->name);
07506                continue;
07507             }
07508             if (!(container = ast_calloc(1, sizeof(*container)))) {
07509                ast_log(LOG_WARNING, "Unable to allocate memory for speeddial %s container. Ignoring speeddial.\n", v->name);
07510                ast_free(sd);
07511                continue;
07512             }
07513 
07514             ast_copy_string(buf, v->value, sizeof(buf));
07515             exten = strsep(&stringp, ",");
07516             if ((context = strchr(exten, '@'))) {
07517                *context++ = '\0';
07518             }
07519             label = stringp;
07520             ast_mutex_init(&sd->lock);
07521             ast_copy_string(sd->exten, exten, sizeof(sd->exten));
07522             if (!ast_strlen_zero(context)) {
07523                sd->isHint = 1;
07524                sd->instance = lineInstance++;
07525                ast_copy_string(sd->context, context, sizeof(sd->context));
07526             } else {
07527                sd->isHint = 0;
07528                sd->instance = speeddialInstance++;
07529                sd->context[0] = '\0';
07530             }
07531             ast_copy_string(sd->label, S_OR(label, exten), sizeof(sd->label));
07532             sd->parent = CDEV;
07533             container->type = SKINNY_SDCONTAINER;
07534             container->data = sd;
07535             sd->container = container;
07536             AST_LIST_INSERT_HEAD(&CDEV->speeddials, sd, list);
07537             continue;
07538          }
07539       } else if (!strcasecmp(v->name, "addon")) {
07540          if (type & (TYPE_DEVICE)) {
07541             struct skinny_addon *a;
07542             if (!(a = ast_calloc(1, sizeof(*a)))) {
07543                ast_log(LOG_WARNING, "Unable to allocate memory for addon %s. Ignoring addon.\n", v->name);
07544                continue;
07545             } else {
07546                ast_mutex_init(&a->lock);
07547                ast_copy_string(a->type, v->value, sizeof(a->type));
07548                AST_LIST_INSERT_HEAD(&CDEV->addons, a, list);
07549             }
07550             continue;
07551          }
07552 
07553       } else {
07554          ast_log(LOG_WARNING, "Don't know keyword '%s' at line %d\n", v->name, v->lineno);
07555          continue;
07556       }
07557       ast_log(LOG_WARNING, "Invalid category used: %s at line %d\n", v->name, v->lineno);
07558    }
07559  }
07560  
07561  static struct skinny_line *config_line(const char *lname, struct ast_variable *v)
07562  {
07563    struct skinny_line *l, *temp;
07564    int update = 0;
07565    struct skinny_container *container;
07566  
07567    ast_log(LOG_NOTICE, "Configuring skinny line %s.\n", lname);
07568 
07569    /* We find the old line and remove it just before the new
07570       line is created */
07571    AST_LIST_LOCK(&lines);
07572    AST_LIST_TRAVERSE(&lines, temp, all) {
07573       if (!strcasecmp(lname, temp->name) && temp->prune) {
07574          update = 1;
07575          break;
07576       }
07577    }
07578 
07579    if (!(l = skinny_line_alloc())) {
07580       ast_verb(1, "Unable to allocate memory for line %s.\n", lname);
07581       AST_LIST_UNLOCK(&lines);
07582       return NULL;
07583    }
07584    if (!(container = ast_calloc(1, sizeof(*container)))) {
07585       ast_log(LOG_WARNING, "Unable to allocate memory for line %s container.\n", lname);
07586       skinny_line_destroy(l);
07587       AST_LIST_UNLOCK(&lines);
07588       return NULL;
07589    }
07590 
07591    container->type = SKINNY_LINECONTAINER;
07592    container->data = l;
07593    l->container = container;
07594    
07595    memcpy(l, default_line, sizeof(*default_line));
07596    ast_mutex_init(&l->lock);
07597    ast_copy_string(l->name, lname, sizeof(l->name));
07598    ast_format_cap_copy(l->confcap, default_cap);
07599    AST_LIST_INSERT_TAIL(&lines, l, all);
07600 
07601    ast_mutex_lock(&l->lock);
07602    AST_LIST_UNLOCK(&lines);
07603 
07604    config_parse_variables(TYPE_LINE, l, v);
07605          
07606    if (!ast_strlen_zero(l->mailbox)) {
07607       char *cfg_mailbox, *cfg_context;
07608       cfg_context = cfg_mailbox = ast_strdupa(l->mailbox);
07609       ast_verb(3, "Setting mailbox '%s' on line %s\n", cfg_mailbox, l->name);
07610       strsep(&cfg_context, "@");
07611       if (ast_strlen_zero(cfg_context))
07612           cfg_context = "default";
07613       l->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, "skinny MWI subsciption", l,
07614          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, cfg_mailbox,
07615          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, cfg_context,
07616          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
07617          AST_EVENT_IE_END);
07618    }
07619 
07620    if (!ast_strlen_zero(vmexten) && ast_strlen_zero(l->vmexten)) {
07621       ast_copy_string(l->vmexten, vmexten, sizeof(l->vmexten));
07622    }
07623  
07624    ast_mutex_unlock(&l->lock);
07625    
07626    /* We do not want to unlink or free the line yet, it needs
07627       to be available to detect a device reconfig when we load the
07628       devices.  Old lines will be pruned after the reload completes */
07629 
07630    ast_verb(3, "%s config for line '%s'\n", update ? "Updated" : (skinnyreload ? "Reloaded" : "Created"), l->name);
07631 
07632    return l;
07633  }
07634  
07635  static struct skinny_device *config_device(const char *dname, struct ast_variable *v)
07636  {
07637    struct skinny_device *d, *temp;
07638    struct skinny_line *l, *ltemp;
07639    struct skinny_subchannel *sub;
07640    int update = 0;
07641  
07642    ast_log(LOG_NOTICE, "Configuring skinny device %s.\n", dname);
07643 
07644    AST_LIST_LOCK(&devices);
07645    AST_LIST_TRAVERSE(&devices, temp, list) {
07646       if (!strcasecmp(dname, temp->name) && temp->prune) {
07647          update = 1;
07648          break;
07649       }
07650    }
07651 
07652    if (!(d = skinny_device_alloc())) {
07653       ast_verb(1, "Unable to allocate memory for device %s.\n", dname);
07654       AST_LIST_UNLOCK(&devices);
07655       return NULL;
07656    }
07657    memcpy(d, default_device, sizeof(*default_device));
07658    ast_mutex_init(&d->lock);
07659    ast_copy_string(d->name, dname, sizeof(d->name));
07660    ast_format_cap_copy(d->confcap, default_cap);
07661    AST_LIST_INSERT_TAIL(&devices, d, list);
07662 
07663    ast_mutex_lock(&d->lock);
07664    AST_LIST_UNLOCK(&devices);
07665  
07666    config_parse_variables(TYPE_DEVICE, d, v);
07667  
07668    if (!AST_LIST_FIRST(&d->lines)) {
07669       ast_log(LOG_ERROR, "A Skinny device must have at least one line!\n");
07670       ast_mutex_unlock(&d->lock);
07671       return NULL;
07672    }
07673    if (/*d->addr.sin_addr.s_addr && */!ntohs(d->addr.sin_port)) {
07674       d->addr.sin_port = htons(DEFAULT_SKINNY_PORT);
07675    }
07676  
07677    if (skinnyreload){
07678       AST_LIST_LOCK(&devices);
07679       AST_LIST_TRAVERSE(&devices, temp, list) {
07680          if (strcasecmp(d->id, temp->id) || !temp->prune || !temp->session) {
07681             continue;
07682          }
07683          ast_mutex_lock(&d->lock);
07684          d->session = temp->session;
07685          d->session->device = d;
07686          d->hookstate = temp->hookstate;
07687 
07688          AST_LIST_LOCK(&d->lines);
07689          AST_LIST_TRAVERSE(&d->lines, l, list){
07690 
07691             AST_LIST_LOCK(&temp->lines);
07692             AST_LIST_TRAVERSE(&temp->lines, ltemp, list) {
07693                if (strcasecmp(l->name, ltemp->name)) {
07694                   continue;
07695                }
07696                ast_mutex_lock(&ltemp->lock);
07697                l->instance = ltemp->instance;
07698                if (l == temp->activeline) {
07699                   d->activeline = l;
07700                }
07701                if (!AST_LIST_EMPTY(&ltemp->sub)) {
07702                   ast_mutex_lock(&l->lock);
07703                   l->sub = ltemp->sub;
07704                   l->activesub = ltemp->activesub;
07705                   AST_LIST_TRAVERSE(&l->sub, sub, list) {
07706                      sub->line = l;
07707                   }
07708                   ast_mutex_unlock(&l->lock);
07709                }
07710                ast_mutex_unlock(&ltemp->lock);
07711             }
07712             AST_LIST_UNLOCK(&temp->lines);
07713          }
07714          AST_LIST_UNLOCK(&d->lines);
07715          ast_mutex_unlock(&d->lock);
07716       }
07717       AST_LIST_UNLOCK(&devices);
07718    }
07719 
07720    ast_mutex_unlock(&d->lock);
07721 
07722    ast_verb(3, "%s config for device '%s'\n", update ? "Updated" : (skinnyreload ? "Reloaded" : "Created"), d->name);
07723    
07724    return d;
07725 
07726  }
07727  
07728  static int config_load(void)
07729  {
07730    int on = 1;
07731    struct ast_config *cfg;
07732    char *cat;
07733    int oldport = ntohs(bindaddr.sin_port);
07734    struct ast_flags config_flags = { 0 };
07735    
07736    ast_log(LOG_NOTICE, "Configuring skinny from %s\n", config);
07737   
07738    if (gethostname(ourhost, sizeof(ourhost))) {
07739       ast_log(LOG_WARNING, "Unable to get hostname, Skinny disabled.\n");
07740       return 0;
07741    }
07742    cfg = ast_config_load(config, config_flags);
07743   
07744    /* We *must* have a config file otherwise stop immediately */
07745    if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
07746       ast_log(LOG_NOTICE, "Unable to load config %s, Skinny disabled.\n", config);
07747       return -1;
07748    }
07749    memset(&bindaddr, 0, sizeof(bindaddr));
07750    memset(&default_prefs, 0, sizeof(default_prefs));
07751    memset(&vmexten, '\0', sizeof(vmexten));
07752 
07753    /* Copy the default jb config over global_jbconf */
07754    memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
07755 
07756    /* load the general section */
07757    cat = ast_category_browse(cfg, "general");
07758    config_parse_variables(TYPE_GENERAL, NULL, ast_variable_browse(cfg, "general"));
07759 
07760    if (ntohl(bindaddr.sin_addr.s_addr)) {
07761       __ourip = bindaddr.sin_addr;
07762    } else {
07763       hp = ast_gethostbyname(ourhost, &ahp);
07764       if (!hp) {
07765          ast_log(LOG_WARNING, "Unable to get our IP address, Skinny disabled\n");
07766          ast_config_destroy(cfg);
07767          return 0;
07768       }
07769       memcpy(&__ourip, hp->h_addr, sizeof(__ourip));
07770    }
07771    if (!ntohs(bindaddr.sin_port)) {
07772       bindaddr.sin_port = htons(DEFAULT_SKINNY_PORT);
07773    }
07774    bindaddr.sin_family = AF_INET;
07775 
07776    /* load the lines sections */
07777    default_line->confprefs = default_prefs;
07778    config_parse_variables(TYPE_DEF_LINE, default_line, ast_variable_browse(cfg, "lines"));
07779    cat = ast_category_browse(cfg, "lines");
07780    while (cat && strcasecmp(cat, "general") && strcasecmp(cat, "devices")) {
07781       config_line(cat, ast_variable_browse(cfg, cat));
07782       cat = ast_category_browse(cfg, cat);
07783    }
07784       
07785    /* load the devices sections */
07786    default_device->confprefs = default_prefs;
07787    config_parse_variables(TYPE_DEF_DEVICE, default_device, ast_variable_browse(cfg, "devices"));
07788    cat = ast_category_browse(cfg, "devices");
07789    while (cat && strcasecmp(cat, "general") && strcasecmp(cat, "lines")) {
07790       config_device(cat, ast_variable_browse(cfg, cat));
07791       cat = ast_category_browse(cfg, cat);
07792    }
07793 
07794    ast_mutex_lock(&netlock);
07795    if ((skinnysock > -1) && (ntohs(bindaddr.sin_port) != oldport)) {
07796       close(skinnysock);
07797       skinnysock = -1;
07798    }
07799    if (skinnysock < 0) {
07800       skinnysock = socket(AF_INET, SOCK_STREAM, 0);
07801       if(setsockopt(skinnysock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
07802          ast_log(LOG_ERROR, "Set Socket Options failed: errno %d, %s\n", errno, strerror(errno));
07803          ast_config_destroy(cfg);
07804          ast_mutex_unlock(&netlock);
07805          return 0;
07806       }
07807       if (skinnysock < 0) {
07808          ast_log(LOG_WARNING, "Unable to create Skinny socket: %s\n", strerror(errno));
07809       } else {
07810          if (bind(skinnysock, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) {
07811             ast_log(LOG_WARNING, "Failed to bind to %s:%d: %s\n",
07812                   ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port),
07813                      strerror(errno));
07814             close(skinnysock);
07815             skinnysock = -1;
07816             ast_config_destroy(cfg);
07817             ast_mutex_unlock(&netlock);
07818             return 0;
07819          }
07820          if (listen(skinnysock, DEFAULT_SKINNY_BACKLOG)) {
07821                ast_log(LOG_WARNING, "Failed to start listening to %s:%d: %s\n",
07822                   ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port),
07823                      strerror(errno));
07824                close(skinnysock);
07825                skinnysock = -1;
07826                ast_config_destroy(cfg);
07827                ast_mutex_unlock(&netlock);
07828                return 0;
07829          }
07830          ast_verb(2, "Skinny listening on %s:%d\n",
07831                ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port));
07832          ast_set_qos(skinnysock, qos.tos, qos.cos, "Skinny");
07833          ast_pthread_create_background(&accept_t, NULL, accept_thread, NULL);
07834       }
07835    }
07836    ast_mutex_unlock(&netlock);
07837    ast_config_destroy(cfg);
07838    return 1;
07839 }
07840 
07841 static void delete_devices(void)
07842 {
07843    struct skinny_device *d;
07844    struct skinny_line *l;
07845    struct skinny_speeddial *sd;
07846    struct skinny_addon *a;
07847 
07848    AST_LIST_LOCK(&devices);
07849    AST_LIST_LOCK(&lines);
07850 
07851    /* Delete all devices */
07852    while ((d = AST_LIST_REMOVE_HEAD(&devices, list))) {
07853       /* Delete all lines for this device */
07854       while ((l = AST_LIST_REMOVE_HEAD(&d->lines, list))) {
07855          AST_LIST_REMOVE(&lines, l, all);
07856          AST_LIST_REMOVE(&d->lines, l, list);
07857          l = skinny_line_destroy(l);
07858       }
07859       /* Delete all speeddials for this device */
07860       while ((sd = AST_LIST_REMOVE_HEAD(&d->speeddials, list))) {
07861          free(sd->container);
07862          free(sd);
07863       }
07864       /* Delete all addons for this device */
07865       while ((a = AST_LIST_REMOVE_HEAD(&d->addons, list))) {
07866          free(a);
07867       }
07868       d = skinny_device_destroy(d);
07869    }
07870    AST_LIST_UNLOCK(&lines);
07871    AST_LIST_UNLOCK(&devices);
07872 }
07873 
07874 int skinny_reload(void)
07875 {
07876    struct skinny_device *d;
07877    struct skinny_line *l;
07878    struct skinny_speeddial *sd;
07879    struct skinny_addon *a;
07880 
07881    if (skinnyreload) {
07882       ast_verb(3, "Chan_skinny is already reloading.\n");
07883       return 0;
07884    }
07885 
07886    skinnyreload = 1;
07887 
07888    /* Mark all devices and lines as candidates to be pruned */
07889    AST_LIST_LOCK(&devices);
07890    AST_LIST_TRAVERSE(&devices, d, list) {
07891       d->prune = 1;
07892    }
07893    AST_LIST_UNLOCK(&devices);
07894 
07895    AST_LIST_LOCK(&lines);
07896    AST_LIST_TRAVERSE(&lines, l, all) {
07897       l->prune = 1;
07898    }
07899    AST_LIST_UNLOCK(&lines);
07900 
07901         config_load();
07902 
07903    /* Remove any devices that no longer exist in the config */
07904    AST_LIST_LOCK(&devices);
07905    AST_LIST_TRAVERSE_SAFE_BEGIN(&devices, d, list) {
07906       if (!d->prune) {
07907          continue;
07908       }
07909       ast_verb(3, "Removing device '%s'\n", d->name);
07910       /* Delete all lines for this device. 
07911          We do not want to free the line here, that
07912          will happen below. */
07913       while ((l = AST_LIST_REMOVE_HEAD(&d->lines, list))) {
07914       }
07915       /* Delete all speeddials for this device */
07916       while ((sd = AST_LIST_REMOVE_HEAD(&d->speeddials, list))) {
07917          free(sd);
07918       }
07919       /* Delete all addons for this device */
07920       while ((a = AST_LIST_REMOVE_HEAD(&d->addons, list))) {
07921          free(a);
07922       }
07923       AST_LIST_REMOVE_CURRENT(list);
07924       d = skinny_device_destroy(d);
07925    }
07926    AST_LIST_TRAVERSE_SAFE_END;
07927    AST_LIST_UNLOCK(&devices);
07928 
07929    AST_LIST_LOCK(&lines);  
07930    AST_LIST_TRAVERSE_SAFE_BEGIN(&lines, l, all) {
07931       if (l->prune) {
07932          AST_LIST_REMOVE_CURRENT(all);
07933          l = skinny_line_destroy(l);
07934       }
07935    }
07936    AST_LIST_TRAVERSE_SAFE_END;
07937    AST_LIST_UNLOCK(&lines);  
07938 
07939    AST_LIST_TRAVERSE(&devices, d, list) {
07940       /* Do a soft reset to re-register the devices after
07941          cleaning up the removed devices and lines */
07942       if (d->session) {
07943          ast_verb(3, "Restarting device '%s'\n", d->name);
07944          transmit_reset(d, 1);
07945       }
07946    }
07947 
07948    skinnyreload = 0;
07949         return 0;
07950 }
07951 
07952 static int load_module(void)
07953 {
07954    int res = 0;
07955    struct ast_format tmpfmt;
07956    if (!(default_cap = ast_format_cap_alloc())) {
07957       return AST_MODULE_LOAD_DECLINE;
07958    }
07959    if (!(skinny_tech.capabilities = ast_format_cap_alloc())) {
07960       return AST_MODULE_LOAD_DECLINE;
07961    }
07962 
07963    ast_format_cap_add_all_by_type(skinny_tech.capabilities, AST_FORMAT_TYPE_AUDIO);
07964    ast_format_cap_add(default_cap, ast_format_set(&tmpfmt, AST_FORMAT_ULAW, 0));
07965    ast_format_cap_add(default_cap, ast_format_set(&tmpfmt, AST_FORMAT_ALAW, 0));
07966 
07967    for (; res < ARRAY_LEN(soft_key_template_default); res++) {
07968       soft_key_template_default[res].softKeyEvent = htolel(soft_key_template_default[res].softKeyEvent);
07969    }
07970    /* load and parse config */
07971    res = config_load();
07972    if (res == -1) {
07973       return AST_MODULE_LOAD_DECLINE;
07974    }
07975 
07976    /* Make sure we can register our skinny channel type */
07977    if (ast_channel_register(&skinny_tech)) {
07978       ast_log(LOG_ERROR, "Unable to register channel class 'Skinny'\n");
07979       return -1;
07980    }
07981 
07982    ast_rtp_glue_register(&skinny_rtp_glue);
07983    ast_cli_register_multiple(cli_skinny, ARRAY_LEN(cli_skinny));
07984 
07985    ast_manager_register_xml("SKINNYdevices", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_devices);
07986    ast_manager_register_xml("SKINNYshowdevice", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_device);
07987    ast_manager_register_xml("SKINNYlines", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_lines);
07988    ast_manager_register_xml("SKINNYshowline", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_line);
07989 
07990    sched = ast_sched_context_create();
07991    if (!sched) {
07992       ast_log(LOG_WARNING, "Unable to create schedule context\n");
07993       return AST_MODULE_LOAD_FAILURE;
07994    }
07995    if (ast_sched_start_thread(sched)) {
07996       ast_sched_context_destroy(sched);
07997       sched = NULL;
07998       return AST_MODULE_LOAD_FAILURE;
07999    }
08000 
08001    return AST_MODULE_LOAD_SUCCESS;
08002 }
08003 
08004 static int unload_module(void)
08005 {
08006    struct skinnysession *s;
08007    struct skinny_device *d;
08008    struct skinny_line *l;
08009    struct skinny_subchannel *sub;
08010    struct ast_context *con;
08011 
08012    ast_rtp_glue_unregister(&skinny_rtp_glue);
08013    ast_channel_unregister(&skinny_tech);
08014    ast_cli_unregister_multiple(cli_skinny, ARRAY_LEN(cli_skinny));
08015 
08016    ast_manager_unregister("SKINNYdevices");
08017    ast_manager_unregister("SKINNYshowdevice");
08018    ast_manager_unregister("SKINNYlines");
08019    ast_manager_unregister("SKINNYshowline");
08020    
08021    AST_LIST_LOCK(&sessions);
08022    /* Destroy all the interfaces and free their memory */
08023    while((s = AST_LIST_REMOVE_HEAD(&sessions, list))) {
08024       d = s->device;
08025       AST_LIST_TRAVERSE(&d->lines, l, list){
08026          ast_mutex_lock(&l->lock);
08027          AST_LIST_TRAVERSE(&l->sub, sub, list) {
08028             ast_mutex_lock(&sub->lock);
08029             if (sub->owner) {
08030                ast_softhangup(sub->owner, AST_SOFTHANGUP_APPUNLOAD);
08031             }
08032             ast_mutex_unlock(&sub->lock);
08033          }
08034          if (l->mwi_event_sub)
08035             ast_event_unsubscribe(l->mwi_event_sub);
08036          ast_mutex_unlock(&l->lock);
08037          manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: Skinny\r\nPeer: Skinny/%s@%s\r\nPeerStatus: Unregistered\r\n", l->name, d->name);
08038          unregister_exten(l);
08039       }
08040       if (s->fd > -1)
08041          close(s->fd);
08042       pthread_cancel(s->t);
08043       pthread_kill(s->t, SIGURG);
08044       pthread_join(s->t, NULL);
08045       free(s);
08046    }
08047    AST_LIST_UNLOCK(&sessions);
08048 
08049    delete_devices();
08050 
08051    ast_mutex_lock(&netlock);
08052    if (accept_t && (accept_t != AST_PTHREADT_STOP)) {
08053       pthread_cancel(accept_t);
08054       pthread_kill(accept_t, SIGURG);
08055       pthread_join(accept_t, NULL);
08056    }
08057    accept_t = AST_PTHREADT_STOP;
08058    ast_mutex_unlock(&netlock);
08059 
08060    close(skinnysock);
08061    if (sched) {
08062       ast_sched_context_destroy(sched);
08063    }
08064 
08065    con = ast_context_find(used_context);
08066    if (con)
08067       ast_context_destroy(con, "Skinny");
08068 
08069    default_cap = ast_format_cap_destroy(default_cap);
08070    skinny_tech.capabilities = ast_format_cap_destroy(skinny_tech.capabilities);
08071    return 0;
08072 }
08073 
08074 static int reload(void)
08075 {
08076    skinny_reload();
08077    return 0;
08078 }
08079 
08080 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Skinny Client Control Protocol (Skinny)",
08081       .load = load_module,
08082       .unload = unload_module,
08083       .reload = reload,
08084       .load_pri = AST_MODPRI_CHANNEL_DRIVER,
08085 );