Mon Jun 27 16:50:52 2011

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

Generated on Mon Jun 27 16:50:52 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7