Sun Oct 16 2011 08:41:36

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: 328209 $")
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, "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, "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 (!c->caller.id.number.valid
02681       || ast_strlen_zero(c->caller.id.number.str)
02682       || !c->connected.id.number.valid
02683       || ast_strlen_zero(c->connected.id.number.str))
02684       return;
02685 
02686    if (sub->owner->_state == AST_STATE_UP) {
02687       transmit_callstate(d, l->instance, sub->callid, SKINNY_CONNECTED);
02688       transmit_displaypromptstatus(d, "Connected", 0, l->instance, sub->callid);
02689       if (sub->outgoing)
02690          transmit_callinfo(d,
02691             S_COR(c->connected.id.name.valid, c->connected.id.name.str, ""),
02692             c->connected.id.number.str,
02693             l->cid_name, l->cid_num, l->instance, sub->callid, 1);
02694       else
02695          transmit_callinfo(d, l->cid_name, l->cid_num,
02696             S_COR(c->connected.id.name.valid, c->connected.id.name.str, ""),
02697             c->connected.id.number.str,
02698             l->instance, sub->callid, 2);
02699    } else {
02700       if (sub->outgoing) {
02701          transmit_callstate(d, l->instance, sub->callid, SKINNY_RINGIN);
02702          transmit_displaypromptstatus(d, "Ring-In", 0, l->instance, sub->callid);
02703          transmit_callinfo(d,
02704             S_COR(c->connected.id.name.valid, c->connected.id.name.str, ""),
02705             c->connected.id.number.str,
02706             l->cid_name, l->cid_num, l->instance, sub->callid, 1);
02707       } else {
02708          if (!sub->ringing) {
02709             transmit_callstate(d, l->instance, sub->callid, SKINNY_RINGOUT);
02710             transmit_displaypromptstatus(d, "Ring-Out", 0, l->instance, sub->callid);
02711             sub->ringing = 1;
02712          } else {
02713             transmit_callstate(d, l->instance, sub->callid, SKINNY_PROGRESS);
02714             transmit_displaypromptstatus(d, "Call Progress", 0, l->instance, sub->callid);
02715             sub->progress = 1;
02716          }
02717 
02718          transmit_callinfo(d, l->cid_name, l->cid_num,
02719             S_COR(c->connected.id.name.valid, c->connected.id.name.str, ""),
02720             c->connected.id.number.str,
02721             l->instance, sub->callid, 2);
02722       }
02723    }
02724 }
02725 
02726 static void mwi_event_cb(const struct ast_event *event, void *userdata)
02727 {
02728    struct skinny_line *l = userdata;
02729    struct skinny_device *d = l->device;
02730    if (d) {
02731       struct skinnysession *s = d->session;
02732       struct skinny_line *l2;
02733       int new_msgs = 0;
02734       int dev_msgs = 0;
02735 
02736       if (s) {
02737          if (event) {
02738             l->newmsgs = ast_event_get_ie_uint(event, AST_EVENT_IE_NEWMSGS);
02739          }
02740 
02741          if (l->newmsgs) {
02742             transmit_lamp_indication(d, STIMULUS_VOICEMAIL, l->instance, l->mwiblink?SKINNY_LAMP_BLINK:SKINNY_LAMP_ON);
02743          } else {
02744             transmit_lamp_indication(d, STIMULUS_VOICEMAIL, l->instance, SKINNY_LAMP_OFF);
02745          }
02746 
02747          /* find out wether the device lamp should be on or off */
02748          AST_LIST_TRAVERSE(&d->lines, l2, list) {
02749             if (l2->newmsgs) {
02750                dev_msgs++;
02751             }
02752          }
02753 
02754          if (dev_msgs) {
02755             transmit_lamp_indication(d, STIMULUS_VOICEMAIL, 0, d->mwiblink?SKINNY_LAMP_BLINK:SKINNY_LAMP_ON);
02756          } else {
02757             transmit_lamp_indication(d, STIMULUS_VOICEMAIL, 0, SKINNY_LAMP_OFF);
02758          }
02759          ast_verb(3, "Skinny mwi_event_cb found %d new messages\n", new_msgs);
02760       }
02761    }
02762 }
02763 
02764 /* I do not believe skinny can deal with video.
02765    Anyone know differently? */
02766 /* Yes, it can.  Currently 7985 and Cisco VT Advantage do video. */
02767 static enum ast_rtp_glue_result skinny_get_vrtp_peer(struct ast_channel *c, struct ast_rtp_instance **instance)
02768 {
02769    struct skinny_subchannel *sub = NULL;
02770 
02771    if (!(sub = c->tech_pvt) || !(sub->vrtp))
02772       return AST_RTP_GLUE_RESULT_FORBID;
02773 
02774    ao2_ref(sub->vrtp, +1);
02775    *instance = sub->vrtp;
02776 
02777    return AST_RTP_GLUE_RESULT_REMOTE;
02778 }
02779 
02780 static enum ast_rtp_glue_result skinny_get_rtp_peer(struct ast_channel *c, struct ast_rtp_instance **instance)
02781 {
02782    struct skinny_subchannel *sub = NULL;
02783    struct skinny_line *l;
02784    enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_REMOTE;
02785 
02786    if (skinnydebug)
02787       ast_verb(1, "skinny_get_rtp_peer() Channel = %s\n", c->name);
02788 
02789 
02790    if (!(sub = c->tech_pvt))
02791       return AST_RTP_GLUE_RESULT_FORBID;
02792 
02793    ast_mutex_lock(&sub->lock);
02794 
02795    if (!(sub->rtp)){
02796       ast_mutex_unlock(&sub->lock);
02797       return AST_RTP_GLUE_RESULT_FORBID;
02798    }
02799 
02800    ao2_ref(sub->rtp, +1);
02801    *instance = sub->rtp;
02802 
02803    l = sub->parent;
02804 
02805    if (!l->directmedia || l->nat){
02806       res = AST_RTP_GLUE_RESULT_LOCAL;
02807       if (skinnydebug)
02808          ast_verb(1, "skinny_get_rtp_peer() Using AST_RTP_GLUE_RESULT_LOCAL \n");
02809    }
02810 
02811    ast_mutex_unlock(&sub->lock);
02812 
02813    return res;
02814 
02815 }
02816 
02817 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)
02818 {
02819    struct skinny_subchannel *sub;
02820    struct skinny_line *l;
02821    struct skinny_device *d;
02822    struct ast_format_list fmt;
02823    struct sockaddr_in us = { 0, };
02824    struct sockaddr_in them = { 0, };
02825    struct ast_sockaddr them_tmp;
02826    struct ast_sockaddr us_tmp;
02827    
02828    sub = c->tech_pvt;
02829 
02830    if (c->_state != AST_STATE_UP)
02831       return 0;
02832 
02833    if (!sub) {
02834       return -1;
02835    }
02836 
02837    l = sub->parent;
02838    d = l->device;
02839 
02840    if (rtp){
02841       ast_rtp_instance_get_remote_address(rtp, &them_tmp);
02842       ast_sockaddr_to_sin(&them_tmp, &them);
02843 
02844       /* Shutdown any early-media or previous media on re-invite */
02845       transmit_stopmediatransmission(d, sub);
02846       
02847       if (skinnydebug)
02848          ast_verb(1, "Peerip = %s:%d\n", ast_inet_ntoa(them.sin_addr), ntohs(them.sin_port));
02849 
02850       fmt = ast_codec_pref_getsize(&l->prefs, ast_best_codec(l->capability));
02851 
02852       if (skinnydebug)
02853          ast_verb(1, "Setting payloadType to '%s' (%d ms)\n", ast_getformatname(fmt.bits), fmt.cur_ms);
02854 
02855       if (!(l->directmedia) || (l->nat)){
02856          ast_rtp_instance_get_local_address(rtp, &us_tmp);
02857          ast_sockaddr_to_sin(&us_tmp, &us);
02858          us.sin_addr.s_addr = us.sin_addr.s_addr ? us.sin_addr.s_addr : d->ourip.s_addr;
02859          transmit_startmediatransmission(d, sub, us, fmt);
02860       } else {
02861          transmit_startmediatransmission(d, sub, them, fmt);
02862       }
02863 
02864       return 0;
02865    }
02866    /* Need a return here to break the bridge */
02867    return 0;
02868 }
02869 
02870 static struct ast_rtp_glue skinny_rtp_glue = {
02871    .type = "Skinny",
02872    .get_rtp_info = skinny_get_rtp_peer,
02873    .get_vrtp_info = skinny_get_vrtp_peer,
02874    .update_peer = skinny_set_rtp_peer,
02875 };
02876 
02877 static char *handle_skinny_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02878 {
02879    switch (cmd) {
02880    case CLI_INIT:
02881 #ifdef SKINNY_DEVMODE
02882       e->command = "skinny set debug {off|on|packet}";
02883       e->usage =
02884          "Usage: skinny set debug {off|on|packet}\n"
02885          "       Enables/Disables dumping of Skinny packets for debugging purposes\n";
02886 #else
02887       e->command = "skinny set debug {off|on}";
02888       e->usage =
02889          "Usage: skinny set debug {off|on}\n"
02890          "       Enables/Disables dumping of Skinny packets for debugging purposes\n";
02891 #endif
02892       return NULL;
02893    case CLI_GENERATE:
02894       return NULL;
02895    }
02896    
02897    if (a->argc != e->args)
02898       return CLI_SHOWUSAGE;
02899 
02900    if (!strncasecmp(a->argv[e->args - 1], "on", 2)) {
02901       skinnydebug = 1;
02902       ast_cli(a->fd, "Skinny Debugging Enabled\n");
02903       return CLI_SUCCESS;
02904    } else if (!strncasecmp(a->argv[e->args - 1], "off", 3)) {
02905       skinnydebug = 0;
02906       ast_cli(a->fd, "Skinny Debugging Disabled\n");
02907       return CLI_SUCCESS;
02908 #ifdef SKINNY_DEVMODE
02909    } else if (!strncasecmp(a->argv[e->args - 1], "packet", 6)) {
02910       skinnydebug = 2;
02911       ast_cli(a->fd, "Skinny Debugging Enabled including Packets\n");
02912       return CLI_SUCCESS;
02913 #endif
02914    } else {
02915       return CLI_SHOWUSAGE;
02916    }
02917 }
02918 
02919 static char *handle_skinny_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02920 {
02921    switch (cmd) {
02922    case CLI_INIT:
02923       e->command = "skinny reload";
02924       e->usage =
02925          "Usage: skinny reload\n"
02926          "       Reloads the chan_skinny configuration\n";
02927       return NULL;
02928    case CLI_GENERATE:
02929       return NULL;
02930    }
02931    
02932    if (a->argc != e->args)
02933       return CLI_SHOWUSAGE;
02934 
02935    skinny_reload();
02936    return CLI_SUCCESS;
02937 
02938 }
02939 
02940 static char *complete_skinny_devices(const char *word, int state)
02941 {
02942    struct skinny_device *d;
02943    char *result = NULL;
02944    int wordlen = strlen(word), which = 0;
02945 
02946    AST_LIST_TRAVERSE(&devices, d, list) {
02947       if (!strncasecmp(word, d->id, wordlen) && ++which > state)
02948          result = ast_strdup(d->id);
02949    }
02950 
02951    return result;
02952 }
02953 
02954 static char *complete_skinny_show_device(const char *line, const char *word, int pos, int state)
02955 {
02956    return (pos == 3 ? ast_strdup(complete_skinny_devices(word, state)) : NULL);
02957 }
02958 
02959 static char *complete_skinny_reset(const char *line, const char *word, int pos, int state)
02960 {
02961    return (pos == 2 ? ast_strdup(complete_skinny_devices(word, state)) : NULL);
02962 }
02963 
02964 static char *complete_skinny_show_line(const char *line, const char *word, int pos, int state)
02965 {
02966    struct skinny_device *d;
02967    struct skinny_line *l;
02968    char *result = NULL;
02969    int wordlen = strlen(word), which = 0;
02970 
02971    if (pos != 3)
02972       return NULL;
02973    
02974    AST_LIST_TRAVERSE(&devices, d, list) {
02975       AST_LIST_TRAVERSE(&d->lines, l, list) {
02976          if (!strncasecmp(word, l->name, wordlen) && ++which > state)
02977             result = ast_strdup(l->name);
02978       }
02979    }
02980 
02981    return result;
02982 }
02983 
02984 static char *handle_skinny_reset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02985 {
02986    struct skinny_device *d;
02987    struct skinny_req *req;
02988 
02989    switch (cmd) {
02990    case CLI_INIT:
02991       e->command = "skinny reset";
02992       e->usage =
02993          "Usage: skinny reset <DeviceId|DeviceName|all> [restart]\n"
02994          "       Causes a Skinny device to reset itself, optionally with a full restart\n";
02995       return NULL;
02996    case CLI_GENERATE:
02997       return complete_skinny_reset(a->line, a->word, a->pos, a->n);
02998    }
02999 
03000    if (a->argc < 3 || a->argc > 4)
03001       return CLI_SHOWUSAGE;
03002 
03003    AST_LIST_LOCK(&devices);
03004    AST_LIST_TRAVERSE(&devices, d, list) {
03005       int fullrestart = 0;
03006       if (!strcasecmp(a->argv[2], d->id) || !strcasecmp(a->argv[2], d->name) || !strcasecmp(a->argv[2], "all")) {
03007          if (!(d->session))
03008             continue;
03009 
03010          if (!(req = req_alloc(sizeof(struct reset_message), RESET_MESSAGE)))
03011             continue;
03012 
03013          if (a->argc == 4 && !strcasecmp(a->argv[3], "restart"))
03014             fullrestart = 1;
03015 
03016          if (fullrestart)
03017             req->data.reset.resetType = 2;
03018          else
03019             req->data.reset.resetType = 1;
03020 
03021          ast_verb(3, "%s device %s.\n", (fullrestart) ? "Restarting" : "Resetting", d->id);
03022          transmit_response(d, req);
03023       }
03024    }
03025    AST_LIST_UNLOCK(&devices);
03026    return CLI_SUCCESS;
03027 }
03028 
03029 static char *device2str(int type)
03030 {
03031    char *tmp;
03032 
03033    switch (type) {
03034    case SKINNY_DEVICE_NONE:
03035       return "No Device";
03036    case SKINNY_DEVICE_30SPPLUS:
03037       return "30SP Plus";
03038    case SKINNY_DEVICE_12SPPLUS:
03039       return "12SP Plus";
03040    case SKINNY_DEVICE_12SP:
03041       return "12SP";
03042    case SKINNY_DEVICE_12:
03043       return "12";
03044    case SKINNY_DEVICE_30VIP:
03045       return "30VIP";
03046    case SKINNY_DEVICE_7910:
03047       return "7910";
03048    case SKINNY_DEVICE_7960:
03049       return "7960";
03050    case SKINNY_DEVICE_7940:
03051       return "7940";
03052    case SKINNY_DEVICE_7935:
03053       return "7935";
03054    case SKINNY_DEVICE_ATA186:
03055       return "ATA186";
03056    case SKINNY_DEVICE_7941:
03057       return "7941";
03058    case SKINNY_DEVICE_7971:
03059       return "7971";
03060    case SKINNY_DEVICE_7914:
03061       return "7914";
03062    case SKINNY_DEVICE_7985:
03063       return "7985";
03064    case SKINNY_DEVICE_7911:
03065       return "7911";
03066    case SKINNY_DEVICE_7961GE:
03067       return "7961GE";
03068    case SKINNY_DEVICE_7941GE:
03069       return "7941GE";
03070    case SKINNY_DEVICE_7931:
03071       return "7931";
03072    case SKINNY_DEVICE_7921:
03073       return "7921";
03074    case SKINNY_DEVICE_7906:
03075       return "7906";
03076    case SKINNY_DEVICE_7962:
03077       return "7962";
03078    case SKINNY_DEVICE_7937:
03079       return "7937";
03080    case SKINNY_DEVICE_7942:
03081       return "7942";
03082    case SKINNY_DEVICE_7945:
03083       return "7945";
03084    case SKINNY_DEVICE_7965:
03085       return "7965";
03086    case SKINNY_DEVICE_7975:
03087       return "7975";
03088    case SKINNY_DEVICE_7905:
03089       return "7905";
03090    case SKINNY_DEVICE_7920:
03091       return "7920";
03092    case SKINNY_DEVICE_7970:
03093       return "7970";
03094    case SKINNY_DEVICE_7912:
03095       return "7912";
03096    case SKINNY_DEVICE_7902:
03097       return "7902";
03098    case SKINNY_DEVICE_CIPC:
03099       return "IP Communicator";
03100    case SKINNY_DEVICE_7961:
03101       return "7961";
03102    case SKINNY_DEVICE_7936:
03103       return "7936";
03104    case SKINNY_DEVICE_SCCPGATEWAY_AN:
03105       return "SCCPGATEWAY_AN";
03106    case SKINNY_DEVICE_SCCPGATEWAY_BRI:
03107       return "SCCPGATEWAY_BRI";
03108    case SKINNY_DEVICE_UNKNOWN:
03109       return "Unknown";
03110    default:
03111       if (!(tmp = ast_threadstorage_get(&device2str_threadbuf, DEVICE2STR_BUFSIZE)))
03112          return "Unknown";
03113       snprintf(tmp, DEVICE2STR_BUFSIZE, "UNKNOWN-%d", type);
03114       return tmp;
03115    }
03116 }
03117 
03118 /*! \brief Print codec list from preference to CLI/manager */
03119 static void print_codec_to_cli(int fd, struct ast_codec_pref *pref)
03120 {
03121    int x, codec;
03122 
03123    for(x = 0; x < 32 ; x++) {
03124       codec = ast_codec_pref_index(pref, x);
03125       if (!codec)
03126          break;
03127       ast_cli(fd, "%s", ast_getformatname(codec));
03128       ast_cli(fd, ":%d", pref->framing[x]);
03129       if (x < 31 && ast_codec_pref_index(pref, x + 1))
03130          ast_cli(fd, ",");
03131    }
03132    if (!x)
03133       ast_cli(fd, "none");
03134 }
03135 
03136 static char *_skinny_show_devices(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[])
03137 {
03138    struct skinny_device *d;
03139    struct skinny_line *l;
03140    const char *id;
03141    char idtext[256] = "";
03142    int total_devices = 0;
03143 
03144    if (s) { /* Manager - get ActionID */
03145       id = astman_get_header(m, "ActionID");
03146       if (!ast_strlen_zero(id))
03147          snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
03148    }
03149 
03150    switch (argc) {
03151    case 3:
03152       break;
03153    default:
03154       return CLI_SHOWUSAGE;
03155    }
03156 
03157    if (!s) {
03158       ast_cli(fd, "Name                 DeviceId         IP              Type            R NL\n");
03159       ast_cli(fd, "-------------------- ---------------- --------------- --------------- - --\n");
03160    }
03161 
03162    AST_LIST_LOCK(&devices);
03163    AST_LIST_TRAVERSE(&devices, d, list) {
03164       int numlines = 0;
03165       total_devices++;
03166       AST_LIST_TRAVERSE(&d->lines, l, list) {
03167          numlines++;
03168       }
03169       if (!s) {
03170          ast_cli(fd, "%-20s %-16s %-15s %-15s %c %2d\n",
03171             d->name,
03172             d->id,
03173             d->session?ast_inet_ntoa(d->session->sin.sin_addr):"",
03174             device2str(d->type),
03175             d->registered?'Y':'N',
03176             numlines);
03177       } else {
03178          astman_append(s,
03179             "Event: DeviceEntry\r\n%s"
03180             "Channeltype: SKINNY\r\n"
03181             "ObjectName: %s\r\n"
03182             "ChannelObjectType: device\r\n"
03183             "DeviceId: %s\r\n"
03184             "IPaddress: %s\r\n"
03185             "Type: %s\r\n"
03186             "Devicestatus: %s\r\n"
03187             "NumberOfLines: %d\r\n",
03188             idtext,
03189             d->name,
03190             d->id,
03191             d->session?ast_inet_ntoa(d->session->sin.sin_addr):"-none-",
03192             device2str(d->type),
03193             d->registered?"registered":"unregistered",
03194             numlines);
03195       }
03196    }
03197    AST_LIST_UNLOCK(&devices);
03198 
03199    if (total)
03200       *total = total_devices;
03201    
03202    return CLI_SUCCESS;
03203 }
03204 
03205 /*! \brief  Show SKINNY devices in the manager API */
03206 /*    Inspired from chan_sip */
03207 static int manager_skinny_show_devices(struct mansession *s, const struct message *m)
03208 {
03209    const char *id = astman_get_header(m, "ActionID");
03210    const char *a[] = {"skinny", "show", "devices"};
03211    char idtext[256] = "";
03212    int total = 0;
03213 
03214    if (!ast_strlen_zero(id))
03215       snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
03216 
03217    astman_send_listack(s, m, "Device status list will follow", "start");
03218    /* List the devices in separate manager events */
03219    _skinny_show_devices(-1, &total, s, m, 3, a);
03220    /* Send final confirmation */
03221    astman_append(s,
03222    "Event: DevicelistComplete\r\n"
03223    "EventList: Complete\r\n"
03224    "ListItems: %d\r\n"
03225    "%s"
03226    "\r\n", total, idtext);
03227    return 0;
03228 }
03229 
03230 static char *handle_skinny_show_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03231 {
03232 
03233    switch (cmd) {
03234    case CLI_INIT:
03235       e->command = "skinny show devices";
03236       e->usage =
03237          "Usage: skinny show devices\n"
03238          "       Lists all devices known to the Skinny subsystem.\n";
03239       return NULL;
03240    case CLI_GENERATE:
03241       return NULL;
03242    }
03243 
03244    return _skinny_show_devices(a->fd, NULL, NULL, NULL, a->argc, (const char **) a->argv);
03245 }
03246 
03247 static char *_skinny_show_device(int type, int fd, struct mansession *s, const struct message *m, int argc, const char *argv[])
03248 {
03249    struct skinny_device *d;
03250    struct skinny_line *l;
03251    struct skinny_speeddial *sd;
03252    struct skinny_addon *sa;
03253    char codec_buf[512];
03254 
03255    if (argc < 4) {
03256       return CLI_SHOWUSAGE;
03257    }
03258 
03259    AST_LIST_LOCK(&devices);
03260    AST_LIST_TRAVERSE(&devices, d, list) {
03261       if (!strcasecmp(argv[3], d->id) || !strcasecmp(argv[3], d->name)) {
03262          int numlines = 0, numaddons = 0, numspeeddials = 0;
03263 
03264          AST_LIST_TRAVERSE(&d->lines, l, list){
03265             numlines++;
03266          }
03267 
03268          AST_LIST_TRAVERSE(&d->addons, sa, list) {
03269             numaddons++;
03270          }
03271 
03272          AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
03273             numspeeddials++;
03274          }
03275 
03276          if (type == 0) { /* CLI */
03277             ast_cli(fd, "Name:        %s\n", d->name);
03278             ast_cli(fd, "Id:          %s\n", d->id);
03279             ast_cli(fd, "version:     %s\n", S_OR(d->version_id, "Unknown"));
03280             ast_cli(fd, "Ip address:  %s\n", (d->session ? ast_inet_ntoa(d->session->sin.sin_addr) : "Unknown"));
03281             ast_cli(fd, "Port:        %d\n", (d->session ? ntohs(d->session->sin.sin_port) : 0));
03282             ast_cli(fd, "Device Type: %s\n", device2str(d->type));
03283             ast_cli(fd, "Conf Codecs:");
03284             ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, d->confcapability);
03285             ast_cli(fd, "%s\n", codec_buf);
03286             ast_cli(fd, "Neg Codecs: ");
03287             ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, d->capability);
03288             ast_cli(fd, "%s\n", codec_buf);
03289             ast_cli(fd, "Registered:  %s\n", (d->registered ? "Yes" : "No"));
03290             ast_cli(fd, "Lines:       %d\n", numlines);
03291             AST_LIST_TRAVERSE(&d->lines, l, list) {
03292                ast_cli(fd, "  %s (%s)\n", l->name, l->label);
03293             }
03294             AST_LIST_TRAVERSE(&d->addons, sa, list) {
03295                numaddons++;
03296             }  
03297             ast_cli(fd, "Addons:      %d\n", numaddons);
03298             AST_LIST_TRAVERSE(&d->addons, sa, list) {
03299                ast_cli(fd, "  %s\n", sa->type);
03300             }
03301             AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
03302                numspeeddials++;
03303             }
03304             ast_cli(fd, "Speeddials:  %d\n", numspeeddials);
03305             AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
03306                ast_cli(fd, "  %s (%s) ishint: %d\n", sd->exten, sd->label, sd->isHint);
03307             }
03308          } else { /* manager */
03309             astman_append(s, "Channeltype: SKINNY\r\n");
03310             astman_append(s, "ObjectName: %s\r\n", d->name);
03311             astman_append(s, "ChannelObjectType: device\r\n");
03312             astman_append(s, "Id: %s\r\n", d->id);
03313             astman_append(s, "version: %s\r\n", S_OR(d->version_id, "Unknown"));
03314             astman_append(s, "Ipaddress: %s\r\n", (d->session ? ast_inet_ntoa(d->session->sin.sin_addr) : "Unknown"));
03315             astman_append(s, "Port: %d\r\n", (d->session ? ntohs(d->session->sin.sin_port) : 0));
03316             astman_append(s, "DeviceType: %s\r\n", device2str(d->type));
03317             astman_append(s, "Codecs: ");
03318             ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, d->confcapability);
03319             astman_append(s, "%s\r\n", codec_buf);
03320             astman_append(s, "CodecOrder: ");
03321             ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, d->capability);
03322             astman_append(s, "%s\r\n", codec_buf);
03323             astman_append(s, "Devicestatus: %s\r\n", (d->registered?"registered":"unregistered"));
03324             astman_append(s, "NumberOfLines: %d\r\n", numlines);
03325             AST_LIST_TRAVERSE(&d->lines, l, list) {
03326                astman_append(s, "Line: %s (%s)\r\n", l->name, l->label);
03327             }
03328             astman_append(s, "NumberOfAddons: %d\r\n", numaddons);
03329             AST_LIST_TRAVERSE(&d->addons, sa, list) {
03330                astman_append(s, "Addon: %s\r\n", sa->type);
03331             }
03332             astman_append(s, "NumberOfSpeeddials: %d\r\n", numspeeddials);
03333             AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
03334                astman_append(s, "Speeddial: %s (%s) ishint: %d\r\n", sd->exten, sd->label, sd->isHint);
03335             }
03336          }
03337       }
03338    }
03339    AST_LIST_UNLOCK(&devices);
03340    return CLI_SUCCESS;
03341 }
03342 
03343 static int manager_skinny_show_device(struct mansession *s, const struct message *m)
03344 {
03345    const char *a[4];
03346    const char *device;
03347 
03348    device = astman_get_header(m, "Device");
03349    if (ast_strlen_zero(device)) {
03350       astman_send_error(s, m, "Device: <name> missing.");
03351       return 0;
03352    }
03353    a[0] = "skinny";
03354    a[1] = "show";
03355    a[2] = "device";
03356    a[3] = device;
03357 
03358    _skinny_show_device(1, -1, s, m, 4, a);
03359    astman_append(s, "\r\n\r\n" );
03360    return 0;
03361 }
03362 
03363 /*! \brief Show device information */
03364 static char *handle_skinny_show_device(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03365 {
03366    switch (cmd) {
03367    case CLI_INIT:
03368       e->command = "skinny show device";
03369       e->usage =
03370          "Usage: skinny show device <DeviceId|DeviceName>\n"
03371          "       Lists all deviceinformation of a specific device known to the Skinny subsystem.\n";
03372       return NULL;
03373    case CLI_GENERATE:
03374       return complete_skinny_show_device(a->line, a->word, a->pos, a->n);
03375    }
03376 
03377    return _skinny_show_device(0, a->fd, NULL, NULL, a->argc, (const char **) a->argv);
03378 }
03379 
03380 static char *_skinny_show_lines(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[])
03381 {
03382    struct skinny_line *l;
03383    struct skinny_subchannel *sub;
03384    int total_lines = 0;
03385    int verbose = 0;
03386    const char *id;
03387    char idtext[256] = "";
03388 
03389    if (s) { /* Manager - get ActionID */
03390       id = astman_get_header(m, "ActionID");
03391       if (!ast_strlen_zero(id))
03392          snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
03393    }
03394 
03395    switch (argc) {
03396    case 4:
03397       verbose = 1;
03398       break;
03399    case 3:
03400       verbose = 0;
03401       break;
03402    default:
03403       return CLI_SHOWUSAGE;
03404    }
03405 
03406    if (!s) {
03407       ast_cli(fd, "Name                 Device Name          Instance Label               \n");
03408       ast_cli(fd, "-------------------- -------------------- -------- --------------------\n");
03409    }
03410    AST_LIST_LOCK(&lines);
03411    AST_LIST_TRAVERSE(&lines, l, all) {
03412       total_lines++;
03413       if (!s) {
03414          ast_cli(fd, "%-20s %-20s %8d %-20s\n",
03415             l->name,
03416             (l->device ? l->device->name : "Not connected"),
03417             l->instance,
03418             l->label);
03419          if (verbose) {
03420             AST_LIST_TRAVERSE(&l->sub, sub, list) {
03421                ast_cli(fd, "  %s> %s to %s\n",
03422                   (sub == l->activesub?"Active  ":"Inactive"),
03423                   sub->owner->name,
03424                   (ast_bridged_channel(sub->owner)?ast_bridged_channel(sub->owner)->name:"")
03425                );
03426             }
03427          }
03428       } else {
03429          astman_append(s,
03430             "Event: LineEntry\r\n%s"
03431             "Channeltype: SKINNY\r\n"
03432             "ObjectName: %s\r\n"
03433             "ChannelObjectType: line\r\n"
03434             "Device: %s\r\n"
03435             "Instance: %d\r\n"
03436             "Label: %s\r\n",
03437             idtext,
03438             l->name,
03439             (l->device?l->device->name:"None"),
03440             l->instance,
03441             l->label);
03442       }
03443    }
03444    AST_LIST_UNLOCK(&lines);
03445 
03446    if (total) {
03447       *total = total_lines;
03448    }
03449 
03450    return CLI_SUCCESS;
03451 }
03452 
03453 /*! \brief  Show Skinny lines in the manager API */
03454 /*    Inspired from chan_sip */
03455 static int manager_skinny_show_lines(struct mansession *s, const struct message *m)
03456 {
03457    const char *id = astman_get_header(m, "ActionID");
03458    const char *a[] = {"skinny", "show", "lines"};
03459    char idtext[256] = "";
03460    int total = 0;
03461 
03462    if (!ast_strlen_zero(id))
03463       snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
03464 
03465    astman_send_listack(s, m, "Line status list will follow", "start");
03466    /* List the lines in separate manager events */
03467    _skinny_show_lines(-1, &total, s, m, 3, a);
03468    /* Send final confirmation */
03469    astman_append(s,
03470    "Event: LinelistComplete\r\n"
03471    "EventList: Complete\r\n"
03472    "ListItems: %d\r\n"
03473    "%s"
03474    "\r\n", total, idtext);
03475    return 0;
03476 }
03477 
03478 static char *handle_skinny_show_lines(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03479 {
03480    switch (cmd) {
03481    case CLI_INIT:
03482       e->command = "skinny show lines [verbose]";
03483       e->usage =
03484          "Usage: skinny show lines\n"
03485          "       Lists all lines known to the Skinny subsystem.\n"
03486          "       If 'verbose' is specified, the output includes\n"
03487          "       information about subs for each line.\n";
03488       return NULL;
03489    case CLI_GENERATE:
03490       return NULL;
03491    }
03492 
03493    if (a->argc == e->args) {
03494       if (strcasecmp(a->argv[e->args-1], "verbose")) {
03495          return CLI_SHOWUSAGE;
03496       }
03497    } else if (a->argc != e->args - 1) {
03498       return CLI_SHOWUSAGE;
03499    }
03500 
03501    return _skinny_show_lines(a->fd, NULL, NULL, NULL, a->argc, (const char **) a->argv);
03502 }
03503 
03504 static char *_skinny_show_line(int type, int fd, struct mansession *s, const struct message *m, int argc, const char *argv[])
03505 {
03506    struct skinny_device *d;
03507    struct skinny_line *l;
03508    struct ast_codec_pref *pref;
03509    int x = 0, codec = 0;
03510    char codec_buf[512];
03511    char group_buf[256];
03512    char cbuf[256];
03513 
03514    switch (argc) {
03515    case 4:
03516       break;
03517    case 6:
03518       break;
03519    default:
03520       return CLI_SHOWUSAGE;
03521    }
03522 
03523    AST_LIST_LOCK(&devices);
03524 
03525    /* Show all lines matching the one supplied */
03526    AST_LIST_TRAVERSE(&devices, d, list) {
03527       if (argc == 6 && (strcasecmp(argv[5], d->id) && strcasecmp(argv[5], d->name))) {
03528          continue;
03529       }
03530       AST_LIST_TRAVERSE(&d->lines, l, list) {
03531          if (strcasecmp(argv[3], l->name)) {
03532             continue;
03533          }
03534          if (type == 0) { /* CLI */
03535             ast_cli(fd, "Line:             %s\n", l->name);
03536             ast_cli(fd, "On Device:        %s\n", d->name);
03537             ast_cli(fd, "Line Label:       %s\n", l->label);
03538             ast_cli(fd, "Extension:        %s\n", S_OR(l->exten, "<not set>"));
03539             ast_cli(fd, "Context:          %s\n", l->context);
03540             ast_cli(fd, "CallGroup:        %s\n", ast_print_group(group_buf, sizeof(group_buf), l->callgroup));
03541             ast_cli(fd, "PickupGroup:      %s\n", ast_print_group(group_buf, sizeof(group_buf), l->pickupgroup));
03542             ast_cli(fd, "Language:         %s\n", S_OR(l->language, "<not set>"));
03543             ast_cli(fd, "Accountcode:      %s\n", S_OR(l->accountcode, "<not set>"));
03544             ast_cli(fd, "AmaFlag:          %s\n", ast_cdr_flags2str(l->amaflags));
03545             ast_cli(fd, "CallerId Number:  %s\n", S_OR(l->cid_num, "<not set>"));
03546             ast_cli(fd, "CallerId Name:    %s\n", S_OR(l->cid_name, "<not set>"));
03547             ast_cli(fd, "Hide CallerId:    %s\n", (l->hidecallerid ? "Yes" : "No"));
03548             ast_cli(fd, "CFwdAll:          %s\n", S_COR((l->cfwdtype & SKINNY_CFWD_ALL), l->call_forward_all, "<not set>"));
03549             ast_cli(fd, "CFwdBusy:         %s\n", S_COR((l->cfwdtype & SKINNY_CFWD_BUSY), l->call_forward_busy, "<not set>"));
03550             ast_cli(fd, "CFwdNoAnswer:     %s\n", S_COR((l->cfwdtype & SKINNY_CFWD_NOANSWER), l->call_forward_noanswer, "<not set>"));
03551             ast_cli(fd, "VoicemailBox:     %s\n", S_OR(l->mailbox, "<not set>"));
03552             ast_cli(fd, "VoicemailNumber:  %s\n", S_OR(l->vmexten, "<not set>"));
03553             ast_cli(fd, "MWIblink:         %d\n", l->mwiblink);
03554             ast_cli(fd, "Regextension:     %s\n", S_OR(l->regexten, "<not set>"));
03555             ast_cli(fd, "Regcontext:       %s\n", S_OR(l->regcontext, "<not set>"));
03556             ast_cli(fd, "MoHInterpret:     %s\n", S_OR(l->mohinterpret, "<not set>"));
03557             ast_cli(fd, "MoHSuggest:       %s\n", S_OR(l->mohsuggest, "<not set>"));
03558             ast_cli(fd, "Last dialed nr:   %s\n", S_OR(l->lastnumberdialed, "<no calls made yet>"));
03559             ast_cli(fd, "Last CallerID:    %s\n", S_OR(l->lastcallerid, "<not set>"));
03560             ast_cli(fd, "Transfer enabled: %s\n", (l->transfer ? "Yes" : "No"));
03561             ast_cli(fd, "Callwaiting:      %s\n", (l->callwaiting ? "Yes" : "No"));
03562             ast_cli(fd, "3Way Calling:     %s\n", (l->threewaycalling ? "Yes" : "No"));
03563             ast_cli(fd, "Can forward:      %s\n", (l->cancallforward ? "Yes" : "No"));
03564             ast_cli(fd, "Do Not Disturb:   %s\n", (l->dnd ? "Yes" : "No"));
03565             ast_cli(fd, "NAT:              %s\n", (l->nat ? "Yes" : "No"));
03566             ast_cli(fd, "immediate:        %s\n", (l->immediate ? "Yes" : "No"));
03567             ast_cli(fd, "Group:            %d\n", l->group);
03568             ast_cli(fd, "Parkinglot:       %s\n", S_OR(l->parkinglot, "<not set>"));
03569             ast_cli(fd, "Conf Codecs:      ");
03570             ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, l->confcapability);
03571             ast_cli(fd, "%s\n", codec_buf);
03572             ast_cli(fd, "Neg Codecs:       ");
03573             ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, l->capability);
03574             ast_cli(fd, "%s\n", codec_buf);
03575             ast_cli(fd, "Codec Order:      (");
03576             print_codec_to_cli(fd, &l->prefs);
03577             ast_cli(fd, ")\n");
03578             ast_cli(fd, "\n");
03579          } else { /* manager */
03580             astman_append(s, "Channeltype: SKINNY\r\n");
03581             astman_append(s, "ObjectName: %s\r\n", l->name);
03582             astman_append(s, "ChannelObjectType: line\r\n");
03583             astman_append(s, "Device: %s\r\n", d->name);
03584             astman_append(s, "LineLabel: %s\r\n", l->label);
03585             astman_append(s, "Extension: %s\r\n", S_OR(l->exten, "<not set>"));
03586             astman_append(s, "Context: %s\r\n", l->context);
03587             astman_append(s, "CallGroup: %s\r\n", ast_print_group(group_buf, sizeof(group_buf), l->callgroup));
03588             astman_append(s, "PickupGroup: %s\r\n", ast_print_group(group_buf, sizeof(group_buf), l->pickupgroup));
03589             astman_append(s, "Language: %s\r\n", S_OR(l->language, "<not set>"));
03590             astman_append(s, "Accountcode: %s\r\n", S_OR(l->accountcode, "<not set>"));
03591             astman_append(s, "AMAflags: %s\r\n", ast_cdr_flags2str(l->amaflags));
03592             astman_append(s, "Callerid: %s\r\n", ast_callerid_merge(cbuf, sizeof(cbuf), l->cid_name, l->cid_num, ""));
03593             astman_append(s, "HideCallerId: %s\r\n", (l->hidecallerid ? "Yes" : "No"));
03594             astman_append(s, "CFwdAll: %s\r\n", S_COR((l->cfwdtype & SKINNY_CFWD_ALL), l->call_forward_all, "<not set>"));
03595             astman_append(s, "CFwdBusy: %s\r\n", S_COR((l->cfwdtype & SKINNY_CFWD_BUSY), l->call_forward_busy, "<not set>"));
03596             astman_append(s, "CFwdNoAnswer: %s\r\n", S_COR((l->cfwdtype & SKINNY_CFWD_NOANSWER), l->call_forward_noanswer, "<not set>"));
03597             astman_append(s, "VoicemailBox: %s\r\n", S_OR(l->mailbox, "<not set>"));
03598             astman_append(s, "VoicemailNumber: %s\r\n", S_OR(l->vmexten, "<not set>"));
03599             astman_append(s, "MWIblink: %d\r\n", l->mwiblink);
03600             astman_append(s, "RegExtension: %s\r\n", S_OR(l->regexten, "<not set>"));
03601             astman_append(s, "Regcontext: %s\r\n", S_OR(l->regcontext, "<not set>"));
03602             astman_append(s, "MoHInterpret: %s\r\n", S_OR(l->mohinterpret, "<not set>"));
03603             astman_append(s, "MoHSuggest: %s\r\n", S_OR(l->mohsuggest, "<not set>"));
03604             astman_append(s, "LastDialedNr: %s\r\n", S_OR(l->lastnumberdialed, "<no calls made yet>"));
03605             astman_append(s, "LastCallerID: %s\r\n", S_OR(l->lastcallerid, "<not set>"));
03606             astman_append(s, "Transfer: %s\r\n", (l->transfer ? "Yes" : "No"));
03607             astman_append(s, "Callwaiting: %s\r\n", (l->callwaiting ? "Yes" : "No"));
03608             astman_append(s, "3WayCalling: %s\r\n", (l->threewaycalling ? "Yes" : "No"));
03609             astman_append(s, "CanForward: %s\r\n", (l->cancallforward ? "Yes" : "No"));
03610             astman_append(s, "DoNotDisturb: %s\r\n", (l->dnd ? "Yes" : "No"));
03611             astman_append(s, "NAT: %s\r\n", (l->nat ? "Yes" : "No"));
03612             astman_append(s, "immediate: %s\r\n", (l->immediate ? "Yes" : "No"));
03613             astman_append(s, "Group: %d\r\n", l->group);
03614             astman_append(s, "Parkinglot: %s\r\n", S_OR(l->parkinglot, "<not set>"));
03615             ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, l->confcapability);
03616             astman_append(s, "Codecs: %s\r\n", codec_buf);
03617             astman_append(s, "CodecOrder: ");
03618             pref = &l->prefs;
03619             for(x = 0; x < 32 ; x++) {
03620                codec = ast_codec_pref_index(pref, x);
03621                if (!codec)
03622                   break;
03623                astman_append(s, "%s", ast_getformatname(codec));
03624                if (x < 31 && ast_codec_pref_index(pref, x+1))
03625                   astman_append(s, ",");
03626             }
03627             astman_append(s, "\r\n");
03628          }
03629       }
03630    }
03631    
03632    AST_LIST_UNLOCK(&devices);
03633    return CLI_SUCCESS;
03634 }
03635 
03636 static int manager_skinny_show_line(struct mansession *s, const struct message *m)
03637 {
03638    const char *a[4];
03639    const char *line;
03640 
03641    line = astman_get_header(m, "Line");
03642    if (ast_strlen_zero(line)) {
03643       astman_send_error(s, m, "Line: <name> missing.");
03644       return 0;
03645    }
03646    a[0] = "skinny";
03647    a[1] = "show";
03648    a[2] = "line";
03649    a[3] = line;
03650 
03651    _skinny_show_line(1, -1, s, m, 4, a);
03652    astman_append(s, "\r\n\r\n" );
03653    return 0;
03654 }
03655 
03656 /*! \brief List line information. */
03657 static char *handle_skinny_show_line(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03658 {
03659    switch (cmd) {
03660    case CLI_INIT:
03661       e->command = "skinny show line";
03662       e->usage =
03663          "Usage: skinny show line <Line> [ on <DeviceID|DeviceName> ]\n"
03664          "       List all lineinformation of a specific line known to the Skinny subsystem.\n";
03665       return NULL;
03666    case CLI_GENERATE:
03667       return complete_skinny_show_line(a->line, a->word, a->pos, a->n);
03668    }
03669 
03670    return _skinny_show_line(0, a->fd, NULL, NULL, a->argc, (const char **) a->argv);
03671 }
03672 
03673 /*! \brief List global settings for the Skinny subsystem. */
03674 static char *handle_skinny_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03675 {
03676    switch (cmd) {
03677    case CLI_INIT:
03678       e->command = "skinny show settings";
03679       e->usage =
03680          "Usage: skinny show settings\n"
03681          "       Lists all global configuration settings of the Skinny subsystem.\n";
03682       return NULL;
03683    case CLI_GENERATE:
03684       return NULL;
03685    }  
03686 
03687    if (a->argc != 3)
03688       return CLI_SHOWUSAGE;
03689 
03690    ast_cli(a->fd, "\nGlobal Settings:\n");
03691    ast_cli(a->fd, "  Skinny Port:            %d\n", ntohs(bindaddr.sin_port));
03692    ast_cli(a->fd, "  Bindaddress:            %s\n", ast_inet_ntoa(bindaddr.sin_addr));
03693    ast_cli(a->fd, "  KeepAlive:              %d\n", keep_alive);
03694    ast_cli(a->fd, "  Date Format:            %s\n", date_format);
03695    ast_cli(a->fd, "  Voice Mail Extension:   %s\n", S_OR(global_vmexten, "(not set)"));
03696    ast_cli(a->fd, "  Reg. context:           %s\n", S_OR(regcontext, "(not set)"));
03697    ast_cli(a->fd, "  Jitterbuffer enabled:   %s\n", AST_CLI_YESNO(ast_test_flag(&global_jbconf, AST_JB_ENABLED)));
03698     if (ast_test_flag(&global_jbconf, AST_JB_ENABLED)) {
03699       ast_cli(a->fd, "  Jitterbuffer forced:    %s\n", AST_CLI_YESNO(ast_test_flag(&global_jbconf, AST_JB_FORCED)));
03700       ast_cli(a->fd, "  Jitterbuffer max size:  %ld\n", global_jbconf.max_size);
03701       ast_cli(a->fd, "  Jitterbuffer resync:    %ld\n", global_jbconf.resync_threshold);
03702       ast_cli(a->fd, "  Jitterbuffer impl:      %s\n", global_jbconf.impl);
03703       if (!strcasecmp(global_jbconf.impl, "adaptive")) {
03704          ast_cli(a->fd, "  Jitterbuffer tgt extra: %ld\n", global_jbconf.target_extra);
03705       }
03706       ast_cli(a->fd, "  Jitterbuffer log:       %s\n", AST_CLI_YESNO(ast_test_flag(&global_jbconf, AST_JB_LOG)));
03707    }
03708 
03709    return CLI_SUCCESS;
03710 }
03711 
03712 static struct ast_cli_entry cli_skinny[] = {
03713    AST_CLI_DEFINE(handle_skinny_show_devices, "List defined Skinny devices"),
03714    AST_CLI_DEFINE(handle_skinny_show_device, "List Skinny device information"),
03715    AST_CLI_DEFINE(handle_skinny_show_lines, "List defined Skinny lines per device"),
03716    AST_CLI_DEFINE(handle_skinny_show_line, "List Skinny line information"),
03717    AST_CLI_DEFINE(handle_skinny_show_settings, "List global Skinny settings"),
03718    AST_CLI_DEFINE(handle_skinny_set_debug, "Enable/Disable Skinny debugging"),
03719    AST_CLI_DEFINE(handle_skinny_reset, "Reset Skinny device(s)"),
03720    AST_CLI_DEFINE(handle_skinny_reload, "Reload Skinny config"),
03721 };
03722 
03723 static void start_rtp(struct skinny_subchannel *sub)
03724 {
03725    struct skinny_line *l = sub->parent;
03726    struct skinny_device *d = l->device;
03727    int hasvideo = 0;
03728    struct ast_sockaddr bindaddr_tmp;
03729 
03730    ast_mutex_lock(&sub->lock);
03731    /* Allocate the RTP */
03732    ast_sockaddr_from_sin(&bindaddr_tmp, &bindaddr);
03733    sub->rtp = ast_rtp_instance_new("asterisk", sched, &bindaddr_tmp, NULL);
03734    if (hasvideo)
03735       sub->vrtp = ast_rtp_instance_new("asterisk", sched, &bindaddr_tmp, NULL);
03736 
03737    if (sub->rtp) {
03738       ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_RTCP, 1);
03739    }
03740    if (sub->vrtp) {
03741       ast_rtp_instance_set_prop(sub->vrtp, AST_RTP_PROPERTY_RTCP, 1);
03742    }
03743 
03744    if (sub->rtp && sub->owner) {
03745       ast_channel_set_fd(sub->owner, 0, ast_rtp_instance_fd(sub->rtp, 0));
03746       ast_channel_set_fd(sub->owner, 1, ast_rtp_instance_fd(sub->rtp, 1));
03747    }
03748    if (hasvideo && sub->vrtp && sub->owner) {
03749       ast_channel_set_fd(sub->owner, 2, ast_rtp_instance_fd(sub->vrtp, 0));
03750       ast_channel_set_fd(sub->owner, 3, ast_rtp_instance_fd(sub->vrtp, 1));
03751    }
03752    if (sub->rtp) {
03753       ast_rtp_instance_set_qos(sub->rtp, qos.tos_audio, qos.cos_audio, "Skinny RTP");
03754       ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_NAT, l->nat);
03755    }
03756    if (sub->vrtp) {
03757       ast_rtp_instance_set_qos(sub->vrtp, qos.tos_video, qos.cos_video, "Skinny VRTP");
03758       ast_rtp_instance_set_prop(sub->vrtp, AST_RTP_PROPERTY_NAT, l->nat);
03759    }
03760    /* Set Frame packetization */
03761    if (sub->rtp)
03762       ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(sub->rtp), sub->rtp, &l->prefs);
03763 
03764    /* Create the RTP connection */
03765    transmit_connect(d, sub);
03766    ast_mutex_unlock(&sub->lock);
03767 }
03768 
03769 static void *skinny_newcall(void *data)
03770 {
03771    struct ast_channel *c = data;
03772    struct skinny_subchannel *sub = c->tech_pvt;
03773    struct skinny_line *l = sub->parent;
03774    struct skinny_device *d = l->device;
03775    int res = 0;
03776 
03777    ast_copy_string(l->lastnumberdialed, c->exten, sizeof(l->lastnumberdialed));
03778    ast_set_callerid(c,
03779       l->hidecallerid ? "" : l->cid_num,
03780       l->hidecallerid ? "" : l->cid_name,
03781       c->caller.ani.number.valid ? NULL : l->cid_num);
03782 #if 1 /* XXX This code is probably not necessary */
03783    ast_party_number_free(&c->connected.id.number);
03784    ast_party_number_init(&c->connected.id.number);
03785    c->connected.id.number.valid = 1;
03786    c->connected.id.number.str = ast_strdup(c->exten);
03787    ast_party_name_free(&c->connected.id.name);
03788    ast_party_name_init(&c->connected.id.name);
03789 #endif
03790    ast_setstate(c, AST_STATE_RING);
03791    if (!sub->rtp) {
03792       start_rtp(sub);
03793    }
03794    res = ast_pbx_run(c);
03795    if (res) {
03796       ast_log(LOG_WARNING, "PBX exited non-zero\n");
03797       transmit_start_tone(d, SKINNY_REORDER, l->instance, sub->callid);
03798    }
03799    return NULL;
03800 }
03801 
03802 static void *skinny_ss(void *data)
03803 {
03804    struct ast_channel *c = data;
03805    struct skinny_subchannel *sub = c->tech_pvt;
03806    struct skinny_line *l = sub->parent;
03807    struct skinny_device *d = l->device;
03808    int len = 0;
03809    int timeout = firstdigittimeout;
03810    int res = 0;
03811    int loop_pause = 100;
03812 
03813    ast_verb(3, "Starting simple switch on '%s@%s'\n", l->name, d->name);
03814 
03815    len = strlen(d->exten);
03816 
03817    while (len < AST_MAX_EXTENSION-1) {
03818       res = 1;  /* Assume that we will get a digit */
03819       while (strlen(d->exten) == len){
03820          ast_safe_sleep(c, loop_pause);
03821          timeout -= loop_pause;
03822          if ( (timeout -= loop_pause) <= 0){
03823              res = 0;
03824              break;
03825          }
03826       res = 1;
03827       }
03828 
03829       timeout = 0;
03830       len = strlen(d->exten);
03831 
03832       if (!ast_ignore_pattern(c->context, d->exten)) {
03833          transmit_stop_tone(d, l->instance, sub->callid);
03834       }
03835       if (ast_exists_extension(c, c->context, d->exten, 1, l->cid_num)) {
03836          if (!res || !ast_matchmore_extension(c, c->context, d->exten, 1, l->cid_num)) {
03837             if (l->getforward) {
03838                /* Record this as the forwarding extension */
03839                set_callforwards(l, d->exten, l->getforward);
03840                ast_verb(3, "Setting call forward (%d) to '%s' on channel %s\n",
03841                      l->cfwdtype, d->exten, c->name);
03842                transmit_start_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
03843                transmit_lamp_indication(d, STIMULUS_FORWARDALL, 1, SKINNY_LAMP_ON);
03844                transmit_displaynotify(d, "CFwd enabled", 10);
03845                transmit_cfwdstate(d, l);
03846                ast_safe_sleep(c, 500);
03847                ast_indicate(c, -1);
03848                ast_safe_sleep(c, 1000);
03849                memset(d->exten, 0, sizeof(d->exten));
03850                len = 0;
03851                l->getforward = 0;
03852                if (sub->owner && sub->owner->_state != AST_STATE_UP) {
03853                   ast_indicate(c, -1);
03854                   ast_hangup(c);
03855                }
03856                return NULL;
03857             } else {
03858                ast_copy_string(c->exten, d->exten, sizeof(c->exten));
03859                ast_copy_string(l->lastnumberdialed, d->exten, sizeof(l->lastnumberdialed));
03860                memset(d->exten, 0, sizeof(d->exten));
03861                skinny_newcall(c);
03862                return NULL;
03863             }
03864          } else {
03865             /* It's a match, but they just typed a digit, and there is an ambiguous match,
03866                so just set the timeout to matchdigittimeout and wait some more */
03867             timeout = matchdigittimeout;
03868          }
03869       } else if (res == 0) {
03870          ast_debug(1, "Not enough digits (%s) (and no ambiguous match)...\n", d->exten);
03871          memset(d->exten, 0, sizeof(d->exten));
03872          if (l->hookstate == SKINNY_OFFHOOK) {
03873             transmit_start_tone(d, SKINNY_REORDER, l->instance, sub->callid);
03874          }
03875          if (sub->owner && sub->owner->_state != AST_STATE_UP) {
03876             ast_indicate(c, -1);
03877             ast_hangup(c);
03878          }
03879          return NULL;
03880       } else if (!ast_canmatch_extension(c, c->context, d->exten, 1,
03881          S_COR(c->caller.id.number.valid, c->caller.id.number.str, NULL))
03882          && ((d->exten[0] != '*') || (!ast_strlen_zero(d->exten) > 2))) {
03883          ast_log(LOG_WARNING, "Can't match [%s] from '%s' in context %s\n", d->exten,
03884             S_COR(c->caller.id.number.valid, c->caller.id.number.str, "<Unknown Caller>"),
03885             c->context);
03886          memset(d->exten, 0, sizeof(d->exten));
03887          if (l->hookstate == SKINNY_OFFHOOK) {
03888             transmit_start_tone(d, SKINNY_REORDER, l->instance, sub->callid);
03889             /* hang out for 3 seconds to let congestion play */
03890             ast_safe_sleep(c, 3000);
03891          }
03892          break;
03893       }
03894       if (!timeout) {
03895          timeout = gendigittimeout;
03896       }
03897       if (len && !ast_ignore_pattern(c->context, d->exten)) {
03898          ast_indicate(c, -1);
03899       }
03900    }
03901    if (c)
03902       ast_hangup(c);
03903    memset(d->exten, 0, sizeof(d->exten));
03904    return NULL;
03905 }
03906 
03907 
03908 
03909 static int skinny_call(struct ast_channel *ast, char *dest, int timeout)
03910 {
03911    int res = 0;
03912    struct skinny_subchannel *sub = ast->tech_pvt;
03913    struct skinny_line *l = sub->parent;
03914    struct skinny_device *d = l->device;
03915 
03916    if (!d->registered) {
03917       ast_log(LOG_ERROR, "Device not registered, cannot call %s\n", dest);
03918       return -1;
03919    }
03920 
03921    if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
03922       ast_log(LOG_WARNING, "skinny_call called on %s, neither down nor reserved\n", ast->name);
03923       return -1;
03924    }
03925 
03926    if (skinnydebug)
03927       ast_verb(3, "skinny_call(%s)\n", ast->name);
03928 
03929    if (l->dnd) {
03930       ast_queue_control(ast, AST_CONTROL_BUSY);
03931       return -1;
03932    }
03933 
03934    if (AST_LIST_NEXT(sub,list) && !l->callwaiting) {
03935       ast_queue_control(ast, AST_CONTROL_BUSY);
03936       return -1;
03937    }
03938    
03939    switch (l->hookstate) {
03940    case SKINNY_OFFHOOK:
03941       break;
03942    case SKINNY_ONHOOK:
03943       l->activesub = sub;
03944       break;
03945    default:
03946       ast_log(LOG_ERROR, "Don't know how to deal with hookstate %d\n", l->hookstate);
03947       break;
03948    }
03949 
03950    transmit_callstate(d, sub->parent->instance, sub->callid, SKINNY_RINGIN);
03951    transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGIN);
03952    transmit_displaypromptstatus(d, "Ring-In", 0, l->instance, sub->callid);
03953    transmit_callinfo(d,
03954       S_COR(ast->connected.id.name.valid, ast->connected.id.name.str, ""),
03955       S_COR(ast->connected.id.number.valid, ast->connected.id.number.str, ""),
03956       l->cid_name, l->cid_num, l->instance, sub->callid, 1);
03957    transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK);
03958    transmit_ringer_mode(d, SKINNY_RING_INSIDE);
03959 
03960    ast_setstate(ast, AST_STATE_RINGING);
03961    ast_queue_control(ast, AST_CONTROL_RINGING);
03962    sub->outgoing = 1;
03963    return res;
03964 }
03965 
03966 static int skinny_hangup(struct ast_channel *ast)
03967 {
03968    struct skinny_subchannel *sub = ast->tech_pvt;
03969    struct skinny_line *l;
03970    struct skinny_device *d;
03971 
03972    if (!sub) {
03973       ast_debug(1, "Asked to hangup channel not connected\n");
03974       return 0;
03975    }
03976 
03977    l = sub->parent;
03978    d = l->device;
03979 
03980    if (skinnydebug)
03981       ast_verb(3,"Hanging up %s/%d\n",d->name,sub->callid);
03982 
03983    AST_LIST_REMOVE(&l->sub, sub, list);
03984 
03985    if (d->registered) {
03986       /* Ignoring l->type, doesn't seem relevant and previous code 
03987          assigned rather than tested, ie always true */
03988       if (!AST_LIST_EMPTY(&l->sub)) {
03989          if (sub->related) {
03990             sub->related->related = NULL;
03991 
03992          }
03993          if (sub == l->activesub) {      /* we are killing the active sub, but there are other subs on the line*/
03994             ast_verb(4,"Killing active sub %d\n", sub->callid);
03995             if (sub->related) {
03996                l->activesub = sub->related;
03997             } else {
03998                if (AST_LIST_NEXT(sub, list)) {
03999                   l->activesub = AST_LIST_NEXT(sub, list);
04000                } else {
04001                   l->activesub = AST_LIST_FIRST(&l->sub);
04002                }
04003             }
04004             //transmit_callstate(d, l->instance, SKINNY_ONHOOK, sub->callid);
04005             transmit_activatecallplane(d, l);
04006             transmit_closereceivechannel(d, sub);
04007             transmit_stopmediatransmission(d, sub);
04008             transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK);
04009             transmit_stop_tone(d, l->instance, sub->callid);
04010          } else {    /* we are killing a background sub on the line with other subs*/
04011             ast_verb(4,"Killing inactive sub %d\n", sub->callid);
04012             if (AST_LIST_NEXT(sub, list)) {
04013                transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK);
04014             } else {
04015                transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
04016             }
04017          }
04018       } else {                                                /* no more subs on line so make idle */
04019          ast_verb(4,"Killing only sub %d\n", sub->callid);
04020          l->hookstate = SKINNY_ONHOOK;
04021          transmit_closereceivechannel(d, sub);
04022          transmit_stopmediatransmission(d, sub);
04023          transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
04024          transmit_clearpromptmessage(d, l->instance, sub->callid);
04025          transmit_callstate(d, l->instance, sub->callid, SKINNY_ONHOOK);
04026          transmit_selectsoftkeys(d, 0, 0, KEYDEF_ONHOOK);
04027          transmit_activatecallplane(d, l);
04028          l->activesub = NULL;
04029          transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF);
04030          if (sub->parent == d->activeline) {
04031             transmit_activatecallplane(d, l);
04032             transmit_closereceivechannel(d, sub);
04033             transmit_stopmediatransmission(d, sub);
04034             transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
04035             transmit_ringer_mode(d, SKINNY_RING_OFF);
04036             transmit_clear_display_message(d, l->instance, sub->callid);
04037             transmit_stop_tone(d, l->instance, sub->callid);
04038             /* we should check to see if we can start the ringer if another line is ringing */
04039          }
04040       }
04041    }
04042    ast_mutex_lock(&sub->lock);
04043    sub->owner = NULL;
04044    ast->tech_pvt = NULL;
04045    sub->alreadygone = 0;
04046    sub->outgoing = 0;
04047    if (sub->rtp) {
04048       ast_rtp_instance_destroy(sub->rtp);
04049       sub->rtp = NULL;
04050    }
04051    ast_mutex_unlock(&sub->lock);
04052    ast_free(sub);
04053    ast_module_unref(ast_module_info->self);
04054    return 0;
04055 }
04056 
04057 static int skinny_answer(struct ast_channel *ast)
04058 {
04059    int res = 0;
04060    struct skinny_subchannel *sub = ast->tech_pvt;
04061    struct skinny_line *l = sub->parent;
04062    struct skinny_device *d = l->device;
04063 
04064    if (sub->blindxfer) {
04065       if (skinnydebug)
04066          ast_debug(1, "skinny_answer(%s) on %s@%s-%d with BlindXFER, transferring\n",
04067             ast->name, l->name, d->name, sub->callid);
04068       ast_setstate(ast, AST_STATE_UP);
04069       skinny_transfer(sub);
04070       return 0;
04071    }
04072 
04073    sub->cxmode = SKINNY_CX_SENDRECV;
04074    if (!sub->rtp) {
04075       start_rtp(sub);
04076    }
04077    if (skinnydebug)
04078       ast_verb(1, "skinny_answer(%s) on %s@%s-%d\n", ast->name, l->name, d->name, sub->callid);
04079    if (ast->_state != AST_STATE_UP) {
04080       ast_setstate(ast, AST_STATE_UP);
04081    }
04082 
04083    transmit_stop_tone(d, l->instance, sub->callid);
04084    /* order matters here...
04085       for some reason, transmit_callinfo must be before transmit_callstate,
04086       or you won't get keypad messages in some situations. */
04087    transmit_callinfo(d,
04088       S_COR(ast->connected.id.name.valid, ast->connected.id.name.str, ""),
04089       S_COR(ast->connected.id.number.valid, ast->connected.id.number.str, ""),
04090       l->lastnumberdialed, l->lastnumberdialed, l->instance, sub->callid, 2);
04091    transmit_callstate(d, sub->parent->instance, sub->callid, SKINNY_CONNECTED);
04092    transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
04093    transmit_dialednumber(d, l->lastnumberdialed, l->instance, sub->callid);
04094    transmit_displaypromptstatus(d, "Connected", 0, l->instance, sub->callid);
04095    l->activesub = sub;
04096    return res;
04097 }
04098 
04099 /* Retrieve audio/etc from channel.  Assumes sub->lock is already held. */
04100 static struct ast_frame *skinny_rtp_read(struct skinny_subchannel *sub)
04101 {
04102    struct ast_channel *ast = sub->owner;
04103    struct ast_frame *f;
04104 
04105    if (!sub->rtp) {
04106       /* We have no RTP allocated for this channel */
04107       return &ast_null_frame;
04108    }
04109 
04110    switch(ast->fdno) {
04111    case 0:
04112       f = ast_rtp_instance_read(sub->rtp, 0); /* RTP Audio */
04113       break;
04114    case 1:
04115       f = ast_rtp_instance_read(sub->rtp, 1); /* RTCP Control Channel */
04116       break;
04117    case 2:
04118       f = ast_rtp_instance_read(sub->vrtp, 0); /* RTP Video */
04119       break;
04120    case 3:
04121       f = ast_rtp_instance_read(sub->vrtp, 1); /* RTCP Control Channel for video */
04122       break;
04123 #if 0
04124    case 5:
04125       /* Not yet supported */
04126       f = ast_udptl_read(sub->udptl); /* UDPTL for T.38 */
04127       break;
04128 #endif
04129    default:
04130       f = &ast_null_frame;
04131    }
04132 
04133    if (ast) {
04134       /* We already hold the channel lock */
04135       if (f->frametype == AST_FRAME_VOICE) {
04136          if (f->subclass.codec != ast->nativeformats) {
04137             ast_debug(1, "Oooh, format changed to %s\n", ast_getformatname(f->subclass.codec));
04138             ast->nativeformats = f->subclass.codec;
04139             ast_set_read_format(ast, ast->readformat);
04140             ast_set_write_format(ast, ast->writeformat);
04141          }
04142       }
04143    }
04144    return f;
04145 }
04146 
04147 static struct ast_frame *skinny_read(struct ast_channel *ast)
04148 {
04149    struct ast_frame *fr;
04150    struct skinny_subchannel *sub = ast->tech_pvt;
04151    ast_mutex_lock(&sub->lock);
04152    fr = skinny_rtp_read(sub);
04153    ast_mutex_unlock(&sub->lock);
04154    return fr;
04155 }
04156 
04157 static int skinny_write(struct ast_channel *ast, struct ast_frame *frame)
04158 {
04159    struct skinny_subchannel *sub = ast->tech_pvt;
04160    int res = 0;
04161    if (frame->frametype != AST_FRAME_VOICE) {
04162       if (frame->frametype == AST_FRAME_IMAGE) {
04163          return 0;
04164       } else {
04165          ast_log(LOG_WARNING, "Can't send %d type frames with skinny_write\n", frame->frametype);
04166          return 0;
04167       }
04168    } else {
04169       if (!(frame->subclass.codec & ast->nativeformats)) {
04170          char buf[256];
04171          ast_log(LOG_WARNING, "Asked to transmit frame type %s, while native formats is %s (read/write = %s/%s)\n",
04172             ast_getformatname(frame->subclass.codec),
04173             ast_getformatname_multiple(buf, sizeof(buf), ast->nativeformats),
04174             ast_getformatname(ast->readformat),
04175             ast_getformatname(ast->writeformat));
04176          return -1;
04177       }
04178    }
04179    if (sub) {
04180       ast_mutex_lock(&sub->lock);
04181       if (sub->rtp) {
04182          res = ast_rtp_instance_write(sub->rtp, frame);
04183       }
04184       ast_mutex_unlock(&sub->lock);
04185    }
04186    return res;
04187 }
04188 
04189 static int skinny_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
04190 {
04191    struct skinny_subchannel *sub = newchan->tech_pvt;
04192    ast_log(LOG_NOTICE, "skinny_fixup(%s, %s)\n", oldchan->name, newchan->name);
04193    if (sub->owner != oldchan) {
04194       ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, sub->owner);
04195       return -1;
04196    }
04197    sub->owner = newchan;
04198    return 0;
04199 }
04200 
04201 static int skinny_senddigit_begin(struct ast_channel *ast, char digit)
04202 {
04203    return -1; /* Start inband indications */
04204 }
04205 
04206 static int skinny_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration)
04207 {
04208 #if 0
04209    struct skinny_subchannel *sub = ast->tech_pvt;
04210    struct skinny_line *l = sub->parent;
04211    struct skinny_device *d = l->device;
04212    int tmp;
04213    /* not right */
04214    sprintf(tmp, "%d", digit);
04215    //transmit_tone(d, digit, l->instance, sub->callid);
04216 #endif
04217    return -1; /* Stop inband indications */
04218 }
04219 
04220 static int get_devicestate(struct skinny_line *l)
04221 {
04222    struct skinny_subchannel *sub;
04223    int res = AST_DEVICE_UNKNOWN;
04224 
04225    if (!l)
04226       res = AST_DEVICE_INVALID;
04227    else if (!l->device)
04228       res = AST_DEVICE_UNAVAILABLE;
04229    else if (l->dnd)
04230       res = AST_DEVICE_BUSY;
04231    else {
04232       if (l->hookstate == SKINNY_ONHOOK) {
04233          res = AST_DEVICE_NOT_INUSE;
04234       } else {
04235          res = AST_DEVICE_INUSE;
04236       }
04237 
04238       AST_LIST_TRAVERSE(&l->sub, sub, list) {
04239          if (sub->onhold) {
04240             res = AST_DEVICE_ONHOLD;
04241             break;
04242          }
04243       }
04244    }
04245 
04246    return res;
04247 }
04248 
04249 static char *control2str(int ind) {
04250    char *tmp;
04251 
04252    switch (ind) {
04253    case AST_CONTROL_HANGUP:
04254       return "Other end has hungup";
04255    case AST_CONTROL_RING:
04256       return "Local ring";
04257    case AST_CONTROL_RINGING:
04258       return "Remote end is ringing";
04259    case AST_CONTROL_ANSWER:
04260       return "Remote end has answered";
04261    case AST_CONTROL_BUSY:
04262       return "Remote end is busy";
04263    case AST_CONTROL_TAKEOFFHOOK:
04264       return "Make it go off hook";
04265    case AST_CONTROL_OFFHOOK:
04266       return "Line is off hook";
04267    case AST_CONTROL_CONGESTION:
04268       return "Congestion (circuits busy)";
04269    case AST_CONTROL_FLASH:
04270       return "Flash hook";
04271    case AST_CONTROL_WINK:
04272       return "Wink";
04273    case AST_CONTROL_OPTION:
04274       return "Set a low-level option";
04275    case AST_CONTROL_RADIO_KEY:
04276       return "Key Radio";
04277    case AST_CONTROL_RADIO_UNKEY:
04278       return "Un-Key Radio";
04279    case AST_CONTROL_PROGRESS:
04280       return "Remote end is making Progress";
04281    case AST_CONTROL_PROCEEDING:
04282       return "Remote end is proceeding";
04283    case AST_CONTROL_HOLD:
04284       return "Hold";
04285    case AST_CONTROL_UNHOLD:
04286       return "Unhold";
04287    case AST_CONTROL_SRCUPDATE:
04288       return "Media Source Update";
04289    case AST_CONTROL_CONNECTED_LINE:
04290       return "Connected Line";
04291    case AST_CONTROL_REDIRECTING:
04292       return "Redirecting";
04293    case -1:
04294       return "Stop tone";
04295    default:
04296       if (!(tmp = ast_threadstorage_get(&control2str_threadbuf, CONTROL2STR_BUFSIZE)))
04297                         return "Unknown";
04298       snprintf(tmp, CONTROL2STR_BUFSIZE, "UNKNOWN-%d", ind);
04299       return tmp;
04300    }
04301 }
04302 
04303 static int skinny_transfer(struct skinny_subchannel *sub)
04304 {
04305    struct skinny_subchannel *xferor; /* the sub doing the transferring */
04306    struct skinny_subchannel *xferee; /* the sub being transferred */
04307    struct ast_tone_zone_sound *ts = NULL;
04308       
04309    if (ast_bridged_channel(sub->owner) || ast_bridged_channel(sub->related->owner)) {
04310       if (sub->xferor) {
04311          xferor = sub;
04312          xferee = sub->related;
04313       } else {
04314          xferor = sub;
04315          xferee = sub->related;
04316       }
04317       
04318       if (skinnydebug) {
04319          ast_debug(1, "Transferee channels (local/remote): %s and %s\n",
04320             xferee->owner->name, ast_bridged_channel(xferee->owner)?ast_bridged_channel(xferee->owner)->name:"");
04321          ast_debug(1, "Transferor channels (local/remote): %s and %s\n",
04322             xferor->owner->name, ast_bridged_channel(xferor->owner)?ast_bridged_channel(xferor->owner)->name:"");
04323       }
04324       if (ast_bridged_channel(xferor->owner)) {
04325          if (ast_bridged_channel(xferee->owner)) {
04326             ast_queue_control(xferee->owner, AST_CONTROL_UNHOLD);
04327          }
04328          if (xferor->owner->_state == AST_STATE_RING) {
04329             /* play ringing inband */
04330             if ((ts = ast_get_indication_tone(xferor->owner->zone, "ring"))) {
04331                ast_playtones_start(xferor->owner, 0, ts->data, 1);
04332                ts = ast_tone_zone_sound_unref(ts);
04333             }
04334          }
04335          if (skinnydebug)
04336             ast_debug(1, "Transfer Masquerading %s to %s\n",
04337                xferee->owner->name, ast_bridged_channel(xferor->owner)?ast_bridged_channel(xferor->owner)->name:"");
04338          if (ast_channel_masquerade(xferee->owner, ast_bridged_channel(xferor->owner))) {
04339             ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
04340                ast_bridged_channel(xferor->owner)->name, xferee->owner->name);
04341             return -1;
04342          }
04343       } else if (ast_bridged_channel(xferee->owner)) {
04344          ast_queue_control(xferee->owner, AST_CONTROL_UNHOLD);
04345          if (xferor->owner->_state == AST_STATE_RING) {
04346             /* play ringing inband */
04347             if ((ts = ast_get_indication_tone(xferor->owner->zone, "ring"))) {
04348                ast_playtones_start(xferor->owner, 0, ts->data, 1);
04349                ts = ast_tone_zone_sound_unref(ts);
04350             }
04351          }
04352          if (skinnydebug)
04353             ast_debug(1, "Transfer Masquerading %s to %s\n",
04354                xferor->owner->name, ast_bridged_channel(xferee->owner)?ast_bridged_channel(xferee->owner)->name:"");
04355          if (ast_channel_masquerade(xferor->owner, ast_bridged_channel(xferee->owner))) {
04356             ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
04357                ast_bridged_channel(xferee->owner)->name, xferor->owner->name);
04358             return -1;
04359          }
04360          return 0;
04361       } else {
04362          if (option_debug)
04363             ast_log(LOG_DEBUG, "Neither %s nor %s are in a bridge, nothing to transfer\n",
04364                xferor->owner->name, xferee->owner->name);
04365       }
04366    }
04367    return 0;
04368 }
04369 
04370 static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, size_t datalen)
04371 {
04372    struct skinny_subchannel *sub = ast->tech_pvt;
04373    struct skinny_line *l = sub->parent;
04374    struct skinny_device *d = l->device;
04375    struct skinnysession *s = d->session;
04376 
04377    if (!s) {
04378       ast_log(LOG_NOTICE, "Asked to indicate '%s' condition on channel %s, but session does not exist.\n", control2str(ind), ast->name);
04379       return -1;
04380    }
04381 
04382    if (skinnydebug)
04383       ast_verb(3, "Asked to indicate '%s' condition on channel %s\n", control2str(ind), ast->name);
04384    switch(ind) {
04385    case AST_CONTROL_RINGING:
04386       if (sub->blindxfer) {
04387          if (skinnydebug)
04388             ast_debug(1, "Channel %s set up for Blind Xfer, so Xfer rather than ring device\n", ast->name);
04389          skinny_transfer(sub);
04390          break;
04391       }
04392       if (ast->_state != AST_STATE_UP) {
04393          if (!sub->progress) {
04394             if (!d->earlyrtp) {
04395                transmit_start_tone(d, SKINNY_ALERT, l->instance, sub->callid);
04396             }
04397             transmit_callstate(d, sub->parent->instance, sub->callid, SKINNY_RINGOUT);
04398             transmit_dialednumber(d, l->lastnumberdialed, l->instance, sub->callid);
04399             transmit_displaypromptstatus(d, "Ring Out", 0, l->instance, sub->callid);
04400             transmit_callinfo(d,
04401                S_COR(ast->caller.id.name.valid, ast->caller.id.name.str, ""),
04402                S_COR(ast->caller.id.number.valid, ast->caller.id.number.str, ""),
04403                S_COR(ast->connected.id.name.valid, ast->connected.id.name.str, l->lastnumberdialed),
04404                S_COR(ast->connected.id.number.valid, ast->connected.id.number.str, l->lastnumberdialed),
04405                l->instance, sub->callid, 2); /* 2 = outgoing from phone */
04406             sub->ringing = 1;
04407             if (!d->earlyrtp) {
04408                break;
04409             }
04410          }
04411       }
04412       return -1; /* Tell asterisk to provide inband signalling */
04413    case AST_CONTROL_BUSY:
04414       if (ast->_state != AST_STATE_UP) {
04415          if (!d->earlyrtp) {
04416             transmit_start_tone(d, SKINNY_BUSYTONE, l->instance, sub->callid);
04417          }
04418          transmit_callstate(d, sub->parent->instance, sub->callid, SKINNY_BUSY);
04419          sub->alreadygone = 1;
04420          ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
04421          if (!d->earlyrtp) {
04422             break;
04423          }
04424       }
04425       return -1; /* Tell asterisk to provide inband signalling */
04426    case AST_CONTROL_CONGESTION:
04427       if (ast->_state != AST_STATE_UP) {
04428          if (!d->earlyrtp) {
04429             transmit_start_tone(d, SKINNY_REORDER, l->instance, sub->callid);
04430          }
04431          transmit_callstate(d, sub->parent->instance, sub->callid, SKINNY_CONGESTION);
04432          sub->alreadygone = 1;
04433          ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
04434          if (!d->earlyrtp) {
04435             break;
04436          }
04437       }
04438       return -1; /* Tell asterisk to provide inband signalling */
04439    case AST_CONTROL_PROGRESS:
04440       if ((ast->_state != AST_STATE_UP) && !sub->progress && !sub->outgoing) {
04441          if (!d->earlyrtp) {
04442             transmit_start_tone(d, SKINNY_ALERT, l->instance, sub->callid);
04443          }
04444          transmit_callstate(d, sub->parent->instance, sub->callid, SKINNY_PROGRESS);
04445          transmit_displaypromptstatus(d, "Call Progress", 0, l->instance, sub->callid);
04446          transmit_callinfo(d,
04447             S_COR(ast->caller.id.name.valid, ast->caller.id.name.str, ""),
04448             S_COR(ast->caller.id.number.valid, ast->caller.id.number.str, ""),
04449             S_COR(ast->connected.id.name.valid, ast->connected.id.name.str, l->lastnumberdialed),
04450             S_COR(ast->connected.id.number.valid, ast->connected.id.number.str, l->lastnumberdialed),
04451             l->instance, sub->callid, 2); /* 2 = outgoing from phone */
04452          sub->progress = 1;
04453          if (!d->earlyrtp) {
04454             break;
04455          }
04456       }
04457       return -1; /* Tell asterisk to provide inband signalling */
04458    case -1:  /* STOP_TONE */
04459       transmit_stop_tone(d, l->instance, sub->callid);
04460       break;
04461    case AST_CONTROL_HOLD:
04462       ast_moh_start(ast, data, l->mohinterpret);
04463       break;
04464    case AST_CONTROL_UNHOLD:
04465       ast_moh_stop(ast);
04466       break;
04467    case AST_CONTROL_PROCEEDING:
04468       break;
04469    case AST_CONTROL_SRCUPDATE:
04470       ast_rtp_instance_update_source(sub->rtp);
04471       break;
04472    case AST_CONTROL_SRCCHANGE:
04473       ast_rtp_instance_change_source(sub->rtp);
04474       break;
04475    case AST_CONTROL_CONNECTED_LINE:
04476       update_connectedline(sub, data, datalen);
04477       break;
04478    default:
04479       ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", ind);
04480       return -1; /* Tell asterisk to provide inband signalling */
04481    }
04482    return 0;
04483 }
04484 
04485 static struct ast_channel *skinny_new(struct skinny_line *l, int state, const char *linkedid)
04486 {
04487    struct ast_channel *tmp;
04488    struct skinny_subchannel *sub;
04489    struct skinny_device *d = l->device;
04490    struct ast_variable *v = NULL;
04491    int fmt;
04492 
04493    if (!l->device) {
04494       ast_log(LOG_WARNING, "Device for line %s is not registered.\n", l->name);
04495       return NULL;
04496    }
04497 
04498    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);
04499    if (!tmp) {
04500       ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
04501       return NULL;
04502    } else {
04503       sub = ast_calloc(1, sizeof(*sub));
04504       if (!sub) {
04505          ast_log(LOG_WARNING, "Unable to allocate Skinny subchannel\n");
04506          return NULL;
04507       } else {
04508          ast_mutex_init(&sub->lock);
04509 
04510          sub->owner = tmp;
04511          sub->callid = callnums++;
04512          d->lastlineinstance = l->instance;
04513          d->lastcallreference = sub->callid;
04514          sub->cxmode = SKINNY_CX_INACTIVE;
04515          sub->nat = l->nat;
04516          sub->parent = l;
04517          sub->onhold = 0;
04518          sub->blindxfer = 0;
04519          sub->xferor = 0;
04520          sub->related = NULL;
04521 
04522          AST_LIST_INSERT_HEAD(&l->sub, sub, list);
04523          //l->activesub = sub;
04524       }
04525       tmp->tech = &skinny_tech;
04526       tmp->tech_pvt = sub;
04527       tmp->nativeformats = l->capability;
04528       if (!tmp->nativeformats)
04529          // Should throw an error
04530          tmp->nativeformats = default_capability;
04531       fmt = ast_best_codec(tmp->nativeformats);
04532       if (skinnydebug) {
04533          char buf[256];
04534          ast_verb(1, "skinny_new: tmp->nativeformats=%s fmt=%s\n",
04535             ast_getformatname_multiple(buf, sizeof(buf), tmp->nativeformats),
04536             ast_getformatname(fmt));
04537       }
04538       if (sub->rtp) {
04539          ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(sub->rtp, 0));
04540       }
04541       if (state == AST_STATE_RING) {
04542          tmp->rings = 1;
04543       }
04544       tmp->writeformat = fmt;
04545       tmp->rawwriteformat = fmt;
04546       tmp->readformat = fmt;
04547       tmp->rawreadformat = fmt;
04548       if (!ast_strlen_zero(l->language))
04549          ast_string_field_set(tmp, language, l->language);
04550       if (!ast_strlen_zero(l->accountcode))
04551          ast_string_field_set(tmp, accountcode, l->accountcode);
04552       if (!ast_strlen_zero(l->parkinglot))
04553          ast_string_field_set(tmp, parkinglot, l->parkinglot);
04554       if (l->amaflags)
04555          tmp->amaflags = l->amaflags;
04556 
04557       ast_module_ref(ast_module_info->self);
04558       tmp->callgroup = l->callgroup;
04559       tmp->pickupgroup = l->pickupgroup;
04560 
04561       /* XXX Need to figure out how to handle CFwdNoAnswer */
04562       if (l->cfwdtype & SKINNY_CFWD_ALL) {
04563          ast_string_field_set(tmp, call_forward, l->call_forward_all);
04564       } else if (l->cfwdtype & SKINNY_CFWD_BUSY) {
04565          if (get_devicestate(l) != AST_DEVICE_NOT_INUSE) {
04566             ast_string_field_set(tmp, call_forward, l->call_forward_busy);
04567          }
04568       }
04569 
04570       ast_copy_string(tmp->context, l->context, sizeof(tmp->context));
04571       ast_copy_string(tmp->exten, l->exten, sizeof(tmp->exten));
04572 
04573       /* Don't use ast_set_callerid() here because it will
04574        * generate a needless NewCallerID event */
04575       if (!ast_strlen_zero(l->cid_num)) {
04576          tmp->caller.ani.number.valid = 1;
04577          tmp->caller.ani.number.str = ast_strdup(l->cid_num);
04578       }
04579 
04580       tmp->priority = 1;
04581       tmp->adsicpe = AST_ADSI_UNAVAILABLE;
04582 
04583       if (sub->rtp)
04584          ast_jb_configure(tmp, &global_jbconf);
04585 
04586       /* Set channel variables for this call from configuration */
04587       for (v = l->chanvars ; v ; v = v->next)
04588          pbx_builtin_setvar_helper(tmp, v->name, v->value);
04589 
04590       if (state != AST_STATE_DOWN) {
04591          if (ast_pbx_start(tmp)) {
04592             ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
04593             ast_hangup(tmp);
04594             tmp = NULL;
04595          }
04596       }
04597    }
04598    return tmp;
04599 }
04600 
04601 static int skinny_hold(struct skinny_subchannel *sub)
04602 {
04603    struct skinny_line *l = sub->parent;
04604    struct skinny_device *d = l->device;
04605 
04606    /* Don't try to hold a channel that doesn't exist */
04607    if (!sub || !sub->owner)
04608       return 0;
04609 
04610    /* Channel needs to be put on hold */
04611    if (skinnydebug)
04612       ast_verb(1, "Putting on Hold(%d)\n", l->instance);
04613 
04614    ast_queue_control_data(sub->owner, AST_CONTROL_HOLD,
04615       S_OR(l->mohsuggest, NULL),
04616       !ast_strlen_zero(l->mohsuggest) ? strlen(l->mohsuggest) + 1 : 0);
04617 
04618    transmit_activatecallplane(d, l);
04619    transmit_closereceivechannel(d, sub);
04620    transmit_stopmediatransmission(d, sub);
04621 
04622    transmit_callstate(d, sub->parent->instance, sub->callid, SKINNY_HOLD);
04623    transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_WINK);
04624    sub->onhold = 1;
04625    return 1;
04626 }
04627 
04628 static int skinny_unhold(struct skinny_subchannel *sub)
04629 {
04630    struct skinny_line *l = sub->parent;
04631    struct skinny_device *d = l->device;
04632 
04633    /* Don't try to unhold a channel that doesn't exist */
04634    if (!sub || !sub->owner)
04635       return 0;
04636 
04637    /* Channel is on hold, so we will unhold */
04638    if (skinnydebug)
04639       ast_verb(1, "Taking off Hold(%d)\n", l->instance);
04640 
04641    ast_queue_control(sub->owner, AST_CONTROL_UNHOLD);
04642 
04643    transmit_activatecallplane(d, l);
04644 
04645    transmit_connect(d, sub);
04646    transmit_callstate(d, sub->parent->instance, sub->callid, SKINNY_CONNECTED);
04647    transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
04648    l->hookstate = SKINNY_OFFHOOK;
04649    sub->onhold = 0;
04650    return 1;
04651 }
04652 
04653 static int handle_hold_button(struct skinny_subchannel *sub)
04654 {
04655    if (!sub)
04656       return -1;
04657    if (sub->related) {
04658       skinny_hold(sub);
04659       skinny_unhold(sub->related);
04660       sub->parent->activesub = sub->related;
04661    } else {
04662       if (sub->onhold) {
04663          skinny_unhold(sub);
04664          transmit_selectsoftkeys(sub->parent->device, sub->parent->instance, sub->callid, KEYDEF_CONNECTED);
04665       } else {
04666          skinny_hold(sub);
04667          transmit_selectsoftkeys(sub->parent->device, sub->parent->instance, sub->callid, KEYDEF_ONHOLD);
04668       }
04669    }
04670    return 1;
04671 }
04672 
04673 static int handle_transfer_button(struct skinny_subchannel *sub)
04674 {
04675    struct skinny_line *l;
04676    struct skinny_device *d;
04677    struct skinny_subchannel *newsub;
04678    struct ast_channel *c;
04679    pthread_t t;
04680 
04681    if (!sub) {
04682       ast_verbose("Transfer: No subchannel to transfer\n");
04683       return -1;
04684    }
04685 
04686    l = sub->parent;
04687    d = l->device;
04688 
04689    if (!sub->related) {
04690       /* Another sub has not been created so this must be first XFER press */
04691       if (!sub->onhold) {
04692          skinny_hold(sub);
04693       }
04694       c = skinny_new(l, AST_STATE_DOWN, NULL);
04695       if (c) {
04696          newsub = c->tech_pvt;
04697          /* point the sub and newsub at each other so we know they are related */
04698          newsub->related = sub;
04699          sub->related = newsub;
04700          newsub->xferor = 1;
04701          l->activesub = newsub;
04702          transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
04703          transmit_activatecallplane(d, l);
04704          transmit_clear_display_message(d, l->instance, newsub->callid);
04705          transmit_start_tone(d, SKINNY_DIALTONE, l->instance, newsub->callid);
04706          transmit_selectsoftkeys(d, l->instance, newsub->callid, KEYDEF_OFFHOOKWITHFEAT);
04707          /* start the switch thread */
04708          if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
04709             ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
04710             ast_hangup(c);
04711          }
04712       } else {
04713          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
04714       }
04715    } else {
04716       /* We already have a related sub so we can either complete XFER or go into BLINDXFER (or cancel BLINDXFER */
04717       if (sub->blindxfer) {
04718          /* toggle blindxfer off */
04719          sub->blindxfer = 0;
04720          sub->related->blindxfer = 0;
04721          /* we really need some indications */
04722       } else {
04723          /* We were doing attended transfer */
04724          if (sub->owner->_state == AST_STATE_DOWN || sub->related->owner->_state == AST_STATE_DOWN) {
04725             /* one of the subs so we cant transfer yet, toggle blindxfer on */
04726             sub->blindxfer = 1;
04727             sub->related->blindxfer = 1;
04728          } else {
04729             /* big assumption we have two channels, lets transfer */
04730             skinny_transfer(sub);
04731          }
04732       }
04733    }
04734    return 0;
04735 }
04736 
04737 static int handle_keep_alive_message(struct skinny_req *req, struct skinnysession *s)
04738 {
04739    if (!(req = req_alloc(0, KEEP_ALIVE_ACK_MESSAGE)))
04740       return -1;
04741 
04742    transmit_response(s->device, req);
04743    return 1;
04744 }
04745 
04746 static int handle_register_message(struct skinny_req *req, struct skinnysession *s)
04747 {
04748    struct skinny_device *d = NULL;
04749    char name[16];
04750    int res;
04751 
04752    memcpy(&name, req->data.reg.name, sizeof(name));
04753 
04754    res = skinny_register(req, s);
04755    if (!res) {
04756       ast_log(LOG_ERROR, "Rejecting Device %s: Device not found\n", name);
04757       if (!(req = req_alloc(sizeof(struct register_rej_message), REGISTER_REJ_MESSAGE)))
04758          return -1;
04759 
04760       snprintf(req->data.regrej.errMsg, sizeof(req->data.regrej.errMsg), "No Authority: %s", name);
04761 
04762       /* transmit_respons in line as we don't have a valid d */
04763       ast_mutex_lock(&s->lock);
04764 
04765       if (letohl(req->len) > SKINNY_MAX_PACKET || letohl(req->len) < 0) {
04766          ast_log(LOG_WARNING, "transmit_response: the length (%d) of the request is out of bounds (%d) \n",  letohl(req->len), SKINNY_MAX_PACKET);
04767          ast_mutex_unlock(&s->lock);
04768          return -1;
04769       }
04770 
04771       memset(s->outbuf, 0, sizeof(s->outbuf));
04772       memcpy(s->outbuf, req, skinny_header_size);
04773       memcpy(s->outbuf+skinny_header_size, &req->data, letohl(req->len));
04774 
04775       res = write(s->fd, s->outbuf, letohl(req->len)+8);
04776 
04777       if (res != letohl(req->len)+8) {
04778          ast_log(LOG_WARNING, "Transmit: write only sent %d out of %d bytes: %s\n", res, letohl(req->len)+8, strerror(errno));
04779       }
04780    
04781       ast_mutex_unlock(&s->lock);
04782 
04783       return 0;
04784    }
04785    ast_atomic_fetchadd_int(&unauth_sessions, -1);
04786 
04787    ast_verb(3, "Device '%s' successfully registered\n", name);
04788 
04789    d = s->device;
04790    
04791    if (!(req = req_alloc(sizeof(struct register_ack_message), REGISTER_ACK_MESSAGE)))
04792       return -1;
04793 
04794    req->data.regack.res[0] = '0';
04795    req->data.regack.res[1] = '\0';
04796    req->data.regack.keepAlive = htolel(keep_alive);
04797    memcpy(req->data.regack.dateTemplate, date_format, sizeof(req->data.regack.dateTemplate));
04798    req->data.regack.res2[0] = '0';
04799    req->data.regack.res2[1] = '\0';
04800    req->data.regack.secondaryKeepAlive = htolel(keep_alive);
04801    transmit_response(d, req);
04802    if (skinnydebug)
04803       ast_verb(1, "Requesting capabilities\n");
04804 
04805    if (!(req = req_alloc(0, CAPABILITIES_REQ_MESSAGE)))
04806       return -1;
04807 
04808    transmit_response(d, req);
04809 
04810    return res;
04811 }
04812 
04813 static int handle_callforward_button(struct skinny_subchannel *sub, int cfwdtype)
04814 {
04815    struct skinny_line *l = sub->parent;
04816    struct skinny_device *d = l->device;
04817    struct ast_channel *c = sub->owner;
04818    pthread_t t;
04819 
04820    if (l->hookstate == SKINNY_ONHOOK) {
04821       l->hookstate = SKINNY_OFFHOOK;
04822       transmit_speaker_mode(d, SKINNY_SPEAKERON);
04823       transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
04824       transmit_activatecallplane(d, l);
04825    }
04826    transmit_clear_display_message(d, l->instance, sub->callid);
04827 
04828    if (l->cfwdtype & cfwdtype) {
04829       set_callforwards(l, NULL, cfwdtype);
04830       ast_safe_sleep(c, 500);
04831       transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
04832       transmit_closereceivechannel(d, sub);
04833       transmit_stopmediatransmission(d, sub);
04834       transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
04835       transmit_clearpromptmessage(d, l->instance, sub->callid);
04836       transmit_callstate(d, l->instance, sub->callid, SKINNY_ONHOOK);
04837       transmit_selectsoftkeys(d, 0, 0, KEYDEF_ONHOOK);
04838       transmit_activatecallplane(d, l);
04839       transmit_displaynotify(d, "CFwd disabled", 10);
04840       if (sub->owner && sub->owner->_state != AST_STATE_UP) {
04841          ast_indicate(c, -1);
04842          ast_hangup(c);
04843       }
04844       transmit_cfwdstate(d, l);
04845    } else {
04846       l->getforward = cfwdtype;
04847       transmit_start_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
04848       transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
04849       if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
04850          ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
04851          ast_hangup(c);
04852       }
04853    }
04854    return 0;
04855 }
04856 static int handle_ip_port_message(struct skinny_req *req, struct skinnysession *s)
04857 {
04858    /* no response necessary */
04859    return 1;
04860 }
04861 
04862 static int handle_keypad_button_message(struct skinny_req *req, struct skinnysession *s)
04863 {
04864    struct skinny_subchannel *sub = NULL;
04865    struct skinny_line *l;
04866    struct skinny_device *d = s->device;
04867    struct ast_frame f = { 0, };
04868    char dgt;
04869    int digit;
04870    int lineInstance;
04871    int callReference;
04872 
04873    digit = letohl(req->data.keypad.button);
04874    lineInstance = letohl(req->data.keypad.lineInstance);
04875    callReference = letohl(req->data.keypad.callReference);
04876 
04877    if (digit == 14) {
04878       dgt = '*';
04879    } else if (digit == 15) {
04880       dgt = '#';
04881    } else if (digit >= 0 && digit <= 9) {
04882       dgt = '0' + digit;
04883    } else {
04884       /* digit=10-13 (A,B,C,D ?), or
04885        * digit is bad value
04886        *
04887        * probably should not end up here, but set
04888        * value for backward compatibility, and log
04889        * a warning.
04890        */
04891       dgt = '0' + digit;
04892       ast_log(LOG_WARNING, "Unsupported digit %d\n", digit);
04893    }
04894 
04895    f.subclass.integer = dgt;
04896 
04897    f.src = "skinny";
04898 
04899    if (lineInstance && callReference)
04900       sub = find_subchannel_by_instance_reference(d, lineInstance, callReference);
04901    else
04902       sub = d->activeline->activesub;
04903       //sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
04904 
04905    if (!sub)
04906       return 0;
04907 
04908    l = sub->parent;
04909    if (sub->owner) {
04910       if (sub->owner->_state == 0) {
04911          f.frametype = AST_FRAME_DTMF_BEGIN;
04912          ast_queue_frame(sub->owner, &f);
04913       }
04914       /* XXX MUST queue this frame to all lines in threeway call if threeway call is active */
04915       f.frametype = AST_FRAME_DTMF_END;
04916       ast_queue_frame(sub->owner, &f);
04917       /* XXX This seriously needs to be fixed */
04918       if (AST_LIST_NEXT(sub, list) && AST_LIST_NEXT(sub, list)->owner) {
04919          if (sub->owner->_state == 0) {
04920             f.frametype = AST_FRAME_DTMF_BEGIN;
04921             ast_queue_frame(AST_LIST_NEXT(sub, list)->owner, &f);
04922          }
04923          f.frametype = AST_FRAME_DTMF_END;
04924          ast_queue_frame(AST_LIST_NEXT(sub, list)->owner, &f);
04925       }
04926    } else {
04927       if (skinnydebug)
04928          ast_verb(1, "No owner: %s\n", l->name);
04929    }
04930    return 1;
04931 }
04932 
04933 static int handle_stimulus_message(struct skinny_req *req, struct skinnysession *s)
04934 {
04935    struct skinny_device *d = s->device;
04936    struct skinny_line *l;
04937    struct skinny_subchannel *sub;
04938    /*struct skinny_speeddial *sd;*/
04939    struct ast_channel *c;
04940    pthread_t t;
04941    int event;
04942    int instance;
04943    int callreference;
04944    /*int res = 0;*/
04945 
04946    event = letohl(req->data.stimulus.stimulus);
04947    instance = letohl(req->data.stimulus.stimulusInstance);
04948    callreference = letohl(req->data.stimulus.callreference); 
04949    if (skinnydebug)
04950       ast_verb(1, "callreference in handle_stimulus_message is '%d'\n", callreference);
04951 
04952    /*  Note that this call should be using the passed in instance and callreference */
04953    sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
04954 
04955    if (!sub) {
04956       l = find_line_by_instance(d, d->lastlineinstance);
04957       if (!l) {
04958          return 0;
04959       }
04960       sub = l->activesub;
04961    } else {
04962       l = sub->parent;
04963    }
04964 
04965    switch(event) {
04966    case STIMULUS_REDIAL:
04967       if (skinnydebug)
04968          ast_verb(1, "Received Stimulus: Redial(%d/%d)\n", instance, callreference);
04969 
04970       if (ast_strlen_zero(l->lastnumberdialed)) {
04971          ast_log(LOG_WARNING, "Attempted redial, but no previously dialed number found.\n");
04972          l->hookstate = SKINNY_ONHOOK;
04973          transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
04974          transmit_closereceivechannel(d, sub);
04975          transmit_stopmediatransmission(d, sub);
04976          transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
04977          transmit_clearpromptmessage(d, l->instance, sub->callid);
04978          transmit_callstate(d, l->instance, sub->callid, SKINNY_ONHOOK);
04979          transmit_selectsoftkeys(d, 0, 0, KEYDEF_ONHOOK);
04980          transmit_activatecallplane(d, l);
04981          break;
04982       }
04983 
04984       c = skinny_new(l, AST_STATE_DOWN, NULL);
04985       if (!c) {
04986          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
04987       } else {
04988          sub = c->tech_pvt;
04989          l = sub->parent;
04990          l->activesub = sub;
04991          if (l->hookstate == SKINNY_ONHOOK) {
04992             l->hookstate = SKINNY_OFFHOOK;
04993             transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
04994             transmit_activatecallplane(d, l);
04995          }
04996          transmit_clear_display_message(d, l->instance, sub->callid);
04997          transmit_start_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
04998          transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
04999 
05000          if (!ast_ignore_pattern(c->context, l->lastnumberdialed)) {
05001             transmit_stop_tone(d, l->instance, sub->callid);
05002          }
05003          ast_copy_string(c->exten, l->lastnumberdialed, sizeof(c->exten));
05004          if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
05005             ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
05006             ast_hangup(c);
05007          }
05008       }
05009       break;
05010    case STIMULUS_SPEEDDIAL:
05011        {
05012       struct skinny_speeddial *sd;
05013 
05014       if (skinnydebug)
05015          ast_verb(1, "Received Stimulus: SpeedDial(%d/%d)\n", instance, callreference);
05016       if (!(sd = find_speeddial_by_instance(d, instance, 0))) {
05017          return 0;
05018       }
05019 
05020       if (!sub || !sub->owner)
05021          c = skinny_new(l, AST_STATE_DOWN, NULL);
05022       else
05023          c = sub->owner;
05024 
05025       if (!c) {
05026          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05027       } else {
05028          sub = c->tech_pvt;
05029          l = sub->parent;
05030          l->activesub = sub;
05031          if (l->hookstate == SKINNY_ONHOOK) {
05032             l->hookstate = SKINNY_OFFHOOK;
05033             transmit_speaker_mode(d, SKINNY_SPEAKERON);
05034             transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
05035             transmit_activatecallplane(d, l);
05036          }
05037          transmit_clear_display_message(d, l->instance, sub->callid);
05038          transmit_start_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05039          transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
05040 
05041          if (!ast_ignore_pattern(c->context, sd->exten)) {
05042             transmit_stop_tone(d, l->instance, sub->callid);
05043          }
05044          if (ast_exists_extension(c, c->context, sd->exten, 1, l->cid_num)) {
05045             ast_copy_string(c->exten, sd->exten, sizeof(c->exten));
05046             ast_copy_string(l->lastnumberdialed, sd->exten, sizeof(l->lastnumberdialed));
05047 
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             break;
05053          }
05054       }
05055        }
05056       break;
05057    case STIMULUS_HOLD:
05058       if (skinnydebug)
05059          ast_verb(1, "Received Stimulus: Hold(%d/%d)\n", instance, callreference);
05060       handle_hold_button(sub);
05061       break;
05062    case STIMULUS_TRANSFER:
05063       if (skinnydebug)
05064          ast_verb(1, "Received Stimulus: Transfer(%d/%d)\n", instance, callreference);
05065       if (l->transfer)
05066          handle_transfer_button(sub);
05067       else
05068          transmit_displaynotify(d, "Transfer disabled", 10);
05069       break;
05070    case STIMULUS_CONFERENCE:
05071       if (skinnydebug)
05072          ast_verb(1, "Received Stimulus: Conference(%d/%d)\n", instance, callreference);
05073       /* XXX determine the best way to pull off a conference.  Meetme? */
05074       break;
05075    case STIMULUS_VOICEMAIL:
05076       if (skinnydebug)
05077          ast_verb(1, "Received Stimulus: Voicemail(%d/%d)\n", instance, callreference);
05078 
05079       if (!sub || !sub->owner) {
05080          c = skinny_new(l, AST_STATE_DOWN, NULL);
05081       } else {
05082          c = sub->owner;
05083       }
05084       if (!c) {
05085          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05086       } else {
05087          sub = c->tech_pvt;
05088          l = sub->parent;
05089          l->activesub = sub;
05090 
05091          if (ast_strlen_zero(l->vmexten))  /* Exit the call if no VM pilot */
05092             break;
05093 
05094          if (l->hookstate == SKINNY_ONHOOK){
05095             l->hookstate = SKINNY_OFFHOOK;
05096             transmit_speaker_mode(d, SKINNY_SPEAKERON);
05097             transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
05098             transmit_activatecallplane(d, l);
05099          }
05100 
05101          transmit_clear_display_message(d, l->instance, sub->callid);
05102          transmit_start_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05103          transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
05104 
05105          if (!ast_ignore_pattern(c->context, l->vmexten)) {
05106             transmit_stop_tone(d, l->instance, sub->callid);
05107          }
05108 
05109          if (ast_exists_extension(c, c->context, l->vmexten, 1, l->cid_num)) {
05110             ast_copy_string(c->exten, l->vmexten, sizeof(c->exten));
05111             ast_copy_string(l->lastnumberdialed, l->vmexten, sizeof(l->lastnumberdialed));
05112             if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
05113                ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
05114                ast_hangup(c);
05115             }
05116             break;
05117          }
05118       }
05119       break;
05120    case STIMULUS_CALLPARK:
05121       {
05122       int extout;
05123       char message[32];
05124 
05125       if (skinnydebug)
05126          ast_verb(1, "Received Stimulus: Park Call(%d/%d)\n", instance, callreference);
05127 
05128       if ((sub && sub->owner) && (sub->owner->_state ==  AST_STATE_UP)){
05129          c = sub->owner;
05130          if (ast_bridged_channel(c)) {
05131             if (!ast_masq_park_call(ast_bridged_channel(c), c, 0, &extout)) {
05132                snprintf(message, sizeof(message), "Call Parked at: %d", extout);
05133                transmit_displaynotify(d, message, 10);
05134             } else {
05135                transmit_displaynotify(d, "Call Park failed", 10);
05136             }
05137          } else {
05138             transmit_displaynotify(d, "Call Park not available", 10);
05139          }
05140       } else {
05141          transmit_displaynotify(d, "Call Park not available", 10);
05142       }
05143       break;
05144       }
05145    case STIMULUS_DND:
05146       if (skinnydebug)
05147          ast_verb(1, "Received Stimulus: DND (%d/%d)\n", instance, callreference);
05148 
05149       /* Do not disturb */
05150       if (l->dnd != 0){
05151          ast_verb(3, "Disabling DND on %s@%s\n", l->name, d->name);
05152          l->dnd = 0;
05153          transmit_lamp_indication(d, STIMULUS_DND, 1, SKINNY_LAMP_ON);
05154          transmit_displaynotify(d, "DnD disabled", 10);
05155       } else {
05156          ast_verb(3, "Enabling DND on %s@%s\n", l->name, d->name);
05157          l->dnd = 1;
05158          transmit_lamp_indication(d, STIMULUS_DND, 1, SKINNY_LAMP_OFF);
05159          transmit_displaynotify(d, "DnD enabled", 10);
05160       }
05161       break;
05162    case STIMULUS_FORWARDALL:
05163       if (skinnydebug)
05164          ast_verb(1, "Received Stimulus: Forward All(%d/%d)\n", instance, callreference);
05165 
05166       if (!sub || !sub->owner) {
05167          c = skinny_new(l, AST_STATE_DOWN, NULL);
05168       } else {
05169          c = sub->owner;
05170       }
05171 
05172       if (!c) {
05173          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05174       } else {
05175          sub = c->tech_pvt;
05176          handle_callforward_button(sub, SKINNY_CFWD_ALL);
05177       }
05178       break;
05179    case STIMULUS_FORWARDBUSY:
05180       if (skinnydebug)
05181          ast_verb(1, "Received Stimulus: Forward Busy (%d/%d)\n", instance, callreference);
05182 
05183       if (!sub || !sub->owner) {
05184          c = skinny_new(l, AST_STATE_DOWN, NULL);
05185       } else {
05186          c = sub->owner;
05187       }
05188 
05189       if (!c) {
05190          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05191       } else {
05192          sub = c->tech_pvt;
05193          handle_callforward_button(sub, SKINNY_CFWD_BUSY);
05194       }
05195       break;
05196    case STIMULUS_FORWARDNOANSWER:
05197       if (skinnydebug)
05198          ast_verb(1, "Received Stimulus: Forward No Answer (%d/%d)\n", instance, callreference);
05199 
05200 #if 0 /* Not sure how to handle this yet */
05201       if (!sub || !sub->owner) {
05202          c = skinny_new(l, AST_STATE_DOWN, NULL);
05203       } else {
05204          c = sub->owner;
05205       }
05206 
05207       if (!c) {
05208          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05209       } else {
05210          sub = c->tech_pvt;
05211          handle_callforward_button(sub, SKINNY_CFWD_NOANSWER);
05212       }
05213 #endif
05214       break;
05215    case STIMULUS_DISPLAY:
05216       /* Not sure what this is */
05217       if (skinnydebug)
05218          ast_verb(1, "Received Stimulus: Display(%d/%d)\n", instance, callreference);
05219       break;
05220    case STIMULUS_LINE:
05221       if (skinnydebug)
05222          ast_verb(1, "Received Stimulus: Line(%d/%d)\n", instance, callreference);
05223 
05224       l = find_line_by_instance(d, instance);
05225 
05226       if (!l) {
05227          return 0;
05228       }
05229 
05230       d->activeline = l;
05231 
05232       /* turn the speaker on */
05233       transmit_speaker_mode(d, SKINNY_SPEAKERON);
05234       transmit_ringer_mode(d, SKINNY_RING_OFF);
05235       transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
05236 
05237       l->hookstate = SKINNY_OFFHOOK;
05238 
05239       if (sub && sub->outgoing) {
05240          /* We're answering a ringing call */
05241          ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
05242          transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
05243          transmit_activatecallplane(d, l);
05244          transmit_stop_tone(d, l->instance, sub->callid);
05245          transmit_callstate(d, sub->parent->instance, sub->callid, SKINNY_CONNECTED);
05246          transmit_displaypromptstatus(d, "Connected", 0, l->instance, sub->callid);
05247          transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
05248          start_rtp(sub);
05249          ast_setstate(sub->owner, AST_STATE_UP);
05250       } else {
05251          if (sub && sub->owner) {
05252             ast_debug(1, "Current subchannel [%s] already has owner\n", sub->owner->name);
05253          } else {
05254             c = skinny_new(l, AST_STATE_DOWN, NULL);
05255             if (c) {
05256                sub = c->tech_pvt;
05257                l->activesub = sub;
05258                transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
05259                transmit_activatecallplane(d, l);
05260                transmit_clear_display_message(d, l->instance, sub->callid);
05261                transmit_start_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05262                transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_OFFHOOK);
05263 
05264                /* start the switch thread */
05265                if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
05266                   ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
05267                   ast_hangup(c);
05268                }
05269             } else {
05270                ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05271             }
05272          }
05273       }
05274       break;
05275    default:
05276       if (skinnydebug)
05277          ast_verb(1, "RECEIVED UNKNOWN STIMULUS:  %d(%d/%d)\n", event, instance, callreference);
05278       break;
05279    }
05280    ast_devstate_changed(AST_DEVICE_UNKNOWN, "Skinny/%s@%s", l->name, d->name);
05281 
05282    return 1;
05283 }
05284 
05285 static int handle_offhook_message(struct skinny_req *req, struct skinnysession *s)
05286 {
05287    struct skinny_device *d = s->device;
05288    struct skinny_line *l;
05289    struct skinny_subchannel *sub;
05290    struct ast_channel *c;
05291    struct skinny_line *tmp;
05292    pthread_t t;
05293    int instance;
05294 
05295    /* if any line on a device is offhook, than the device must be offhook, 
05296       unless we have shared lines CCM seems that it would never get here, 
05297       but asterisk does, so we may need to do more work.  Ugly, we should 
05298       probably move hookstate from line to device, afterall, it's actually
05299        a device that changes hookstates */
05300 
05301    AST_LIST_TRAVERSE(&d->lines, tmp, list) {
05302       if (tmp->hookstate == SKINNY_OFFHOOK) {
05303          ast_verbose(VERBOSE_PREFIX_3 "Got offhook message when device (%s@%s) already offhook\n", tmp->name, d->name);
05304          return 0;
05305       }
05306    }
05307 
05308    instance = letohl(req->data.offhook.instance);
05309 
05310    if (instance) {
05311       sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
05312       if (!sub) {
05313          l = find_line_by_instance(d, d->lastlineinstance);
05314          if (!l) {
05315             return 0;
05316          }
05317       } else {
05318          l = sub->parent;
05319       }
05320    } else {
05321       l = d->activeline;
05322       sub = l->activesub;
05323    }
05324 
05325    /* Not ideal, but let's send updated time at onhook and offhook, as it clears the display */
05326    transmit_definetimedate(d);
05327    
05328    transmit_ringer_mode(d, SKINNY_RING_OFF);
05329    l->hookstate = SKINNY_OFFHOOK;
05330 
05331    ast_devstate_changed(AST_DEVICE_INUSE, "Skinny/%s@%s", l->name, d->name);
05332 
05333    if (sub && sub->onhold) {
05334       return 1;
05335    }
05336 
05337    transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
05338 
05339    if (sub && sub->outgoing) {
05340       /* We're answering a ringing call */
05341       ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
05342       transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
05343       transmit_activatecallplane(d, l);
05344       transmit_stop_tone(d, l->instance, sub->callid);
05345       transmit_callstate(d, sub->parent->instance, sub->callid, SKINNY_CONNECTED);
05346       transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
05347       start_rtp(sub);
05348       ast_setstate(sub->owner, AST_STATE_UP);
05349    } else {
05350       if (sub && sub->owner) {
05351          ast_debug(1, "Current sub [%s] already has owner\n", sub->owner->name);
05352       } else {
05353          c = skinny_new(l, AST_STATE_DOWN, NULL);
05354          if (c) {
05355             sub = c->tech_pvt;
05356             l->activesub = sub;
05357             transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
05358             transmit_activatecallplane(d, l);
05359             transmit_clear_display_message(d, l->instance, sub->callid);
05360             transmit_start_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05361             transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_OFFHOOK);
05362 
05363             /* start the switch thread */
05364             if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
05365                ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
05366                ast_hangup(c);
05367             }
05368          } else {
05369             ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05370          }
05371       }
05372    }
05373    return 1;
05374 }
05375 
05376 static int handle_onhook_message(struct skinny_req *req, struct skinnysession *s)
05377 {
05378    struct skinny_device *d = s->device;
05379    struct skinny_line *l;
05380    struct skinny_subchannel *sub;
05381    int instance;
05382    int reference;
05383    int onlysub = 0;
05384 
05385    instance = letohl(req->data.onhook.instance);
05386    reference = letohl(req->data.onhook.reference);
05387 
05388    if (instance && reference) {
05389       sub = find_subchannel_by_instance_reference(d, instance, reference);
05390       if (!sub) {
05391          return 0;
05392       }
05393       l = sub->parent;
05394    } else {
05395       l = d->activeline;
05396       sub = l->activesub;
05397       if (!sub) {
05398          return 0;
05399       }
05400    }
05401 
05402    if (l->hookstate == SKINNY_ONHOOK) {
05403       /* Something else already put us back on hook */
05404       return 0;
05405    }
05406 
05407    ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
05408 
05409    if (sub->onhold) {
05410       return 0;
05411    }
05412 
05413    if (!AST_LIST_NEXT(sub, list)) {
05414       onlysub = 1;
05415    } else {
05416       AST_LIST_REMOVE(&l->sub, sub, list);
05417    }
05418 
05419    sub->cxmode = SKINNY_CX_RECVONLY;
05420    if (onlysub || sub->xferor){  /* is this the only call to this device? */
05421       l->hookstate = SKINNY_ONHOOK;
05422       if (skinnydebug)
05423          ast_debug(1, "Skinny %s@%s-%d went on hook\n", l->name, d->name, reference);
05424    }
05425 
05426    if (l->hookstate == SKINNY_ONHOOK) {
05427       transmit_closereceivechannel(d, sub);
05428       transmit_stopmediatransmission(d, sub);
05429       transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
05430       transmit_clearpromptmessage(d, instance, sub->callid);
05431       transmit_callstate(d, l->instance, sub->callid, SKINNY_ONHOOK);
05432       transmit_selectsoftkeys(d, 0, 0, KEYDEF_ONHOOK);
05433       transmit_activatecallplane(d, l);
05434    } else if (l->hookstate == SKINNY_OFFHOOK) {
05435       transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
05436       transmit_activatecallplane(d, l);
05437    } else {
05438       transmit_callstate(d, l->instance, sub->callid, l->hookstate);
05439    }
05440 
05441    if (l->transfer && sub->xferor && sub->owner->_state >= AST_STATE_RING) {
05442       /* We're allowed to transfer, we have two active calls and
05443          we made at least one of the calls.  Let's try and transfer */
05444       handle_transfer_button(sub);
05445    } else {
05446       /* Hangup the current call */
05447       /* If there is another active call, skinny_hangup will ring the phone with the other call */
05448       if (sub->xferor && sub->related){
05449          sub->related->related = NULL;
05450          sub->related->blindxfer = 0;
05451       }
05452 
05453       if (sub->owner) {
05454          sub->alreadygone = 1;
05455          ast_queue_hangup(sub->owner);
05456       } else {
05457          ast_log(LOG_WARNING, "Skinny(%s@%s-%d) channel already destroyed\n",
05458             l->name, d->name, sub->callid);
05459       }
05460       /* Not ideal, but let's send updated time at onhook and offhook, as it clears the display */
05461       transmit_definetimedate(d);
05462    }
05463    return 1;
05464 }
05465 
05466 static int handle_capabilities_res_message(struct skinny_req *req, struct skinnysession *s)
05467 {
05468    struct skinny_device *d = s->device;
05469    struct skinny_line *l;
05470    uint32_t count = 0;
05471    format_t codecs = 0;
05472    int i;
05473    char buf[256];
05474 
05475    count = letohl(req->data.caps.count);
05476    if (count > SKINNY_MAX_CAPABILITIES) {
05477       count = SKINNY_MAX_CAPABILITIES;
05478       ast_log(LOG_WARNING, "Received more capabilities than we can handle (%d).  Ignoring the rest.\n", SKINNY_MAX_CAPABILITIES);
05479    }
05480 
05481    for (i = 0; i < count; i++) {
05482       format_t acodec = 0;
05483       int scodec = 0;
05484       scodec = letohl(req->data.caps.caps[i].codec);
05485       acodec = codec_skinny2ast(scodec);
05486       if (skinnydebug)
05487          ast_verb(1, "Adding codec capability '%" PRId64 " (%d)'\n", acodec, scodec);
05488       codecs |= acodec;
05489    }
05490 
05491    d->capability = d->confcapability & codecs;
05492    ast_verb(0, "Device capability set to '%s'\n", ast_getformatname_multiple(buf, sizeof(buf), d->capability));
05493    AST_LIST_TRAVERSE(&d->lines, l, list) {
05494       ast_mutex_lock(&l->lock);
05495       l->capability = l->confcapability & d->capability;
05496       ast_mutex_unlock(&l->lock);
05497    }
05498 
05499    return 1;
05500 }
05501 
05502 static int handle_button_template_req_message(struct skinny_req *req, struct skinnysession *s)
05503 {
05504    struct skinny_device *d = s->device;
05505    struct skinny_line *l;
05506    int i;
05507 
05508    struct skinny_speeddial *sd;
05509    struct button_definition_template btn[42];
05510    int lineInstance = 1;
05511    int speeddialInstance = 1;
05512    int buttonCount = 0;
05513 
05514    if (!(req = req_alloc(sizeof(struct button_template_res_message), BUTTON_TEMPLATE_RES_MESSAGE)))
05515       return -1;
05516 
05517    memset(&btn, 0, sizeof(btn));
05518 
05519    get_button_template(s, btn);
05520 
05521    for (i=0; i<42; i++) {
05522       int btnSet = 0;
05523       switch (btn[i].buttonDefinition) {
05524          case BT_CUST_LINE:
05525             /* assume failure */
05526             req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE;
05527             req->data.buttontemplate.definition[i].instanceNumber = 0;
05528 
05529             AST_LIST_TRAVERSE(&d->lines, l, list) {
05530                if (l->instance == lineInstance) {
05531                   ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
05532                   req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
05533                   req->data.buttontemplate.definition[i].instanceNumber = lineInstance;
05534                   lineInstance++;
05535                   buttonCount++;
05536                   btnSet = 1;
05537                   break;
05538                }
05539             }
05540 
05541             if (!btnSet) {
05542                AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
05543                   if (sd->isHint && sd->instance == lineInstance) {
05544                      ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
05545                      req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
05546                      req->data.buttontemplate.definition[i].instanceNumber = lineInstance;
05547                      lineInstance++;
05548                      buttonCount++;
05549                      btnSet = 1;
05550                      break;
05551                   }
05552                }
05553             }
05554             break;
05555          case BT_CUST_LINESPEEDDIAL:
05556             /* assume failure */
05557             req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE;
05558             req->data.buttontemplate.definition[i].instanceNumber = 0;
05559 
05560             AST_LIST_TRAVERSE(&d->lines, l, list) {
05561                if (l->instance == lineInstance) {
05562                   ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
05563                   req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
05564                   req->data.buttontemplate.definition[i].instanceNumber = lineInstance;
05565                   lineInstance++;
05566                   buttonCount++;
05567                   btnSet = 1;
05568                   break;
05569                }
05570             }
05571 
05572             if (!btnSet) {
05573                AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
05574                   if (sd->isHint && sd->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                   } else if (!sd->isHint && sd->instance == speeddialInstance) {
05583                      ast_verb(0, "Adding button: %d, %d\n", BT_SPEEDDIAL, speeddialInstance);
05584                      req->data.buttontemplate.definition[i].buttonDefinition = BT_SPEEDDIAL;
05585                      req->data.buttontemplate.definition[i].instanceNumber = speeddialInstance;
05586                      speeddialInstance++;
05587                      buttonCount++;
05588                      btnSet = 1;
05589                      break;
05590                   }
05591                }
05592             }
05593             break;
05594          case BT_LINE:
05595             req->data.buttontemplate.definition[i].buttonDefinition = htolel(BT_NONE);
05596             req->data.buttontemplate.definition[i].instanceNumber = htolel(0);
05597 
05598             AST_LIST_TRAVERSE(&d->lines, l, list) {
05599                if (l->instance == lineInstance) {
05600                   ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
05601                   req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
05602                   req->data.buttontemplate.definition[i].instanceNumber = lineInstance;
05603                   lineInstance++;
05604                   buttonCount++;
05605                   btnSet = 1;
05606                   break;
05607                }
05608             }
05609             break;
05610          case BT_SPEEDDIAL:
05611             req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE;
05612             req->data.buttontemplate.definition[i].instanceNumber = 0;
05613 
05614             AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
05615                if (!sd->isHint && sd->instance == speeddialInstance) {
05616                   ast_verb(0, "Adding button: %d, %d\n", BT_SPEEDDIAL, speeddialInstance);
05617                   req->data.buttontemplate.definition[i].buttonDefinition = BT_SPEEDDIAL;
05618                   req->data.buttontemplate.definition[i].instanceNumber = speeddialInstance - 1;
05619                   speeddialInstance++;
05620                   buttonCount++;
05621                   btnSet = 1;
05622                   break;
05623                }
05624             }
05625             break;
05626          case BT_NONE:
05627             break;
05628          default:
05629             ast_verb(0, "Adding button: %d, %d\n", btn[i].buttonDefinition, 0);
05630             req->data.buttontemplate.definition[i].buttonDefinition = htolel(btn[i].buttonDefinition);
05631             req->data.buttontemplate.definition[i].instanceNumber = 0;
05632             buttonCount++;
05633             btnSet = 1;
05634             break;
05635       }
05636    }
05637 
05638    req->data.buttontemplate.buttonOffset = 0;
05639    req->data.buttontemplate.buttonCount = htolel(buttonCount);
05640    req->data.buttontemplate.totalButtonCount = htolel(buttonCount);
05641 
05642    if (skinnydebug)
05643       ast_verb(1, "Sending %d template to %s\n",
05644                d->type,
05645                d->name);
05646    transmit_response(d, req);
05647    return 1;
05648 }
05649 
05650 static int handle_open_receive_channel_ack_message(struct skinny_req *req, struct skinnysession *s)
05651 {
05652    struct skinny_device *d = s->device;
05653    struct skinny_line *l;
05654    struct skinny_subchannel *sub;
05655    struct ast_format_list fmt;
05656    struct sockaddr_in sin = { 0, };
05657    struct sockaddr_in us = { 0, };
05658    struct ast_sockaddr sin_tmp;
05659    struct ast_sockaddr us_tmp;
05660    uint32_t addr;
05661    int port;
05662    int status;
05663    int passthruid;
05664 
05665    status = letohl(req->data.openreceivechannelack.status);
05666    if (status) {
05667       ast_log(LOG_ERROR, "Open Receive Channel Failure\n");
05668       return 0;
05669    }
05670    addr = req->data.openreceivechannelack.ipAddr;
05671    port = letohl(req->data.openreceivechannelack.port);
05672    passthruid = letohl(req->data.openreceivechannelack.passThruId);
05673 
05674    sin.sin_family = AF_INET;
05675    sin.sin_addr.s_addr = addr;
05676    sin.sin_port = htons(port);
05677 
05678    sub = find_subchannel_by_reference(d, passthruid);
05679 
05680    if (!sub)
05681       return 0;
05682 
05683    l = sub->parent;
05684 
05685    if (sub->rtp) {
05686       ast_sockaddr_from_sin(&sin_tmp, &sin);
05687       ast_rtp_instance_set_remote_address(sub->rtp, &sin_tmp);
05688       ast_rtp_instance_get_local_address(sub->rtp, &us_tmp);
05689       ast_sockaddr_to_sin(&us_tmp, &us);
05690       us.sin_addr.s_addr = us.sin_addr.s_addr ? us.sin_addr.s_addr : d->ourip.s_addr;
05691    } else {
05692       ast_log(LOG_ERROR, "No RTP structure, this is very bad\n");
05693       return 0;
05694    }
05695 
05696    if (skinnydebug) {
05697       ast_verb(1, "device ipaddr = %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
05698       ast_verb(1, "asterisk ipaddr = %s:%d\n", ast_inet_ntoa(us.sin_addr), ntohs(us.sin_port));
05699    }
05700 
05701    fmt = ast_codec_pref_getsize(&l->prefs, ast_best_codec(l->capability));
05702 
05703    if (skinnydebug)
05704       ast_verb(1, "Setting payloadType to '%s' (%d ms)\n", ast_getformatname(fmt.bits), fmt.cur_ms);
05705 
05706    transmit_startmediatransmission(d, sub, us, fmt);
05707 
05708    return 1;
05709 }
05710 
05711 static int handle_enbloc_call_message(struct skinny_req *req, struct skinnysession *s)
05712 {
05713    struct skinny_device *d = s->device;
05714    struct skinny_line *l;
05715    struct skinny_subchannel *sub = NULL;
05716    struct ast_channel *c;
05717    pthread_t t;
05718 
05719    if (skinnydebug)
05720       ast_verb(1, "Received Enbloc Call: %s\n", req->data.enbloccallmessage.calledParty);
05721 
05722    sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
05723 
05724    if (!sub) {
05725       l = find_line_by_instance(d, d->lastlineinstance);
05726       if (!l) {
05727          return 0;
05728       }
05729    } else {
05730       l = sub->parent;
05731    }
05732 
05733    c = skinny_new(l, AST_STATE_DOWN, NULL);
05734 
05735    if(!c) {
05736       ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05737    } else {
05738       l->hookstate = SKINNY_OFFHOOK;
05739 
05740       sub = c->tech_pvt;
05741       l->activesub = sub;
05742       transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
05743       transmit_activatecallplane(d, l);
05744       transmit_clear_display_message(d, l->instance, sub->callid);
05745       transmit_start_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05746 
05747       if (!ast_ignore_pattern(c->context, req->data.enbloccallmessage.calledParty)) {
05748          transmit_stop_tone(d, l->instance, sub->callid);
05749       }
05750       ast_copy_string(c->exten, req->data.enbloccallmessage.calledParty, sizeof(c->exten));
05751       if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
05752          ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
05753          ast_hangup(c);
05754       }
05755    }
05756    
05757    return 1;
05758 }
05759 
05760 
05761 static int handle_soft_key_event_message(struct skinny_req *req, struct skinnysession *s)
05762 {
05763    struct skinny_device *d = s->device;
05764    struct skinny_line *l;
05765    struct skinny_subchannel *sub = NULL;
05766    struct ast_channel *c;
05767    pthread_t t;
05768    int event;
05769    int instance;
05770    int callreference;
05771 
05772    event = letohl(req->data.softkeyeventmessage.softKeyEvent);
05773    instance = letohl(req->data.softkeyeventmessage.instance);
05774    callreference = letohl(req->data.softkeyeventmessage.callreference);
05775 
05776    if (instance) {
05777       l = find_line_by_instance(d, instance);
05778       if (callreference) {
05779          sub = find_subchannel_by_instance_reference(d, instance, callreference);
05780       } else {
05781          sub = find_subchannel_by_instance_reference(d, instance, d->lastcallreference);
05782       }
05783    } else {
05784       l = find_line_by_instance(d, d->lastlineinstance);
05785    }
05786 
05787    if (!l) {
05788       if (skinnydebug)
05789          ast_verb(1, "Received Softkey Event: %d(%d/%d)\n", event, instance, callreference);
05790       return 0;
05791    }
05792 
05793    ast_devstate_changed(AST_DEVICE_INUSE, "Skinny/%s@%s", l->name, d->name);
05794 
05795    switch(event) {
05796    case SOFTKEY_NONE:
05797       if (skinnydebug)
05798          ast_verb(1, "Received Softkey Event: None(%d/%d)\n", instance, callreference);
05799       break;
05800    case SOFTKEY_REDIAL:
05801       if (skinnydebug)
05802          ast_verb(1, "Received Softkey Event: Redial(%d/%d)\n", instance, callreference);
05803 
05804       if (ast_strlen_zero(l->lastnumberdialed)) {
05805          ast_log(LOG_WARNING, "Attempted redial, but no previously dialed number found. Ignoring button.\n");
05806          break;
05807       }
05808 
05809       if (!sub || !sub->owner) {
05810          c = skinny_new(l, AST_STATE_DOWN, NULL);
05811       } else {
05812          c = sub->owner;
05813       }
05814 
05815       if (!c) {
05816          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05817       } else {
05818          sub = c->tech_pvt;
05819          l->activesub = sub;
05820          if (l->hookstate == SKINNY_ONHOOK) {
05821             l->hookstate = SKINNY_OFFHOOK;
05822             transmit_speaker_mode(d, SKINNY_SPEAKERON);
05823             transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
05824             transmit_activatecallplane(d, l);
05825          }
05826          transmit_clear_display_message(d, l->instance, sub->callid);
05827          transmit_start_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05828          transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
05829 
05830          if (!ast_ignore_pattern(c->context, l->lastnumberdialed)) {
05831             transmit_stop_tone(d, l->instance, sub->callid);
05832          }
05833          ast_copy_string(c->exten, l->lastnumberdialed, sizeof(c->exten));
05834          if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
05835             ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
05836             ast_hangup(c);
05837          }
05838       }
05839       break;
05840    case SOFTKEY_NEWCALL:  /* Actually the DIAL softkey */
05841       if (skinnydebug)
05842          ast_verb(1, "Received Softkey Event: New Call(%d/%d)\n", instance, callreference);
05843 
05844       /* New Call ALWAYS gets a new sub-channel */
05845       c = skinny_new(l, AST_STATE_DOWN, NULL);
05846       sub = c->tech_pvt;
05847    
05848       /* transmit_ringer_mode(d, SKINNY_RING_OFF);
05849       transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON); */
05850 
05851       /* l->hookstate = SKINNY_OFFHOOK; */
05852 
05853       if (!c) {
05854          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05855       } else {
05856          sub = c->tech_pvt;
05857          l->activesub = sub;
05858          if (l->hookstate == SKINNY_ONHOOK) {
05859             l->hookstate = SKINNY_OFFHOOK;
05860             transmit_speaker_mode(d, SKINNY_SPEAKERON);
05861          }
05862          ast_verb(1, "Call-id: %d\n", sub->callid);
05863 
05864          transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
05865          transmit_activatecallplane(d, l);
05866 
05867          transmit_clear_display_message(d, l->instance, sub->callid);
05868          transmit_start_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05869          transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_OFFHOOK);
05870 
05871          /* start the switch thread */
05872          if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
05873             ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
05874             ast_hangup(c);
05875          }
05876       }
05877       break;
05878    case SOFTKEY_HOLD:
05879       if (skinnydebug)
05880          ast_verb(1, "Received Softkey Event: Hold(%d/%d)\n", instance, callreference);
05881       handle_hold_button(sub);   
05882       break;
05883    case SOFTKEY_TRNSFER:
05884       if (skinnydebug)
05885          ast_verb(1, "Received Softkey Event: Transfer(%d/%d)\n", instance, callreference);
05886       if (l->transfer)
05887          handle_transfer_button(sub);
05888       else
05889          transmit_displaynotify(d, "Transfer disabled", 10);
05890 
05891       break;
05892    case SOFTKEY_DND:
05893       if (skinnydebug)
05894          ast_verb(1, "Received Softkey Event: DND(%d/%d)\n", instance, callreference);
05895 
05896       /* Do not disturb */
05897       if (l->dnd != 0){
05898          ast_verb(3, "Disabling DND on %s@%s\n", l->name, d->name);
05899          l->dnd = 0;
05900          transmit_lamp_indication(d, STIMULUS_DND, 1, SKINNY_LAMP_ON);
05901          transmit_displaynotify(d, "DnD disabled", 10);
05902       } else {
05903          ast_verb(3, "Enabling DND on %s@%s\n", l->name, d->name);
05904          l->dnd = 1;
05905          transmit_lamp_indication(d, STIMULUS_DND, 1, SKINNY_LAMP_OFF);
05906          transmit_displaynotify(d, "DnD enabled", 10);
05907       }
05908       break;
05909    case SOFTKEY_CFWDALL:
05910       if (skinnydebug)
05911          ast_verb(1, "Received Softkey Event: Forward All(%d/%d)\n", instance, callreference);
05912 
05913       if (!sub || !sub->owner) {
05914          c = skinny_new(l, AST_STATE_DOWN, NULL);
05915       } else {
05916          c = sub->owner;
05917       }
05918 
05919       if (!c) {
05920          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05921       } else {
05922          sub = c->tech_pvt;
05923          l->activesub = sub;
05924          handle_callforward_button(sub, SKINNY_CFWD_ALL);
05925       }
05926       break;
05927    case SOFTKEY_CFWDBUSY:
05928       if (skinnydebug)
05929          ast_verb(1, "Received Softkey Event: Forward Busy (%d/%d)\n", instance, callreference);
05930 
05931       if (!sub || !sub->owner) {
05932          c = skinny_new(l, AST_STATE_DOWN, NULL);
05933       } else {
05934          c = sub->owner;
05935       }
05936 
05937       if (!c) {
05938          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05939       } else {
05940          sub = c->tech_pvt;
05941          l->activesub = sub;
05942          handle_callforward_button(sub, SKINNY_CFWD_BUSY);
05943       }
05944       break;
05945    case SOFTKEY_CFWDNOANSWER:
05946       if (skinnydebug)
05947          ast_verb(1, "Received Softkey Event: Forward No Answer (%d/%d)\n", instance, callreference);
05948 
05949 #if 0 /* Not sure how to handle this yet */
05950       if (!sub || !sub->owner) {
05951          c = skinny_new(l, AST_STATE_DOWN, NULL);
05952       } else {
05953          c = sub->owner;
05954       }
05955 
05956       if (!c) {
05957          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05958       } else {
05959          sub = c->tech_pvt;
05960          l->activesub = sub;
05961          handle_callforward_button(sub, SKINNY_CFWD_NOANSWER);
05962       }
05963 #endif
05964       break;
05965    case SOFTKEY_BKSPC:
05966       if (skinnydebug)
05967          ast_verb(1, "Received Softkey Event: Backspace(%d/%d)\n", instance, callreference);
05968       break;
05969    case SOFTKEY_ENDCALL:
05970       if (skinnydebug)
05971          ast_verb(1, "Received Softkey Event: End Call(%d/%d)\n", instance, callreference);
05972 
05973       if (l->hookstate == SKINNY_ONHOOK) {
05974          /* Something else already put us back on hook */
05975          break;
05976       }
05977       if (sub) {
05978          int onlysub = 0;
05979 
05980          if (!AST_LIST_NEXT(sub, list)) {
05981             onlysub = 1;
05982          } else {
05983             AST_LIST_REMOVE(&l->sub, sub, list);
05984          }
05985 
05986          sub->cxmode = SKINNY_CX_RECVONLY;
05987          if (onlysub || sub->xferor){    /*Are there other calls to this device */
05988             l->hookstate = SKINNY_ONHOOK;
05989             if (skinnydebug)
05990                ast_debug(1, "Skinny %s@%s-%d went on hook\n", l->name, d->name, callreference);
05991          }
05992 
05993          if (l->hookstate == SKINNY_ONHOOK) {
05994             transmit_closereceivechannel(d, sub);
05995             transmit_stopmediatransmission(d, sub);
05996             transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
05997             transmit_clearpromptmessage(d, instance, sub->callid);
05998             transmit_callstate(d, l->instance, sub->callid, SKINNY_ONHOOK);
05999             transmit_selectsoftkeys(d, 0, 0, KEYDEF_ONHOOK);
06000             transmit_activatecallplane(d, l);
06001          } else if (l->hookstate == SKINNY_OFFHOOK) {
06002             transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
06003             transmit_activatecallplane(d, l);
06004          } else {
06005             transmit_callstate(d, l->instance, sub->callid, l->hookstate);
06006          }
06007 
06008          ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
06009          if (skinnydebug)
06010             ast_verb(1, "Skinny %s@%s went on hook\n", l->name, d->name);
06011          if (l->transfer && sub->xferor && sub->owner->_state >= AST_STATE_RING) {
06012             /* We're allowed to transfer, we have two active calls and
06013                we made at least one of the calls.  Let's try and transfer */
06014             handle_transfer_button(sub);
06015          } else {
06016             /* Hangup the current call */
06017             /* If there is another active call, skinny_hangup will ring the phone with the other call */
06018             if (sub->xferor && sub->related){
06019                sub->related->related = NULL;
06020                sub->related->blindxfer = 0;
06021             }
06022 
06023             if (sub->owner) {
06024                sub->alreadygone = 1;
06025                ast_queue_hangup(sub->owner);
06026             } else {
06027                ast_log(LOG_WARNING, "Skinny(%s@%s-%d) channel already destroyed\n",
06028                   l->name, d->name, sub->callid);
06029             }
06030          }
06031          if ((l->hookstate == SKINNY_ONHOOK) && (AST_LIST_NEXT(sub, list) && !AST_LIST_NEXT(sub, list)->rtp)) {
06032             ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
06033          }
06034       }
06035       break;
06036    case SOFTKEY_RESUME:
06037       if (skinnydebug)
06038          ast_verb(1, "Received Softkey Event: Resume(%d/%d)\n", instance, callreference);
06039 
06040       if (sub) {
06041          if (sub->onhold) {
06042             skinny_unhold(sub);
06043             transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
06044          } else {
06045             skinny_hold(sub);
06046             transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_ONHOLD);
06047          }
06048       }
06049 
06050       break;
06051    case SOFTKEY_ANSWER:
06052       if (skinnydebug)
06053          ast_verb(1, "Received Softkey Event: Answer(%d/%d)\n", instance, callreference);
06054 
06055       transmit_ringer_mode(d, SKINNY_RING_OFF);
06056       transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
06057       if (l->hookstate == SKINNY_ONHOOK) {
06058          transmit_speaker_mode(d, SKINNY_SPEAKERON);
06059          l->hookstate = SKINNY_OFFHOOK;
06060       }
06061 
06062       if (sub && sub->outgoing) {
06063          /* We're answering a ringing call */
06064          ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
06065          transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
06066          transmit_activatecallplane(d, l);
06067          transmit_stop_tone(d, l->instance, sub->callid);
06068          transmit_callstate(d, sub->parent->instance, sub->callid, SKINNY_CONNECTED);
06069          transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
06070          start_rtp(sub);
06071          ast_setstate(sub->owner, AST_STATE_UP);
06072       }
06073       break;
06074    case SOFTKEY_INFO:
06075       if (skinnydebug)
06076          ast_verb(1, "Received Softkey Event: Info(%d/%d)\n", instance, callreference);
06077       break;
06078    case SOFTKEY_CONFRN:
06079       if (skinnydebug)
06080          ast_verb(1, "Received Softkey Event: Conference(%d/%d)\n", instance, callreference);
06081       /* XXX determine the best way to pull off a conference.  Meetme? */
06082       break;
06083    case SOFTKEY_PARK:
06084       {
06085       int extout;
06086       char message[32];
06087 
06088       if (skinnydebug)
06089          ast_verb(1, "Received Softkey Event: Park Call(%d/%d)\n", instance, callreference);
06090 
06091       if ((sub && sub->owner) && (sub->owner->_state ==  AST_STATE_UP)){
06092          c = sub->owner;
06093          if (ast_bridged_channel(c)) {
06094             if (!ast_masq_park_call(ast_bridged_channel(c), c, 0, &extout)) {
06095                snprintf(message, sizeof(message), "Call Parked at: %d", extout);
06096                transmit_displaynotify(d, message, 10);
06097             } else {
06098                transmit_displaynotify(d, "Call Park failed", 10);
06099             }
06100          } else {
06101             transmit_displaynotify(d, "Call Park not available", 10);
06102          }
06103       } else {
06104          transmit_displaynotify(d, "Call Park not available", 10);
06105       }
06106       break;
06107       }
06108    case SOFTKEY_JOIN:
06109       if (skinnydebug)
06110          ast_verb(1, "Received Softkey Event: Join(%d/%d)\n", instance, callreference);
06111       break;
06112    case SOFTKEY_MEETME:
06113       /* XXX How is this different from CONFRN? */
06114       if (skinnydebug)
06115          ast_verb(1, "Received Softkey Event: Meetme(%d/%d)\n", instance, callreference);
06116       break;
06117    case SOFTKEY_PICKUP:
06118       if (skinnydebug)
06119          ast_verb(1, "Received Softkey Event: Pickup(%d/%d)\n", instance, callreference);
06120       break;
06121    case SOFTKEY_GPICKUP:
06122       if (skinnydebug)
06123          ast_verb(1, "Received Softkey Event: Group Pickup(%d/%d)\n", instance, callreference);
06124       break;
06125    default:
06126       if (skinnydebug)
06127          ast_verb(1, "Received unknown Softkey Event: %d(%d/%d)\n", event, instance, callreference);
06128       break;
06129    }
06130 
06131    return 1;
06132 }
06133 
06134 static int handle_message(struct skinny_req *req, struct skinnysession *s)
06135 {
06136    int res = 0;
06137    struct skinny_speeddial *sd;
06138    struct skinny_line *l;
06139    struct skinny_device *d = s->device;
06140    
06141    if ((!s->device) && (letohl(req->e) != REGISTER_MESSAGE && letohl(req->e) != ALARM_MESSAGE)) {
06142       ast_log(LOG_WARNING, "Client sent message #%d without first registering.\n", req->e);
06143       ast_free(req);
06144       return 0;
06145    }
06146 
06147    SKINNY_DEVONLY(if (skinnydebug > 1) {
06148       ast_verb(4, "Received %s from %s\n", message2str(req->e), s->device->name);
06149    })
06150 
06151    switch(letohl(req->e)) {
06152    case KEEP_ALIVE_MESSAGE:
06153       res = handle_keep_alive_message(req, s);
06154       break;
06155    case REGISTER_MESSAGE:
06156       if (skinnydebug)
06157          ast_verb(1, "Device %s is attempting to register\n", req->data.reg.name);
06158 
06159       res = handle_register_message(req, s);
06160       break;
06161    case IP_PORT_MESSAGE:
06162       res = handle_ip_port_message(req, s);
06163       break;
06164    case KEYPAD_BUTTON_MESSAGE:
06165        {
06166       struct skinny_device *d = s->device;
06167       struct skinny_subchannel *sub;
06168       int lineInstance;
06169       int callReference;
06170 
06171       if (skinnydebug)
06172          ast_verb(1, "Collected digit: [%d]\n", letohl(req->data.keypad.button));
06173 
06174       lineInstance = letohl(req->data.keypad.lineInstance);
06175       callReference = letohl(req->data.keypad.callReference);
06176 
06177       if (lineInstance) {
06178          sub = find_subchannel_by_instance_reference(d, lineInstance, callReference);
06179       } else {
06180          sub = d->activeline->activesub;
06181       }
06182 
06183       if (sub && ((sub->owner && sub->owner->_state <  AST_STATE_UP) || sub->onhold)) {
06184          char dgt;
06185          int digit = letohl(req->data.keypad.button);
06186 
06187          if (digit == 14) {
06188             dgt = '*';
06189          } else if (digit == 15) {
06190             dgt = '#';
06191          } else if (digit >= 0 && digit <= 9) {
06192             dgt = '0' + digit;
06193          } else {
06194             /* digit=10-13 (A,B,C,D ?), or
06195             * digit is bad value
06196             *
06197             * probably should not end up here, but set
06198             * value for backward compatibility, and log
06199             * a warning.
06200             */
06201             dgt = '0' + digit;
06202             ast_log(LOG_WARNING, "Unsupported digit %d\n", digit);
06203          }
06204 
06205          d->exten[strlen(d->exten)] = dgt;
06206          d->exten[strlen(d->exten)+1] = '\0';
06207       } else
06208          res = handle_keypad_button_message(req, s);
06209       }
06210       break;
06211    case ENBLOC_CALL_MESSAGE:
06212       res = handle_enbloc_call_message(req, s);
06213       break;
06214    case STIMULUS_MESSAGE:
06215       res = handle_stimulus_message(req, s);
06216       break;
06217    case OFFHOOK_MESSAGE:
06218       res = handle_offhook_message(req, s);
06219       break;
06220    case ONHOOK_MESSAGE:
06221       res = handle_onhook_message(req, s);
06222       break;
06223    case CAPABILITIES_RES_MESSAGE:
06224       if (skinnydebug)
06225          ast_verb(1, "Received CapabilitiesRes\n");
06226 
06227       res = handle_capabilities_res_message(req, s);
06228       break;
06229    case SPEED_DIAL_STAT_REQ_MESSAGE:
06230       if (skinnydebug)
06231          ast_verb(1, "Received SpeedDialStatRequest\n");
06232       if ( (sd = find_speeddial_by_instance(s->device, letohl(req->data.speeddialreq.speedDialNumber), 0)) ) {
06233          transmit_speeddialstatres(d, sd);
06234       }
06235       break;
06236    case LINE_STATE_REQ_MESSAGE:
06237       if (skinnydebug)
06238          ast_verb(1, "Received LineStatRequest\n");
06239       if ((l = find_line_by_instance(d, letohl(req->data.line.lineNumber)))) {
06240          transmit_linestatres(d, l);
06241       }
06242       break;
06243    case TIME_DATE_REQ_MESSAGE:
06244       if (skinnydebug)
06245          ast_verb(1, "Received Time/Date Request\n");
06246 
06247       transmit_definetimedate(d);
06248       break;
06249    case BUTTON_TEMPLATE_REQ_MESSAGE:
06250       if (skinnydebug)
06251          ast_verb(1, "Buttontemplate requested\n");
06252 
06253       res = handle_button_template_req_message(req, s);
06254       break;
06255    case VERSION_REQ_MESSAGE:
06256       if (skinnydebug)
06257          ast_verb(1, "Version Request\n");
06258       transmit_versionres(d);
06259       break;
06260    case SERVER_REQUEST_MESSAGE:
06261       if (skinnydebug)
06262          ast_verb(1, "Received Server Request\n");
06263       transmit_serverres(d);
06264       break;
06265    case ALARM_MESSAGE:
06266       /* no response necessary */
06267       if (skinnydebug)
06268          ast_verb(1, "Received Alarm Message: %s\n", req->data.alarm.displayMessage);
06269       break;
06270    case OPEN_RECEIVE_CHANNEL_ACK_MESSAGE:
06271       if (skinnydebug)
06272          ast_verb(1, "Received Open Receive Channel Ack\n");
06273 
06274       res = handle_open_receive_channel_ack_message(req, s);
06275       break;
06276    case SOFT_KEY_SET_REQ_MESSAGE:
06277       if (skinnydebug)
06278          ast_verb(1, "Received SoftKeySetReq\n");
06279       transmit_softkeysetres(d);
06280       transmit_selectsoftkeys(d, 0, 0, KEYDEF_ONHOOK);
06281       break;
06282    case SOFT_KEY_EVENT_MESSAGE:
06283       res = handle_soft_key_event_message(req, s);
06284       break;
06285    case UNREGISTER_MESSAGE:
06286       if (skinnydebug)
06287          ast_verb(1, "Received Unregister Request\n");
06288 
06289       res = skinny_unregister(req, s);
06290       break;
06291    case SOFT_KEY_TEMPLATE_REQ_MESSAGE:
06292       if (skinnydebug)
06293          ast_verb(1, "Received SoftKey Template Request\n");
06294       transmit_softkeytemplateres(d);
06295       break;
06296    case HEADSET_STATUS_MESSAGE:
06297       /* XXX umm...okay?  Why do I care? */
06298       break;
06299    case REGISTER_AVAILABLE_LINES_MESSAGE:
06300       /* XXX I have no clue what this is for, but my phone was sending it, so... */
06301       break;
06302    default:
06303       if (skinnydebug)
06304          ast_verb(1, "RECEIVED UNKNOWN MESSAGE TYPE:  %x\n", letohl(req->e));
06305       break;
06306    }
06307    if (res >= 0 && req)
06308       ast_free(req);
06309    return res;
06310 }
06311 
06312 static void destroy_session(struct skinnysession *s)
06313 {
06314    struct skinnysession *cur;
06315    AST_LIST_LOCK(&sessions);
06316    AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, cur, list) {
06317       if (cur == s) {
06318          AST_LIST_REMOVE_CURRENT(list);
06319          if (s->fd > -1) 
06320             close(s->fd);
06321          
06322          if (!s->device)
06323             ast_atomic_fetchadd_int(&unauth_sessions, -1);
06324 
06325          ast_mutex_destroy(&s->lock);
06326          
06327          ast_free(s);
06328       } else {
06329          ast_log(LOG_WARNING, "Trying to delete nonexistent session %p?\n", s);
06330       }
06331    }
06332    AST_LIST_TRAVERSE_SAFE_END
06333    AST_LIST_UNLOCK(&sessions);
06334 }
06335 
06336 static int get_input(struct skinnysession *s)
06337 {
06338    int res;
06339    int dlen = 0;
06340    int timeout = keep_alive * 1100;
06341    time_t now;
06342    int *bufaddr;
06343    struct pollfd fds[1];
06344 
06345    if (!s->device) {
06346       if(time(&now) == -1) {
06347          ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
06348          return -1;
06349       }
06350 
06351       timeout = (auth_timeout - (now - s->start)) * 1000;
06352       if (timeout < 0) {
06353          /* we have timed out */
06354          if (skinnydebug)
06355             ast_verb(1, "Skinny Client failed to authenticate in %d seconds\n", auth_timeout);
06356          return -1;
06357       }
06358    }
06359 
06360    fds[0].fd = s->fd;
06361    fds[0].events = POLLIN;
06362    fds[0].revents = 0;
06363    res = ast_poll(fds, 1, timeout); /* If nothing has happen, client is dead */
06364                    /* we add 10% to the keep_alive to deal */
06365                    /* with network delays, etc */
06366    if (res < 0) {
06367       if (errno != EINTR) {
06368          ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
06369          return res;
06370       }
06371    } else if (res == 0) {
06372       if (skinnydebug) {
06373          if (s->device) {
06374             ast_verb(1, "Skinny Client was lost, unregistering\n");
06375          } else {
06376             ast_verb(1, "Skinny Client failed to authenticate in %d seconds\n", auth_timeout);
06377          }
06378       }
06379       skinny_unregister(NULL, s);
06380       return -1;
06381    }
06382            
06383    if (fds[0].revents) {
06384       ast_mutex_lock(&s->lock);
06385       memset(s->inbuf, 0, sizeof(s->inbuf));
06386       res = read(s->fd, s->inbuf, 4);
06387       if (res < 0) {
06388          ast_log(LOG_WARNING, "read() returned error: %s\n", strerror(errno));
06389 
06390          if (skinnydebug)
06391             ast_verb(1, "Skinny Client was lost, unregistering\n");
06392 
06393          skinny_unregister(NULL, s);
06394          ast_mutex_unlock(&s->lock);
06395          return res;
06396       } else if (res != 4) {
06397          ast_log(LOG_WARNING, "Skinny Client sent less data than expected.  Expected 4 but got %d.\n", res);
06398          ast_mutex_unlock(&s->lock);
06399          
06400          if (res == 0) {
06401             if (skinnydebug)
06402                ast_verb(1, "Skinny Client was lost, unregistering\n");
06403             skinny_unregister(NULL, s);
06404          }
06405 
06406          return -1;
06407       }
06408 
06409       bufaddr = (int *)s->inbuf;
06410       dlen = letohl(*bufaddr);
06411       if (dlen < 4) {
06412          ast_debug(1, "Skinny Client sent invalid data.\n");
06413          ast_mutex_unlock(&s->lock);
06414          return -1;
06415       }
06416       if (dlen+8 > sizeof(s->inbuf)) {
06417          dlen = sizeof(s->inbuf) - 8;
06418       }
06419       *bufaddr = htolel(dlen);
06420 
06421       res = read(s->fd, s->inbuf+4, dlen+4);
06422       ast_mutex_unlock(&s->lock);
06423       if (res < 0) {
06424          ast_log(LOG_WARNING, "read() returned error: %s\n", strerror(errno));
06425          return res;
06426       } else if (res != (dlen+4)) {
06427          ast_log(LOG_WARNING, "Skinny Client sent less data than expected.\n");
06428          return -1;
06429       }
06430       return res;
06431    }
06432    return 0;
06433 }
06434 
06435 static struct skinny_req *skinny_req_parse(struct skinnysession *s)
06436 {
06437    struct skinny_req *req;
06438    int *bufaddr;
06439 
06440    if (!(req = ast_calloc(1, SKINNY_MAX_PACKET)))
06441       return NULL;
06442 
06443    ast_mutex_lock(&s->lock);
06444    memcpy(req, s->inbuf, skinny_header_size);
06445    bufaddr = (int *)(s->inbuf);
06446    memcpy(&req->data, s->inbuf+skinny_header_size, letohl(*bufaddr)-4);
06447 
06448    ast_mutex_unlock(&s->lock);
06449 
06450    if (letohl(req->e) < 0) {
06451       ast_log(LOG_ERROR, "Event Message is NULL from socket %d, This is bad\n", s->fd);
06452       ast_free(req);
06453       return NULL;
06454    }
06455 
06456    return req;
06457 }
06458 
06459 static void *skinny_session(void *data)
06460 {
06461    int res;
06462    struct skinny_req *req;
06463    struct skinnysession *s = data;
06464 
06465    ast_verb(3, "Starting Skinny session from %s\n", ast_inet_ntoa(s->sin.sin_addr));
06466 
06467    for (;;) {
06468       res = get_input(s);
06469       if (res < 0) {
06470          break;
06471       }
06472 
06473       if (res > 0)
06474       {
06475          if (!(req = skinny_req_parse(s))) {
06476             destroy_session(s);
06477             return NULL;
06478          }
06479 
06480          res = handle_message(req, s);
06481          if (res < 0) {
06482             destroy_session(s);
06483             return NULL;
06484          }
06485       }
06486    }
06487    ast_debug(3, "Skinny Session returned: %s\n", strerror(errno));
06488 
06489    if (s) 
06490       destroy_session(s);
06491    
06492    return 0;
06493 }
06494 
06495 static void *accept_thread(void *ignore)
06496 {
06497    int as;
06498    struct sockaddr_in sin;
06499    socklen_t sinlen;
06500    struct skinnysession *s;
06501    struct protoent *p;
06502    int arg = 1;
06503 
06504    for (;;) {
06505       sinlen = sizeof(sin);
06506       as = accept(skinnysock, (struct sockaddr *)&sin, &sinlen);
06507       if (as < 0) {
06508          ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
06509          continue;
06510       }
06511 
06512       if (ast_atomic_fetchadd_int(&unauth_sessions, +1) >= auth_limit) {
06513          close(as);
06514          ast_atomic_fetchadd_int(&unauth_sessions, -1);
06515          continue;
06516       }
06517 
06518       p = getprotobyname("tcp");
06519       if(p) {
06520          if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
06521             ast_log(LOG_WARNING, "Failed to set Skinny tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
06522          }
06523       }
06524       if (!(s = ast_calloc(1, sizeof(struct skinnysession)))) {
06525          close(as);
06526          ast_atomic_fetchadd_int(&unauth_sessions, -1);
06527          continue;
06528       }
06529 
06530       memcpy(&s->sin, &sin, sizeof(sin));
06531       ast_mutex_init(&s->lock);
06532       s->fd = as;
06533 
06534       if(time(&s->start) == -1) {
06535          ast_log(LOG_ERROR, "error executing time(): %s; disconnecting client\n", strerror(errno));
06536          destroy_session(s);
06537          continue;
06538       }
06539 
06540       AST_LIST_LOCK(&sessions);
06541       AST_LIST_INSERT_HEAD(&sessions, s, list);
06542       AST_LIST_UNLOCK(&sessions);
06543 
06544       if (ast_pthread_create(&s->t, NULL, skinny_session, s)) {
06545          destroy_session(s);
06546       }
06547    }
06548    if (skinnydebug)
06549       ast_verb(1, "killing accept thread\n");
06550    close(as);
06551    return 0;
06552 }
06553 
06554 static void *do_monitor(void *data)
06555 {
06556    int res;
06557 
06558    /* This thread monitors all the interfaces which are not yet in use
06559       (and thus do not have a separate thread) indefinitely */
06560    /* From here on out, we die whenever asked */
06561    for(;;) {
06562       pthread_testcancel();
06563       /* Wait for sched or io */
06564       res = ast_sched_wait(sched);
06565       if ((res < 0) || (res > 1000)) {
06566          res = 1000;
06567       }
06568       res = ast_io_wait(io, res);
06569       ast_mutex_lock(&monlock);
06570       if (res >= 0) {
06571          ast_sched_runq(sched);
06572       }
06573       ast_mutex_unlock(&monlock);
06574    }
06575    /* Never reached */
06576    return NULL;
06577 
06578 }
06579 
06580 static int restart_monitor(void)
06581 {
06582    /* If we're supposed to be stopped -- stay stopped */
06583    if (monitor_thread == AST_PTHREADT_STOP)
06584       return 0;
06585 
06586    ast_mutex_lock(&monlock);
06587    if (monitor_thread == pthread_self()) {
06588       ast_mutex_unlock(&monlock);
06589       ast_log(LOG_WARNING, "Cannot kill myself\n");
06590       return -1;
06591    }
06592    if (monitor_thread != AST_PTHREADT_NULL) {
06593       /* Wake up the thread */
06594       pthread_kill(monitor_thread, SIGURG);
06595    } else {
06596       /* Start a new monitor */
06597       if (ast_pthread_create_background(&monitor_thread, NULL, do_monitor, NULL) < 0) {
06598          ast_mutex_unlock(&monlock);
06599          ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
06600          return -1;
06601       }
06602    }
06603    ast_mutex_unlock(&monlock);
06604    return 0;
06605 }
06606 
06607 static int skinny_devicestate(void *data)
06608 {
06609    struct skinny_line *l;
06610    char *tmp;
06611 
06612    tmp = ast_strdupa(data);
06613 
06614    l = find_line_by_name(tmp);
06615 
06616    return get_devicestate(l);
06617 }
06618 
06619 static struct ast_channel *skinny_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause)
06620 {
06621    struct skinny_line *l;
06622    struct ast_channel *tmpc = NULL;
06623    char tmp[256];
06624    char *dest = data;
06625 
06626    if (!(format &= AST_FORMAT_AUDIO_MASK)) {
06627       ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%s'\n", ast_getformatname_multiple(tmp, sizeof(tmp), format));
06628       return NULL;
06629    }
06630 
06631    ast_copy_string(tmp, dest, sizeof(tmp));
06632    if (ast_strlen_zero(tmp)) {
06633       ast_log(LOG_NOTICE, "Skinny channels require a device\n");
06634       return NULL;
06635    }
06636    l = find_line_by_name(tmp);
06637    if (!l) {
06638       ast_log(LOG_NOTICE, "No available lines on: %s\n", dest);
06639       return NULL;
06640    }
06641    ast_verb(3, "skinny_request(%s)\n", tmp);
06642    tmpc = skinny_new(l, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL);
06643    if (!tmpc) {
06644       ast_log(LOG_WARNING, "Unable to make channel for '%s'\n", tmp);
06645    }
06646    restart_monitor();
06647    return tmpc;
06648 }
06649 
06650  #define TYPE_GENERAL   1
06651  #define TYPE_DEF_DEVICE 2
06652  #define TYPE_DEF_LINE  4
06653  #define TYPE_DEVICE    8
06654  #define TYPE_LINE   16
06655  
06656  #define CLINE_OPTS  ((struct skinny_line_options *)item)
06657  #define CLINE    ((struct skinny_line *)item)
06658  #define CDEV_OPTS   ((struct skinny_device_options *)item)
06659  #define CDEV     ((struct skinny_device *)item)
06660  
06661  static void config_parse_variables(int type, void *item, struct ast_variable *vptr)
06662  {
06663    struct ast_variable *v;
06664    int lineInstance = 1;
06665    int speeddialInstance = 1;
06666    
06667    while(vptr) {
06668       v = vptr;
06669       vptr = vptr->next;
06670  
06671       if (type & (TYPE_GENERAL)) {
06672          char newcontexts[AST_MAX_CONTEXT];
06673          char oldcontexts[AST_MAX_CONTEXT];
06674          char *stringp, *context, *oldregcontext;
06675          if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) {
06676             v = v->next;
06677             continue;
06678          }
06679          if (!strcasecmp(v->name, "bindaddr")) {
06680             if (!(hp = ast_gethostbyname(v->value, &ahp))) {
06681                ast_log(LOG_WARNING, "Invalid address: %s\n", v->value);
06682             } else {
06683                memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr));
06684             }
06685             continue;
06686          } else if (!strcasecmp(v->name, "keepalive")) {
06687             keep_alive = atoi(v->value);
06688             continue;
06689          } else if (!strcasecmp(v->name, "authtimeout")) {
06690             int timeout = atoi(v->value);
06691 
06692             if (timeout < 1) {
06693                ast_log(LOG_WARNING, "Invalid authtimeout value '%s', using default value\n", v->value);
06694                auth_timeout = DEFAULT_AUTH_TIMEOUT;
06695             } else {
06696                auth_timeout = timeout;
06697             }
06698             continue;
06699          } else if (!strcasecmp(v->name, "authlimit")) {
06700             int limit = atoi(v->value);
06701 
06702             if (limit < 1) {
06703                ast_log(LOG_WARNING, "Invalid authlimit value '%s', using default value\n", v->value);
06704                auth_limit = DEFAULT_AUTH_LIMIT;
06705             } else {
06706                auth_limit = limit;
06707             }
06708             continue;
06709          } else if (!strcasecmp(v->name, "regcontext")) {
06710             ast_copy_string(newcontexts, v->value, sizeof(newcontexts));
06711             stringp = newcontexts;
06712             /* Initialize copy of current global_regcontext for later use in removing stale contexts */
06713             ast_copy_string(oldcontexts, regcontext, sizeof(oldcontexts));
06714             oldregcontext = oldcontexts;
06715             /* Let's remove any contexts that are no longer defined in regcontext */
06716             cleanup_stale_contexts(stringp, oldregcontext);
06717             /* Create contexts if they don't exist already */
06718             while ((context = strsep(&stringp, "&"))) {
06719                ast_copy_string(used_context, context, sizeof(used_context));
06720                ast_context_find_or_create(NULL, NULL, context, "Skinny");
06721             }
06722             ast_copy_string(regcontext, v->value, sizeof(regcontext));
06723             continue;
06724          } else if (!strcasecmp(v->name, "dateformat")) {
06725             memcpy(date_format, v->value, sizeof(date_format));
06726             continue;
06727          } else if (!strcasecmp(v->name, "tos")) {
06728             if (ast_str2tos(v->value, &qos.tos))
06729                ast_log(LOG_WARNING, "Invalid tos value at line %d, refer to QoS documentation\n", v->lineno);
06730             continue;
06731          } else if (!strcasecmp(v->name, "tos_audio")) {
06732             if (ast_str2tos(v->value, &qos.tos_audio))
06733                ast_log(LOG_WARNING, "Invalid tos_audio value at line %d, refer to QoS documentation\n", v->lineno);
06734             continue;
06735          } else if (!strcasecmp(v->name, "tos_video")) {
06736             if (ast_str2tos(v->value, &qos.tos_video))
06737                ast_log(LOG_WARNING, "Invalid tos_video value at line %d, refer to QoS documentation\n", v->lineno);
06738             continue;
06739          } else if (!strcasecmp(v->name, "cos")) {
06740             if (ast_str2cos(v->value, &qos.cos))
06741                ast_log(LOG_WARNING, "Invalid cos value at line %d, refer to QoS documentation\n", v->lineno);
06742             continue;
06743          } else if (!strcasecmp(v->name, "cos_audio")) {
06744             if (ast_str2cos(v->value, &qos.cos_audio))
06745                ast_log(LOG_WARNING, "Invalid cos_audio value at line %d, refer to QoS documentation\n", v->lineno);
06746             continue;
06747          } else if (!strcasecmp(v->name, "cos_video")) {
06748             if (ast_str2cos(v->value, &qos.cos_video))
06749                ast_log(LOG_WARNING, "Invalid cos_video value at line %d, refer to QoS documentation\n", v->lineno);
06750             continue;
06751          } else if (!strcasecmp(v->name, "bindport")) {
06752             if (sscanf(v->value, "%5d", &ourport) == 1) {
06753                bindaddr.sin_port = htons(ourport);
06754             } else {
06755                ast_log(LOG_WARNING, "Invalid bindport '%s' at line %d of %s\n", v->value, v->lineno, config);
06756             }
06757             continue;
06758          } else if (!strcasecmp(v->name, "allow")) {
06759             ast_parse_allow_disallow(&default_prefs, &default_capability, v->value, 1);
06760             continue;
06761          } else if (!strcasecmp(v->name, "disallow")) {
06762             ast_parse_allow_disallow(&default_prefs, &default_capability, v->value, 0);
06763             continue;
06764          } 
06765       }
06766  
06767       if (!strcasecmp(v->name, "transfer")) {
06768          if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06769             CDEV_OPTS->transfer = ast_true(v->value);
06770             continue;
06771          } else if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06772             CLINE_OPTS->transfer = ast_true(v->value);
06773             continue;
06774          }
06775       } else if (!strcasecmp(v->name, "callwaiting")) {
06776          if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06777             CDEV_OPTS->callwaiting = ast_true(v->value);
06778             continue;
06779          } else if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06780             CLINE_OPTS->callwaiting = ast_true(v->value);
06781             continue;
06782          }
06783       } else if (!strcasecmp(v->name, "directmedia") || !strcasecmp(v->name, "canreinvite")) {
06784          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06785             CLINE_OPTS->directmedia = ast_true(v->value);
06786             continue;
06787          }
06788       } else if (!strcasecmp(v->name, "nat")) {
06789          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06790             CLINE_OPTS->nat = ast_true(v->value);
06791             continue;
06792          }
06793       } else if (!strcasecmp(v->name, "context")) {
06794          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06795             ast_copy_string(CLINE_OPTS->context, v->value, sizeof(CLINE_OPTS->context));
06796             continue;
06797          }
06798       }else if (!strcasecmp(v->name, "vmexten")) {
06799          if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06800             ast_copy_string(CDEV_OPTS->vmexten, v->value, sizeof(CDEV_OPTS->vmexten));
06801             continue;
06802          } else if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06803             ast_copy_string(CLINE_OPTS->vmexten, v->value, sizeof(CLINE_OPTS->vmexten));
06804             continue;
06805          }
06806       } else if (!strcasecmp(v->name, "mwiblink")) {
06807          if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06808             CDEV_OPTS->mwiblink = ast_true(v->value);
06809             continue;
06810          } else if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06811             CLINE_OPTS->mwiblink = ast_true(v->value);
06812             continue;
06813          }
06814       } else if (!strcasecmp(v->name, "linelabel")) {
06815          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06816             ast_copy_string(CLINE_OPTS->label, v->value, sizeof(CLINE_OPTS->label));
06817             continue;
06818          }
06819       } else if (!strcasecmp(v->name, "callerid")) {
06820          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06821             if (!strcasecmp(v->value, "asreceived")) {
06822                CLINE_OPTS->cid_num[0] = '\0';
06823                CLINE_OPTS->cid_name[0] = '\0';
06824             } else {
06825                ast_callerid_split(v->value, CLINE_OPTS->cid_name, sizeof(CLINE_OPTS->cid_name), CLINE_OPTS->cid_num, sizeof(CLINE_OPTS->cid_num));
06826             }
06827             continue;
06828          }
06829       } else if (!strcasecmp(v->name, "amaflags")) {
06830          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06831             int tempamaflags = ast_cdr_amaflags2int(v->value);
06832             if (tempamaflags < 0) {
06833                ast_log(LOG_WARNING, "Invalid AMA flags: %s at line %d\n", v->value, v->lineno);
06834             } else {
06835                CLINE_OPTS->amaflags = tempamaflags;
06836             }
06837             continue;
06838          }
06839       } else if (!strcasecmp(v->name, "regexten")) {
06840          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06841             ast_copy_string(CLINE_OPTS->regexten, v->value, sizeof(CLINE_OPTS->regexten));
06842             continue;
06843          }
06844       } else if (!strcasecmp(v->name, "language")) {
06845          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06846             ast_copy_string(CLINE_OPTS->language, v->value, sizeof(CLINE_OPTS->language));
06847             continue;
06848          }
06849       } else if (!strcasecmp(v->name, "accountcode")) {
06850          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06851             ast_copy_string(CLINE_OPTS->accountcode, v->value, sizeof(CLINE_OPTS->accountcode));
06852             continue;
06853          }
06854       } else if (!strcasecmp(v->name, "mohinterpret") || !strcasecmp(v->name, "musiconhold")) {
06855          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06856             ast_copy_string(CLINE_OPTS->mohinterpret, v->value, sizeof(CLINE_OPTS->mohinterpret));
06857             continue;
06858          }
06859       } else if (!strcasecmp(v->name, "mohsuggest")) {
06860          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06861             ast_copy_string(CLINE_OPTS->mohsuggest, v->value, sizeof(CLINE_OPTS->mohsuggest));
06862             continue;
06863          }
06864       } else if (!strcasecmp(v->name, "callgroup")) {
06865          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06866             CLINE_OPTS->callgroup = ast_get_group(v->value);
06867             continue;
06868          }
06869       } else if (!strcasecmp(v->name, "pickupgroup")) {
06870          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06871             CLINE_OPTS->pickupgroup = ast_get_group(v->value);
06872             continue;
06873          }
06874       } else if (!strcasecmp(v->name, "immediate")) {
06875          if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE | TYPE_DEF_LINE | TYPE_LINE)) {
06876             CLINE_OPTS->immediate = ast_true(v->value);
06877             continue;
06878          }
06879       } else if (!strcasecmp(v->name, "cancallforward")) {
06880          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06881             CLINE_OPTS->cancallforward = ast_true(v->value);
06882             continue;
06883          }
06884       } else if (!strcasecmp(v->name, "mailbox")) {
06885          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06886             ast_copy_string(CLINE_OPTS->mailbox, v->value, sizeof(CLINE_OPTS->mailbox));
06887             continue;
06888          }
06889       } else if ( !strcasecmp(v->name, "parkinglot")) {
06890          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06891             ast_copy_string(CLINE_OPTS->parkinglot, v->value, sizeof(CLINE_OPTS->parkinglot));
06892             continue;
06893          }
06894       } else if (!strcasecmp(v->name, "hasvoicemail")) {
06895          if (type & (TYPE_LINE)) {
06896             if (ast_true(v->value) && ast_strlen_zero(CLINE->mailbox)) {
06897                ast_copy_string(CLINE->mailbox, CLINE->name, sizeof(CLINE->mailbox));
06898             }
06899             continue;
06900          }
06901       } else if (!strcasecmp(v->name, "callreturn")) {
06902          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06903             CLINE_OPTS->callreturn = ast_true(v->value);
06904             continue;
06905          }
06906       } else if (!strcasecmp(v->name, "threewaycalling")) {
06907          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06908             CLINE_OPTS->threewaycalling = ast_true(v->value);
06909             continue;
06910          }
06911       } else if (!strcasecmp(v->name, "setvar")) {
06912          if (type & (TYPE_LINE)) {
06913             CLINE->chanvars = add_var(v->value, CLINE->chanvars);
06914             continue;
06915          }
06916       } else if (!strcasecmp(v->name, "earlyrtp")) {
06917          if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06918             CDEV_OPTS->earlyrtp = ast_true(v->value);
06919             continue;
06920          }
06921       } else if (!strcasecmp(v->name, "host")) {
06922          if (type & (TYPE_DEVICE)) {
06923             struct ast_sockaddr CDEV_addr_tmp;
06924 
06925             if (ast_get_ip(&CDEV_addr_tmp, v->value)) {
06926                ast_log(LOG_WARNING, "Bad IP '%s' at line %d.\n", v->value, v->lineno);
06927             }
06928             ast_sockaddr_to_sin(&CDEV_addr_tmp,
06929                       &CDEV->addr);
06930             continue;
06931          }
06932       } else if (!strcasecmp(v->name, "port")) {
06933          if (type & (TYPE_DEF_DEVICE)) {
06934             CDEV->addr.sin_port = htons(atoi(v->value));
06935             continue;
06936          }
06937       } else if (!strcasecmp(v->name, "device")) {
06938          if (type & (TYPE_DEVICE)) {
06939             ast_copy_string(CDEV_OPTS->id, v->value, sizeof(CDEV_OPTS->id));
06940             continue;
06941          }
06942       } else if (!strcasecmp(v->name, "permit") || !strcasecmp(v->name, "deny")) {
06943          if (type & (TYPE_DEVICE)) {
06944             CDEV->ha = ast_append_ha(v->name, v->value, CDEV->ha, NULL);
06945             continue;
06946          }
06947       } else if (!strcasecmp(v->name, "allow")) {
06948          if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06949             ast_parse_allow_disallow(&CDEV_OPTS->confprefs, &CDEV_OPTS->confcapability, v->value, 1);
06950             continue;
06951          }
06952          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06953             ast_parse_allow_disallow(&CLINE_OPTS->confprefs, &CLINE_OPTS->confcapability, v->value, 1);
06954             continue;
06955          }
06956       } else if (!strcasecmp(v->name, "disallow")) {
06957          if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06958             ast_parse_allow_disallow(&CDEV_OPTS->confprefs, &CDEV_OPTS->confcapability, v->value, 0);
06959             continue;
06960          }
06961          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06962             ast_parse_allow_disallow(&CLINE_OPTS->confprefs, &CLINE_OPTS->confcapability, v->value, 0);
06963             continue;
06964          }
06965       } else if (!strcasecmp(v->name, "version")) {
06966          if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06967             ast_copy_string(CDEV_OPTS->version_id, v->value, sizeof(CDEV_OPTS->version_id));
06968             continue;
06969          }
06970       } else if (!strcasecmp(v->name, "line")) {
06971          if (type & (TYPE_DEVICE)) {
06972             struct skinny_line *l;
06973             AST_LIST_TRAVERSE(&lines, l, all) {
06974                if (!strcasecmp(v->value, l->name) && !l->prune) {
06975 
06976                   /* FIXME: temp solution about line conflicts */
06977                   struct skinny_device *d;
06978                   struct skinny_line *l2;
06979                   int lineinuse = 0;
06980                   AST_LIST_TRAVERSE(&devices, d, list) {
06981                      AST_LIST_TRAVERSE(&d->lines, l2, list) {
06982                         if (l2 == l && strcasecmp(d->id, CDEV->id)) {
06983                            ast_log(LOG_WARNING, "Line %s already used by %s. Not connecting to %s.\n", l->name, d->name, CDEV->name);
06984                            lineinuse++;
06985                         }
06986                      }
06987                   }
06988                   if (!lineinuse) {
06989                      if (!AST_LIST_FIRST(&CDEV->lines)) {
06990                         CDEV->activeline = l;
06991                      }
06992                      lineInstance++;
06993                      AST_LIST_INSERT_HEAD(&CDEV->lines, l, list);
06994                   }
06995                   break;
06996                }
06997             }
06998             continue;
06999          }
07000       } else if (!strcasecmp(v->name, "speeddial")) {
07001          if (type & (TYPE_DEVICE)) {
07002             struct skinny_speeddial *sd;
07003             if (!(sd = ast_calloc(1, sizeof(*sd)))) {
07004                ast_log(LOG_WARNING, "Unable to allocate memory for speeddial %s. Ignoring speeddial.\n", v->name);
07005                continue;
07006             } else {
07007                char buf[256];
07008                char *stringp = buf, *exten, *context, *label;
07009                   ast_copy_string(buf, v->value, sizeof(buf));
07010                exten = strsep(&stringp, ",");
07011                if ((context = strchr(exten, '@'))) {
07012                   *context++ = '\0';
07013                }
07014                label = stringp;
07015                ast_mutex_init(&sd->lock);
07016                ast_copy_string(sd->exten, exten, sizeof(sd->exten));
07017                if (!ast_strlen_zero(context)) {
07018                   sd->isHint = 1;
07019                   sd->instance = lineInstance++;
07020                   ast_copy_string(sd->context, context, sizeof(sd->context));
07021                } else {
07022                   sd->isHint = 0;
07023                   sd->instance = speeddialInstance++;
07024                   sd->context[0] = '\0';
07025                }
07026                ast_copy_string(sd->label, S_OR(label, exten), sizeof(sd->label));
07027                sd->parent = CDEV;
07028                AST_LIST_INSERT_HEAD(&CDEV->speeddials, sd, list);
07029             }
07030             continue;
07031          }
07032       } else if (!strcasecmp(v->name, "addon")) {
07033          if (type & (TYPE_DEVICE)) {
07034             struct skinny_addon *a;
07035             if (!(a = ast_calloc(1, sizeof(*a)))) {
07036                ast_log(LOG_WARNING, "Unable to allocate memory for addon %s. Ignoring addon.\n", v->name);
07037                continue;
07038             } else {
07039                ast_mutex_init(&a->lock);
07040                ast_copy_string(a->type, v->value, sizeof(a->type));
07041                AST_LIST_INSERT_HEAD(&CDEV->addons, a, list);
07042             }
07043             continue;
07044          }
07045 
07046       } else {
07047          ast_log(LOG_WARNING, "Don't know keyword '%s' at line %d\n", v->name, v->lineno);
07048          continue;
07049       }
07050       ast_log(LOG_WARNING, "Invalid category used: %s at line %d\n", v->name, v->lineno);
07051    }
07052  }
07053  
07054  static struct skinny_line *config_line(const char *lname, struct ast_variable *v)
07055  {
07056    struct skinny_line *l, *temp;
07057    int update = 0;
07058  
07059    ast_log(LOG_NOTICE, "Configuring skinny line %s.\n", lname);
07060 
07061    /* We find the old line and remove it just before the new
07062       line is created */
07063    AST_LIST_LOCK(&lines);
07064    AST_LIST_TRAVERSE(&lines, temp, all) {
07065       if (!strcasecmp(lname, temp->name) && temp->prune) {
07066          update = 1;
07067          break;
07068       }
07069    }
07070 
07071    if (!(l=ast_calloc(1, sizeof(*l)))) {
07072       ast_verb(1, "Unable to allocate memory for line %s.\n", lname);
07073       AST_LIST_UNLOCK(&lines);
07074       return NULL;
07075    }
07076 
07077    memcpy(l, default_line, sizeof(*default_line));
07078    ast_mutex_init(&l->lock);
07079    ast_copy_string(l->name, lname, sizeof(l->name));
07080    AST_LIST_INSERT_TAIL(&lines, l, all);
07081 
07082    ast_mutex_lock(&l->lock);
07083    AST_LIST_UNLOCK(&lines);
07084 
07085    config_parse_variables(TYPE_LINE, l, v);
07086          
07087    if (!ast_strlen_zero(l->mailbox)) {
07088       char *cfg_mailbox, *cfg_context;
07089       cfg_context = cfg_mailbox = ast_strdupa(l->mailbox);
07090       ast_verb(3, "Setting mailbox '%s' on line %s\n", cfg_mailbox, l->name);
07091       strsep(&cfg_context, "@");
07092       if (ast_strlen_zero(cfg_context))
07093           cfg_context = "default";
07094       l->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, "skinny MWI subsciption", l,
07095          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, cfg_mailbox,
07096          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, cfg_context,
07097          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
07098          AST_EVENT_IE_END);
07099    }
07100  
07101    ast_mutex_unlock(&l->lock);
07102    
07103    /* We do not want to unlink or free the line yet, it needs
07104       to be available to detect a device reconfig when we load the
07105       devices.  Old lines will be pruned after the reload completes */
07106 
07107    ast_verb(3, "%s config for line '%s'\n", update ? "Updated" : (skinnyreload ? "Reloaded" : "Created"), l->name);
07108 
07109    return l;
07110  }
07111  
07112  static struct skinny_device *config_device(const char *dname, struct ast_variable *v)
07113  {
07114    struct skinny_device *d, *temp;
07115    struct skinny_line *l, *ltemp;
07116    struct skinny_subchannel *sub;
07117    int update = 0;
07118  
07119    ast_log(LOG_NOTICE, "Configuring skinny device %s.\n", dname);
07120 
07121    AST_LIST_LOCK(&devices);
07122    AST_LIST_TRAVERSE(&devices, temp, list) {
07123       if (!strcasecmp(dname, temp->name) && temp->prune) {
07124          update = 1;
07125          break;
07126       }
07127    }
07128 
07129    if (!(d = ast_calloc(1, sizeof(*d)))) {
07130       ast_verb(1, "Unable to allocate memory for device %s.\n", dname);
07131       AST_LIST_UNLOCK(&devices);
07132       return NULL;
07133    }
07134    memcpy(d, default_device, sizeof(*default_device));
07135    ast_mutex_init(&d->lock);
07136    ast_copy_string(d->name, dname, sizeof(d->name));
07137    AST_LIST_INSERT_TAIL(&devices, d, list);
07138 
07139    ast_mutex_lock(&d->lock);
07140    AST_LIST_UNLOCK(&devices);
07141  
07142    config_parse_variables(TYPE_DEVICE, d, v);
07143  
07144    if (!AST_LIST_FIRST(&d->lines)) {
07145       ast_log(LOG_ERROR, "A Skinny device must have at least one line!\n");
07146       ast_mutex_unlock(&d->lock);
07147       return NULL;
07148    }
07149    if (/*d->addr.sin_addr.s_addr && */!ntohs(d->addr.sin_port)) {
07150       d->addr.sin_port = htons(DEFAULT_SKINNY_PORT);
07151    }
07152  
07153    if (skinnyreload){
07154       AST_LIST_LOCK(&devices);
07155       AST_LIST_TRAVERSE(&devices, temp, list) {
07156          if (strcasecmp(d->id, temp->id) || !temp->prune || !temp->session) {
07157             continue;
07158          }
07159          ast_mutex_lock(&d->lock);
07160          d->session = temp->session;
07161          d->session->device = d;
07162 
07163          AST_LIST_LOCK(&d->lines);
07164          AST_LIST_TRAVERSE(&d->lines, l, list){
07165             l->device = d; 
07166 
07167             AST_LIST_LOCK(&temp->lines);
07168             AST_LIST_TRAVERSE(&temp->lines, ltemp, list) {
07169                if (strcasecmp(l->name, ltemp->name)) {
07170                   continue;
07171                }
07172                ast_mutex_lock(&ltemp->lock);
07173                l->instance = ltemp->instance;
07174                l->hookstate = ltemp->hookstate;
07175                if (!AST_LIST_EMPTY(&ltemp->sub)) {
07176                   ast_mutex_lock(&l->lock);
07177                   l->sub = ltemp->sub;
07178                   AST_LIST_TRAVERSE(&l->sub, sub, list) {
07179                      sub->parent = l;
07180                   }
07181                   ast_mutex_unlock(&l->lock);
07182                }
07183                ast_mutex_unlock(&ltemp->lock);
07184             }
07185             AST_LIST_UNLOCK(&temp->lines);
07186          }
07187          AST_LIST_UNLOCK(&d->lines);
07188          ast_mutex_unlock(&d->lock);
07189       }
07190       AST_LIST_UNLOCK(&devices);
07191    }
07192 
07193    ast_mutex_unlock(&d->lock);
07194 
07195    ast_verb(3, "%s config for device '%s'\n", update ? "Updated" : (skinnyreload ? "Reloaded" : "Created"), d->name);
07196    
07197    return d;
07198 
07199  }
07200  
07201  static int config_load(void)
07202  {
07203    int on = 1;
07204    struct ast_config *cfg;
07205    char *cat;
07206    int oldport = ntohs(bindaddr.sin_port);
07207    struct ast_flags config_flags = { 0 };
07208    
07209    ast_log(LOG_NOTICE, "Configuring skinny from %s\n", config);
07210   
07211    if (gethostname(ourhost, sizeof(ourhost))) {
07212       ast_log(LOG_WARNING, "Unable to get hostname, Skinny disabled.\n");
07213       return 0;
07214    }
07215    cfg = ast_config_load(config, config_flags);
07216   
07217    /* We *must* have a config file otherwise stop immediately */
07218    if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
07219       ast_log(LOG_NOTICE, "Unable to load config %s, Skinny disabled.\n", config);
07220       return -1;
07221    }
07222    memset(&bindaddr, 0, sizeof(bindaddr));
07223    memset(&default_prefs, 0, sizeof(default_prefs));
07224 
07225    /* Copy the default jb config over global_jbconf */
07226    memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
07227 
07228    /* load the general section */
07229    cat = ast_category_browse(cfg, "general");
07230    config_parse_variables(TYPE_GENERAL, NULL, ast_variable_browse(cfg, "general"));
07231 
07232    if (ntohl(bindaddr.sin_addr.s_addr)) {
07233       __ourip = bindaddr.sin_addr;
07234    } else {
07235       hp = ast_gethostbyname(ourhost, &ahp);
07236       if (!hp) {
07237          ast_log(LOG_WARNING, "Unable to get our IP address, Skinny disabled\n");
07238          ast_config_destroy(cfg);
07239          return 0;
07240       }
07241       memcpy(&__ourip, hp->h_addr, sizeof(__ourip));
07242    }
07243    if (!ntohs(bindaddr.sin_port)) {
07244       bindaddr.sin_port = htons(DEFAULT_SKINNY_PORT);
07245    }
07246    bindaddr.sin_family = AF_INET;
07247 
07248    /* load the lines sections */
07249    default_line->confcapability = default_capability;
07250    default_line->confprefs = default_prefs;
07251    config_parse_variables(TYPE_DEF_LINE, default_line, ast_variable_browse(cfg, "lines"));
07252    cat = ast_category_browse(cfg, "lines");
07253    while (cat && strcasecmp(cat, "general") && strcasecmp(cat, "devices")) {
07254       config_line(cat, ast_variable_browse(cfg, cat));
07255       cat = ast_category_browse(cfg, cat);
07256    }
07257       
07258    /* load the devices sections */
07259    default_device->confcapability = default_capability;
07260    default_device->confprefs = default_prefs;
07261    config_parse_variables(TYPE_DEF_DEVICE, default_device, ast_variable_browse(cfg, "devices"));
07262    cat = ast_category_browse(cfg, "devices");
07263    while (cat && strcasecmp(cat, "general") && strcasecmp(cat, "lines")) {
07264       config_device(cat, ast_variable_browse(cfg, cat));
07265       cat = ast_category_browse(cfg, cat);
07266    }
07267 
07268    ast_mutex_lock(&netlock);
07269    if ((skinnysock > -1) && (ntohs(bindaddr.sin_port) != oldport)) {
07270       close(skinnysock);
07271       skinnysock = -1;
07272    }
07273    if (skinnysock < 0) {
07274       skinnysock = socket(AF_INET, SOCK_STREAM, 0);
07275       if(setsockopt(skinnysock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
07276          ast_log(LOG_ERROR, "Set Socket Options failed: errno %d, %s\n", errno, strerror(errno));
07277          ast_config_destroy(cfg);
07278          ast_mutex_unlock(&netlock);
07279          return 0;
07280       }
07281       if (skinnysock < 0) {
07282          ast_log(LOG_WARNING, "Unable to create Skinny socket: %s\n", strerror(errno));
07283       } else {
07284          if (bind(skinnysock, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) {
07285             ast_log(LOG_WARNING, "Failed to bind to %s:%d: %s\n",
07286                   ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port),
07287                      strerror(errno));
07288             close(skinnysock);
07289             skinnysock = -1;
07290             ast_config_destroy(cfg);
07291             ast_mutex_unlock(&netlock);
07292             return 0;
07293          }
07294          if (listen(skinnysock, DEFAULT_SKINNY_BACKLOG)) {
07295                ast_log(LOG_WARNING, "Failed to start listening to %s:%d: %s\n",
07296                   ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port),
07297                      strerror(errno));
07298                close(skinnysock);
07299                skinnysock = -1;
07300                ast_config_destroy(cfg);
07301                ast_mutex_unlock(&netlock);
07302                return 0;
07303          }
07304          ast_verb(2, "Skinny listening on %s:%d\n",
07305                ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port));
07306          ast_netsock_set_qos(skinnysock, qos.tos, qos.cos, "Skinny");
07307          ast_pthread_create_background(&accept_t, NULL, accept_thread, NULL);
07308       }
07309    }
07310    ast_mutex_unlock(&netlock);
07311    ast_config_destroy(cfg);
07312    return 1;
07313 }
07314 
07315 static void delete_devices(void)
07316 {
07317    struct skinny_device *d;
07318    struct skinny_line *l;
07319    struct skinny_speeddial *sd;
07320    struct skinny_addon *a;
07321 
07322    AST_LIST_LOCK(&devices);
07323    AST_LIST_LOCK(&lines);
07324 
07325    /* Delete all devices */
07326    while ((d = AST_LIST_REMOVE_HEAD(&devices, list))) {
07327       /* Delete all lines for this device */
07328       while ((l = AST_LIST_REMOVE_HEAD(&d->lines, list))) {
07329          AST_LIST_REMOVE(&lines, l, all);
07330          free(l);
07331       }
07332       /* Delete all speeddials for this device */
07333       while ((sd = AST_LIST_REMOVE_HEAD(&d->speeddials, list))) {
07334          free(sd);
07335       }
07336       /* Delete all addons for this device */
07337       while ((a = AST_LIST_REMOVE_HEAD(&d->addons, list))) {
07338          free(a);
07339       } 
07340       free(d);
07341    }
07342    AST_LIST_UNLOCK(&lines);
07343    AST_LIST_UNLOCK(&devices);
07344 }
07345 
07346 int skinny_reload(void)
07347 {
07348    struct skinny_device *d;
07349    struct skinny_line *l;
07350    struct skinny_speeddial *sd;
07351    struct skinny_addon *a;
07352    struct skinny_req *req;
07353 
07354    if (skinnyreload) {
07355       ast_verb(3, "Chan_skinny is already reloading.\n");
07356       return 0;
07357    }
07358 
07359    skinnyreload = 1;
07360 
07361    /* Mark all devices and lines as candidates to be pruned */
07362    AST_LIST_LOCK(&devices);
07363    AST_LIST_TRAVERSE(&devices, d, list) {
07364       d->prune = 1;
07365    }
07366    AST_LIST_UNLOCK(&devices);
07367 
07368    AST_LIST_LOCK(&lines);
07369    AST_LIST_TRAVERSE(&lines, l, all) {
07370       l->prune = 1;
07371    }
07372    AST_LIST_UNLOCK(&lines);
07373 
07374         config_load();
07375 
07376    /* Remove any devices that no longer exist in the config */
07377    AST_LIST_LOCK(&devices);
07378    AST_LIST_TRAVERSE_SAFE_BEGIN(&devices, d, list) {
07379       if (!d->prune) {
07380          continue;
07381       }
07382       ast_verb(3, "Removing device '%s'\n", d->name);
07383       /* Delete all lines for this device. 
07384          We do not want to free the line here, that
07385          will happen below. */
07386       while ((l = AST_LIST_REMOVE_HEAD(&d->lines, list))) {
07387       }
07388       /* Delete all speeddials for this device */
07389       while ((sd = AST_LIST_REMOVE_HEAD(&d->speeddials, list))) {
07390          free(sd);
07391       }
07392       /* Delete all addons for this device */
07393       while ((a = AST_LIST_REMOVE_HEAD(&d->addons, list))) {
07394          free(a);
07395       }
07396       AST_LIST_REMOVE_CURRENT(list);
07397       free(d);
07398    }
07399    AST_LIST_TRAVERSE_SAFE_END;
07400    AST_LIST_UNLOCK(&devices);
07401 
07402    AST_LIST_LOCK(&lines);  
07403    AST_LIST_TRAVERSE_SAFE_BEGIN(&lines, l, all) {
07404       if (l->prune) {
07405          AST_LIST_REMOVE_CURRENT(all);
07406          free(l);
07407       }
07408    }
07409    AST_LIST_TRAVERSE_SAFE_END;
07410    AST_LIST_UNLOCK(&lines);  
07411 
07412    AST_LIST_TRAVERSE(&devices, d, list) {
07413       /* Do a soft reset to re-register the devices after
07414          cleaning up the removed devices and lines */
07415       if (d->session) {
07416          ast_verb(3, "Restarting device '%s'\n", d->name);
07417          if ((req = req_alloc(sizeof(struct reset_message), RESET_MESSAGE))) {
07418             req->data.reset.resetType = 2;
07419             transmit_response(d, req);
07420          }
07421       }
07422    }
07423    
07424    skinnyreload = 0;
07425         return 0;
07426 }
07427 
07428 static int load_module(void)
07429 {
07430    int res = 0;
07431 
07432    for (; res < ARRAY_LEN(soft_key_template_default); res++) {
07433       soft_key_template_default[res].softKeyEvent = htolel(soft_key_template_default[res].softKeyEvent);
07434    }
07435    /* load and parse config */
07436    res = config_load();
07437    if (res == -1) {
07438       return AST_MODULE_LOAD_DECLINE;
07439    }
07440 
07441    /* Make sure we can register our skinny channel type */
07442    if (ast_channel_register(&skinny_tech)) {
07443       ast_log(LOG_ERROR, "Unable to register channel class 'Skinny'\n");
07444       return -1;
07445    }
07446 
07447    ast_rtp_glue_register(&skinny_rtp_glue);
07448    ast_cli_register_multiple(cli_skinny, ARRAY_LEN(cli_skinny));
07449 
07450    ast_manager_register_xml("SKINNYdevices", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_devices);
07451    ast_manager_register_xml("SKINNYshowdevice", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_device);
07452    ast_manager_register_xml("SKINNYlines", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_lines);
07453    ast_manager_register_xml("SKINNYshowline", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_line);
07454 
07455    sched = sched_context_create();
07456    if (!sched) {
07457       ast_log(LOG_WARNING, "Unable to create schedule context\n");
07458    }
07459    io = io_context_create();
07460    if (!io) {
07461       ast_log(LOG_WARNING, "Unable to create I/O context\n");
07462    }
07463    /* And start the monitor for the first time */
07464    restart_monitor();
07465 
07466    return AST_MODULE_LOAD_SUCCESS;
07467 }
07468 
07469 static int unload_module(void)
07470 {
07471    struct skinnysession *s;
07472    struct skinny_device *d;
07473    struct skinny_line *l;
07474    struct skinny_subchannel *sub;
07475    struct ast_context *con;
07476 
07477    ast_rtp_glue_unregister(&skinny_rtp_glue);
07478    ast_channel_unregister(&skinny_tech);
07479    ast_cli_unregister_multiple(cli_skinny, ARRAY_LEN(cli_skinny));
07480 
07481    ast_manager_unregister("SKINNYdevices");
07482    ast_manager_unregister("SKINNYshowdevice");
07483    ast_manager_unregister("SKINNYlines");
07484    ast_manager_unregister("SKINNYshowline");
07485    
07486    AST_LIST_LOCK(&sessions);
07487    /* Destroy all the interfaces and free their memory */
07488    while((s = AST_LIST_REMOVE_HEAD(&sessions, list))) {
07489       d = s->device;
07490       AST_LIST_TRAVERSE(&d->lines, l, list){
07491          ast_mutex_lock(&l->lock);
07492          AST_LIST_TRAVERSE(&l->sub, sub, list) {
07493             ast_mutex_lock(&sub->lock);
07494             if (sub->owner) {
07495                sub->alreadygone = 1;
07496                ast_softhangup(sub->owner, AST_SOFTHANGUP_APPUNLOAD);
07497             }
07498             ast_mutex_unlock(&sub->lock);
07499          }
07500          if (l->mwi_event_sub)
07501             ast_event_unsubscribe(l->mwi_event_sub);
07502          ast_mutex_unlock(&l->lock);
07503          manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: Skinny\r\nPeer: Skinny/%s@%s\r\nPeerStatus: Unregistered\r\n", l->name, d->name);
07504          unregister_exten(l);
07505       }
07506       if (s->fd > -1)
07507          close(s->fd);
07508       pthread_cancel(s->t);
07509       pthread_kill(s->t, SIGURG);
07510       pthread_join(s->t, NULL);
07511       free(s);
07512    }
07513    AST_LIST_UNLOCK(&sessions);
07514 
07515    delete_devices();
07516 
07517    ast_mutex_lock(&monlock);
07518    if ((monitor_thread != AST_PTHREADT_NULL) && (monitor_thread != AST_PTHREADT_STOP)) {
07519       pthread_cancel(monitor_thread);
07520       pthread_kill(monitor_thread, SIGURG);
07521       pthread_join(monitor_thread, NULL);
07522    }
07523    monitor_thread = AST_PTHREADT_STOP;
07524    ast_mutex_unlock(&monlock);
07525 
07526    ast_mutex_lock(&netlock);
07527    if (accept_t && (accept_t != AST_PTHREADT_STOP)) {
07528       pthread_cancel(accept_t);
07529       pthread_kill(accept_t, SIGURG);
07530       pthread_join(accept_t, NULL);
07531    }
07532    accept_t = AST_PTHREADT_STOP;
07533    ast_mutex_unlock(&netlock);
07534 
07535    close(skinnysock);
07536    if (sched)
07537       sched_context_destroy(sched);
07538 
07539    con = ast_context_find(used_context);
07540    if (con)
07541       ast_context_destroy(con, "Skinny");
07542    
07543    return 0;
07544 }
07545 
07546 static int reload(void)
07547 {
07548    skinny_reload();
07549    return 0;
07550 }
07551 
07552 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Skinny Client Control Protocol (Skinny)",
07553       .load = load_module,
07554       .unload = unload_module,
07555       .reload = reload,
07556       .load_pri = AST_MODPRI_CHANNEL_DRIVER,
07557 );