Fri Nov 12 11:46:24 2010

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: 249895 $")
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    default:
04262       ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", ind);
04263       return -1; /* Tell asterisk to provide inband signalling */
04264    }
04265    return 0;
04266 }
04267 
04268 static struct ast_channel *skinny_new(struct skinny_line *l, int state)
04269 {
04270    struct ast_channel *tmp;
04271    struct skinny_subchannel *sub;
04272    struct skinny_device *d = l->device;
04273    struct ast_variable *v = NULL;
04274    int fmt;
04275 
04276    if (!l->device) {
04277       ast_log(LOG_WARNING, "Device for line %s is not registered.\n", l->name);
04278       return NULL;
04279    }
04280 
04281    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);
04282    if (!tmp) {
04283       ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
04284       return NULL;
04285    } else {
04286       sub = ast_calloc(1, sizeof(*sub));
04287       if (!sub) {
04288          ast_log(LOG_WARNING, "Unable to allocate Skinny subchannel\n");
04289          return NULL;
04290       } else {
04291          ast_mutex_init(&sub->lock);
04292 
04293          sub->owner = tmp;
04294          sub->callid = callnums++;
04295          d->lastlineinstance = l->instance;
04296          d->lastcallreference = sub->callid;
04297          sub->cxmode = SKINNY_CX_INACTIVE;
04298          sub->nat = l->nat;
04299          sub->parent = l;
04300          sub->onhold = 0;
04301          sub->blindxfer = 0;
04302          sub->xferor = 0;
04303          sub->related = NULL;
04304 
04305          AST_LIST_INSERT_HEAD(&l->sub, sub, list);
04306          //l->activesub = sub;
04307       }
04308       tmp->tech = &skinny_tech;
04309       tmp->tech_pvt = sub;
04310       tmp->nativeformats = l->capability;
04311       if (!tmp->nativeformats)
04312          // Should throw an error
04313          tmp->nativeformats = default_capability;
04314       fmt = ast_best_codec(tmp->nativeformats);
04315       if (skinnydebug)
04316          ast_verb(1, "skinny_new: tmp->nativeformats=%d fmt=%d\n", tmp->nativeformats, fmt);
04317       if (sub->rtp) {
04318          ast_channel_set_fd(tmp, 0, ast_rtp_fd(sub->rtp));
04319       }
04320       if (state == AST_STATE_RING) {
04321          tmp->rings = 1;
04322       }
04323       tmp->writeformat = fmt;
04324       tmp->rawwriteformat = fmt;
04325       tmp->readformat = fmt;
04326       tmp->rawreadformat = fmt;
04327       if (!ast_strlen_zero(l->language))
04328          ast_string_field_set(tmp, language, l->language);
04329       if (!ast_strlen_zero(l->accountcode))
04330          ast_string_field_set(tmp, accountcode, l->accountcode);
04331       if (!ast_strlen_zero(l->parkinglot))
04332          ast_string_field_set(tmp, parkinglot, l->parkinglot);
04333       if (l->amaflags)
04334          tmp->amaflags = l->amaflags;
04335 
04336       ast_module_ref(ast_module_info->self);
04337       tmp->callgroup = l->callgroup;
04338       tmp->pickupgroup = l->pickupgroup;
04339 
04340       /* XXX Need to figure out how to handle CFwdNoAnswer */
04341       if (l->cfwdtype & SKINNY_CFWD_ALL) {
04342          ast_string_field_set(tmp, call_forward, l->call_forward_all);
04343       } else if (l->cfwdtype & SKINNY_CFWD_BUSY) {
04344          if (get_devicestate(l) != AST_DEVICE_NOT_INUSE) {
04345             ast_string_field_set(tmp, call_forward, l->call_forward_busy);
04346          }
04347       }
04348 
04349       ast_copy_string(tmp->context, l->context, sizeof(tmp->context));
04350       ast_copy_string(tmp->exten, l->exten, sizeof(tmp->exten));
04351 
04352       /* Don't use ast_set_callerid() here because it will
04353        * generate a needless NewCallerID event */
04354       tmp->cid.cid_ani = ast_strdup(l->cid_num);
04355 
04356       tmp->priority = 1;
04357       tmp->adsicpe = AST_ADSI_UNAVAILABLE;
04358 
04359       if (sub->rtp)
04360          ast_jb_configure(tmp, &global_jbconf);
04361 
04362       /* Set channel variables for this call from configuration */
04363       for (v = l->chanvars ; v ; v = v->next)
04364          pbx_builtin_setvar_helper(tmp, v->name, v->value);
04365 
04366       if (state != AST_STATE_DOWN) {
04367          if (ast_pbx_start(tmp)) {
04368             ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
04369             ast_hangup(tmp);
04370             tmp = NULL;
04371          }
04372       }
04373    }
04374    return tmp;
04375 }
04376 
04377 static int skinny_hold(struct skinny_subchannel *sub)
04378 {
04379    struct skinny_line *l = sub->parent;
04380    struct skinny_device *d = l->device;
04381 
04382    /* Don't try to hold a channel that doesn't exist */
04383    if (!sub || !sub->owner)
04384       return 0;
04385 
04386    /* Channel needs to be put on hold */
04387    if (skinnydebug)
04388       ast_verb(1, "Putting on Hold(%d)\n", l->instance);
04389 
04390    ast_queue_control_data(sub->owner, AST_CONTROL_HOLD,
04391       S_OR(l->mohsuggest, NULL),
04392       !ast_strlen_zero(l->mohsuggest) ? strlen(l->mohsuggest) + 1 : 0);
04393 
04394    transmit_activatecallplane(d, l);
04395    transmit_closereceivechannel(d, sub);
04396    transmit_stopmediatransmission(d, sub);
04397 
04398    transmit_callstateonly(d, sub, SKINNY_HOLD);
04399    transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_WINK);
04400    sub->onhold = 1;
04401    return 1;
04402 }
04403 
04404 static int skinny_unhold(struct skinny_subchannel *sub)
04405 {
04406    struct skinny_line *l = sub->parent;
04407    struct skinny_device *d = l->device;
04408 
04409    /* Don't try to unhold a channel that doesn't exist */
04410    if (!sub || !sub->owner)
04411       return 0;
04412 
04413    /* Channel is on hold, so we will unhold */
04414    if (skinnydebug)
04415       ast_verb(1, "Taking off Hold(%d)\n", l->instance);
04416 
04417    ast_queue_control(sub->owner, AST_CONTROL_UNHOLD);
04418 
04419    transmit_activatecallplane(d, l);
04420 
04421    transmit_connect(d, sub);
04422    transmit_callstateonly(d, sub, SKINNY_CONNECTED);
04423    transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
04424    l->hookstate = SKINNY_OFFHOOK;
04425    sub->onhold = 0;
04426    return 1;
04427 }
04428 
04429 static int handle_hold_button(struct skinny_subchannel *sub)
04430 {
04431    if (!sub)
04432       return -1;
04433    if (sub->related) {
04434       skinny_hold(sub);
04435       skinny_unhold(sub->related);
04436       sub->parent->activesub = sub->related;
04437    } else {
04438       if (sub->onhold) {
04439          skinny_unhold(sub);
04440          transmit_selectsoftkeys(sub->parent->device, sub->parent->instance, sub->callid, KEYDEF_CONNECTED);
04441       } else {
04442          skinny_hold(sub);
04443          transmit_selectsoftkeys(sub->parent->device, sub->parent->instance, sub->callid, KEYDEF_ONHOLD);
04444       }
04445    }
04446    return 1;
04447 }
04448 
04449 static int handle_transfer_button(struct skinny_subchannel *sub)
04450 {
04451    struct skinny_line *l = sub->parent;
04452    struct skinny_device *d = l->device;
04453    struct skinny_subchannel *newsub;
04454    struct ast_channel *c;
04455    pthread_t t;
04456 
04457    if (!sub) {
04458       ast_verbose("Transfer: No subchannel to transfer\n");
04459       return -1;
04460    }
04461    if (!sub->related) {
04462       /* Another sub has not been created so this must be first XFER press */
04463       if (!sub->onhold) {
04464          skinny_hold(sub);
04465       }
04466       c = skinny_new(l, AST_STATE_DOWN);
04467       if (c) {
04468          newsub = c->tech_pvt;
04469          /* point the sub and newsub at each other so we know they are related */
04470          newsub->related = sub;
04471          sub->related = newsub;
04472          newsub->xferor = 1;
04473          l->activesub = newsub;
04474          transmit_callstate(d, l->instance, SKINNY_OFFHOOK, newsub->callid);
04475          if (skinnydebug)
04476             ast_debug(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
04477          transmit_displaymessage(d, NULL, l->instance, newsub->callid); /* clear display */
04478          transmit_tone(d, SKINNY_DIALTONE, l->instance, newsub->callid);
04479          transmit_selectsoftkeys(d, l->instance, newsub->callid, KEYDEF_OFFHOOKWITHFEAT);
04480          /* start the switch thread */
04481          if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
04482             ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
04483             ast_hangup(c);
04484          }
04485       } else {
04486          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
04487       }
04488    } else {
04489       /* We already have a related sub so we can either complete XFER or go into BLINDXFER (or cancel BLINDXFER */
04490       if (sub->blindxfer) {
04491          /* toggle blindxfer off */
04492          sub->blindxfer = 0;
04493          sub->related->blindxfer = 0;
04494          /* we really need some indications */
04495       } else {
04496          /* We were doing attended transfer */
04497          if (sub->owner->_state == AST_STATE_DOWN || sub->related->owner->_state == AST_STATE_DOWN) {
04498             /* one of the subs so we cant transfer yet, toggle blindxfer on */
04499             sub->blindxfer = 1;
04500             sub->related->blindxfer = 1;
04501          } else {
04502             /* big assumption we have two channels, lets transfer */
04503             skinny_transfer(sub);
04504          }
04505       }
04506    }
04507    return 0;
04508 }
04509 
04510 static int handle_keep_alive_message(struct skinny_req *req, struct skinnysession *s)
04511 {
04512    if (!(req = req_alloc(0, KEEP_ALIVE_ACK_MESSAGE)))
04513       return -1;
04514 
04515    transmit_response(s->device, req);
04516    return 1;
04517 }
04518 
04519 static int handle_register_message(struct skinny_req *req, struct skinnysession *s)
04520 {
04521    struct skinny_device *d = NULL;
04522    char name[16];
04523    int res;
04524 
04525    memcpy(&name, req->data.reg.name, sizeof(name));
04526 
04527    res = skinny_register(req, s);
04528    if (!res) {
04529       ast_log(LOG_ERROR, "Rejecting Device %s: Device not found\n", name);
04530       if (!(req = req_alloc(sizeof(struct register_rej_message), REGISTER_REJ_MESSAGE)))
04531          return -1;
04532 
04533       snprintf(req->data.regrej.errMsg, sizeof(req->data.regrej.errMsg), "No Authority: %s", name);
04534 
04535       /* transmit_respons in line as we don't have a valid d */
04536       ast_mutex_lock(&s->lock);
04537 
04538       if (letohl(req->len > SKINNY_MAX_PACKET) || letohl(req->len < 0)) {
04539          ast_log(LOG_WARNING, "transmit_response: the length of the request is out of bounds\n");
04540          ast_mutex_unlock(&s->lock);
04541          return -1;
04542       }
04543 
04544       memset(s->outbuf, 0, sizeof(s->outbuf));
04545       memcpy(s->outbuf, req, skinny_header_size);
04546       memcpy(s->outbuf+skinny_header_size, &req->data, letohl(req->len));
04547 
04548       res = write(s->fd, s->outbuf, letohl(req->len)+8);
04549 
04550       if (res != letohl(req->len)+8) {
04551          ast_log(LOG_WARNING, "Transmit: write only sent %d out of %d bytes: %s\n", res, letohl(req->len)+8, strerror(errno));
04552       }
04553    
04554       ast_mutex_unlock(&s->lock);
04555 
04556       return 0;
04557    }
04558    ast_verb(3, "Device '%s' successfully registered\n", name);
04559 
04560    d = s->device;
04561    
04562    if (!(req = req_alloc(sizeof(struct register_ack_message), REGISTER_ACK_MESSAGE)))
04563       return -1;
04564 
04565    req->data.regack.res[0] = '0';
04566    req->data.regack.res[1] = '\0';
04567    req->data.regack.keepAlive = htolel(keep_alive);
04568    memcpy(req->data.regack.dateTemplate, date_format, sizeof(req->data.regack.dateTemplate));
04569    req->data.regack.res2[0] = '0';
04570    req->data.regack.res2[1] = '\0';
04571    req->data.regack.secondaryKeepAlive = htolel(keep_alive);
04572    transmit_response(d, req);
04573    if (skinnydebug)
04574       ast_verb(1, "Requesting capabilities\n");
04575 
04576    if (!(req = req_alloc(0, CAPABILITIES_REQ_MESSAGE)))
04577       return -1;
04578 
04579    transmit_response(d, req);
04580 
04581    return res;
04582 }
04583 
04584 static int handle_callforward_button(struct skinny_subchannel *sub, int cfwdtype)
04585 {
04586    struct skinny_line *l = sub->parent;
04587    struct skinny_device *d = l->device;
04588    struct ast_channel *c = sub->owner;
04589    pthread_t t;
04590 
04591    if (l->hookstate == SKINNY_ONHOOK) {
04592       l->hookstate = SKINNY_OFFHOOK;
04593       transmit_speaker_mode(d, SKINNY_SPEAKERON);
04594       transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
04595    }
04596    if (skinnydebug)
04597       ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
04598    transmit_displaymessage(d, NULL, l->instance, sub->callid); /* clear display */
04599 
04600    if (l->cfwdtype & cfwdtype) {
04601       set_callforwards(l, NULL, cfwdtype);
04602       ast_safe_sleep(c, 500);
04603       transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
04604       transmit_callstate(d, l->instance, SKINNY_ONHOOK, sub->callid);
04605       transmit_displaynotify(d, "CFwd disabled", 10);
04606       if (sub->owner && sub->owner->_state != AST_STATE_UP) {
04607          ast_indicate(c, -1);
04608          ast_hangup(c);
04609       }
04610       transmit_cfwdstate(d, l);
04611    } else {
04612       l->getforward = cfwdtype;
04613       transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
04614       transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
04615       if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
04616          ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
04617          ast_hangup(c);
04618       }
04619    }
04620    return 0;
04621 }
04622 static int handle_ip_port_message(struct skinny_req *req, struct skinnysession *s)
04623 {
04624    /* no response necessary */
04625    return 1;
04626 }
04627 
04628 static int handle_keypad_button_message(struct skinny_req *req, struct skinnysession *s)
04629 {
04630    struct skinny_subchannel *sub = NULL;
04631    struct skinny_line *l;
04632    struct skinny_device *d = s->device;
04633    struct ast_frame f = { 0, };
04634    char dgt;
04635    int digit;
04636    int lineInstance;
04637    int callReference;
04638 
04639    digit = letohl(req->data.keypad.button);
04640    lineInstance = letohl(req->data.keypad.lineInstance);
04641    callReference = letohl(req->data.keypad.callReference);
04642 
04643    if (digit == 14) {
04644       dgt = '*';
04645    } else if (digit == 15) {
04646       dgt = '#';
04647    } else if (digit >= 0 && digit <= 9) {
04648       dgt = '0' + digit;
04649    } else {
04650       /* digit=10-13 (A,B,C,D ?), or
04651        * digit is bad value
04652        *
04653        * probably should not end up here, but set
04654        * value for backward compatibility, and log
04655        * a warning.
04656        */
04657       dgt = '0' + digit;
04658       ast_log(LOG_WARNING, "Unsupported digit %d\n", digit);
04659    }
04660 
04661    f.subclass = dgt;
04662 
04663    f.src = "skinny";
04664 
04665    if (lineInstance && callReference)
04666       sub = find_subchannel_by_instance_reference(d, lineInstance, callReference);
04667    else
04668       sub = d->activeline->activesub;
04669       //sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
04670 
04671    if (!sub)
04672       return 0;
04673 
04674    l = sub->parent;
04675    if (sub->owner) {
04676       if (sub->owner->_state == 0) {
04677          f.frametype = AST_FRAME_DTMF_BEGIN;
04678          ast_queue_frame(sub->owner, &f);
04679       }
04680       /* XXX MUST queue this frame to all lines in threeway call if threeway call is active */
04681       f.frametype = AST_FRAME_DTMF_END;
04682       ast_queue_frame(sub->owner, &f);
04683       /* XXX This seriously needs to be fixed */
04684       if (AST_LIST_NEXT(sub, list) && AST_LIST_NEXT(sub, list)->owner) {
04685          if (sub->owner->_state == 0) {
04686             f.frametype = AST_FRAME_DTMF_BEGIN;
04687             ast_queue_frame(AST_LIST_NEXT(sub, list)->owner, &f);
04688          }
04689          f.frametype = AST_FRAME_DTMF_END;
04690          ast_queue_frame(AST_LIST_NEXT(sub, list)->owner, &f);
04691       }
04692    } else {
04693       if (skinnydebug)
04694          ast_verb(1, "No owner: %s\n", l->name);
04695    }
04696    return 1;
04697 }
04698 
04699 static int handle_stimulus_message(struct skinny_req *req, struct skinnysession *s)
04700 {
04701    struct skinny_device *d = s->device;
04702    struct skinny_line *l;
04703    struct skinny_subchannel *sub;
04704    /*struct skinny_speeddial *sd;*/
04705    struct ast_channel *c;
04706    pthread_t t;
04707    int event;
04708    int instance;
04709    int callreference;
04710    /*int res = 0;*/
04711 
04712    event = letohl(req->data.stimulus.stimulus);
04713    instance = letohl(req->data.stimulus.stimulusInstance);
04714    callreference = letohl(req->data.stimulus.callreference); 
04715    if (skinnydebug)
04716       ast_verb(1, "callreference in handle_stimulus_message is '%d'\n", callreference);
04717 
04718    /*  Note that this call should be using the passed in instance and callreference */
04719    sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
04720 
04721    if (!sub) {
04722       l = find_line_by_instance(d, d->lastlineinstance);
04723       if (!l) {
04724          return 0;
04725       }
04726       sub = l->activesub;
04727    } else {
04728       l = sub->parent;
04729    }
04730 
04731    switch(event) {
04732    case STIMULUS_REDIAL:
04733       if (skinnydebug)
04734          ast_verb(1, "Received Stimulus: Redial(%d/%d)\n", instance, callreference);
04735 
04736       if (ast_strlen_zero(l->lastnumberdialed)) {
04737          ast_log(LOG_WARNING, "Attempted redial, but no previously dialed number found.\n");
04738          l->hookstate = SKINNY_ONHOOK;
04739          transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
04740          transmit_callstate(d, l->instance, SKINNY_ONHOOK, instance);
04741          break;
04742       }
04743 
04744       c = skinny_new(l, AST_STATE_DOWN);
04745       if (!c) {
04746          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
04747       } else {
04748          sub = c->tech_pvt;
04749          l = sub->parent;
04750          l->activesub = sub;
04751          if (l->hookstate == SKINNY_ONHOOK) {
04752             l->hookstate = SKINNY_OFFHOOK;
04753             transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
04754          }
04755          if (skinnydebug)
04756             ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
04757          transmit_displaymessage(d, NULL, l->instance, sub->callid); /* clear display */
04758          transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
04759          transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
04760 
04761          if (!ast_ignore_pattern(c->context, l->lastnumberdialed)) {
04762             transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
04763          }
04764          ast_copy_string(c->exten, l->lastnumberdialed, sizeof(c->exten));
04765          if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
04766             ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
04767             ast_hangup(c);
04768          }
04769       }
04770       break;
04771    case STIMULUS_SPEEDDIAL:
04772        {
04773       struct skinny_speeddial *sd;
04774 
04775       if (skinnydebug)
04776          ast_verb(1, "Received Stimulus: SpeedDial(%d/%d)\n", instance, callreference);
04777       if (!(sd = find_speeddial_by_instance(d, instance, 0))) {
04778          return 0;
04779       }
04780 
04781       if (!sub || !sub->owner)
04782          c = skinny_new(l, AST_STATE_DOWN);
04783       else
04784          c = sub->owner;
04785 
04786       if (!c) {
04787          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
04788       } else {
04789          sub = c->tech_pvt;
04790          l = sub->parent;
04791          l->activesub = sub;
04792          if (l->hookstate == SKINNY_ONHOOK) {
04793             l->hookstate = SKINNY_OFFHOOK;
04794             transmit_speaker_mode(d, SKINNY_SPEAKERON);
04795             transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
04796          }
04797          if (skinnydebug)
04798             ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
04799          transmit_displaymessage(d, NULL, l->instance, sub->callid); /* clear display */
04800          transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
04801          transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
04802 
04803          if (!ast_ignore_pattern(c->context, sd->exten)) {
04804             transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
04805          }
04806          if (ast_exists_extension(c, c->context, sd->exten, 1, l->cid_num)) {
04807             ast_copy_string(c->exten, sd->exten, sizeof(c->exten));
04808             ast_copy_string(l->lastnumberdialed, sd->exten, sizeof(l->lastnumberdialed));
04809 
04810             if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
04811                ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
04812                ast_hangup(c);
04813             }
04814             break;
04815          }
04816       }
04817        }
04818       break;
04819    case STIMULUS_HOLD:
04820       if (skinnydebug)
04821          ast_verb(1, "Received Stimulus: Hold(%d/%d)\n", instance, callreference);
04822       handle_hold_button(sub);
04823       break;
04824    case STIMULUS_TRANSFER:
04825       if (skinnydebug)
04826          ast_verb(1, "Received Stimulus: Transfer(%d/%d)\n", instance, callreference);
04827       if (l->transfer)
04828          handle_transfer_button(sub);
04829       else
04830          transmit_displaynotify(d, "Transfer disabled", 10);
04831       break;
04832    case STIMULUS_CONFERENCE:
04833       if (skinnydebug)
04834          ast_verb(1, "Received Stimulus: Conference(%d/%d)\n", instance, callreference);
04835       /* XXX determine the best way to pull off a conference.  Meetme? */
04836       break;
04837    case STIMULUS_VOICEMAIL:
04838       if (skinnydebug)
04839          ast_verb(1, "Received Stimulus: Voicemail(%d/%d)\n", instance, callreference);
04840 
04841       if (!sub || !sub->owner) {
04842          c = skinny_new(l, AST_STATE_DOWN);
04843       } else {
04844          c = sub->owner;
04845       }
04846       if (!c) {
04847          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
04848       } else {
04849          sub = c->tech_pvt;
04850          l = sub->parent;
04851          l->activesub = sub;
04852 
04853          if (ast_strlen_zero(l->vmexten))  /* Exit the call if no VM pilot */
04854             break;
04855 
04856          if (l->hookstate == SKINNY_ONHOOK){
04857             l->hookstate = SKINNY_OFFHOOK;
04858             transmit_speaker_mode(d, SKINNY_SPEAKERON);
04859             transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
04860          }
04861 
04862          if (skinnydebug)
04863             ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
04864 
04865          transmit_displaymessage(d, NULL, l->instance, sub->callid); /* clear display */
04866          transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
04867          transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
04868 
04869          if (!ast_ignore_pattern(c->context, l->vmexten)) {
04870             transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
04871          }
04872 
04873          if (ast_exists_extension(c, c->context, l->vmexten, 1, l->cid_num)) {
04874             ast_copy_string(c->exten, l->vmexten, sizeof(c->exten));
04875             ast_copy_string(l->lastnumberdialed, l->vmexten, sizeof(l->lastnumberdialed));
04876             if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
04877                ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
04878                ast_hangup(c);
04879             }
04880             break;
04881          }
04882       }
04883       break;
04884    case STIMULUS_CALLPARK:
04885       {
04886       int extout;
04887       char message[32];
04888 
04889       if (skinnydebug)
04890          ast_verb(1, "Received Stimulus: Park Call(%d/%d)\n", instance, callreference);
04891 
04892       if ((sub && sub->owner) && (sub->owner->_state ==  AST_STATE_UP)){
04893          c = sub->owner;
04894          if (!ast_masq_park_call(ast_bridged_channel(c), c, 0, &extout)) {
04895             snprintf(message, sizeof(message), "Call Parked at: %d", extout);
04896             transmit_displaynotify(d, message, 10);
04897          } else {
04898             transmit_displaynotify(d, "Call Park failed", 10);
04899          }
04900       } else {
04901          transmit_displaynotify(d, "Call Park not available", 10);
04902       }
04903       }
04904       break;
04905    case STIMULUS_DND:
04906       if (skinnydebug)
04907          ast_verb(1, "Received Stimulus: DND (%d/%d)\n", instance, callreference);
04908 
04909       /* Do not disturb */
04910       if (l->dnd != 0){
04911          ast_verb(3, "Disabling DND on %s@%s\n", l->name, d->name);
04912          l->dnd = 0;
04913          transmit_lamp_indication(d, STIMULUS_DND, 1, SKINNY_LAMP_ON);
04914          transmit_displaynotify(d, "DnD disabled", 10);
04915       } else {
04916          ast_verb(3, "Enabling DND on %s@%s\n", l->name, d->name);
04917          l->dnd = 1;
04918          transmit_lamp_indication(d, STIMULUS_DND, 1, SKINNY_LAMP_OFF);
04919          transmit_displaynotify(d, "DnD enabled", 10);
04920       }
04921       break;
04922    case STIMULUS_FORWARDALL:
04923       if (skinnydebug)
04924          ast_verb(1, "Received Stimulus: Forward All(%d/%d)\n", instance, callreference);
04925 
04926       if (!sub || !sub->owner) {
04927          c = skinny_new(l, AST_STATE_DOWN);
04928       } else {
04929          c = sub->owner;
04930       }
04931 
04932       if (!c) {
04933          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
04934       } else {
04935          sub = c->tech_pvt;
04936          handle_callforward_button(sub, SKINNY_CFWD_ALL);
04937       }
04938       break;
04939    case STIMULUS_FORWARDBUSY:
04940       if (skinnydebug)
04941          ast_verb(1, "Received Stimulus: Forward Busy (%d/%d)\n", instance, callreference);
04942 
04943       if (!sub || !sub->owner) {
04944          c = skinny_new(l, AST_STATE_DOWN);
04945       } else {
04946          c = sub->owner;
04947       }
04948 
04949       if (!c) {
04950          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
04951       } else {
04952          sub = c->tech_pvt;
04953          handle_callforward_button(sub, SKINNY_CFWD_BUSY);
04954       }
04955       break;
04956    case STIMULUS_FORWARDNOANSWER:
04957       if (skinnydebug)
04958          ast_verb(1, "Received Stimulus: Forward No Answer (%d/%d)\n", instance, callreference);
04959 
04960 #if 0 /* Not sure how to handle this yet */
04961       if (!sub || !sub->owner) {
04962          c = skinny_new(l, AST_STATE_DOWN);
04963       } else {
04964          c = sub->owner;
04965       }
04966 
04967       if (!c) {
04968          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
04969       } else {
04970          sub = c->tech_pvt;
04971          handle_callforward_button(sub, SKINNY_CFWD_NOANSWER);
04972       }
04973 #endif
04974       break;
04975    case STIMULUS_DISPLAY:
04976       /* Not sure what this is */
04977       if (skinnydebug)
04978          ast_verb(1, "Received Stimulus: Display(%d/%d)\n", instance, callreference);
04979       break;
04980    case STIMULUS_LINE:
04981       if (skinnydebug)
04982          ast_verb(1, "Received Stimulus: Line(%d/%d)\n", instance, callreference);
04983 
04984       l = find_line_by_instance(d, instance);
04985 
04986       if (!l) {
04987          return 0;
04988       }
04989 
04990       d->activeline = l;
04991 
04992       /* turn the speaker on */
04993       transmit_speaker_mode(d, SKINNY_SPEAKERON);
04994       transmit_ringer_mode(d, SKINNY_RING_OFF);
04995       transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
04996 
04997       l->hookstate = SKINNY_OFFHOOK;
04998 
04999       if (sub && sub->outgoing) {
05000          /* We're answering a ringing call */
05001          ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
05002          transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
05003          transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
05004          transmit_callstateonly(d, sub, SKINNY_CONNECTED);
05005          transmit_displaypromptstatus(d, "Connected", 0, l->instance, sub->callid);
05006          transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
05007          start_rtp(sub);
05008          ast_setstate(sub->owner, AST_STATE_UP);
05009       } else {
05010          if (sub && sub->owner) {
05011             ast_debug(1, "Current subchannel [%s] already has owner\n", sub->owner->name);
05012          } else {
05013             c = skinny_new(l, AST_STATE_DOWN);
05014             if (c) {
05015                sub = c->tech_pvt;
05016                l->activesub = sub;
05017                transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
05018                if (skinnydebug)
05019                   ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
05020                transmit_displaymessage(d, NULL, l->instance, sub->callid); /* clear display */
05021                transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05022                transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_OFFHOOK);
05023 
05024                /* start the switch thread */
05025                if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
05026                   ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
05027                   ast_hangup(c);
05028                }
05029             } else {
05030                ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05031             }
05032          }
05033       }
05034       break;
05035    default:
05036       if (skinnydebug)
05037          ast_verb(1, "RECEIVED UNKNOWN STIMULUS:  %d(%d/%d)\n", event, instance, callreference);
05038       break;
05039    }
05040    ast_devstate_changed(AST_DEVICE_UNKNOWN, "Skinny/%s@%s", l->name, d->name);
05041 
05042    return 1;
05043 }
05044 
05045 static int handle_offhook_message(struct skinny_req *req, struct skinnysession *s)
05046 {
05047    struct skinny_device *d = s->device;
05048    struct skinny_line *l;
05049    struct skinny_subchannel *sub;
05050    struct ast_channel *c;
05051    struct skinny_line *tmp;
05052    pthread_t t;
05053    int instance;
05054    int reference;
05055 
05056    /* if any line on a device is offhook, than the device must be offhook, 
05057       unless we have shared lines CCM seems that it would never get here, 
05058       but asterisk does, so we may need to do more work.  Ugly, we should 
05059       probably move hookstate from line to device, afterall, it's actually
05060        a device that changes hookstates */
05061 
05062    AST_LIST_TRAVERSE(&d->lines, tmp, list) {
05063       if (tmp->hookstate == SKINNY_OFFHOOK) {
05064          ast_verbose(VERBOSE_PREFIX_3 "Got offhook message when device (%s@%s) already offhook\n", tmp->name, d->name);
05065          return 0;
05066       }
05067    }
05068 
05069    instance = letohl(req->data.offhook.instance);
05070    reference = letohl(req->data.offhook.reference);
05071 
05072    if (instance) {
05073       sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
05074       if (!sub) {
05075          l = find_line_by_instance(d, d->lastlineinstance);
05076          if (!l) {
05077             return 0;
05078          }
05079       } else {
05080          l = sub->parent;
05081       }
05082    } else {
05083       l = d->activeline;
05084       sub = l->activesub;
05085    }
05086 
05087    transmit_ringer_mode(d, SKINNY_RING_OFF);
05088    l->hookstate = SKINNY_OFFHOOK;
05089 
05090    ast_devstate_changed(AST_DEVICE_INUSE, "Skinny/%s@%s", l->name, d->name);
05091 
05092    if (sub && sub->onhold) {
05093       return 1;
05094    }
05095 
05096    transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
05097 
05098    if (sub && sub->outgoing) {
05099       /* We're answering a ringing call */
05100       ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
05101       transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
05102       transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
05103       transmit_callstateonly(d, sub, SKINNY_CONNECTED);
05104       transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
05105       start_rtp(sub);
05106       ast_setstate(sub->owner, AST_STATE_UP);
05107    } else {
05108       if (sub && sub->owner) {
05109          ast_debug(1, "Current sub [%s] already has owner\n", sub->owner->name);
05110       } else {
05111          c = skinny_new(l, AST_STATE_DOWN);
05112          if (c) {
05113             sub = c->tech_pvt;
05114             l->activesub = sub;
05115             transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
05116             if (skinnydebug)
05117                ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
05118             transmit_displaymessage(d, NULL, l->instance, sub->callid); /* clear display */
05119             transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05120             transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_OFFHOOK);
05121 
05122             /* start the switch thread */
05123             if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
05124                ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
05125                ast_hangup(c);
05126             }
05127          } else {
05128             ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05129          }
05130       }
05131    }
05132    return 1;
05133 }
05134 
05135 static int handle_onhook_message(struct skinny_req *req, struct skinnysession *s)
05136 {
05137    struct skinny_device *d = s->device;
05138    struct skinny_line *l;
05139    struct skinny_subchannel *sub;
05140    int instance;
05141    int reference;
05142    int onlysub = 0;
05143 
05144    instance = letohl(req->data.onhook.instance);
05145    reference = letohl(req->data.onhook.reference);
05146 
05147    if (instance && reference) {
05148       sub = find_subchannel_by_instance_reference(d, instance, reference);
05149       if (!sub) {
05150          return 0;
05151       }
05152       l = sub->parent;
05153    } else {
05154       l = d->activeline;
05155       sub = l->activesub;
05156       if (!sub) {
05157          return 0;
05158       }
05159    }
05160 
05161    if (l->hookstate == SKINNY_ONHOOK) {
05162       /* Something else already put us back on hook */
05163       return 0;
05164    }
05165 
05166    ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
05167 
05168    if (sub->onhold) {
05169       return 0;
05170    }
05171 
05172    if (!AST_LIST_NEXT(sub, list)) {
05173       onlysub = 1;
05174    } else {
05175       AST_LIST_REMOVE(&l->sub, sub, list);
05176    }
05177 
05178    sub->cxmode = SKINNY_CX_RECVONLY;
05179    if (onlysub || sub->xferor){  /* is this the only call to this device? */
05180       l->hookstate = SKINNY_ONHOOK;
05181       if (skinnydebug)
05182          ast_debug(1, "Skinny %s@%s-%d went on hook\n", l->name, d->name, reference);
05183    }
05184 
05185    transmit_callstate(d, l->instance, l->hookstate, sub->callid);
05186    if (l->transfer && sub->xferor && sub->owner->_state >= AST_STATE_RING) {
05187       /* We're allowed to transfer, we have two active calls and
05188          we made at least one of the calls.  Let's try and transfer */
05189       handle_transfer_button(sub);
05190    } else {
05191       /* Hangup the current call */
05192       /* If there is another active call, skinny_hangup will ring the phone with the other call */
05193       if (sub->xferor && sub->related){
05194          sub->related->related = NULL;
05195          sub->related->blindxfer = 0;
05196       }
05197 
05198       if (sub->owner) {
05199          sub->alreadygone = 1;
05200          ast_queue_hangup(sub->owner);
05201       } else {
05202          ast_log(LOG_WARNING, "Skinny(%s@%s-%d) channel already destroyed\n",
05203             l->name, d->name, sub->callid);
05204       }
05205    }
05206    return 1;
05207 }
05208 
05209 static int handle_capabilities_res_message(struct skinny_req *req, struct skinnysession *s)
05210 {
05211    struct skinny_device *d = s->device;
05212    struct skinny_line *l;
05213    uint32_t count = 0;
05214    int codecs = 0;
05215    int i;
05216 
05217    count = letohl(req->data.caps.count);
05218    if (count > SKINNY_MAX_CAPABILITIES) {
05219       count = SKINNY_MAX_CAPABILITIES;
05220       ast_log(LOG_WARNING, "Received more capabilities than we can handle (%d).  Ignoring the rest.\n", SKINNY_MAX_CAPABILITIES);
05221    }
05222 
05223    for (i = 0; i < count; i++) {
05224       int acodec = 0;
05225       int scodec = 0;
05226       scodec = letohl(req->data.caps.caps[i].codec);
05227       acodec = codec_skinny2ast(scodec);
05228       if (skinnydebug)
05229          ast_verb(1, "Adding codec capability '%d (%d)'\n", acodec, scodec);
05230       codecs |= acodec;
05231    }
05232 
05233    d->capability = d->confcapability & codecs;
05234    ast_verb(0, "Device capability set to '%d'\n", d->capability);
05235    AST_LIST_TRAVERSE(&d->lines, l, list) {
05236       ast_mutex_lock(&l->lock);
05237       l->capability = l->confcapability & d->capability;
05238       ast_mutex_unlock(&l->lock);
05239    }
05240 
05241    return 1;
05242 }
05243 
05244 static int handle_speed_dial_stat_req_message(struct skinny_req *req, struct skinnysession *s)
05245 {
05246    struct skinny_device *d = s->device;
05247    struct skinny_speeddial *sd;
05248    int instance;
05249 
05250    instance = letohl(req->data.speeddialreq.speedDialNumber);
05251 
05252    sd = find_speeddial_by_instance(d, instance, 0);
05253 
05254    if (!sd) {
05255       return 0;
05256    }
05257 
05258    if (!(req = req_alloc(sizeof(struct speed_dial_stat_res_message), SPEED_DIAL_STAT_RES_MESSAGE)))
05259       return -1;
05260 
05261    req->data.speeddialreq.speedDialNumber = htolel(instance);
05262    ast_copy_string(req->data.speeddial.speedDialDirNumber, sd->exten, sizeof(req->data.speeddial.speedDialDirNumber));
05263    ast_copy_string(req->data.speeddial.speedDialDisplayName, sd->label, sizeof(req->data.speeddial.speedDialDisplayName));
05264 
05265    transmit_response(d, req);
05266    return 1;
05267 }
05268 
05269 static int handle_line_state_req_message(struct skinny_req *req, struct skinnysession *s)
05270 {
05271    struct skinny_device *d = s->device;
05272    struct skinny_line *l;
05273    struct skinny_speeddial *sd = NULL;
05274    int instance;
05275 
05276    instance = letohl(req->data.line.lineNumber);
05277 
05278    AST_LIST_LOCK(&devices);
05279 
05280    l = find_line_by_instance(d, instance);
05281 
05282    if (!l) {
05283       sd = find_speeddial_by_instance(d, instance, 1);
05284    }
05285 
05286    if (!l && !sd) {
05287       return 0;
05288    }
05289 
05290    AST_LIST_UNLOCK(&devices);
05291 
05292    if (!(req = req_alloc(sizeof(struct line_stat_res_message), LINE_STAT_RES_MESSAGE)))
05293       return -1;
05294 
05295    req->data.linestat.lineNumber = letohl(instance);
05296    if (!l) {
05297       memcpy(req->data.linestat.lineDirNumber, sd->label, sizeof(req->data.linestat.lineDirNumber));
05298       memcpy(req->data.linestat.lineDisplayName, sd->label, sizeof(req->data.linestat.lineDisplayName));
05299    } else {
05300       memcpy(req->data.linestat.lineDirNumber, l->name, sizeof(req->data.linestat.lineDirNumber));
05301       memcpy(req->data.linestat.lineDisplayName, l->label, sizeof(req->data.linestat.lineDisplayName));
05302    }
05303    transmit_response(d, req);
05304    return 1;
05305 }
05306 
05307 static int handle_time_date_req_message(struct skinny_req *req, struct skinnysession *s)
05308 {
05309    struct timeval now = ast_tvnow();
05310    struct ast_tm cmtime;
05311 
05312    if (!(req = req_alloc(sizeof(struct definetimedate_message), DEFINETIMEDATE_MESSAGE)))
05313       return -1;
05314 
05315    ast_localtime(&now, &cmtime, NULL);
05316    req->data.definetimedate.year = htolel(cmtime.tm_year+1900);
05317    req->data.definetimedate.month = htolel(cmtime.tm_mon+1);
05318    req->data.definetimedate.dayofweek = htolel(cmtime.tm_wday);
05319    req->data.definetimedate.day = htolel(cmtime.tm_mday);
05320    req->data.definetimedate.hour = htolel(cmtime.tm_hour);
05321    req->data.definetimedate.minute = htolel(cmtime.tm_min);
05322    req->data.definetimedate.seconds = htolel(cmtime.tm_sec);
05323    req->data.definetimedate.milliseconds = htolel(cmtime.tm_usec / 1000);
05324    req->data.definetimedate.timestamp = htolel(now.tv_sec);
05325    transmit_response(s->device, req);
05326    return 1;
05327 }
05328 
05329 static int handle_button_template_req_message(struct skinny_req *req, struct skinnysession *s)
05330 {
05331    struct skinny_device *d = s->device;
05332    struct skinny_line *l;
05333    int i;
05334 
05335    struct skinny_speeddial *sd;
05336    struct button_definition_template btn[42];
05337    int lineInstance = 1;
05338    int speeddialInstance = 1;
05339    int buttonCount = 0;
05340 
05341    if (!(req = req_alloc(sizeof(struct button_template_res_message), BUTTON_TEMPLATE_RES_MESSAGE)))
05342       return -1;
05343 
05344    memset(&btn, 0, sizeof(btn));
05345 
05346    get_button_template(s, btn);
05347 
05348    for (i=0; i<42; i++) {
05349       int btnSet = 0;
05350       switch (btn[i].buttonDefinition) {
05351          case BT_CUST_LINE:
05352             /* assume failure */
05353             req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE;
05354             req->data.buttontemplate.definition[i].instanceNumber = htolel(0);
05355 
05356             AST_LIST_TRAVERSE(&d->lines, l, list) {
05357                if (l->instance == lineInstance) {
05358                   ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
05359                   req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
05360                   req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance);
05361                   lineInstance++;
05362                   buttonCount++;
05363                   btnSet = 1;
05364                   break;
05365                }
05366             }
05367 
05368             if (!btnSet) {
05369                AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
05370                   if (sd->isHint && sd->instance == lineInstance) {
05371                      ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
05372                      req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
05373                      req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance);
05374                      lineInstance++;
05375                      buttonCount++;
05376                      btnSet = 1;
05377                      break;
05378                   }
05379                }
05380             }
05381             break;
05382          case BT_CUST_LINESPEEDDIAL:
05383             /* assume failure */
05384             req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE;
05385             req->data.buttontemplate.definition[i].instanceNumber = htolel(0);
05386 
05387             AST_LIST_TRAVERSE(&d->lines, l, list) {
05388                if (l->instance == lineInstance) {
05389                   ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
05390                   req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
05391                   req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance);
05392                   lineInstance++;
05393                   buttonCount++;
05394                   btnSet = 1;
05395                   break;
05396                }
05397             }
05398 
05399             if (!btnSet) {
05400                AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
05401                   if (sd->isHint && sd->instance == lineInstance) {
05402                      ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
05403                      req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
05404                      req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance);
05405                      lineInstance++;
05406                      buttonCount++;
05407                      btnSet = 1;
05408                      break;
05409                   } else if (!sd->isHint && sd->instance == speeddialInstance) {
05410                      ast_verb(0, "Adding button: %d, %d\n", BT_SPEEDDIAL, speeddialInstance);
05411                      req->data.buttontemplate.definition[i].buttonDefinition = BT_SPEEDDIAL;
05412                      req->data.buttontemplate.definition[i].instanceNumber = htolel(speeddialInstance);
05413                      speeddialInstance++;
05414                      buttonCount++;
05415                      btnSet = 1;
05416                      break;
05417                   }
05418                }
05419             }
05420             break;
05421          case BT_LINE:
05422             req->data.buttontemplate.definition[i].buttonDefinition = htolel(BT_NONE);
05423             req->data.buttontemplate.definition[i].instanceNumber = htolel(0);
05424 
05425             AST_LIST_TRAVERSE(&d->lines, l, list) {
05426                if (l->instance == lineInstance) {
05427                   ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
05428                   req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
05429                   req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance);
05430                   lineInstance++;
05431                   buttonCount++;
05432                   btnSet = 1;
05433                   break;
05434                }
05435             }
05436             break;
05437          case BT_SPEEDDIAL:
05438             req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE;
05439             req->data.buttontemplate.definition[i].instanceNumber = 0;
05440 
05441             AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
05442                if (!sd->isHint && sd->instance == speeddialInstance) {
05443                   ast_verb(0, "Adding button: %d, %d\n", BT_SPEEDDIAL, speeddialInstance);
05444                   req->data.buttontemplate.definition[i].buttonDefinition = BT_SPEEDDIAL;
05445                   req->data.buttontemplate.definition[i].instanceNumber = htolel(speeddialInstance - 1);
05446                   speeddialInstance++;
05447                   buttonCount++;
05448                   btnSet = 1;
05449                   break;
05450                }
05451             }
05452             break;
05453          case BT_NONE:
05454             break;
05455          default:
05456             ast_verb(0, "Adding button: %d, %d\n", btn[i].buttonDefinition, 0);
05457             req->data.buttontemplate.definition[i].buttonDefinition = htolel(btn[i].buttonDefinition);
05458             req->data.buttontemplate.definition[i].instanceNumber = htolel(0);
05459             buttonCount++;
05460             btnSet = 1;
05461             break;
05462       }
05463    }
05464 
05465    req->data.buttontemplate.buttonOffset = htolel(0);
05466    req->data.buttontemplate.buttonCount = htolel(buttonCount);
05467    req->data.buttontemplate.totalButtonCount = htolel(buttonCount);
05468 
05469    if (skinnydebug)
05470       ast_verb(1, "Sending %d template to %s\n",
05471                d->type,
05472                d->name);
05473    transmit_response(d, req);
05474    return 1;
05475 }
05476 
05477 static int handle_version_req_message(struct skinny_req *req, struct skinnysession *s)
05478 {
05479    struct skinny_device *d = s->device;
05480    if (!(req = req_alloc(sizeof(struct version_res_message), VERSION_RES_MESSAGE)))
05481       return -1;
05482 
05483    ast_copy_string(req->data.version.version, d->version_id, sizeof(req->data.version.version));
05484    transmit_response(d, req);
05485    return 1;
05486 }
05487 
05488 static int handle_server_request_message(struct skinny_req *req, struct skinnysession *s)
05489 {
05490    struct skinny_device *d = s->device;
05491    if (!(req = req_alloc(sizeof(struct server_res_message), SERVER_RES_MESSAGE)))
05492       return -1;
05493 
05494    memcpy(req->data.serverres.server[0].serverName, ourhost,
05495          sizeof(req->data.serverres.server[0].serverName));
05496    req->data.serverres.serverListenPort[0] = htolel(ourport);
05497    req->data.serverres.serverIpAddr[0] = htolel(d->ourip.s_addr);
05498    transmit_response(d, req);
05499    return 1;
05500 }
05501 
05502 static int handle_alarm_message(struct skinny_req *req, struct skinnysession *s)
05503 {
05504    /* no response necessary */
05505    if (skinnydebug)
05506       ast_verb(1, "Received Alarm Message: %s\n", req->data.alarm.displayMessage);
05507 
05508    return 1;
05509 }
05510 
05511 static int handle_open_receive_channel_ack_message(struct skinny_req *req, struct skinnysession *s)
05512 {
05513    struct skinny_device *d = s->device;
05514    struct skinny_line *l;
05515    struct skinny_subchannel *sub;
05516    struct ast_format_list fmt;
05517    struct sockaddr_in sin;
05518    struct sockaddr_in us;
05519    uint32_t addr;
05520    int port;
05521    int status;
05522    int passthruid;
05523 
05524    status = letohl(req->data.openreceivechannelack.status);
05525    if (status) {
05526       ast_log(LOG_ERROR, "Open Receive Channel Failure\n");
05527       return 0;
05528    }
05529    addr = letohl(req->data.openreceivechannelack.ipAddr);
05530    port = letohl(req->data.openreceivechannelack.port);
05531    passthruid = letohl(req->data.openreceivechannelack.passThruId);
05532 
05533    sin.sin_family = AF_INET;
05534    sin.sin_addr.s_addr = addr;
05535    sin.sin_port = htons(port);
05536 
05537    sub = find_subchannel_by_reference(d, passthruid);
05538 
05539    if (!sub)
05540       return 0;
05541 
05542    l = sub->parent;
05543 
05544    if (sub->rtp) {
05545       ast_rtp_set_peer(sub->rtp, &sin);
05546       ast_rtp_get_us(sub->rtp, &us);
05547    } else {
05548       ast_log(LOG_ERROR, "No RTP structure, this is very bad\n");
05549       return 0;
05550    }
05551 
05552    if (skinnydebug)
05553       ast_verb(1, "ipaddr = %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
05554 
05555    if (!(req = req_alloc(sizeof(struct start_media_transmission_message), START_MEDIA_TRANSMISSION_MESSAGE)))
05556       return -1;
05557 
05558    fmt = ast_codec_pref_getsize(&l->prefs, ast_best_codec(l->capability));
05559 
05560    if (skinnydebug)
05561       ast_verb(1, "Setting payloadType to '%d' (%d ms)\n", fmt.bits, fmt.cur_ms);
05562 
05563    req->data.startmedia.conferenceId = htolel(sub->callid);
05564    req->data.startmedia.passThruPartyId = htolel(sub->callid);
05565    req->data.startmedia.remoteIp = htolel(d->ourip.s_addr);
05566    req->data.startmedia.remotePort = htolel(ntohs(us.sin_port));
05567    req->data.startmedia.packetSize = htolel(fmt.cur_ms);
05568    req->data.startmedia.payloadType = htolel(codec_ast2skinny(fmt.bits));
05569    req->data.startmedia.qualifier.precedence = htolel(127);
05570    req->data.startmedia.qualifier.vad = htolel(0);
05571    req->data.startmedia.qualifier.packets = htolel(0);
05572    req->data.startmedia.qualifier.bitRate = htolel(0);
05573    transmit_response(d, req);
05574 
05575    return 1;
05576 }
05577 
05578 static int handle_enbloc_call_message(struct skinny_req *req, struct skinnysession *s)
05579 {
05580    struct skinny_device *d = s->device;
05581    struct skinny_line *l;
05582    struct skinny_subchannel *sub = NULL;
05583    struct ast_channel *c;
05584    pthread_t t;
05585 
05586    if (skinnydebug)
05587       ast_verb(1, "Received Enbloc Call: %s\n", req->data.enbloccallmessage.calledParty);
05588 
05589    sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
05590 
05591    if (!sub) {
05592       l = find_line_by_instance(d, d->lastlineinstance);
05593       if (!l) {
05594          return 0;
05595       }
05596    } else {
05597       l = sub->parent;
05598    }
05599 
05600    c = skinny_new(l, AST_STATE_DOWN);
05601 
05602    if(!c) {
05603       ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05604    } else {
05605       l->hookstate = SKINNY_OFFHOOK;
05606 
05607       sub = c->tech_pvt;
05608       l->activesub = sub;
05609       transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
05610       if (skinnydebug)
05611          ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
05612       transmit_displaymessage(d, NULL, l->instance, sub->callid); /* clear display */
05613       transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05614 
05615       if (!ast_ignore_pattern(c->context, req->data.enbloccallmessage.calledParty)) {
05616          transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
05617       }
05618       ast_copy_string(c->exten, req->data.enbloccallmessage.calledParty, sizeof(c->exten));
05619       if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
05620          ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
05621          ast_hangup(c);
05622       }
05623    }
05624    
05625    return 1;
05626 }
05627 
05628 
05629 static int handle_soft_key_set_req_message(struct skinny_req *req, struct skinnysession *s)
05630 {
05631    int i;
05632    int x;
05633    int y;
05634    const struct soft_key_definitions *softkeymode = soft_key_default_definitions;
05635    struct skinny_device *d = s->device;
05636 
05637    if (!(req = req_alloc(sizeof(struct soft_key_set_res_message), SOFT_KEY_SET_RES_MESSAGE)))
05638       return -1;
05639 
05640    req->data.softkeysets.softKeySetOffset = htolel(0);
05641    req->data.softkeysets.softKeySetCount = htolel(11);
05642    req->data.softkeysets.totalSoftKeySetCount = htolel(11);
05643    for (x = 0; x < sizeof(soft_key_default_definitions) / sizeof(struct soft_key_definitions); x++) {
05644       const uint8_t *defaults = softkeymode->defaults;
05645       /* XXX I wanted to get the size of the array dynamically, but that wasn't wanting to work.
05646          This will have to do for now. */
05647       for (y = 0; y < softkeymode->count; y++) {
05648          for (i = 0; i < (sizeof(soft_key_template_default) / sizeof(struct soft_key_template_definition)); i++) {
05649             if (defaults[y] == i+1) {
05650                req->data.softkeysets.softKeySetDefinition[softkeymode->mode].softKeyTemplateIndex[y] = htolel(i+1);
05651                req->data.softkeysets.softKeySetDefinition[softkeymode->mode].softKeyInfoIndex[y] = htolel(i+301);
05652             }
05653          }
05654       }
05655       softkeymode++;
05656    }
05657    transmit_response(d, req);
05658    transmit_selectsoftkeys(d, 0, 0, KEYDEF_ONHOOK);
05659    return 1;
05660 }
05661 
05662 static int handle_soft_key_event_message(struct skinny_req *req, struct skinnysession *s)
05663 {
05664    struct skinny_device *d = s->device;
05665    struct skinny_line *l;
05666    struct skinny_subchannel *sub = NULL;
05667    struct ast_channel *c;
05668    pthread_t t;
05669    int event;
05670    int instance;
05671    int callreference;
05672 
05673    event = letohl(req->data.softkeyeventmessage.softKeyEvent);
05674    instance = letohl(req->data.softkeyeventmessage.instance);
05675    callreference = letohl(req->data.softkeyeventmessage.callreference);
05676 
05677    if (instance) {
05678       l = find_line_by_instance(d, instance);
05679       if (callreference) {
05680          sub = find_subchannel_by_instance_reference(d, instance, callreference);
05681       } else {
05682          sub = find_subchannel_by_instance_reference(d, instance, d->lastcallreference);
05683       }
05684    } else {
05685       l = find_line_by_instance(d, d->lastlineinstance);
05686    }
05687 
05688    if (!l) {
05689       if (skinnydebug)
05690          ast_verb(1, "Received Softkey Event: %d(%d/%d)\n", event, instance, callreference);
05691       return 0;
05692    }
05693 
05694    ast_devstate_changed(AST_DEVICE_INUSE, "Skinny/%s@%s", l->name, d->name);
05695 
05696    switch(event) {
05697    case SOFTKEY_NONE:
05698       if (skinnydebug)
05699          ast_verb(1, "Received Softkey Event: None(%d/%d)\n", instance, callreference);
05700       break;
05701    case SOFTKEY_REDIAL:
05702       if (skinnydebug)
05703          ast_verb(1, "Received Softkey Event: Redial(%d/%d)\n", instance, callreference);
05704 
05705       if (ast_strlen_zero(l->lastnumberdialed)) {
05706          ast_log(LOG_WARNING, "Attempted redial, but no previously dialed number found.\n");
05707          l->hookstate = SKINNY_ONHOOK;
05708          transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
05709          transmit_callstate(d, l->instance, SKINNY_ONHOOK, instance);
05710          break;
05711       }
05712 
05713       if (!sub || !sub->owner) {
05714          c = skinny_new(l, AST_STATE_DOWN);
05715       } else {
05716          c = sub->owner;
05717       }
05718 
05719       if (!c) {
05720          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05721       } else {
05722          sub = c->tech_pvt;
05723          l->activesub = sub;
05724          if (l->hookstate == SKINNY_ONHOOK) {
05725             l->hookstate = SKINNY_OFFHOOK;
05726             transmit_speaker_mode(d, SKINNY_SPEAKERON);
05727             transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
05728          }
05729          if (skinnydebug)
05730             ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
05731          transmit_displaymessage(d, NULL, l->instance, sub->callid); /* clear display */
05732          transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05733          transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
05734 
05735          if (!ast_ignore_pattern(c->context, l->lastnumberdialed)) {
05736             transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
05737          }
05738          ast_copy_string(c->exten, l->lastnumberdialed, sizeof(c->exten));
05739          if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
05740             ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
05741             ast_hangup(c);
05742          }
05743       }
05744       break;
05745    case SOFTKEY_NEWCALL:  /* Actually the DIAL softkey */
05746       if (skinnydebug)
05747          ast_verb(1, "Received Softkey Event: New Call(%d/%d)\n", instance, callreference);
05748 
05749       /* New Call ALWAYS gets a new sub-channel */
05750       c = skinny_new(l, AST_STATE_DOWN);
05751       sub = c->tech_pvt;
05752    
05753       /* transmit_ringer_mode(d, SKINNY_RING_OFF);
05754       transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON); */
05755 
05756       /* l->hookstate = SKINNY_OFFHOOK; */
05757 
05758       if (!c) {
05759          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05760       } else {
05761          sub = c->tech_pvt;
05762          l->activesub = sub;
05763          if (l->hookstate == SKINNY_ONHOOK) {
05764             l->hookstate = SKINNY_OFFHOOK;
05765             transmit_speaker_mode(d, SKINNY_SPEAKERON);
05766          }
05767          ast_verb(1, "Call-id: %d\n", sub->callid);
05768 
05769          transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
05770 
05771          if (skinnydebug)
05772             ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
05773          transmit_displaymessage(d, NULL, l->instance, sub->callid); /* clear display */
05774          transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05775          transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_OFFHOOK);
05776 
05777          /* start the switch thread */
05778          if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
05779             ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
05780             ast_hangup(c);
05781          }
05782       }
05783       break;
05784    case SOFTKEY_HOLD:
05785       if (skinnydebug)
05786          ast_verb(1, "Received Softkey Event: Hold(%d/%d)\n", instance, callreference);
05787       handle_hold_button(sub);   
05788       break;
05789    case SOFTKEY_TRNSFER:
05790       if (skinnydebug)
05791          ast_verb(1, "Received Softkey Event: Transfer(%d/%d)\n", instance, callreference);
05792       if (l->transfer)
05793          handle_transfer_button(sub);
05794       else
05795          transmit_displaynotify(d, "Transfer disabled", 10);
05796 
05797       break;
05798    case SOFTKEY_DND:
05799       if (skinnydebug)
05800          ast_verb(1, "Received Softkey Event: DND(%d/%d)\n", instance, callreference);
05801 
05802       /* Do not disturb */
05803       if (l->dnd != 0){
05804          ast_verb(3, "Disabling DND on %s@%s\n", l->name, d->name);
05805          l->dnd = 0;
05806          transmit_lamp_indication(d, STIMULUS_DND, 1, SKINNY_LAMP_ON);
05807          transmit_displaynotify(d, "DnD disabled", 10);
05808       } else {
05809          ast_verb(3, "Enabling DND on %s@%s\n", l->name, d->name);
05810          l->dnd = 1;
05811          transmit_lamp_indication(d, STIMULUS_DND, 1, SKINNY_LAMP_OFF);
05812          transmit_displaynotify(d, "DnD enabled", 10);
05813       }
05814       break;
05815    case SOFTKEY_CFWDALL:
05816       if (skinnydebug)
05817          ast_verb(1, "Received Softkey Event: Forward All(%d/%d)\n", instance, callreference);
05818 
05819       if (!sub || !sub->owner) {
05820          c = skinny_new(l, AST_STATE_DOWN);
05821       } else {
05822          c = sub->owner;
05823       }
05824 
05825       if (!c) {
05826          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05827       } else {
05828          sub = c->tech_pvt;
05829          l->activesub = sub;
05830          handle_callforward_button(sub, SKINNY_CFWD_ALL);
05831       }
05832       break;
05833    case SOFTKEY_CFWDBUSY:
05834       if (skinnydebug)
05835          ast_verb(1, "Received Softkey Event: Forward Busy (%d/%d)\n", instance, callreference);
05836 
05837       if (!sub || !sub->owner) {
05838          c = skinny_new(l, AST_STATE_DOWN);
05839       } else {
05840          c = sub->owner;
05841       }
05842 
05843       if (!c) {
05844          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05845       } else {
05846          sub = c->tech_pvt;
05847          l->activesub = sub;
05848          handle_callforward_button(sub, SKINNY_CFWD_BUSY);
05849       }
05850       break;
05851    case SOFTKEY_CFWDNOANSWER:
05852       if (skinnydebug)
05853          ast_verb(1, "Received Softkey Event: Forward No Answer (%d/%d)\n", instance, callreference);
05854 
05855 #if 0 /* Not sure how to handle this yet */
05856       if (!sub || !sub->owner) {
05857          c = skinny_new(l, AST_STATE_DOWN);
05858       } else {
05859          c = sub->owner;
05860       }
05861 
05862       if (!c) {
05863          ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05864       } else {
05865          sub = c->tech_pvt;
05866          l->activesub = sub;
05867          handle_callforward_button(sub, SKINNY_CFWD_NOANSWER);
05868       }
05869 #endif
05870       break;
05871    case SOFTKEY_BKSPC:
05872       if (skinnydebug)
05873          ast_verb(1, "Received Softkey Event: Backspace(%d/%d)\n", instance, callreference);
05874       break;
05875    case SOFTKEY_ENDCALL:
05876       if (skinnydebug)
05877          ast_verb(1, "Received Softkey Event: End Call(%d/%d)\n", instance, callreference);
05878 
05879       if (l->hookstate == SKINNY_ONHOOK) {
05880          /* Something else already put us back on hook */
05881          break;
05882       }
05883       if (sub) {
05884          int onlysub = 0;
05885 
05886          if (!AST_LIST_NEXT(sub, list)) {
05887             onlysub = 1;
05888          } else {
05889             AST_LIST_REMOVE(&l->sub, sub, list);
05890          }
05891 
05892          sub->cxmode = SKINNY_CX_RECVONLY;
05893          if (onlysub || sub->xferor){    /*Are there other calls to this device */
05894             l->hookstate = SKINNY_ONHOOK;
05895             if (skinnydebug)
05896                ast_debug(1, "Skinny %s@%s-%d went on hook\n", l->name, d->name, callreference);
05897          }
05898 
05899          transmit_callstate(d, l->instance, l->hookstate, sub->callid);
05900          ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
05901          if (skinnydebug)
05902             ast_verb(1, "Skinny %s@%s went on hook\n", l->name, d->name);
05903          if (l->transfer && sub->xferor && sub->owner->_state >= AST_STATE_RING) {
05904             /* We're allowed to transfer, we have two active calls and
05905                we made at least one of the calls.  Let's try and transfer */
05906             handle_transfer_button(sub);
05907          } else {
05908             /* Hangup the current call */
05909             /* If there is another active call, skinny_hangup will ring the phone with the other call */
05910             if (sub->xferor && sub->related){
05911                sub->related->related = NULL;
05912                sub->related->blindxfer = 0;
05913             }
05914 
05915             if (sub->owner) {
05916                sub->alreadygone = 1;
05917                ast_queue_hangup(sub->owner);
05918             } else {
05919                ast_log(LOG_WARNING, "Skinny(%s@%s-%d) channel already destroyed\n",
05920                   l->name, d->name, sub->callid);
05921             }
05922          }
05923          if ((l->hookstate == SKINNY_ONHOOK) && (AST_LIST_NEXT(sub, list) && !AST_LIST_NEXT(sub, list)->rtp)) {
05924             ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
05925          }
05926       }
05927       break;
05928    case SOFTKEY_RESUME:
05929       if (skinnydebug)
05930          ast_verb(1, "Received Softkey Event: Resume(%d/%d)\n", instance, callreference);
05931 
05932       if (sub) {
05933          if (sub->onhold) {
05934             skinny_unhold(sub);
05935             transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
05936          } else {
05937             skinny_hold(sub);
05938             transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_ONHOLD);
05939          }
05940       }
05941 
05942       break;
05943    case SOFTKEY_ANSWER:
05944       if (skinnydebug)
05945          ast_verb(1, "Received Softkey Event: Answer(%d/%d)\n", instance, callreference);
05946 
05947       transmit_ringer_mode(d, SKINNY_RING_OFF);
05948       transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
05949       if (l->hookstate == SKINNY_ONHOOK) {
05950          transmit_speaker_mode(d, SKINNY_SPEAKERON);
05951          l->hookstate = SKINNY_OFFHOOK;
05952       }
05953 
05954       if (sub && sub->outgoing) {
05955          /* We're answering a ringing call */
05956          ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
05957          transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
05958          transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
05959          transmit_callstateonly(d, sub, SKINNY_CONNECTED);
05960          transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
05961          start_rtp(sub);
05962          ast_setstate(sub->owner, AST_STATE_UP);
05963       }
05964       break;
05965    case SOFTKEY_INFO:
05966       if (skinnydebug)
05967          ast_verb(1, "Received Softkey Event: Info(%d/%d)\n", instance, callreference);
05968       break;
05969    case SOFTKEY_CONFRN:
05970       if (skinnydebug)
05971          ast_verb(1, "Received Softkey Event: Conference(%d/%d)\n", instance, callreference);
05972       /* XXX determine the best way to pull off a conference.  Meetme? */
05973       break;
05974    case SOFTKEY_PARK:
05975       {
05976       int extout;
05977       char message[32];
05978 
05979       if (skinnydebug)
05980          ast_verb(1, "Received Softkey Event: Park Call(%d/%d)\n", instance, callreference);
05981 
05982       if ((sub && sub->owner) && (sub->owner->_state ==  AST_STATE_UP)){
05983          c = sub->owner;
05984          if (!ast_masq_park_call(ast_bridged_channel(c), c, 0, &extout)) {
05985             snprintf(message, sizeof(message), "Call Parked at: %d", extout);
05986             transmit_displaynotify(d, message, 10);
05987          } else {
05988             transmit_displaynotify(d, "Call Park failed", 10);
05989          }
05990       } else {
05991          transmit_displaynotify(d, "Call Park not available", 10);
05992       }
05993       }
05994       break;
05995    case SOFTKEY_JOIN:
05996       if (skinnydebug)
05997          ast_verb(1, "Received Softkey Event: Join(%d/%d)\n", instance, callreference);
05998       break;
05999    case SOFTKEY_MEETME:
06000       /* XXX How is this different from CONFRN? */
06001       if (skinnydebug)
06002          ast_verb(1, "Received Softkey Event: Meetme(%d/%d)\n", instance, callreference);
06003       break;
06004    case SOFTKEY_PICKUP:
06005       if (skinnydebug)
06006          ast_verb(1, "Received Softkey Event: Pickup(%d/%d)\n", instance, callreference);
06007       break;
06008    case SOFTKEY_GPICKUP:
06009       if (skinnydebug)
06010          ast_verb(1, "Received Softkey Event: Group Pickup(%d/%d)\n", instance, callreference);
06011       break;
06012    default:
06013       if (skinnydebug)
06014          ast_verb(1, "Received unknown Softkey Event: %d(%d/%d)\n", event, instance, callreference);
06015       break;
06016    }
06017 
06018    return 1;
06019 }
06020 
06021 static int handle_unregister_message(struct skinny_req *req, struct skinnysession *s)
06022 {
06023    return skinny_unregister(req, s);
06024 }
06025 
06026 static int handle_soft_key_template_req_message(struct skinny_req *req, struct skinnysession *s)
06027 {
06028    if (!(req = req_alloc(sizeof(struct soft_key_template_res_message), SOFT_KEY_TEMPLATE_RES_MESSAGE)))
06029       return -1;
06030 
06031    req->data.softkeytemplate.softKeyOffset = htolel(0);
06032    req->data.softkeytemplate.softKeyCount = htolel(sizeof(soft_key_template_default) / sizeof(struct soft_key_template_definition));
06033    req->data.softkeytemplate.totalSoftKeyCount = htolel(sizeof(soft_key_template_default) / sizeof(struct soft_key_template_definition));
06034    memcpy(req->data.softkeytemplate.softKeyTemplateDefinition,
06035       soft_key_template_default,
06036       sizeof(soft_key_template_default));
06037    transmit_response(s->device, req);
06038    return 1;
06039 }
06040 
06041 static int handle_headset_status_message(struct skinny_req *req, struct skinnysession *s)
06042 {
06043    /* XXX umm...okay?  Why do I care? */
06044    return 1;
06045 }
06046 
06047 static int handle_register_available_lines_message(struct skinny_req *req, struct skinnysession *s)
06048 {
06049    /* XXX I have no clue what this is for, but my phone was sending it, so... */
06050    return 1;
06051 }
06052 
06053 static int handle_message(struct skinny_req *req, struct skinnysession *s)
06054 {
06055    int res = 0;
06056 
06057    if ((!s->device) && (letohl(req->e) != REGISTER_MESSAGE && letohl(req->e) != ALARM_MESSAGE)) {
06058       ast_log(LOG_WARNING, "Client sent message #%d without first registering.\n", req->e);
06059       ast_free(req);
06060       return 0;
06061    }
06062 
06063    SKINNY_DEVONLY(if (skinnydebug > 1) {
06064       ast_verb(4, "Received %s from %s\n", message2str(req->e), s->device->name);
06065    })
06066 
06067    switch(letohl(req->e)) {
06068    case KEEP_ALIVE_MESSAGE:
06069       res = handle_keep_alive_message(req, s);
06070       break;
06071    case REGISTER_MESSAGE:
06072       if (skinnydebug)
06073          ast_verb(1, "Device %s is attempting to register\n", req->data.reg.name);
06074 
06075       res = handle_register_message(req, s);
06076       break;
06077    case IP_PORT_MESSAGE:
06078       res = handle_ip_port_message(req, s);
06079       break;
06080    case KEYPAD_BUTTON_MESSAGE:
06081        {
06082       struct skinny_device *d = s->device;
06083       struct skinny_subchannel *sub;
06084       int lineInstance;
06085       int callReference;
06086 
06087       if (skinnydebug)
06088          ast_verb(1, "Collected digit: [%d]\n", letohl(req->data.keypad.button));
06089 
06090       lineInstance = letohl(req->data.keypad.lineInstance);
06091       callReference = letohl(req->data.keypad.callReference);
06092 
06093       if (lineInstance) {
06094          sub = find_subchannel_by_instance_reference(d, lineInstance, callReference);
06095       } else {
06096          sub = d->activeline->activesub;
06097       }
06098 
06099       if (sub && ((sub->owner && sub->owner->_state <  AST_STATE_UP) || sub->onhold)) {
06100          char dgt;
06101          int digit = letohl(req->data.keypad.button);
06102 
06103          if (digit == 14) {
06104             dgt = '*';
06105          } else if (digit == 15) {
06106             dgt = '#';
06107          } else if (digit >= 0 && digit <= 9) {
06108             dgt = '0' + digit;
06109          } else {
06110             /* digit=10-13 (A,B,C,D ?), or
06111             * digit is bad value
06112             *
06113             * probably should not end up here, but set
06114             * value for backward compatibility, and log
06115             * a warning.
06116             */
06117             dgt = '0' + digit;
06118             ast_log(LOG_WARNING, "Unsupported digit %d\n", digit);
06119          }
06120 
06121          d->exten[strlen(d->exten)] = dgt;
06122          d->exten[strlen(d->exten)+1] = '\0';
06123       } else
06124          res = handle_keypad_button_message(req, s);
06125       }
06126       break;
06127    case ENBLOC_CALL_MESSAGE:
06128       res = handle_enbloc_call_message(req, s);
06129       break;
06130    case STIMULUS_MESSAGE:
06131       res = handle_stimulus_message(req, s);
06132       break;
06133    case OFFHOOK_MESSAGE:
06134       res = handle_offhook_message(req, s);
06135       break;
06136    case ONHOOK_MESSAGE:
06137       res = handle_onhook_message(req, s);
06138       break;
06139    case CAPABILITIES_RES_MESSAGE:
06140       if (skinnydebug)
06141          ast_verb(1, "Received CapabilitiesRes\n");
06142 
06143       res = handle_capabilities_res_message(req, s);
06144       break;
06145    case SPEED_DIAL_STAT_REQ_MESSAGE:
06146       if (skinnydebug)
06147          ast_verb(1, "Received SpeedDialStatRequest\n");
06148 
06149       res = handle_speed_dial_stat_req_message(req, s);
06150       break;
06151    case LINE_STATE_REQ_MESSAGE:
06152       if (skinnydebug)
06153          ast_verb(1, "Received LineStatRequest\n");
06154       res = handle_line_state_req_message(req, s);
06155       break;
06156    case TIME_DATE_REQ_MESSAGE:
06157       if (skinnydebug)
06158          ast_verb(1, "Received Time/Date Request\n");
06159 
06160       res = handle_time_date_req_message(req, s);
06161       break;
06162    case BUTTON_TEMPLATE_REQ_MESSAGE:
06163       if (skinnydebug)
06164          ast_verb(1, "Buttontemplate requested\n");
06165 
06166       res = handle_button_template_req_message(req, s);
06167       break;
06168    case VERSION_REQ_MESSAGE:
06169       if (skinnydebug)
06170          ast_verb(1, "Version Request\n");
06171 
06172       res = handle_version_req_message(req, s);
06173       break;
06174    case SERVER_REQUEST_MESSAGE:
06175       if (skinnydebug)
06176          ast_verb(1, "Received Server Request\n");
06177 
06178       res = handle_server_request_message(req, s);
06179       break;
06180    case ALARM_MESSAGE:
06181       res = handle_alarm_message(req, s);
06182       break;
06183    case OPEN_RECEIVE_CHANNEL_ACK_MESSAGE:
06184       if (skinnydebug)
06185          ast_verb(1, "Received Open Receive Channel Ack\n");
06186 
06187       res = handle_open_receive_channel_ack_message(req, s);
06188       break;
06189    case SOFT_KEY_SET_REQ_MESSAGE:
06190       if (skinnydebug)
06191          ast_verb(1, "Received SoftKeySetReq\n");
06192 
06193       res = handle_soft_key_set_req_message(req, s);
06194       break;
06195    case SOFT_KEY_EVENT_MESSAGE:
06196       res = handle_soft_key_event_message(req, s);
06197       break;
06198    case UNREGISTER_MESSAGE:
06199       if (skinnydebug)
06200          ast_verb(1, "Received Unregister Request\n");
06201 
06202       res = handle_unregister_message(req, s);
06203       break;
06204    case SOFT_KEY_TEMPLATE_REQ_MESSAGE:
06205       if (skinnydebug)
06206          ast_verb(1, "Received SoftKey Template Request\n");
06207 
06208       res = handle_soft_key_template_req_message(req, s);
06209       break;
06210    case HEADSET_STATUS_MESSAGE:
06211       res = handle_headset_status_message(req, s);
06212       break;
06213    case REGISTER_AVAILABLE_LINES_MESSAGE:
06214       res = handle_register_available_lines_message(req, s);
06215       break;
06216    default:
06217       if (skinnydebug)
06218          ast_verb(1, "RECEIVED UNKNOWN MESSAGE TYPE:  %x\n", letohl(req->e));
06219       break;
06220    }
06221    if (res >= 0 && req)
06222       ast_free(req);
06223    return res;
06224 }
06225 
06226 static void destroy_session(struct skinnysession *s)
06227 {
06228    struct skinnysession *cur;
06229    AST_LIST_LOCK(&sessions);
06230    AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, cur, list) {
06231       if (cur == s) {
06232          AST_LIST_REMOVE_CURRENT(list);
06233          if (s->fd > -1) 
06234             close(s->fd);
06235          
06236          ast_mutex_destroy(&s->lock);
06237          
06238          ast_free(s);
06239       } else {
06240          ast_log(LOG_WARNING, "Trying to delete nonexistent session %p?\n", s);
06241       }
06242    }
06243    AST_LIST_TRAVERSE_SAFE_END
06244    AST_LIST_UNLOCK(&sessions);
06245 }
06246 
06247 static int get_input(struct skinnysession *s)
06248 {
06249    int res;
06250    int dlen = 0;
06251    int *bufaddr;
06252    struct pollfd fds[1];
06253 
06254    fds[0].fd = s->fd;
06255    fds[0].events = POLLIN;
06256    fds[0].revents = 0;
06257    res = ast_poll(fds, 1, (keep_alive * 1100)); /* If nothing has happen, client is dead */
06258                    /* we add 10% to the keep_alive to deal */
06259                    /* with network delays, etc */
06260    if (res < 0) {
06261       if (errno != EINTR) {
06262          ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
06263          return res;
06264       }
06265    } else if (res == 0) {
06266       if (skinnydebug)
06267          ast_verb(1, "Skinny Client was lost, unregistering\n");
06268       skinny_unregister(NULL, s);
06269       return -1;
06270    }
06271            
06272    if (fds[0].revents) {
06273       ast_mutex_lock(&s->lock);
06274       memset(s->inbuf, 0, sizeof(s->inbuf));
06275       res = read(s->fd, s->inbuf, 4);
06276       if (res < 0) {
06277          ast_log(LOG_WARNING, "read() returned error: %s\n", strerror(errno));
06278 
06279          if (skinnydebug)
06280             ast_verb(1, "Skinny Client was lost, unregistering\n");
06281 
06282          skinny_unregister(NULL, s);
06283          ast_mutex_unlock(&s->lock);
06284          return res;
06285       } else if (res != 4) {
06286          ast_log(LOG_WARNING, "Skinny Client sent less data than expected.  Expected 4 but got %d.\n", res);
06287          ast_mutex_unlock(&s->lock);
06288          
06289          if (res == 0) {
06290             if (skinnydebug)
06291                ast_verb(1, "Skinny Client was lost, unregistering\n");
06292             skinny_unregister(NULL, s);
06293          }
06294 
06295          return -1;
06296       }
06297 
06298       bufaddr = (int *)s->inbuf;
06299       dlen = letohl(*bufaddr);
06300       if (dlen < 4) {
06301          ast_debug(1, "Skinny Client sent invalid data.\n");
06302          ast_mutex_unlock(&s->lock);
06303          return -1;
06304       }
06305       if (dlen+8 > sizeof(s->inbuf)) {
06306          dlen = sizeof(s->inbuf) - 8;
06307       }
06308       *bufaddr = htolel(dlen);
06309 
06310       res = read(s->fd, s->inbuf+4, dlen+4);
06311       ast_mutex_unlock(&s->lock);
06312       if (res < 0) {
06313          ast_log(LOG_WARNING, "read() returned error: %s\n", strerror(errno));
06314          return res;
06315       } else if (res != (dlen+4)) {
06316          ast_log(LOG_WARNING, "Skinny Client sent less data than expected.\n");
06317          return -1;
06318       }
06319       return res;
06320    }
06321    return 0;
06322 }
06323 
06324 static struct skinny_req *skinny_req_parse(struct skinnysession *s)
06325 {
06326    struct skinny_req *req;
06327    int *bufaddr;
06328 
06329    if (!(req = ast_calloc(1, SKINNY_MAX_PACKET)))
06330       return NULL;
06331 
06332    ast_mutex_lock(&s->lock);
06333    memcpy(req, s->inbuf, skinny_header_size);
06334    bufaddr = (int *)(s->inbuf);
06335    memcpy(&req->data, s->inbuf+skinny_header_size, letohl(*bufaddr)-4);
06336 
06337    ast_mutex_unlock(&s->lock);
06338 
06339    if (letohl(req->e) < 0) {
06340       ast_log(LOG_ERROR, "Event Message is NULL from socket %d, This is bad\n", s->fd);
06341       ast_free(req);
06342       return NULL;
06343    }
06344 
06345    return req;
06346 }
06347 
06348 static void *skinny_session(void *data)
06349 {
06350    int res;
06351    struct skinny_req *req;
06352    struct skinnysession *s = data;
06353 
06354    ast_verb(3, "Starting Skinny session from %s\n", ast_inet_ntoa(s->sin.sin_addr));
06355 
06356    for (;;) {
06357       res = get_input(s);
06358       if (res < 0) {
06359          break;
06360       }
06361 
06362       if (res > 0)
06363       {
06364          if (!(req = skinny_req_parse(s))) {
06365             destroy_session(s);
06366             return NULL;
06367          }
06368 
06369          res = handle_message(req, s);
06370          if (res < 0) {
06371             destroy_session(s);
06372             return NULL;
06373          }
06374       }
06375    }
06376    ast_debug(3, "Skinny Session returned: %s\n", strerror(errno));
06377 
06378    if (s) 
06379       destroy_session(s);
06380    
06381    return 0;
06382 }
06383 
06384 static void *accept_thread(void *ignore)
06385 {
06386    int as;
06387    struct sockaddr_in sin;
06388    socklen_t sinlen;
06389    struct skinnysession *s;
06390    struct protoent *p;
06391    int arg = 1;
06392 
06393    for (;;) {
06394       sinlen = sizeof(sin);
06395       as = accept(skinnysock, (struct sockaddr *)&sin, &sinlen);
06396       if (as < 0) {
06397          ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
06398          continue;
06399       }
06400       p = getprotobyname("tcp");
06401       if(p) {
06402          if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
06403             ast_log(LOG_WARNING, "Failed to set Skinny tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
06404          }
06405       }
06406       if (!(s = ast_calloc(1, sizeof(struct skinnysession))))
06407          continue;
06408 
06409       memcpy(&s->sin, &sin, sizeof(sin));
06410       ast_mutex_init(&s->lock);
06411       s->fd = as;
06412       AST_LIST_LOCK(&sessions);
06413       AST_LIST_INSERT_HEAD(&sessions, s, list);
06414       AST_LIST_UNLOCK(&sessions);
06415 
06416       if (ast_pthread_create(&s->t, NULL, skinny_session, s)) {
06417          destroy_session(s);
06418       }
06419    }
06420    if (skinnydebug)
06421       ast_verb(1, "killing accept thread\n");
06422    close(as);
06423    return 0;
06424 }
06425 
06426 static void *do_monitor(void *data)
06427 {
06428    int res;
06429 
06430    /* This thread monitors all the interfaces which are not yet in use
06431       (and thus do not have a separate thread) indefinitely */
06432    /* From here on out, we die whenever asked */
06433    for(;;) {
06434       pthread_testcancel();
06435       /* Wait for sched or io */
06436       res = ast_sched_wait(sched);
06437       if ((res < 0) || (res > 1000)) {
06438          res = 1000;
06439       }
06440       res = ast_io_wait(io, res);
06441       ast_mutex_lock(&monlock);
06442       if (res >= 0) {
06443          ast_sched_runq(sched);
06444       }
06445       ast_mutex_unlock(&monlock);
06446    }
06447    /* Never reached */
06448    return NULL;
06449 
06450 }
06451 
06452 static int restart_monitor(void)
06453 {
06454    /* If we're supposed to be stopped -- stay stopped */
06455    if (monitor_thread == AST_PTHREADT_STOP)
06456       return 0;
06457 
06458    ast_mutex_lock(&monlock);
06459    if (monitor_thread == pthread_self()) {
06460       ast_mutex_unlock(&monlock);
06461       ast_log(LOG_WARNING, "Cannot kill myself\n");
06462       return -1;
06463    }
06464    if (monitor_thread != AST_PTHREADT_NULL) {
06465       /* Wake up the thread */
06466       pthread_kill(monitor_thread, SIGURG);
06467    } else {
06468       /* Start a new monitor */
06469       if (ast_pthread_create_background(&monitor_thread, NULL, do_monitor, NULL) < 0) {
06470          ast_mutex_unlock(&monlock);
06471          ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
06472          return -1;
06473       }
06474    }
06475    ast_mutex_unlock(&monlock);
06476    return 0;
06477 }
06478 
06479 static int skinny_devicestate(void *data)
06480 {
06481    struct skinny_line *l;
06482    char *tmp;
06483 
06484    tmp = ast_strdupa(data);
06485 
06486    l = find_line_by_name(tmp);
06487 
06488    return get_devicestate(l);
06489 }
06490 
06491 static struct ast_channel *skinny_request(const char *type, int format, void *data, int *cause)
06492 {
06493    int oldformat;
06494    
06495    struct skinny_line *l;
06496    struct ast_channel *tmpc = NULL;
06497    char tmp[256];
06498    char *dest = data;
06499 
06500    oldformat = format;
06501    
06502    if (!(format &= AST_FORMAT_AUDIO_MASK)) {
06503       ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", format);
06504       return NULL;
06505    }
06506 
06507    ast_copy_string(tmp, dest, sizeof(tmp));
06508    if (ast_strlen_zero(tmp)) {
06509       ast_log(LOG_NOTICE, "Skinny channels require a device\n");
06510       return NULL;
06511    }
06512    l = find_line_by_name(tmp);
06513    if (!l) {
06514       ast_log(LOG_NOTICE, "No available lines on: %s\n", dest);
06515       return NULL;
06516    }
06517    ast_verb(3, "skinny_request(%s)\n", tmp);
06518    tmpc = skinny_new(l, AST_STATE_DOWN);
06519    if (!tmpc) {
06520       ast_log(LOG_WARNING, "Unable to make channel for '%s'\n", tmp);
06521    }
06522    restart_monitor();
06523    return tmpc;
06524 }
06525 
06526  #define TYPE_GENERAL   1
06527  #define TYPE_DEF_DEVICE 2
06528  #define TYPE_DEF_LINE  4
06529  #define TYPE_DEVICE    8
06530  #define TYPE_LINE   16
06531  
06532  #define CLINE_OPTS  ((struct skinny_line_options *)item)
06533  #define CLINE    ((struct skinny_line *)item)
06534  #define CDEV_OPTS   ((struct skinny_device_options *)item)
06535  #define CDEV     ((struct skinny_device *)item)
06536  
06537  static void config_parse_variables(int type, void *item, struct ast_variable *vptr)
06538  {
06539    struct ast_variable *v;
06540    int lineInstance = 1;
06541    int speeddialInstance = 1;
06542    
06543    while(vptr) {
06544       v = vptr;
06545       vptr = vptr->next;
06546  
06547       if (type & (TYPE_GENERAL)) {
06548          char newcontexts[AST_MAX_CONTEXT];
06549          char oldcontexts[AST_MAX_CONTEXT];
06550          char *stringp, *context, *oldregcontext;
06551          if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) {
06552             v = v->next;
06553             continue;
06554          }
06555          if (!strcasecmp(v->name, "bindaddr")) {
06556             if (!(hp = ast_gethostbyname(v->value, &ahp))) {
06557                ast_log(LOG_WARNING, "Invalid address: %s\n", v->value);
06558             } else {
06559                memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr));
06560             }
06561             continue;
06562          } else if (!strcasecmp(v->name, "keepalive")) {
06563             keep_alive = atoi(v->value);
06564             continue;
06565          } else if (!strcasecmp(v->name, "regcontext")) {
06566             ast_copy_string(newcontexts, v->value, sizeof(newcontexts));
06567             stringp = newcontexts;
06568             /* Initialize copy of current global_regcontext for later use in removing stale contexts */
06569             ast_copy_string(oldcontexts, regcontext, sizeof(oldcontexts));
06570             oldregcontext = oldcontexts;
06571             /* Let's remove any contexts that are no longer defined in regcontext */
06572             cleanup_stale_contexts(stringp, oldregcontext);
06573             /* Create contexts if they don't exist already */
06574             while ((context = strsep(&stringp, "&"))) {
06575                ast_copy_string(used_context, context, sizeof(used_context));
06576                ast_context_find_or_create(NULL, NULL, context, "Skinny");
06577             }
06578             ast_copy_string(regcontext, v->value, sizeof(regcontext));
06579             continue;
06580          } else if (!strcasecmp(v->name, "dateformat")) {
06581             memcpy(date_format, v->value, sizeof(date_format));
06582             continue;
06583          } else if (!strcasecmp(v->name, "tos")) {
06584             if (ast_str2tos(v->value, &qos.tos))
06585                ast_log(LOG_WARNING, "Invalid tos value at line %d, refer to QoS documentation\n", v->lineno);
06586             continue;
06587          } else if (!strcasecmp(v->name, "tos_audio")) {
06588             if (ast_str2tos(v->value, &qos.tos_audio))
06589                ast_log(LOG_WARNING, "Invalid tos_audio value at line %d, refer to QoS documentation\n", v->lineno);
06590             continue;
06591          } else if (!strcasecmp(v->name, "tos_video")) {
06592             if (ast_str2tos(v->value, &qos.tos_video))
06593                ast_log(LOG_WARNING, "Invalid tos_video value at line %d, refer to QoS documentation\n", v->lineno);
06594             continue;
06595          } else if (!strcasecmp(v->name, "cos")) {
06596             if (ast_str2cos(v->value, &qos.cos))
06597                ast_log(LOG_WARNING, "Invalid cos value at line %d, refer to QoS documentation\n", v->lineno);
06598             continue;
06599          } else if (!strcasecmp(v->name, "cos_audio")) {
06600             if (ast_str2cos(v->value, &qos.cos_audio))
06601                ast_log(LOG_WARNING, "Invalid cos_audio value at line %d, refer to QoS documentation\n", v->lineno);
06602             continue;
06603          } else if (!strcasecmp(v->name, "cos_video")) {
06604             if (ast_str2cos(v->value, &qos.cos_video))
06605                ast_log(LOG_WARNING, "Invalid cos_video value at line %d, refer to QoS documentation\n", v->lineno);
06606             continue;
06607          } else if (!strcasecmp(v->name, "bindport")) {
06608             if (sscanf(v->value, "%5d", &ourport) == 1) {
06609                bindaddr.sin_port = htons(ourport);
06610             } else {
06611                ast_log(LOG_WARNING, "Invalid bindport '%s' at line %d of %s\n", v->value, v->lineno, config);
06612             }
06613             continue;
06614          } else if (!strcasecmp(v->name, "allow")) {
06615             ast_parse_allow_disallow(&default_prefs, &default_capability, v->value, 1);
06616             continue;
06617          } else if (!strcasecmp(v->name, "disallow")) {
06618             ast_parse_allow_disallow(&default_prefs, &default_capability, v->value, 0);
06619             continue;
06620          } 
06621       }
06622  
06623       if (!strcasecmp(v->name, "transfer")) {
06624          if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06625             CDEV_OPTS->transfer = ast_true(v->value);
06626             continue;
06627          } else if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06628             CLINE_OPTS->transfer = ast_true(v->value);
06629             continue;
06630          }
06631       } else if (!strcasecmp(v->name, "callwaiting")) {
06632          if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06633             CDEV_OPTS->callwaiting = ast_true(v->value);
06634             continue;
06635          } else if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06636             CLINE_OPTS->callwaiting = ast_true(v->value);
06637             continue;
06638          }
06639       } else if (!strcasecmp(v->name, "directmedia") || !strcasecmp(v->name, "canreinvite")) {
06640          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06641             CLINE_OPTS->directmedia = ast_true(v->value);
06642             continue;
06643          }
06644       } else if (!strcasecmp(v->name, "nat")) {
06645          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06646             CLINE_OPTS->nat = ast_true(v->value);
06647             continue;
06648          }
06649       } else if (!strcasecmp(v->name, "context")) {
06650          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06651             ast_copy_string(CLINE_OPTS->context, v->value, sizeof(CLINE_OPTS->context));
06652             continue;
06653          }
06654       }else if (!strcasecmp(v->name, "vmexten")) {
06655          if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06656             ast_copy_string(CDEV_OPTS->vmexten, v->value, sizeof(CDEV_OPTS->vmexten));
06657             continue;
06658          } else if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06659             ast_copy_string(CLINE_OPTS->vmexten, v->value, sizeof(CLINE_OPTS->vmexten));
06660             continue;
06661          }
06662       } else if (!strcasecmp(v->name, "mwiblink")) {
06663          if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06664             CDEV_OPTS->mwiblink = ast_true(v->value);
06665             continue;
06666          } else if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06667             CLINE_OPTS->mwiblink = ast_true(v->value);
06668             continue;
06669          }
06670       } else if (!strcasecmp(v->name, "linelabel")) {
06671          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06672             ast_copy_string(CLINE_OPTS->label, v->value, sizeof(CLINE_OPTS->label));
06673             continue;
06674          }
06675       } else if (!strcasecmp(v->name, "callerid")) {
06676          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06677             if (!strcasecmp(v->value, "asreceived")) {
06678                CLINE_OPTS->cid_num[0] = '\0';
06679                CLINE_OPTS->cid_name[0] = '\0';
06680             } else {
06681                ast_callerid_split(v->value, CLINE_OPTS->cid_name, sizeof(CLINE_OPTS->cid_name), CLINE_OPTS->cid_num, sizeof(CLINE_OPTS->cid_num));
06682             }
06683             continue;
06684          }
06685       } else if (!strcasecmp(v->name, "amaflags")) {
06686          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06687             int tempamaflags = ast_cdr_amaflags2int(v->value);
06688             if (tempamaflags < 0) {
06689                ast_log(LOG_WARNING, "Invalid AMA flags: %s at line %d\n", v->value, v->lineno);
06690             } else {
06691                CLINE_OPTS->amaflags = tempamaflags;
06692             }
06693             continue;
06694          }
06695       } else if (!strcasecmp(v->name, "regexten")) {
06696          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06697             ast_copy_string(CLINE_OPTS->regexten, v->value, sizeof(CLINE_OPTS->regexten));
06698             continue;
06699          }
06700       } else if (!strcasecmp(v->name, "language")) {
06701          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06702             ast_copy_string(CLINE_OPTS->language, v->value, sizeof(CLINE_OPTS->language));
06703             continue;
06704          }
06705       } else if (!strcasecmp(v->name, "accountcode")) {
06706          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06707             ast_copy_string(CLINE_OPTS->accountcode, v->value, sizeof(CLINE_OPTS->accountcode));
06708             continue;
06709          }
06710       } else if (!strcasecmp(v->name, "mohinterpret") || !strcasecmp(v->name, "musiconhold")) {
06711          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06712             ast_copy_string(CLINE_OPTS->mohinterpret, v->value, sizeof(CLINE_OPTS->mohinterpret));
06713             continue;
06714          }
06715       } else if (!strcasecmp(v->name, "mohsuggest")) {
06716          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06717             ast_copy_string(CLINE_OPTS->mohsuggest, v->value, sizeof(CLINE_OPTS->mohsuggest));
06718             continue;
06719          }
06720       } else if (!strcasecmp(v->name, "callgroup")) {
06721          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06722             CLINE_OPTS->callgroup = ast_get_group(v->value);
06723             continue;
06724          }
06725       } else if (!strcasecmp(v->name, "pickupgroup")) {
06726          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06727             CLINE_OPTS->pickupgroup = ast_get_group(v->value);
06728             continue;
06729          }
06730       } else if (!strcasecmp(v->name, "immediate")) {
06731          if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE | TYPE_DEF_LINE | TYPE_LINE)) {
06732             CLINE_OPTS->immediate = ast_true(v->value);
06733             continue;
06734          }
06735       } else if (!strcasecmp(v->name, "cancallforward")) {
06736          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06737             CLINE_OPTS->cancallforward = ast_true(v->value);
06738             continue;
06739          }
06740       } else if (!strcasecmp(v->name, "mailbox")) {
06741          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06742             ast_copy_string(CLINE_OPTS->mailbox, v->value, sizeof(CLINE_OPTS->mailbox));
06743             continue;
06744          }
06745       } else if ( !strcasecmp(v->name, "parkinglot")) {
06746          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06747             ast_copy_string(CLINE_OPTS->parkinglot, v->value, sizeof(CLINE_OPTS->parkinglot));
06748             continue;
06749          }
06750       } else if (!strcasecmp(v->name, "hasvoicemail")) {
06751          if (type & (TYPE_LINE)) {
06752             if (ast_true(v->value) && ast_strlen_zero(CLINE->mailbox)) {
06753                ast_copy_string(CLINE->mailbox, CLINE->name, sizeof(CLINE->mailbox));
06754             }
06755             continue;
06756          }
06757       } else if (!strcasecmp(v->name, "callreturn")) {
06758          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06759             CLINE_OPTS->callreturn = ast_true(v->value);
06760             continue;
06761          }
06762       } else if (!strcasecmp(v->name, "threewaycalling")) {
06763          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06764             CLINE_OPTS->threewaycalling = ast_true(v->value);
06765             continue;
06766          }
06767       } else if (!strcasecmp(v->name, "setvar")) {
06768          if (type & (TYPE_LINE)) {
06769             CLINE->chanvars = add_var(v->value, CLINE->chanvars);
06770             continue;
06771          }
06772       } else if (!strcasecmp(v->name, "earlyrtp")) {
06773          if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06774             CDEV_OPTS->earlyrtp = ast_true(v->value);
06775             continue;
06776          }
06777       } else if (!strcasecmp(v->name, "host")) {
06778          if (type & (TYPE_DEVICE)) {
06779             if (ast_get_ip(&CDEV->addr, v->value)) {
06780                ast_log(LOG_WARNING, "Bad IP '%s' at line %d.\n", v->value, v->lineno);
06781             }
06782             continue;
06783          }
06784       } else if (!strcasecmp(v->name, "port")) {
06785          if (type & (TYPE_DEF_DEVICE)) {
06786             CDEV->addr.sin_port = htons(atoi(v->value));
06787             continue;
06788          }
06789       } else if (!strcasecmp(v->name, "device")) {
06790          if (type & (TYPE_DEVICE)) {
06791             ast_copy_string(CDEV_OPTS->id, v->value, sizeof(CDEV_OPTS->id));
06792             continue;
06793          }
06794       } else if (!strcasecmp(v->name, "permit") || !strcasecmp(v->name, "deny")) {
06795          if (type & (TYPE_DEVICE)) {
06796             CDEV->ha = ast_append_ha(v->name, v->value, CDEV->ha, NULL);
06797             continue;
06798          }
06799       } else if (!strcasecmp(v->name, "allow")) {
06800          if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06801             ast_parse_allow_disallow(&CDEV_OPTS->confprefs, &CDEV_OPTS->confcapability, v->value, 1);
06802             continue;
06803          }
06804          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06805             ast_parse_allow_disallow(&CLINE_OPTS->confprefs, &CLINE_OPTS->confcapability, v->value, 1);
06806             continue;
06807          }
06808       } else if (!strcasecmp(v->name, "disallow")) {
06809          if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06810             ast_parse_allow_disallow(&CDEV_OPTS->confprefs, &CDEV_OPTS->confcapability, v->value, 0);
06811             continue;
06812          }
06813          if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06814             ast_parse_allow_disallow(&CLINE_OPTS->confprefs, &CLINE_OPTS->confcapability, v->value, 0);
06815             continue;
06816          }
06817       } else if (!strcasecmp(v->name, "version")) {
06818          if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06819             ast_copy_string(CDEV_OPTS->version_id, v->value, sizeof(CDEV_OPTS->version_id));
06820             continue;
06821          }
06822       } else if (!strcasecmp(v->name, "line")) {
06823          if (type & (TYPE_DEVICE)) {
06824             struct skinny_line *l;
06825             AST_LIST_TRAVERSE(&lines, l, all) {
06826                if (!strcasecmp(v->value, l->name) && !l->prune) {
06827 
06828                   /* FIXME: temp solution about line conflicts */
06829                   struct skinny_device *d;
06830                   struct skinny_line *l2;
06831                   int lineinuse = 0;
06832                   AST_LIST_TRAVERSE(&devices, d, list) {
06833                      AST_LIST_TRAVERSE(&d->lines, l2, list) {
06834                         if (l2 == l && strcasecmp(d->id, CDEV->id)) {
06835                            ast_log(LOG_WARNING, "Line %s already used by %s. Not connecting to %s.\n", l->name, d->name, CDEV->name);
06836                            lineinuse++;
06837                         }
06838                      }
06839                   }
06840                   if (!lineinuse) {
06841                      if (!AST_LIST_FIRST(&CDEV->lines)) {
06842                         CDEV->activeline = l;
06843                      }
06844                      lineInstance++;
06845                      AST_LIST_INSERT_HEAD(&CDEV->lines, l, list);
06846                   }
06847                   break;
06848                }
06849             }
06850             continue;
06851          }
06852       } else if (!strcasecmp(v->name, "speeddial")) {
06853          if (type & (TYPE_DEVICE)) {
06854             struct skinny_speeddial *sd;
06855             if (!(sd = ast_calloc(1, sizeof(*sd)))) {
06856                ast_log(LOG_WARNING, "Unable to allocate memory for speeddial %s. Ignoring speeddial.\n", v->name);
06857                continue;
06858             } else {
06859                char buf[256];
06860                char *stringp = buf, *exten, *context, *label;
06861                   ast_copy_string(buf, v->value, sizeof(buf));
06862                exten = strsep(&stringp, ",");
06863                if ((context = strchr(exten, '@'))) {
06864                   *context++ = '\0';
06865                }
06866                label = stringp;
06867                ast_mutex_init(&sd->lock);
06868                ast_copy_string(sd->exten, exten, sizeof(sd->exten));
06869                if (!ast_strlen_zero(context)) {
06870                   sd->isHint = 1;
06871                   sd->instance = lineInstance++;
06872                   ast_copy_string(sd->context, context, sizeof(sd->context));
06873                } else {
06874                   sd->isHint = 0;
06875                   sd->instance = speeddialInstance++;
06876                   sd->context[0] = '\0';
06877                }
06878                ast_copy_string(sd->label, S_OR(label, exten), sizeof(sd->label));
06879                sd->parent = CDEV;
06880                AST_LIST_INSERT_HEAD(&CDEV->speeddials, sd, list);
06881             }
06882             continue;
06883          }
06884       } else if (!strcasecmp(v->name, "addon")) {
06885          if (type & (TYPE_DEVICE)) {
06886             struct skinny_addon *a;
06887             if (!(a = ast_calloc(1, sizeof(*a)))) {
06888                ast_log(LOG_WARNING, "Unable to allocate memory for addon %s. Ignoring addon.\n", v->name);
06889                continue;
06890             } else {
06891                ast_mutex_init(&a->lock);
06892                ast_copy_string(a->type, v->value, sizeof(a->type));
06893                AST_LIST_INSERT_HEAD(&CDEV->addons, a, list);
06894             }
06895             continue;
06896          }
06897 
06898       } else {
06899          ast_log(LOG_WARNING, "Don't know keyword '%s' at line %d\n", v->name, v->lineno);
06900          continue;
06901       }
06902       ast_log(LOG_WARNING, "Invalid category used: %s at line %d\n", v->name, v->lineno);
06903    }
06904  }
06905  
06906  static struct skinny_line *config_line(const char *lname, struct ast_variable *v)
06907  {
06908    struct skinny_line *l, *temp;
06909    int update = 0;
06910  
06911    ast_log(LOG_NOTICE, "Configuring skinny line %s.\n", lname);
06912 
06913    /* We find the old line and remove it just before the new
06914       line is created */
06915    AST_LIST_LOCK(&lines);
06916    AST_LIST_TRAVERSE(&lines, temp, all) {
06917       if (!strcasecmp(lname, temp->name) && temp->prune) {
06918          update = 1;
06919          break;
06920       }
06921    }
06922 
06923    if (!(l=ast_calloc(1, sizeof(*l)))) {
06924       ast_verb(1, "Unable to allocate memory for line %s.\n", lname);
06925       AST_LIST_UNLOCK(&lines);
06926       return NULL;
06927    }
06928 
06929    memcpy(l, default_line, sizeof(*default_line));
06930    ast_mutex_init(&l->lock);
06931    ast_copy_string(l->name, lname, sizeof(l->name));
06932    AST_LIST_INSERT_TAIL(&lines, l, all);
06933 
06934    ast_mutex_lock(&l->lock);
06935    AST_LIST_UNLOCK(&lines);
06936 
06937    config_parse_variables(TYPE_LINE, l, v);
06938          
06939    if (!ast_strlen_zero(l->mailbox)) {
06940       char *cfg_mailbox, *cfg_context;
06941       cfg_context = cfg_mailbox = ast_strdupa(l->mailbox);
06942       ast_verb(3, "Setting mailbox '%s' on line %s\n", cfg_mailbox, l->name);
06943       strsep(&cfg_context, "@");
06944       if (ast_strlen_zero(cfg_context))
06945           cfg_context = "default";
06946       l->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, l,
06947          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, cfg_mailbox,
06948          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, cfg_context,
06949          AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
06950          AST_EVENT_IE_END);
06951    }
06952  
06953    ast_mutex_unlock(&l->lock);
06954    
06955    /* We do not want to unlink or free the line yet, it needs
06956       to be available to detect a device reconfig when we load the
06957       devices.  Old lines will be pruned after the reload completes */
06958 
06959    ast_verb(3, "%s config for line '%s'\n", update ? "Updated" : (skinnyreload ? "Reloaded" : "Created"), l->name);
06960 
06961    return l;
06962  }
06963  
06964  static struct skinny_device *config_device(const char *dname, struct ast_variable *v)
06965  {
06966    struct skinny_device *d, *temp;
06967    struct skinny_line *l, *ltemp;
06968    struct skinny_subchannel *sub;
06969    int update = 0;
06970  
06971    ast_log(LOG_NOTICE, "Configuring skinny device %s.\n", dname);
06972 
06973    AST_LIST_LOCK(&devices);
06974    AST_LIST_TRAVERSE(&devices, temp, list) {
06975       if (!strcasecmp(dname, temp->name) && temp->prune) {
06976          update = 1;
06977          break;
06978       }
06979    }
06980 
06981    if (!(d = ast_calloc(1, sizeof(*d)))) {
06982       ast_verb(1, "Unable to allocate memory for device %s.\n", dname);
06983       AST_LIST_UNLOCK(&devices);
06984       return NULL;
06985    }
06986    memcpy(d, default_device, sizeof(*default_device));
06987    ast_mutex_init(&d->lock);
06988    ast_copy_string(d->name, dname, sizeof(d->name));
06989    AST_LIST_INSERT_TAIL(&devices, d, list);
06990 
06991    ast_mutex_lock(&d->lock);
06992    AST_LIST_UNLOCK(&devices);
06993  
06994    config_parse_variables(TYPE_DEVICE, d, v);
06995  
06996    if (!AST_LIST_FIRST(&d->lines)) {
06997       ast_log(LOG_ERROR, "A Skinny device must have at least one line!\n");
06998       ast_mutex_unlock(&d->lock);
06999       return NULL;
07000    }
07001    if (/*d->addr.sin_addr.s_addr && */!ntohs(d->addr.sin_port)) {
07002       d->addr.sin_port = htons(DEFAULT_SKINNY_PORT);
07003    }
07004  
07005    if (skinnyreload){
07006       AST_LIST_LOCK(&devices);
07007       AST_LIST_TRAVERSE(&devices, temp, list) {
07008          if (strcasecmp(d->id, temp->id) || !temp->prune || !temp->session) {
07009             continue;
07010          }
07011          ast_mutex_lock(&d->lock);
07012          d->session = temp->session;
07013          d->session->device = d;
07014 
07015          AST_LIST_LOCK(&d->lines);
07016          AST_LIST_TRAVERSE(&d->lines, l, list){
07017             l->device = d; 
07018 
07019             AST_LIST_LOCK(&temp->lines);
07020             AST_LIST_TRAVERSE(&temp->lines, ltemp, list) {
07021                if (strcasecmp(l->name, ltemp->name)) {
07022                   continue;
07023                }
07024                ast_mutex_lock(&ltemp->lock);
07025                l->instance = ltemp->instance;
07026                l->hookstate = ltemp->hookstate;
07027                if (!AST_LIST_EMPTY(&ltemp->sub)) {
07028                   ast_mutex_lock(&l->lock);
07029                   l->sub = ltemp->sub;
07030                   AST_LIST_TRAVERSE(&l->sub, sub, list) {
07031                      sub->parent = l;
07032                   }
07033                   ast_mutex_unlock(&l->lock);
07034                }
07035                ast_mutex_unlock(&ltemp->lock);
07036             }
07037             AST_LIST_UNLOCK(&temp->lines);
07038          }
07039          AST_LIST_UNLOCK(&d->lines);
07040          ast_mutex_unlock(&d->lock);
07041       }
07042       AST_LIST_UNLOCK(&devices);
07043    }
07044 
07045    ast_mutex_unlock(&d->lock);
07046 
07047    ast_verb(3, "%s config for device '%s'\n", update ? "Updated" : (skinnyreload ? "Reloaded" : "Created"), d->name);
07048    
07049    return d;
07050 
07051  }
07052  
07053  static int config_load(void)
07054  {
07055    int on = 1;
07056    struct ast_config *cfg;
07057    char *cat;
07058    struct skinny_device *d;
07059    struct skinny_line *l;
07060    int oldport = ntohs(bindaddr.sin_port);
07061    struct ast_flags config_flags = { 0 };
07062    
07063    ast_log(LOG_NOTICE, "Configuring skinny from %s\n", config);
07064   
07065    if (gethostname(ourhost, sizeof(ourhost))) {
07066       ast_log(LOG_WARNING, "Unable to get hostname, Skinny disabled.\n");
07067       return 0;
07068    }
07069    cfg = ast_config_load(config, config_flags);
07070   
07071    /* We *must* have a config file otherwise stop immediately */
07072    if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
07073       ast_log(LOG_NOTICE, "Unable to load config %s, Skinny disabled.\n", config);
07074       return -1;
07075    }
07076    memset(&bindaddr, 0, sizeof(bindaddr));
07077    memset(&default_prefs, 0, sizeof(default_prefs));
07078 
07079    /* Copy the default jb config over global_jbconf */
07080    memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
07081 
07082    /* load the general section */
07083    cat = ast_category_browse(cfg, "general");
07084    config_parse_variables(TYPE_GENERAL, NULL, ast_variable_browse(cfg, "general"));
07085 
07086    if (ntohl(bindaddr.sin_addr.s_addr)) {
07087       __ourip = bindaddr.sin_addr;
07088    } else {
07089       hp = ast_gethostbyname(ourhost, &ahp);
07090       if (!hp) {
07091          ast_log(LOG_WARNING, "Unable to get our IP address, Skinny disabled\n");
07092          ast_config_destroy(cfg);
07093          return 0;
07094       }
07095       memcpy(&__ourip, hp->h_addr, sizeof(__ourip));
07096    }
07097    if (!ntohs(bindaddr.sin_port)) {
07098       bindaddr.sin_port = ntohs(DEFAULT_SKINNY_PORT);
07099    }
07100    bindaddr.sin_family = AF_INET;
07101 
07102    /* load the lines sections */
07103    default_line->confcapability = default_capability;
07104    default_line->confprefs = default_prefs;
07105    config_parse_variables(TYPE_DEF_LINE, default_line, ast_variable_browse(cfg, "lines"));
07106    cat = ast_category_browse(cfg, "lines");
07107    while (cat && strcasecmp(cat, "general") && strcasecmp(cat, "devices")) {
07108       l = config_line(cat, ast_variable_browse(cfg, cat));
07109       cat = ast_category_browse(cfg, cat);
07110    }
07111       
07112    /* load the devices sections */
07113    default_device->confcapability = default_capability;
07114    default_device->confprefs = default_prefs;
07115    config_parse_variables(TYPE_DEF_DEVICE, default_device, ast_variable_browse(cfg, "devices"));
07116    cat = ast_category_browse(cfg, "devices");
07117    while (cat && strcasecmp(cat, "general") && strcasecmp(cat, "lines")) {
07118       d = config_device(cat, ast_variable_browse(cfg, cat));
07119       cat = ast_category_browse(cfg, cat);
07120    }
07121 
07122    ast_mutex_lock(&netlock);
07123    if ((skinnysock > -1) && (ntohs(bindaddr.sin_port) != oldport)) {
07124       close(skinnysock);
07125       skinnysock = -1;
07126    }
07127    if (skinnysock < 0) {
07128       skinnysock = socket(AF_INET, SOCK_STREAM, 0);
07129       if(setsockopt(skinnysock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
07130          ast_log(LOG_ERROR, "Set Socket Options failed: errno %d, %s\n", errno, strerror(errno));
07131          ast_config_destroy(cfg);
07132          ast_mutex_unlock(&netlock);
07133          return 0;
07134       }
07135       if (skinnysock < 0) {
07136          ast_log(LOG_WARNING, "Unable to create Skinny socket: %s\n", strerror(errno));
07137       } else {
07138          if (bind(skinnysock, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) {
07139             ast_log(LOG_WARNING, "Failed to bind to %s:%d: %s\n",
07140                   ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port),
07141                      strerror(errno));
07142             close(skinnysock);
07143             skinnysock = -1;
07144             ast_config_destroy(cfg);
07145             ast_mutex_unlock(&netlock);
07146             return 0;
07147          }
07148          if (listen(skinnysock, DEFAULT_SKINNY_BACKLOG)) {
07149                ast_log(LOG_WARNING, "Failed to start listening to %s:%d: %s\n",
07150                   ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port),
07151                      strerror(errno));
07152                close(skinnysock);
07153                skinnysock = -1;
07154                ast_config_destroy(cfg);
07155                ast_mutex_unlock(&netlock);
07156                return 0;
07157          }
07158          ast_verb(2, "Skinny listening on %s:%d\n",
07159                ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port));
07160          ast_netsock_set_qos(skinnysock, qos.tos, qos.cos, "Skinny");
07161          ast_pthread_create_background(&accept_t, NULL, accept_thread, NULL);
07162       }
07163    }
07164    ast_mutex_unlock(&netlock);
07165    ast_config_destroy(cfg);
07166    return 1;
07167 }
07168 
07169 static void delete_devices(void)
07170 {
07171    struct skinny_device *d;
07172    struct skinny_line *l;
07173    struct skinny_speeddial *sd;
07174    struct skinny_addon *a;
07175 
07176    AST_LIST_LOCK(&devices);
07177    AST_LIST_LOCK(&lines);
07178 
07179    /* Delete all devices */
07180    while ((d = AST_LIST_REMOVE_HEAD(&devices, list))) {
07181       /* Delete all lines for this device */
07182       while ((l = AST_LIST_REMOVE_HEAD(&d->lines, list))) {
07183          AST_LIST_REMOVE(&lines, l, all);
07184          free(l);
07185       }
07186       /* Delete all speeddials for this device */
07187       while ((sd = AST_LIST_REMOVE_HEAD(&d->speeddials, list))) {
07188          free(sd);
07189       }
07190       /* Delete all addons for this device */
07191       while ((a = AST_LIST_REMOVE_HEAD(&d->addons, list))) {
07192          free(a);
07193       } 
07194       free(d);
07195    }
07196    AST_LIST_UNLOCK(&lines);
07197    AST_LIST_UNLOCK(&devices);
07198 }
07199 
07200 int skinny_reload(void)
07201 {
07202    struct skinny_device *d;
07203    struct skinny_line *l;
07204    struct skinny_speeddial *sd;
07205    struct skinny_addon *a;
07206    struct skinny_req *req;
07207 
07208    if (skinnyreload) {
07209       ast_verb(3, "Chan_skinny is already reloading.\n");
07210       return 0;
07211    }
07212 
07213    skinnyreload = 1;
07214 
07215    /* Mark all devices and lines as candidates to be pruned */
07216    AST_LIST_LOCK(&devices);
07217    AST_LIST_TRAVERSE(&devices, d, list) {
07218       d->prune = 1;
07219    }
07220    AST_LIST_UNLOCK(&devices);
07221 
07222    AST_LIST_LOCK(&lines);
07223    AST_LIST_TRAVERSE(&lines, l, all) {
07224       l->prune = 1;
07225    }
07226    AST_LIST_UNLOCK(&lines);
07227 
07228         config_load();
07229 
07230    /* Remove any devices that no longer exist in the config */
07231    AST_LIST_LOCK(&devices);
07232    AST_LIST_TRAVERSE_SAFE_BEGIN(&devices, d, list) {
07233       if (!d->prune) {
07234          continue;
07235       }
07236       ast_verb(3, "Removing device '%s'\n", d->name);
07237       /* Delete all lines for this device. 
07238          We do not want to free the line here, that
07239          will happen below. */
07240       while ((l = AST_LIST_REMOVE_HEAD(&d->lines, list))) {
07241       }
07242       /* Delete all speeddials for this device */
07243       while ((sd = AST_LIST_REMOVE_HEAD(&d->speeddials, list))) {
07244          free(sd);
07245       }
07246       /* Delete all addons for this device */
07247       while ((a = AST_LIST_REMOVE_HEAD(&d->addons, list))) {
07248          free(a);
07249       }
07250       AST_LIST_REMOVE_CURRENT(list);
07251       free(d);
07252    }
07253    AST_LIST_TRAVERSE_SAFE_END;
07254    AST_LIST_UNLOCK(&devices);
07255 
07256    AST_LIST_LOCK(&lines);  
07257    AST_LIST_TRAVERSE_SAFE_BEGIN(&lines, l, all) {
07258       if (l->prune) {
07259          AST_LIST_REMOVE_CURRENT(all);
07260          free(l);
07261       }
07262    }
07263    AST_LIST_TRAVERSE_SAFE_END;
07264    AST_LIST_UNLOCK(&lines);  
07265 
07266    AST_LIST_TRAVERSE(&devices, d, list) {
07267       /* Do a soft reset to re-register the devices after
07268          cleaning up the removed devices and lines */
07269       if (d->session) {
07270          ast_verb(3, "Restarting device '%s'\n", d->name);
07271          if ((req = req_alloc(sizeof(struct reset_message), RESET_MESSAGE))) {
07272             req->data.reset.resetType = 2;
07273             transmit_response(d, req);
07274          }
07275       }
07276    }
07277    
07278    skinnyreload = 0;
07279         return 0;
07280 }
07281 
07282 static int load_module(void)
07283 {
07284    int res = 0;
07285 
07286    for (; res < ARRAY_LEN(soft_key_template_default); res++) {
07287       soft_key_template_default[res].softKeyEvent = htolel(soft_key_template_default[res].softKeyEvent);
07288    }
07289    /* load and parse config */
07290    res = config_load();
07291    if (res == -1) {
07292       return AST_MODULE_LOAD_DECLINE;
07293    }
07294 
07295    /* Make sure we can register our skinny channel type */
07296    if (ast_channel_register(&skinny_tech)) {
07297       ast_log(LOG_ERROR, "Unable to register channel class 'Skinny'\n");
07298       return -1;
07299    }
07300 
07301    ast_rtp_proto_register(&skinny_rtp);
07302    ast_cli_register_multiple(cli_skinny, ARRAY_LEN(cli_skinny));
07303 
07304    ast_manager_register2("SKINNYdevices", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_devices,
07305          "List SKINNY devices (text format)", mandescr_show_devices);
07306    ast_manager_register2("SKINNYshowdevice", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_device,
07307          "Show SKINNY device (text format)", mandescr_show_device);
07308    ast_manager_register2("SKINNYlines", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_lines,
07309          "List SKINNY lines (text format)", mandescr_show_lines);
07310    ast_manager_register2("SKINNYshowline", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_line,
07311          "Show SKINNY line (text format)", mandescr_show_line);
07312 
07313    sched = sched_context_create();
07314    if (!sched) {
07315       ast_log(LOG_WARNING, "Unable to create schedule context\n");
07316    }
07317    io = io_context_create();
07318    if (!io) {
07319       ast_log(LOG_WARNING, "Unable to create I/O context\n");
07320    }
07321    /* And start the monitor for the first time */
07322    restart_monitor();
07323 
07324    return AST_MODULE_LOAD_SUCCESS;
07325 }
07326 
07327 static int unload_module(void)
07328 {
07329    struct skinnysession *s;
07330    struct skinny_device *d;
07331    struct skinny_line *l;
07332    struct skinny_subchannel *sub;
07333    struct ast_context *con;
07334 
07335    ast_rtp_proto_unregister(&skinny_rtp);
07336    ast_channel_unregister(&skinny_tech);
07337    ast_cli_unregister_multiple(cli_skinny, ARRAY_LEN(cli_skinny));
07338 
07339    ast_manager_unregister("SKINNYdevices");
07340    ast_manager_unregister("SKINNYshowdevice");
07341    ast_manager_unregister("SKINNYlines");
07342    ast_manager_unregister("SKINNYshowline");
07343    
07344    AST_LIST_LOCK(&sessions);
07345    /* Destroy all the interfaces and free their memory */
07346    while((s = AST_LIST_REMOVE_HEAD(&sessions, list))) {
07347       d = s->device;
07348       AST_LIST_TRAVERSE(&d->lines, l, list){
07349          ast_mutex_lock(&l->lock);
07350          AST_LIST_TRAVERSE(&l->sub, sub, list) {
07351             ast_mutex_lock(&sub->lock);
07352             if (sub->owner) {
07353                sub->alreadygone = 1;
07354                ast_softhangup(sub->owner, AST_SOFTHANGUP_APPUNLOAD);
07355             }
07356             ast_mutex_unlock(&sub->lock);
07357          }
07358          if (l->mwi_event_sub)
07359             ast_event_unsubscribe(l->mwi_event_sub);
07360          ast_mutex_unlock(&l->lock);
07361          unregister_exten(l);
07362       }
07363       if (s->fd > -1)
07364          close(s->fd);
07365       pthread_cancel(s->t);
07366       pthread_kill(s->t, SIGURG);
07367       pthread_join(s->t, NULL);
07368       free(s);
07369    }
07370    AST_LIST_UNLOCK(&sessions);
07371 
07372    delete_devices();
07373 
07374    ast_mutex_lock(&monlock);
07375    if ((monitor_thread != AST_PTHREADT_NULL) && (monitor_thread != AST_PTHREADT_STOP)) {
07376       pthread_cancel(monitor_thread);
07377       pthread_kill(monitor_thread, SIGURG);
07378       pthread_join(monitor_thread, NULL);
07379    }
07380    monitor_thread = AST_PTHREADT_STOP;
07381    ast_mutex_unlock(&monlock);
07382 
07383    ast_mutex_lock(&netlock);
07384    if (accept_t && (accept_t != AST_PTHREADT_STOP)) {
07385       pthread_cancel(accept_t);
07386       pthread_kill(accept_t, SIGURG);
07387       pthread_join(accept_t, NULL);
07388    }
07389    accept_t = AST_PTHREADT_STOP;
07390    ast_mutex_unlock(&netlock);
07391 
07392    close(skinnysock);
07393    if (sched)
07394       sched_context_destroy(sched);
07395 
07396    con = ast_context_find(used_context);
07397    if (con)
07398       ast_context_destroy(con, "Skinny");
07399    
07400    return 0;
07401 }
07402 
07403 static int reload(void)
07404 {
07405    skinny_reload();
07406    return 0;
07407 }
07408 
07409 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Skinny Client Control Protocol (Skinny)",
07410       .load = load_module,
07411       .unload = unload_module,
07412       .reload = reload,
07413 );

Generated by  doxygen 1.6.2