Sat Feb 11 06:33:11 2012

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

Generated on Sat Feb 11 06:33:12 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6