Wed Aug 7 17:15:40 2019

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

Generated on 7 Aug 2019 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1