Sat Mar 10 01:54:12 2012

Asterisk developer's documentation


chan_skinny.c

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

Generated on Sat Mar 10 01:54:13 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7