Wed Apr 6 11:29:43 2011

Asterisk developer's documentation


chan_skinny.c

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

Generated on Wed Apr 6 11:29:43 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7