Thu Apr 8 01:20:57 2010

Asterisk developer's documentation


chan_skinny.c

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

Generated on 8 Apr 2010 for Asterisk - the Open Source PBX by  doxygen 1.6.1