Tue Aug 24 2010 19:41:29

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