Thu Oct 11 06:47:13 2012

Asterisk developer's documentation


chan_skinny.c

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