Fri Jun 19 12:09:39 2009

Asterisk developer's documentation


chan_skinny.c

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

Generated on Fri Jun 19 12:09:40 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7