Wed Oct 28 11:51:01 2009

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