Fri Sep 29 11:12:27 2006

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  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Implementation of the Skinny protocol
00022  * 
00023  * \author Jeremy McNamara & Florian Overkamp
00024  * \ingroup channel_drivers
00025  */
00026 
00027 
00028 #include <stdio.h>
00029 #include <stdlib.h>
00030 #include <string.h>
00031 #include <unistd.h>
00032 #include <sys/socket.h>
00033 #include <netinet/in.h>
00034 #include <netinet/tcp.h>
00035 #include <sys/ioctl.h>
00036 #include <net/if.h>
00037 #include <errno.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.h"
00046 
00047 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 41411 $")
00048 
00049 #include "asterisk/lock.h"
00050 #include "asterisk/channel.h"
00051 #include "asterisk/config.h"
00052 #include "asterisk/logger.h"
00053 #include "asterisk/module.h"
00054 #include "asterisk/pbx.h"
00055 #include "asterisk/options.h"
00056 #include "asterisk/lock.h"
00057 #include "asterisk/sched.h"
00058 #include "asterisk/io.h"
00059 #include "asterisk/rtp.h"
00060 #include "asterisk/acl.h"
00061 #include "asterisk/callerid.h"
00062 #include "asterisk/cli.h"
00063 #include "asterisk/say.h"
00064 #include "asterisk/cdr.h"
00065 #include "asterisk/astdb.h"
00066 #include "asterisk/features.h"
00067 #include "asterisk/app.h"
00068 #include "asterisk/musiconhold.h"
00069 #include "asterisk/utils.h"
00070 #include "asterisk/dsp.h"
00071 
00072 /************************************************************************************/
00073 /*                         Skinny/Asterisk Protocol Settings                        */
00074 /************************************************************************************/
00075 static const char desc[] = "Skinny Client Control Protocol (Skinny)";
00076 static const char tdesc[] = "Skinny Client Control Protocol (Skinny)";
00077 static const char type[] = "Skinny";
00078 static const char config[] = "skinny.conf";
00079 
00080 /* Just about everybody seems to support ulaw, so make it a nice default */
00081 static int capability = AST_FORMAT_ULAW;
00082 
00083 #define DEFAULT_SKINNY_PORT   2000
00084 #define DEFAULT_SKINNY_BACKLOG  2
00085 #define SKINNY_MAX_PACKET  1000
00086 
00087 static int  keep_alive = 120;
00088 static char date_format[6] = "D-M-Y";
00089 static char version_id[16] = "P002F202";
00090 
00091 /* these should be in an include file, but dunno what to include */
00092 typedef unsigned char  UINT8;
00093 typedef unsigned short UINT16;
00094 typedef unsigned int   UINT32;
00095 
00096 #if __BYTE_ORDER == __LITTLE_ENDIAN
00097 #define letohl(x) (x)
00098 #define letohs(x) (x)
00099 #define htolel(x) (x)
00100 #define htoles(x) (x)
00101 #else
00102 #if defined(SOLARIS) || defined(__Darwin__) || defined(__NetBSD__)
00103 #define __bswap_16(x) \
00104      ((((x) & 0xff00) >> 8) | \
00105       (((x) & 0x00ff) << 8))
00106 #define __bswap_32(x) \
00107      ((((x) & 0xff000000) >> 24) | \
00108       (((x) & 0x00ff0000) >>  8) | \
00109       (((x) & 0x0000ff00) <<  8) | \
00110       (((x) & 0x000000ff) << 24))
00111 #else
00112 #include <bits/byteswap.h>
00113 #endif
00114 #define letohl(x) __bswap_32(x)
00115 #define letohs(x) __bswap_16(x)
00116 #define htolel(x) __bswap_32(x)
00117 #define htoles(x) __bswap_16(x)
00118 #endif
00119 
00120 
00121 /************************************************************************************/
00122 /*                                Protocol Messages                                 */
00123 /************************************************************************************/
00124 /* message types */
00125 #define  KEEP_ALIVE_MESSAGE 0x0000
00126 /* no additional struct */
00127 
00128 #define  REGISTER_MESSAGE 0x0001
00129 typedef struct register_message {
00130    char name[16];
00131    int userId;
00132    int instance;
00133    char ip[4];
00134    int type;
00135    int maxStreams;
00136 } register_message;
00137 
00138 #define IP_PORT_MESSAGE 0x0002
00139 
00140 #define KEYPAD_BUTTON_MESSAGE 0x0003
00141 typedef struct keypad_button_message {
00142    int button;
00143 } keypad_button_message;
00144 
00145 #define STIMULUS_MESSAGE 0x0005
00146 typedef struct stimulus_message {
00147    int stimulus;
00148    int stimulusInstance;
00149 } stimulus_message;
00150       
00151 #define OFFHOOK_MESSAGE 0x0006
00152 #define ONHOOK_MESSAGE 0x0007
00153 
00154 #define  CAPABILITIES_RES_MESSAGE 0x0010
00155 typedef struct station_capabilities {  
00156    int codec;
00157    int frames;
00158    union {
00159       char res[8];
00160       long rate;
00161    } payloads; 
00162 } station_capabilities;
00163 
00164 typedef struct capabilities_res_message {
00165    int count;
00166    struct station_capabilities caps[18];
00167 } capabilities_res_message;
00168 
00169 #define SPEED_DIAL_STAT_REQ_MESSAGE 0x000A
00170 typedef struct speed_dial_stat_req_message {
00171    int speedDialNumber;
00172 } speed_dial_stat_req_message;
00173 
00174 #define  LINE_STATE_REQ_MESSAGE 0x000B
00175 typedef struct line_state_req_message {
00176    int lineNumber;
00177 } line_state_req_message;
00178 
00179 #define  TIME_DATE_REQ_MESSAGE 0x000D
00180 #define  VERSION_REQ_MESSAGE 0x000F
00181 #define BUTTON_TEMPLATE_REQ_MESSAGE 0x000E
00182 #define SERVER_REQUEST_MESSAGE 0x0012
00183 #define ALARM_MESSAGE 0x0020
00184 
00185 #define OPEN_RECIEVE_CHANNEL_ACK_MESSAGE 0x0022 
00186 typedef struct open_recieve_channel_ack_message {
00187    int status;
00188    char ipAddr[4];
00189    int port;
00190    int passThruId;
00191 } open_recieve_channel_ack_message;
00192 
00193 #define  SOFT_KEY_SET_REQ_MESSAGE 0x0025
00194 #define UNREGISTER_MESSAGE 0x0027
00195 #define  SOFT_KEY_TEMPLATE_REQ_MESSAGE 0x0028
00196 
00197 #define  REGISTER_ACK_MESSAGE 0x0081
00198 typedef struct register_ack_message {
00199    int keepAlive;
00200    char dateTemplate[6];
00201    char res[2];
00202    int secondaryKeepAlive;
00203    char res2[4];
00204 } register_ack_message;
00205 
00206 #define  START_TONE_MESSAGE 0x0082
00207 typedef struct start_tone_message {
00208    int tone;
00209 } start_tone_message;
00210 
00211 #define STOP_TONE_MESSAGE 0x0083
00212 
00213 #define SET_RINGER_MESSAGE 0x0085
00214 typedef struct set_ringer_message {
00215    int ringerMode;
00216 } set_ringer_message;
00217 
00218 #define SET_LAMP_MESSAGE 0x0086
00219 typedef struct set_lamp_message {
00220    int stimulus;
00221    int stimulusInstance;
00222    int deviceStimulus;
00223 } set_lamp_message;
00224 
00225 #define SET_SPEAKER_MESSAGE 0x0088 
00226 typedef struct set_speaker_message {
00227    int mode;
00228 } set_speaker_message;
00229 
00230 #define START_MEDIA_TRANSMISSION_MESSAGE 0x008A
00231 typedef struct media_qualifier {
00232    int precedence;
00233    int vad;
00234    int packets;
00235    int bitRate;
00236 } media_qualifier;
00237 
00238 typedef struct start_media_transmission_message {
00239    int conferenceId;
00240    int passThruPartyId;
00241    char remoteIp[4];
00242    int remotePort;
00243    int packetSize;
00244    int payloadType;
00245    media_qualifier qualifier;
00246 } start_media_transmission_message;
00247 
00248 #define STOP_MEDIA_TRANSMISSION_MESSAGE 0x008B
00249 typedef struct stop_media_transmission_message {
00250    int conferenceId;
00251         int passThruPartyId;
00252 } stop_media_transmission_message;
00253 
00254 #define CALL_INFO_MESSAGE 0x008F
00255 typedef struct call_info_message {
00256    char callingPartyName[40];
00257    char callingParty[24];
00258    char calledPartyName[40];
00259    char calledParty[24];
00260    int  instance;
00261    int  reference;
00262    int  type;
00263    char originalCalledPartyName[40];
00264    char originalCalledParty[24];
00265 } call_info_message;
00266 
00267 #define SPEED_DIAL_STAT_RES_MESSAGE 0x0091
00268 typedef struct speed_dial_stat_res_message {
00269    int speedDialNumber;
00270    char speedDialDirNumber[24];
00271    char speedDialDisplayName[40];
00272 } speed_dial_stat_res_message;
00273 
00274 #define LINE_STAT_RES_MESSAGE 0x0092
00275 typedef struct line_stat_res_message {
00276    int  linenumber;
00277    char lineDirNumber[24];
00278    char lineDisplayName[42];
00279    int  space;
00280 } line_stat_res_message;
00281 
00282 #define DEFINETIMEDATE_MESSAGE 0x0094
00283 typedef struct definetimedate_message {
00284    int year;   /* since 1900 */
00285    int month;
00286    int dayofweek; /* monday = 1 */
00287    int day;
00288    int hour;
00289    int minute;
00290    int seconds;
00291    int milliseconds;
00292    int timestamp;
00293 } definetimedate_message;
00294  
00295 #define DISPLAYTEXT_MESSAGE 0x0099
00296 typedef struct displaytext_message {
00297    char text[40];
00298 } displaytext_message;
00299 
00300 #define CLEAR_DISPLAY_MESSAGE 0x009A
00301 
00302 #define  REGISTER_REJ_MESSAGE 0x009D
00303 typedef struct register_rej_message {
00304    char errMsg[33];
00305 } register_rej_message;
00306 
00307 #define CAPABILITIES_REQ_MESSAGE 0x009B
00308 
00309 #define SERVER_RES_MESSAGE 0x009E
00310 typedef struct server_identifier {
00311    char serverName[48];
00312 } server_identifier;
00313 
00314 typedef struct server_res_message {
00315    server_identifier server[5];
00316    int serverListenPort[5];
00317    int serverIpAddr[5];
00318 } server_res_message;
00319 
00320 #define BUTTON_TEMPLATE_RES_MESSAGE 0x0097
00321 
00322 typedef struct buttondefinition {
00323    UINT8 instanceNumber;
00324    UINT8 buttonDefinition;
00325 } button_definition;
00326 
00327 #define STIMULUS_REDIAL    0x01
00328 #define STIMULUS_SPEEDDIAL    0x02
00329 #define STIMULUS_HOLD      0x03
00330 #define STIMULUS_TRANSFER  0x04
00331 #define STIMULUS_FORWARDALL   0x05
00332 #define STIMULUS_FORWARDBUSY  0x06
00333 #define STIMULUS_FORWARDNOANSWER 0x07
00334 #define STIMULUS_DISPLAY   0x08
00335 #define STIMULUS_LINE      0x09
00336 #define STIMULUS_VOICEMAIL    0x0F
00337 #define STIMULUS_AUTOANSWER   0x11
00338 #define STIMULUS_CONFERENCE   0x7D
00339 #define STIMULUS_CALLPARK  0x7E
00340 #define STIMULUS_CALLPICKUP   0x7F
00341 #define STIMULUS_NONE      0xFF
00342 
00343 button_definition button_def_30vip[] = {
00344    { 1, STIMULUS_LINE },      /* Line 1 */
00345    { 2, STIMULUS_LINE },      /* Line 2 */
00346    { 3, STIMULUS_LINE },      /* Line 3 */
00347    { 4, STIMULUS_LINE },      /* Line 4 */
00348    { 1, STIMULUS_CALLPARK },  /* Call Park */
00349    { 0, STIMULUS_NONE },
00350    { 1, STIMULUS_SPEEDDIAL }, /* Speeddial 1 */
00351    { 2, STIMULUS_SPEEDDIAL }, /* Speeddial 2 */
00352    { 3, STIMULUS_SPEEDDIAL }, /* Speeddial 3 */
00353    { 4, STIMULUS_SPEEDDIAL }, /* Speeddial 4 */
00354    { 5, STIMULUS_SPEEDDIAL }, /* Speeddial 5 */
00355    { 6, STIMULUS_SPEEDDIAL }, /* Speeddial 6 */
00356    { 1, STIMULUS_VOICEMAIL }, /* Voicemail */
00357    { 1, STIMULUS_FORWARDALL },   /* Forward All */
00358    { 1, STIMULUS_CONFERENCE },   /* Conference */
00359    { 0, STIMULUS_NONE },
00360    { 0, STIMULUS_NONE },
00361    { 0, STIMULUS_NONE },
00362    { 0, STIMULUS_NONE },
00363    { 0, STIMULUS_NONE },
00364    { 7, STIMULUS_SPEEDDIAL }, /* Speeddial 7 */
00365    { 8, STIMULUS_SPEEDDIAL }, /* Speeddial 8 */
00366    { 9, STIMULUS_SPEEDDIAL }, /* Speeddial 9 */
00367    { 10, STIMULUS_SPEEDDIAL } /* Speeddial 10 */
00368 };
00369 
00370 button_definition button_def_12sp[] = {
00371    { 1, STIMULUS_LINE },      /* Line 1 */
00372    { 1, STIMULUS_LINE },      /* Line 1 */
00373    { 1, STIMULUS_SPEEDDIAL }, /* Speeddial 1 */
00374    { 2, STIMULUS_SPEEDDIAL }, /* Speeddial 2 */
00375    { 3, STIMULUS_SPEEDDIAL }, /* Speeddial 3 */
00376    { 4, STIMULUS_SPEEDDIAL }, /* Speeddial 4 */
00377    { 1, STIMULUS_VOICEMAIL }, /* Voicemail */
00378    { 5, STIMULUS_SPEEDDIAL }, /* Speeddial 5 */
00379    { 6, STIMULUS_SPEEDDIAL }, /* Speeddial 6 */
00380    { 7, STIMULUS_SPEEDDIAL }, /* Speeddial 7 */
00381    { 8, STIMULUS_SPEEDDIAL }, /* Speeddial 8 */
00382    { 9, STIMULUS_SPEEDDIAL }  /* Speeddial 9 */
00383 };
00384 
00385 button_definition button_def_7902[] = {
00386    { 1, STIMULUS_LINE },      /* Line 1 */
00387    { 1, STIMULUS_HOLD },      /* Hold */
00388    { 1, STIMULUS_TRANSFER },  
00389    { 1, STIMULUS_DISPLAY },
00390    { 1, STIMULUS_VOICEMAIL },
00391    { 1, STIMULUS_CONFERENCE },
00392    { 1, STIMULUS_FORWARDALL },
00393    { 1, STIMULUS_SPEEDDIAL }, /* Speeddial 1 */
00394    { 2, STIMULUS_SPEEDDIAL }, /* Speeddial 2 */
00395    { 3, STIMULUS_SPEEDDIAL }, /* Speeddial 3 */
00396    { 4, STIMULUS_SPEEDDIAL }, /* Speeddial 4 */
00397    { 1, STIMULUS_REDIAL }
00398 };
00399 
00400 button_definition button_def_7910[] = {
00401    { 1, STIMULUS_LINE },      /* Line 1 */
00402    { 1, STIMULUS_HOLD },      /* Hold */
00403    { 1, STIMULUS_TRANSFER },  
00404    { 1, STIMULUS_DISPLAY },
00405    { 1, STIMULUS_VOICEMAIL },
00406    { 1, STIMULUS_CONFERENCE },
00407    { 1, STIMULUS_FORWARDALL },
00408    { 1, STIMULUS_SPEEDDIAL }, /* Speeddial 1 */
00409    { 2, STIMULUS_SPEEDDIAL }, /* Speeddial 2 */
00410    { 1, STIMULUS_REDIAL }
00411 };
00412 
00413 button_definition button_def_7920[] = {
00414    { 1, STIMULUS_LINE },      /* Line 1 */
00415    { 2, STIMULUS_LINE },      /* Line 2 */
00416    { 1, STIMULUS_SPEEDDIAL }, /* Speeddial 1 */
00417    { 2, STIMULUS_SPEEDDIAL }, /* Speeddial 2 */
00418    { 3, STIMULUS_SPEEDDIAL }, /* Speeddial 3 */
00419    { 4, STIMULUS_SPEEDDIAL }  /* Speeddial 4 */
00420 };
00421 
00422 button_definition button_def_7935[] = {
00423    { 1, STIMULUS_LINE },      /* Line 1 */
00424    { 2, STIMULUS_LINE }    /* Line 2 */
00425 };
00426 
00427 button_definition button_def_7940[] = {
00428    { 1, STIMULUS_LINE },      /* Line 1 */
00429    { 2, STIMULUS_LINE }    /* Line 2 */
00430 };
00431 
00432 button_definition button_def_7960[] = {
00433    { 1, STIMULUS_LINE },      /* Line 1 */
00434    { 2, STIMULUS_LINE },      /* Line 2 */
00435    { 3, STIMULUS_LINE },      /* Line 3 */
00436    { 1, STIMULUS_SPEEDDIAL }, /* Speeddial 1 */
00437    { 2, STIMULUS_SPEEDDIAL }, /* Speeddial 2 */
00438    { 3, STIMULUS_SPEEDDIAL }  /* Speeddial 3 */
00439 };
00440 
00441 button_definition button_def_7970[] = {
00442    { 1, STIMULUS_LINE },      /* Line 1 */
00443    { 2, STIMULUS_LINE },      /* Line 2 */
00444    { 3, STIMULUS_LINE },      /* Line 3 */
00445    { 1, STIMULUS_SPEEDDIAL }, /* Speeddial 1 */
00446    { 2, STIMULUS_SPEEDDIAL }, /* Speeddial 2 */
00447    { 3, STIMULUS_SPEEDDIAL }, /* Speeddial 3 */
00448    { 4, STIMULUS_SPEEDDIAL }, /* Speeddial 4 */
00449    { 5, STIMULUS_SPEEDDIAL }  /* Speeddial 5 */
00450 };
00451 
00452 button_definition button_def_none = { 0, STIMULUS_NONE };
00453 
00454 typedef struct button_defs {
00455    char *type;
00456    int num_buttons;
00457    button_definition *button_def;
00458 } button_defs_t;
00459 
00460 button_defs_t button_defs[] = {
00461    { "12SP",   12,   button_def_12sp }, /* First one is used if 
00462                         there's no match */
00463    { "30VIP",  26,   button_def_30vip },
00464    { "7902",   12,   button_def_7902 },
00465    { "7910",   10,   button_def_7910 },
00466    { "7920",   6, button_def_7920 },
00467    { "7935",   2, button_def_7935 },
00468    { "7940",   2, button_def_7940 },
00469    { "7960",   6, button_def_7960 },
00470    { "7970",   8, button_def_7970 },
00471    { NULL,     0, NULL }
00472 };
00473 
00474 typedef struct button_template_res_message {
00475    UINT32 buttonOffset;
00476    UINT32 buttonCount;
00477    UINT32 totalButtonCount;
00478    button_definition definition[42];
00479 } button_template_res_message;
00480 
00481 #define  VERSION_RES_MESSAGE 0x0098
00482 typedef struct version_res_message {
00483    char version[16];
00484 } version_res_message;
00485 
00486 #define  KEEP_ALIVE_ACK_MESSAGE 0x0100
00487 
00488 #define OPEN_RECIEVE_CHANNEL_MESSAGE 0x0105
00489 typedef struct open_recieve_channel_message {
00490    int conferenceId;
00491    int partyId;
00492    int packets;
00493    int capability;
00494    int echo;
00495    int bitrate;
00496 } open_recieve_channel_message;
00497 
00498 #define CLOSE_RECIEVE_CHANNEL_MESSAGE 0x0106
00499 typedef struct close_recieve_channel_message {
00500    int conferenceId;
00501    int partyId;
00502 } close_recieve_channel_message;
00503 
00504 #define  SOFT_KEY_TEMPLATE_RES_MESSAGE 0x0108
00505 
00506 typedef struct soft_key_template_definition {
00507    char softKeyLabel[16];
00508    int softKeyEvent;
00509 } soft_key_template_definition;
00510 
00511 soft_key_template_definition soft_key_template_default[] = {
00512    { "Redial",    1 },
00513    { "NewCall",      2 },
00514    { "Hold",      3 },
00515    { "Trnsfer",      4 },
00516    { "CFwdAll",      5 },
00517    { "CFwdBusy",     6 },
00518    { "CFwdNoAnswer", 7 },
00519    { "<<",        8 },
00520    { "EndCall",      9 },
00521    { "Resume",    10 },
00522    { "Answer",    11 },
00523    { "Info",      12 },
00524    { "Confrn",    13 },
00525    { "Park",      14 },
00526    { "Join",      15 },
00527    { "MeetMe",    16 },
00528    { "PickUp",    17 },
00529    { "GPickUp",      18 },
00530 };
00531 
00532 typedef struct soft_key_template {
00533    int softKeyOffset;
00534    int softKeyCount;
00535    int totalSoftKeyCount;
00536     soft_key_template_definition softKeyTemplateDefinition[32];
00537 } soft_key_template;
00538 
00539 #define  SOFT_KEY_SET_RES_MESSAGE 0x0109
00540 static const char *soft_key_set_hack = {
00541    "\x01\x02\x05\x03\x09\x0a\x0b\x10\x11\x12\x04\x0e\x0d\x00\x00\x00"
00542    "\x2d\x01\x2e\x01\x31\x01\x2f\x01\x35\x01\x36\x01\x37\x01\x3c\x01"
00543    "\x3d\x01\x3e\x01\x30\x01\x3a\x01\x39\x01\x00\x00\x00\x00\x00\x00"
00544    "\x03\x09\x04\x0e\x0d\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00545    "\x2f\x01\x35\x01\x30\x01\x3a\x01\x39\x01\x3f\x01\x00\x00\x00\x00"
00546    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00547    "\x0a\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00548    "\x36\x01\x2e\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00549    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00550    "\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00551    "\x37\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00552    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00553    "\x01\x09\x05\x10\x11\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00554    "\x2d\x01\x35\x01\x31\x01\x3c\x01\x3d\x01\x3e\x01\x00\x00\x00\x00"
00555    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00556    "\x00\x09\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00557    "\x00\x00\x35\x01\x30\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00558    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00559    "\x08\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00560    "\x34\x01\x35\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00561    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00562    "\x00\x09\x0d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00563    "\x00\x00\x35\x01\x39\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00564    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00565    "\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00566    "\x00\x00\x35\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00567    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00568    "\x01\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00569    "\x2d\x01\x35\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00570    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00571    "\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00572    "\x41\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00573    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
00574 };
00575 
00576 typedef struct soft_key_set_definition {
00577    UINT8  softKeyTemplateIndex[16];
00578    UINT16 softKeyInfoIndex[16];
00579 } soft_key_set_definition;
00580 
00581 typedef struct soft_key_sets {
00582    UINT32 softKeySetOffset;
00583    UINT32 softKeySetCount;
00584    UINT32 totalSoftKeySetCount;
00585    soft_key_set_definition softKeySetDefinition[16];
00586    UINT32 res;
00587 } soft_key_sets;
00588 
00589 #define SELECT_SOFT_KEYS_MESSAGE 0x0110
00590 typedef struct select_soft_keys_message {
00591    int instance;
00592    int reference;
00593    int softKeySetIndex;
00594    int validKeyMask;
00595 } select_soft_keys_message;
00596 
00597 #define CALL_STATE_MESSAGE 0x0111
00598 typedef struct call_state_message {
00599    int callState;
00600    int lineInstance;
00601    int callReference;
00602 } call_state_message;
00603 
00604 #define DISPLAY_PROMPT_STATUS_MESSAGE 0x0112
00605 typedef struct display_prompt_status_message {
00606    int messageTimeout;
00607    char promptMessage[32];
00608    int lineInstance;
00609    int callReference;
00610 } display_prompt_status_message;
00611 
00612 #define DISPLAY_NOTIFY_MESSAGE 0x0114
00613 typedef struct display_notify_message {
00614    int displayTimeout;
00615    char displayMessage[100];
00616 } display_notify_message;
00617 
00618 #define ACTIVATE_CALL_PLANE_MESSAGE 0x0116
00619 typedef struct activate_call_plane_message {
00620    int lineInstance;
00621 } activate_call_plane_message;
00622 
00623 #define DIALLED_NUMBER_MESSAGE 0x011D
00624 typedef struct dialled_number_message {
00625    char dialledNumber[24];
00626    int lineInstance;
00627    int callReference;
00628 } dialled_number_message;
00629 
00630 /* packet composition */
00631 typedef struct {
00632    int len;
00633    int res;
00634    int e;
00635    union {
00636       speed_dial_stat_req_message speeddialreq;
00637       register_message reg;
00638       register_ack_message regack;
00639       register_rej_message regrej;
00640       capabilities_res_message caps;
00641       version_res_message version;
00642       button_template_res_message buttontemplate;
00643       displaytext_message displaytext;
00644       display_prompt_status_message displaypromptstatus;
00645       definetimedate_message definetimedate;
00646       start_tone_message starttone;
00647       speed_dial_stat_res_message speeddial;
00648       line_state_req_message line;
00649       line_stat_res_message linestat;
00650       soft_key_sets softkeysets;
00651       soft_key_template softkeytemplate;
00652       server_res_message serverres;
00653       set_lamp_message setlamp;
00654       set_ringer_message setringer;
00655       call_state_message callstate;
00656       keypad_button_message keypad;
00657       select_soft_keys_message selectsoftkey;
00658       activate_call_plane_message activatecallplane;
00659       stimulus_message stimulus;
00660       set_speaker_message setspeaker;
00661       call_info_message callinfo;
00662       start_media_transmission_message startmedia;
00663       stop_media_transmission_message stopmedia;
00664       open_recieve_channel_message openrecievechannel;
00665       open_recieve_channel_ack_message openrecievechannelack;
00666       close_recieve_channel_message closerecievechannel;
00667       display_notify_message displaynotify;
00668       dialled_number_message diallednumber;
00669    } data;
00670 } skinny_req;
00671 
00672 /************************************************************************************/
00673 /*                            Asterisk specific globals                             */
00674 /************************************************************************************/
00675 
00676 static int skinnydebug = 1;   /* XXX for now, enable debugging default */
00677 
00678 /* a hostname, portnumber, socket and such is usefull for VoIP protocols */
00679 static struct sockaddr_in bindaddr;
00680 static char ourhost[256];
00681 static int ourport;
00682 static struct in_addr __ourip;
00683 struct ast_hostent ahp; struct hostent *hp;
00684 static int skinnysock  = -1;
00685 static pthread_t tcp_thread;
00686 static pthread_t accept_t;
00687 static char context[AST_MAX_CONTEXT] = "default";
00688 static char language[MAX_LANGUAGE] = "";
00689 static char musicclass[MAX_MUSICCLASS] = "";
00690 static char cid_num[AST_MAX_EXTENSION] = "";
00691 static char cid_name[AST_MAX_EXTENSION] = "";
00692 static char linelabel[AST_MAX_EXTENSION] ="";
00693 static int nat = 0;
00694 static ast_group_t cur_callergroup = 0;
00695 static ast_group_t cur_pickupgroup = 0;
00696 static int immediate = 0;
00697 static int callwaiting = 0;
00698 static int callreturn = 0;
00699 static int threewaycalling = 0;
00700 static int mwiblink = 0;
00701 /* This is for flashhook transfers */
00702 static int transfer = 0;
00703 static int cancallforward = 0;
00704 /* static int busycount = 3;*/
00705 static char accountcode[AST_MAX_ACCOUNT_CODE] = "";
00706 static char mailbox[AST_MAX_EXTENSION];
00707 static int amaflags = 0;
00708 static int callnums = 1;
00709 
00710 #define SUB_REAL 0
00711 #define SUB_ALT  1
00712 #define MAX_SUBS 2
00713 
00714 #define SKINNY_SPEAKERON 1
00715 #define SKINNY_SPEAKEROFF 2
00716 
00717 #define SKINNY_OFFHOOK 1
00718 #define SKINNY_ONHOOK 2
00719 #define SKINNY_RINGOUT 3
00720 #define SKINNY_RINGIN 4
00721 #define SKINNY_CONNECTED 5
00722 #define SKINNY_BUSY 6
00723 #define SKINNY_CONGESTION 7
00724 #define SKINNY_HOLD 8
00725 #define SKINNY_CALLWAIT 9
00726 #define SKINNY_TRANSFER 10
00727 #define SKINNY_PARK 11
00728 #define SKINNY_PROGRESS 12
00729 #define SKINNY_INVALID 14
00730 
00731 #define SKINNY_SILENCE     0x00
00732 #define SKINNY_DIALTONE    0x21
00733 #define SKINNY_BUSYTONE    0x23
00734 #define SKINNY_ALERT    0x24
00735 #define SKINNY_REORDER     0x25
00736 #define SKINNY_CALLWAITTONE   0x2D
00737 #define SKINNY_NOTONE      0x7F
00738 
00739 #define SKINNY_LAMP_OFF 1
00740 #define SKINNY_LAMP_ON  2
00741 #define SKINNY_LAMP_WINK 3
00742 #define SKINNY_LAMP_FLASH 4
00743 #define SKINNY_LAMP_BLINK 5
00744 
00745 #define SKINNY_RING_OFF 1
00746 #define SKINNY_RING_INSIDE 2
00747 #define SKINNY_RING_OUTSIDE 3
00748 #define SKINNY_RING_FEATURE 4
00749 
00750 #define TYPE_TRUNK 1
00751 #define TYPE_LINE 2
00752 
00753 /* Skinny rtp stream modes. Do we really need this? */
00754 #define SKINNY_CX_SENDONLY 0
00755 #define SKINNY_CX_RECVONLY 1
00756 #define SKINNY_CX_SENDRECV 2
00757 #define SKINNY_CX_CONF     3
00758 #define SKINNY_CX_CONFERENCE 3
00759 #define SKINNY_CX_MUTE     4
00760 #define SKINNY_CX_INACTIVE 4
00761 
00762 #if 0
00763 static char *skinny_cxmodes[] = {
00764     "sendonly",
00765     "recvonly",
00766     "sendrecv",
00767     "confrnce",
00768     "inactive"
00769 };
00770 #endif
00771 
00772 /* driver scheduler */
00773 static struct sched_context *sched;
00774 static struct io_context *io;
00775 
00776 /* usage count and locking */
00777 static int usecnt = 0;
00778 AST_MUTEX_DEFINE_STATIC(usecnt_lock);
00779 
00780 /* Protect the monitoring thread, so only one process can kill or start it, and not
00781    when it's doing something critical. */
00782 AST_MUTEX_DEFINE_STATIC(monlock);
00783 /* Protect the network socket */
00784 AST_MUTEX_DEFINE_STATIC(netlock);
00785 /* Protect the session list */
00786 AST_MUTEX_DEFINE_STATIC(sessionlock);
00787 /* Protect the device list */
00788 AST_MUTEX_DEFINE_STATIC(devicelock);
00789 #if 0
00790 /* Protect the paging device list */
00791 AST_MUTEX_DEFINE_STATIC(pagingdevicelock);
00792 #endif
00793 
00794 /* This is the thread for the monitor which checks for input on the channels
00795    which are not currently in use.  */
00796 static pthread_t monitor_thread = AST_PTHREADT_NULL;
00797 
00798 /* Wait up to 16 seconds for first digit */
00799 static int firstdigittimeout = 16000;
00800 
00801 /* How long to wait for following digits */
00802 static int gendigittimeout = 8000;
00803 
00804 /* How long to wait for an extra digit, if there is an ambiguous match */
00805 static int matchdigittimeout = 3000;
00806 
00807 struct skinny_subchannel {
00808    ast_mutex_t lock;
00809    unsigned int callid;
00810    struct ast_channel *owner;
00811    struct skinny_line *parent;
00812    struct ast_rtp *rtp;
00813    time_t lastouttime;
00814    int progress;
00815    int ringing;
00816    int lastout;
00817    int cxmode;
00818    int nat;
00819    int outgoing;
00820    int alreadygone;
00821    struct skinny_subchannel *next; 
00822 };
00823 
00824 struct skinny_line {
00825    ast_mutex_t lock;
00826    char name[80];
00827    char label[42];               /* Label that shows next to the line buttons */
00828    struct skinny_subchannel *sub;         /* pointer to our current connection, channel and stuff */
00829    char accountcode[AST_MAX_ACCOUNT_CODE];
00830    char exten[AST_MAX_EXTENSION];         /* Extention where to start */
00831    char context[AST_MAX_CONTEXT];
00832    char language[MAX_LANGUAGE];
00833    char cid_num[AST_MAX_EXTENSION];    /* Caller*ID */
00834    char cid_name[AST_MAX_EXTENSION];      /* Caller*ID */
00835    char lastcallerid[AST_MAX_EXTENSION];     /* Last Caller*ID */
00836    char call_forward[AST_MAX_EXTENSION];  
00837    char mailbox[AST_MAX_EXTENSION];
00838    char musicclass[MAX_MUSICCLASS];
00839    int curtone;               /* Current tone being played */
00840    ast_group_t callgroup;
00841    ast_group_t pickupgroup;
00842    int callwaiting;
00843    int transfer;
00844    int threewaycalling;
00845    int mwiblink;
00846    int cancallforward;
00847    int callreturn;
00848    int dnd; /* How does this affect callwait?  Do we just deny a skinny_request if we're dnd? */
00849    int hascallerid;
00850    int hidecallerid;
00851    int amaflags;
00852    int type;
00853    int instance;
00854    int group;
00855    int needdestroy;
00856    int capability;
00857    int nonCodecCapability;
00858    int onhooktime;
00859    int msgstate;     /* voicemail message state */
00860    int immediate;
00861    int hookstate;
00862    int progress;
00863    struct skinny_line *next;
00864    struct skinny_device *parent;
00865 };
00866 
00867 static struct skinny_device {
00868    /* A device containing one or more lines */
00869    char name[80];
00870    char id[16];
00871    char version_id[16]; 
00872    int type;
00873    int registered;
00874    char model[6];
00875    struct sockaddr_in addr;
00876    struct in_addr ourip;
00877    struct skinny_line *lines;
00878    struct ast_ha *ha;
00879    struct skinnysession *session;
00880    struct skinny_device *next;
00881 } *devices = NULL;
00882 
00883 struct skinny_paging_device {
00884    char name[80];
00885    char id[16];
00886    struct skinny_device ** devices;
00887    struct skinny_paging_device *next;
00888 };
00889 
00890 static struct skinnysession {
00891    pthread_t t;
00892    ast_mutex_t lock;
00893    struct sockaddr_in sin;
00894    int fd;
00895    char inbuf[SKINNY_MAX_PACKET];
00896    struct skinny_device *device;
00897    struct skinnysession *next;
00898 } *sessions = NULL;
00899 
00900 static struct ast_channel *skinny_request(const char *type, int format, void *data, int *cause);
00901 static int skinny_call(struct ast_channel *ast, char *dest, int timeout);
00902 static int skinny_hangup(struct ast_channel *ast);
00903 static int skinny_answer(struct ast_channel *ast);
00904 static struct ast_frame *skinny_read(struct ast_channel *ast);
00905 static int skinny_write(struct ast_channel *ast, struct ast_frame *frame);
00906 static int skinny_indicate(struct ast_channel *ast, int ind);
00907 static int skinny_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
00908 static int skinny_senddigit(struct ast_channel *ast, char digit);
00909 
00910 static const struct ast_channel_tech skinny_tech = {
00911    .type = type,
00912    .description = tdesc,
00913    .capabilities = AST_FORMAT_ULAW,
00914    .properties = AST_CHAN_TP_WANTSJITTER,
00915    .requester = skinny_request,
00916    .call = skinny_call,
00917    .hangup = skinny_hangup,
00918    .answer = skinny_answer,
00919    .read = skinny_read,
00920    .write = skinny_write,
00921    .indicate = skinny_indicate,
00922    .fixup = skinny_fixup,
00923    .send_digit = skinny_senddigit,
00924 /* .bridge = ast_rtp_bridge, */
00925 };
00926 
00927 static skinny_req *req_alloc(size_t size)
00928 {
00929    skinny_req *req;
00930    req = malloc(size+12);
00931    if (!req) {
00932       return NULL;
00933    }  
00934    memset(req, 0, size+12);
00935    return req;
00936 }
00937 
00938 static struct skinny_subchannel *find_subchannel_by_line(struct skinny_line *l)
00939 {
00940    /* XXX Need to figure out how to determine which sub we want */
00941    struct skinny_subchannel *sub = l->sub;
00942    return sub;
00943 }
00944 
00945 static struct skinny_subchannel *find_subchannel_by_name(char *dest)
00946 {
00947    struct skinny_line *l;
00948    struct skinny_device *d;
00949    char line[256];
00950    char *at;
00951    char *device;
00952    
00953    strncpy(line, dest, sizeof(line) - 1);
00954    at = strchr(line, '@');
00955    if (!at) {
00956       ast_log(LOG_NOTICE, "Device '%s' has no @ (at) sign!\n", dest);
00957       return NULL;
00958    }
00959    *at = '\0';
00960    at++;
00961    device = at;
00962    ast_mutex_lock(&devicelock);
00963    d = devices;
00964    while(d) {
00965       if (!strcasecmp(d->name, device)) {
00966          if (skinnydebug) {
00967             ast_verbose("Found device: %s\n", d->name);
00968          }
00969          /* Found the device */
00970          l = d->lines;
00971          while (l) {
00972             /* Search for the right line */
00973             if (!strcasecmp(l->name, line)) {
00974                ast_mutex_unlock(&devicelock);
00975                return l->sub;
00976             }
00977             l = l->next;
00978          }
00979       }
00980       d = d->next;
00981    }
00982    /* Device not found*/
00983    ast_mutex_unlock(&devicelock);
00984    return NULL;
00985 }
00986 
00987 static int transmit_response(struct skinnysession *s, skinny_req *req)
00988 {
00989    int res = 0;
00990    ast_mutex_lock(&s->lock);
00991    
00992 #if 0
00993    if (skinnydebug) {
00994       ast_verbose("writing packet type %04X (%d bytes) to socket %d\n", letohl(req->e), letohl(req->len)+8, s->fd);
00995    }
00996 #endif
00997 
00998    res = write(s->fd, req, letohl(req->len)+8);
00999    if (res != letohl(req->len)+8) {
01000       ast_log(LOG_WARNING, "Transmit: write only sent %d out of %d bytes: %s\n", res, letohl(req->len)+8, strerror(errno));
01001    }
01002    ast_mutex_unlock(&s->lock);
01003    return 1;
01004 }
01005 
01006 /* XXX Do this right */
01007 static int convert_cap(int capability)
01008 {
01009    return 4; /* ulaw (this is not the same as asterisk's '4'  */
01010 
01011 }
01012 
01013 static void transmit_speaker_mode(struct skinnysession *s, int mode)
01014 {
01015    skinny_req *req;
01016 
01017    req = req_alloc(sizeof(struct set_speaker_message));
01018    if (!req) {
01019       ast_log(LOG_ERROR, "Unable to allocate skinny_request, this is bad\n");
01020       return;
01021    }
01022    req->len = htolel(sizeof(set_speaker_message)+4);
01023    req->e = htolel(SET_SPEAKER_MESSAGE);
01024    req->data.setspeaker.mode = htolel(mode); 
01025    transmit_response(s, req);
01026 }
01027 
01028 static void transmit_callstate(struct skinnysession *s, int instance, int state, unsigned callid)
01029 { 
01030    skinny_req *req;
01031    int memsize = sizeof(struct call_state_message);
01032 
01033    req = req_alloc(memsize);
01034    if (!req) {
01035       ast_log(LOG_ERROR, "Unable to allocate skinny_request, this is bad\n");
01036       return;
01037    }  
01038    if (state == SKINNY_ONHOOK) {
01039       transmit_speaker_mode(s, SKINNY_SPEAKEROFF);
01040    }
01041    req->len = htolel(sizeof(call_state_message)+4);
01042    req->e = htolel(CALL_STATE_MESSAGE);
01043    req->data.callstate.callState = htolel(state);
01044    req->data.callstate.lineInstance = htolel(instance);
01045    req->data.callstate.callReference = htolel(callid);
01046    transmit_response(s, req);
01047    if (state == SKINNY_OFFHOOK) {
01048       memset(req, 0, memsize);
01049       req->len = htolel(sizeof(activate_call_plane_message)+4);
01050       req->e = htolel(ACTIVATE_CALL_PLANE_MESSAGE);
01051       req->data.activatecallplane.lineInstance = htolel(instance);
01052       transmit_response(s, req);
01053    } else if (state == SKINNY_ONHOOK) {
01054       memset(req, 0, memsize);
01055       req->len = htolel(sizeof(activate_call_plane_message)+4);
01056       req->e = htolel(ACTIVATE_CALL_PLANE_MESSAGE);
01057       req->data.activatecallplane.lineInstance = 0;
01058       transmit_response(s, req);
01059       memset(req, 0, memsize);
01060       req->len = htolel(sizeof(close_recieve_channel_message)+4);
01061       req->e = htolel(CLOSE_RECIEVE_CHANNEL_MESSAGE);
01062       req->data.closerecievechannel.conferenceId = 0;
01063       req->data.closerecievechannel.partyId = 0;
01064       transmit_response(s, req);
01065       memset(req, 0, memsize);
01066                 req->len = htolel(sizeof(stop_media_transmission_message)+4);
01067                 req->e = htolel(STOP_MEDIA_TRANSMISSION_MESSAGE);
01068                 req->data.stopmedia.conferenceId = 0;   
01069                 req->data.stopmedia.passThruPartyId = 0;
01070                 transmit_response(s, req);   
01071    }
01072 }  
01073 
01074 static void transmit_callinfo(struct skinnysession *s, char *fromname, char *fromnum, char *toname, char *tonum, int instance, int callid, int calltype)
01075 {
01076    skinny_req *req;
01077 
01078    req = req_alloc(sizeof(struct call_info_message));
01079    if (!req) {
01080       ast_log(LOG_ERROR, "Unable to allocate skinny_request, this is bad\n");
01081       return;
01082    }  
01083 
01084    req->len = htolel(sizeof(struct call_info_message));
01085    req->e = htolel(CALL_INFO_MESSAGE);
01086 
01087    if (fromname) {
01088       ast_copy_string(req->data.callinfo.callingPartyName, fromname, sizeof(req->data.callinfo.callingPartyName));
01089    }
01090    if (fromnum) {
01091       ast_copy_string(req->data.callinfo.callingParty, fromnum, sizeof(req->data.callinfo.callingParty));
01092    }
01093    if (toname) {
01094       ast_copy_string(req->data.callinfo.calledPartyName, toname, sizeof(req->data.callinfo.calledPartyName));
01095    }
01096    if (tonum) {
01097       ast_copy_string(req->data.callinfo.calledParty, tonum, sizeof(req->data.callinfo.calledParty));
01098    }
01099    req->data.callinfo.instance = htolel(instance);
01100    req->data.callinfo.reference = htolel(callid);
01101    req->data.callinfo.type = htolel(calltype);
01102    transmit_response(s, req);
01103 }
01104 
01105 static void transmit_connect(struct skinnysession *s)
01106 {
01107    skinny_req *req;
01108    struct skinny_line *l = s->device->lines;
01109 
01110    req = req_alloc(sizeof(struct open_recieve_channel_message));
01111    if (!req) {
01112       ast_log(LOG_ERROR, "Unable to allocate skinny_request, this is bad\n");
01113       return;
01114    }  
01115    req->len = htolel(sizeof(struct open_recieve_channel_message));
01116    req->e = htolel(OPEN_RECIEVE_CHANNEL_MESSAGE);
01117    req->data.openrecievechannel.conferenceId = 0;
01118    req->data.openrecievechannel.partyId = 0;
01119    req->data.openrecievechannel.packets = htolel(20);
01120    req->data.openrecievechannel.capability = htolel(convert_cap(l->capability)); 
01121    req->data.openrecievechannel.echo = 0;
01122    req->data.openrecievechannel.bitrate = 0;
01123    transmit_response(s, req);
01124 }  
01125 
01126 static void transmit_tone(struct skinnysession *s, int tone)
01127 {
01128    skinny_req *req;
01129 
01130    if (tone > 0) {
01131       req = req_alloc(sizeof(struct start_tone_message));
01132    } else {
01133       req = req_alloc(4);
01134    }
01135    if (!req) {
01136       ast_log(LOG_ERROR, "Unable to allocate skinny_request, this is bad\n");
01137       return;
01138    }  
01139    if (tone > 0) {
01140       req->len = htolel(sizeof(start_tone_message)+4);
01141       req->e = htolel(START_TONE_MESSAGE);
01142       req->data.starttone.tone = htolel(tone); 
01143    } else {
01144       req->len = htolel(4);
01145       req->e = htolel(STOP_TONE_MESSAGE);
01146    }
01147    transmit_response(s, req);
01148 }
01149 
01150 #if 0
01151 /* XXX need to properly deal with softkeys */
01152 static void transmit_selectsoftkeys(struct skinnysession *s, int instance, int callid, int softkey)
01153 {
01154    skinny_req *req;
01155    int memsize = sizeof(struct select_soft_keys_message);
01156 
01157    req = req_alloc(memsize);
01158    if (!req) {
01159       ast_log(LOG_ERROR, "Unable to allocate skinny_request, this is bad\n");
01160       return;
01161    }  
01162    memset(req, 0, memsize);
01163    req->len = htolel(sizeof(select_soft_keys_message)+4);
01164    req->e = htolel(SELECT_SOFT_KEYS_MESSAGE);
01165    req->data.selectsoftkey.instance = htolel(instance);
01166    req->data.selectsoftkey.reference = htolel(callid);
01167    req->data.selectsoftkey.softKeySetIndex = htolel(softkey);
01168    transmit_response(s, req);
01169 }
01170 #endif
01171 
01172 static void transmit_lamp_indication(struct skinnysession *s, int stimulus, int instance, int indication)
01173 {
01174    skinny_req *req;
01175 
01176    req = req_alloc(sizeof(struct set_lamp_message));
01177    if (!req) {
01178       ast_log(LOG_ERROR, "Unable to allocate skinny_request, this is bad\n");
01179       return;
01180    }  
01181    req->len = htolel(sizeof(set_lamp_message)+4);
01182    req->e = htolel(SET_LAMP_MESSAGE);
01183    req->data.setlamp.stimulus = htolel(stimulus);
01184    req->data.setlamp.stimulusInstance = htolel(instance);
01185    req->data.setlamp.deviceStimulus = htolel(indication);
01186    transmit_response(s, req);
01187 }
01188 
01189 static void transmit_ringer_mode(struct skinnysession *s, int mode)
01190 {
01191    skinny_req *req;
01192 
01193    req = req_alloc(sizeof(struct set_ringer_message));
01194    if (!req) {
01195       ast_log(LOG_ERROR, "Unable to allocate skinny_request, this is bad\n");
01196       return;
01197    }
01198    req->len = htolel(sizeof(set_ringer_message)+4);
01199    req->e = htolel(SET_RINGER_MESSAGE); 
01200    req->data.setringer.ringerMode = htolel(mode); 
01201    transmit_response(s, req);
01202 }
01203 
01204 static void transmit_displaymessage(struct skinnysession *s, char *text)
01205 {
01206    skinny_req *req;
01207 
01208    if (text == 0) {
01209       req = req_alloc(4);
01210       req->len = htolel(4);
01211       req->e = htolel(CLEAR_DISPLAY_MESSAGE);
01212    } else {
01213       req = req_alloc(sizeof(struct displaytext_message));
01214 
01215       strncpy(req->data.displaytext.text, text, sizeof(req->data.displaytext.text)-1);
01216       req->len = htolel(sizeof(displaytext_message) + 4);
01217       req->e = htolel(DISPLAYTEXT_MESSAGE);
01218       if (skinnydebug) {
01219          ast_verbose("Displaying message '%s'\n", req->data.displaytext.text);
01220       }
01221    }
01222 
01223    if (!req) {
01224       ast_log(LOG_ERROR, "Unable to allocate skinny_request, this is bad\n");
01225       return;
01226    }
01227    transmit_response(s, req);
01228 }
01229 
01230 static void transmit_displaynotify(struct skinnysession *s, char *text, int t)
01231 {
01232    skinny_req *req;
01233 
01234    req = req_alloc(sizeof(struct display_notify_message));
01235 
01236         if (!req) {
01237                 ast_log(LOG_ERROR, "Unable to allocate skinny_request, this is bad\n");
01238                 return;
01239         }
01240 
01241    req->e = htolel(DISPLAY_NOTIFY_MESSAGE);
01242    req->len = htolel(sizeof(display_notify_message) + 4);
01243    strncpy(req->data.displaynotify.displayMessage, text, sizeof(req->data.displaynotify.displayMessage)-1);
01244    req->data.displaynotify.displayTimeout = htolel(t);
01245 
01246    if (skinnydebug) {
01247       ast_verbose("Displaying notify '%s'\n", text);
01248    }
01249    
01250    transmit_response(s, req);
01251 }
01252 
01253 static void transmit_displaypromptstatus(struct skinnysession *s, char *text, int t, int instance, int callid)
01254 {
01255    skinny_req *req;
01256 
01257    req = req_alloc(sizeof(struct display_prompt_status_message));
01258 
01259         if (!req) {
01260                 ast_log(LOG_ERROR, "Unable to allocate skinny_request, this is bad\n");
01261                 return;
01262         }
01263 
01264    req->e = htolel(DISPLAY_PROMPT_STATUS_MESSAGE);
01265    req->len = htolel(sizeof(display_prompt_status_message) + 4);
01266    strncpy(req->data.displaypromptstatus.promptMessage, text, sizeof(req->data.displaypromptstatus.promptMessage)-1);
01267    req->data.displaypromptstatus.messageTimeout = htolel(t);
01268    req->data.displaypromptstatus.lineInstance = htolel(instance);
01269    req->data.displaypromptstatus.callReference = htolel(callid);
01270 
01271    if (skinnydebug) {
01272       ast_verbose("Displaying Prompt Status '%s'\n", text);
01273    }
01274 
01275    transmit_response(s, req);
01276 }
01277 
01278 static void transmit_diallednumber(struct skinnysession *s, char *text, int instance, int callid)
01279 {
01280    skinny_req *req;
01281 
01282    req = req_alloc(sizeof(struct dialled_number_message));
01283 
01284         if (!req) {
01285                 ast_log(LOG_ERROR, "Unable to allocate skinny_request, this is bad\n");
01286                 return;
01287         }
01288 
01289    req->e = htolel(DIALLED_NUMBER_MESSAGE);
01290    req->len = htolel(sizeof(dialled_number_message) + 4);
01291    strncpy(req->data.diallednumber.dialledNumber, text, sizeof(req->data.diallednumber.dialledNumber)-1);
01292    req->data.diallednumber.lineInstance = htolel(instance);
01293         req->data.diallednumber.callReference = htolel(callid);
01294 
01295    transmit_response(s, req);
01296 }
01297 
01298 static int has_voicemail(struct skinny_line *l)
01299 {
01300    return ast_app_has_voicemail(l->mailbox, NULL);
01301 }
01302 
01303 
01304 static void do_housekeeping(struct skinnysession *s)
01305 {
01306    int new;
01307    int old;
01308    struct skinny_subchannel *sub;
01309    struct skinny_line *l = s->device->lines;
01310 
01311    sub = find_subchannel_by_line(l);
01312    transmit_displaymessage(s, NULL);
01313 
01314    if (has_voicemail(sub->parent)) {
01315       if (skinnydebug) {
01316          ast_verbose("Checking for voicemail Skinny %s@%s\n", sub->parent->name, sub->parent->parent->name);
01317       }
01318       ast_app_messagecount(sub->parent->mailbox, &new, &old);
01319       if (skinnydebug) {
01320          ast_verbose("Skinny %s@%s has voicemail!\n", sub->parent->name, sub->parent->parent->name);
01321       }
01322       transmit_lamp_indication(s, STIMULUS_VOICEMAIL, l->instance, l->mwiblink?SKINNY_LAMP_BLINK:SKINNY_LAMP_ON);
01323    } else {
01324       transmit_lamp_indication(s, STIMULUS_VOICEMAIL, l->instance, SKINNY_LAMP_OFF);
01325    }
01326 
01327 }
01328 
01329 /* I do not believe skinny can deal with video. 
01330    Anyone know differently? */
01331 static struct ast_rtp *skinny_get_vrtp_peer(struct ast_channel *chan)
01332 {
01333    return NULL;
01334 }
01335 
01336 static struct ast_rtp *skinny_get_rtp_peer(struct ast_channel *chan)
01337 {
01338    struct skinny_subchannel *sub;
01339    sub = chan->tech_pvt;
01340    if (sub && sub->rtp) {
01341       return sub->rtp;
01342    }
01343    return NULL;
01344 }
01345 
01346 static int skinny_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, int codecs, int nat_active)
01347 {
01348    struct skinny_subchannel *sub;
01349    sub = chan->tech_pvt;
01350    if (sub) {
01351       /* transmit_modify_with_sdp(sub, rtp); @@FIXME@@ if needed */
01352       return 0;
01353    }
01354    return -1;
01355 }
01356 
01357 static struct ast_rtp_protocol skinny_rtp = {
01358    .type = type,
01359    .get_rtp_info = skinny_get_rtp_peer,
01360    .get_vrtp_info =  skinny_get_vrtp_peer,
01361    .set_rtp_peer = skinny_set_rtp_peer,
01362 };
01363 
01364 static int skinny_do_debug(int fd, int argc, char *argv[])
01365 {
01366    if (argc != 2) {
01367       return RESULT_SHOWUSAGE;
01368    }
01369    skinnydebug = 1;
01370    ast_cli(fd, "Skinny Debugging Enabled\n");
01371    return RESULT_SUCCESS;
01372 }
01373 
01374 static int skinny_no_debug(int fd, int argc, char *argv[])
01375 {
01376    if (argc != 3) {
01377       return RESULT_SHOWUSAGE;
01378    }
01379    skinnydebug = 0;
01380    ast_cli(fd, "Skinny Debugging Disabled\n");
01381    return RESULT_SUCCESS;
01382 }
01383 
01384 static int skinny_show_devices(int fd, int argc, char *argv[])
01385 {
01386    struct skinny_device  *d;
01387    struct skinny_line *l;
01388    int numlines = 0;
01389    char iabuf[INET_ADDRSTRLEN];
01390 
01391    if (argc != 3) {
01392       return RESULT_SHOWUSAGE;
01393    }
01394    ast_mutex_lock(&devicelock);
01395    d = devices;
01396 
01397    ast_cli(fd, "Name                 DeviceId         IP              TypeId R Model  NL\n");
01398    ast_cli(fd, "-------------------- ---------------- --------------- ------ - ------ --\n");
01399    while(d) {
01400       l = d->lines;
01401       numlines = 0;
01402       while(l) { numlines++; l = l->next; }
01403 
01404       ast_cli(fd, "%-20s %-16s %-16s %6X %c %-6s %2d\n", 
01405             d->name, 
01406             d->id, 
01407             ast_inet_ntoa(iabuf, sizeof(iabuf), d->addr.sin_addr), 
01408             d->type, 
01409             d->registered?'Y':'N', 
01410             d->model, 
01411             numlines);
01412 
01413       d = d->next;
01414    }
01415    ast_mutex_unlock(&devicelock);
01416    return RESULT_SUCCESS;
01417 }
01418 
01419 static int skinny_show_lines(int fd, int argc, char *argv[])
01420 {
01421    struct skinny_device  *d;
01422    struct skinny_line *l;
01423 
01424    if (argc != 3) {
01425       return RESULT_SHOWUSAGE;
01426    }
01427    ast_mutex_lock(&devicelock);
01428    d = devices;
01429    while(d) {
01430       l = d->lines;
01431       while (l) {
01432          ast_cli(fd, "%-20s %2d %-20s %-20s  %c  %c\n",
01433             l->parent->name,
01434             l->instance,
01435             l->name,
01436             l->label,
01437             l->sub->owner?'Y':'N',
01438             l->sub->rtp?'Y':'N');
01439          l = l->next;
01440       }
01441       d = d->next;
01442    }
01443    ast_mutex_unlock(&devicelock);
01444    return RESULT_SUCCESS;
01445 }
01446 
01447 static char show_devices_usage[] = 
01448 "Usage: skinny show devices\n"
01449 "       Lists all devices known to the Skinny subsystem.\n";
01450 
01451 static char show_lines_usage[] = 
01452 "Usage: skinny show lines\n"
01453 "       Lists all lines known to the Skinny subsystem.\n";
01454 
01455 static char debug_usage[] = 
01456 "Usage: skinny debug\n"
01457 "       Enables dumping of Skinny packets for debugging purposes\n";
01458 
01459 static char no_debug_usage[] = 
01460 "Usage: skinny no debug\n"
01461 "       Disables dumping of Skinny packets for debugging purposes\n";
01462 
01463 static struct ast_cli_entry  cli_show_devices =
01464    { { "skinny", "show", "devices", NULL }, skinny_show_devices, "Show defined Skinny devices", show_devices_usage };
01465 
01466 static struct ast_cli_entry  cli_show_lines =
01467    { { "skinny", "show", "lines", NULL }, skinny_show_lines, "Show defined Skinny lines per device", show_lines_usage };
01468 
01469 static struct ast_cli_entry  cli_debug =
01470    { { "skinny", "debug", NULL }, skinny_do_debug, "Enable Skinny debugging", debug_usage };
01471 
01472 static struct ast_cli_entry  cli_no_debug =
01473    { { "skinny", "no", "debug", NULL }, skinny_no_debug, "Disable Skinny debugging", no_debug_usage };
01474 
01475 #if 0
01476 static struct skinny_paging_device *build_paging_device(char *cat, struct ast_variable *v)
01477 {
01478    return NULL;
01479 }
01480 #endif
01481 
01482 static struct skinny_device *build_device(char *cat, struct ast_variable *v)
01483 {
01484    struct skinny_device *d;
01485    struct skinny_line *l;
01486    struct skinny_subchannel *sub;
01487    int i=0, y=0;
01488    
01489    d = malloc(sizeof(struct skinny_device));
01490    if (d) {
01491       memset(d, 0, sizeof(struct skinny_device));
01492       strncpy(d->name, cat, sizeof(d->name) - 1);
01493       while(v) {
01494          if (!strcasecmp(v->name, "host")) {
01495             if (ast_get_ip(&d->addr, v->value)) {
01496                free(d);
01497                return NULL;
01498             }           
01499          } else if (!strcasecmp(v->name, "port")) {
01500             d->addr.sin_port = htons(atoi(v->value));
01501          } else if (!strcasecmp(v->name, "device")) {
01502                   strncpy(d->id, v->value, sizeof(d->id)-1);
01503          } else if (!strcasecmp(v->name, "permit") || !strcasecmp(v->name, "deny")) {
01504             d->ha = ast_append_ha(v->name, v->value, d->ha);
01505          } else if (!strcasecmp(v->name, "context")) {
01506             strncpy(context, v->value, sizeof(context) - 1);
01507          } else if (!strcasecmp(v->name, "version")) {
01508                      strncpy(d->version_id, v->value, sizeof(d->version_id) -1); 
01509          } else if (!strcasecmp(v->name, "nat")) {
01510             nat = ast_true(v->value);
01511             } else if (!strcasecmp(v->name, "model")) {
01512             strncpy(d->model, v->value, sizeof(d->model) - 1);
01513          } else if (!strcasecmp(v->name, "callerid")) {
01514             if (!strcasecmp(v->value, "asreceived")) {
01515                cid_num[0] = '\0';
01516                cid_name[0] = '\0';
01517             } else {
01518                ast_callerid_split(v->value, cid_name, sizeof(cid_name), cid_num, sizeof(cid_num));
01519             }
01520          } else if (!strcasecmp(v->name, "language")) {
01521             strncpy(language, v->value, sizeof(language)-1);
01522                } else if (!strcasecmp(v->name, "accountcode")) {
01523                   strncpy(accountcode, v->value, sizeof(accountcode)-1);
01524                } else if (!strcasecmp(v->name, "amaflags")) {
01525                   y = ast_cdr_amaflags2int(v->value);
01526                   if (y < 0) {
01527                   ast_log(LOG_WARNING, "Invalid AMA flags: %s at line %d\n", v->value, v->lineno);
01528                   } else {
01529                      amaflags = y;
01530                      }
01531          } else if (!strcasecmp(v->name, "musiconhold")) {
01532                      strncpy(musicclass, v->value, sizeof(musicclass)-1);
01533                   } else if (!strcasecmp(v->name, "callgroup")) {
01534                      cur_callergroup = ast_get_group(v->value);
01535                   } else if (!strcasecmp(v->name, "pickupgroup")) {
01536                      cur_pickupgroup = ast_get_group(v->value);
01537                   } else if (!strcasecmp(v->name, "immediate")) {
01538                      immediate = ast_true(v->value);
01539                   } else if (!strcasecmp(v->name, "cancallforward")) {
01540                      cancallforward = ast_true(v->value);
01541                   } else if (!strcasecmp(v->name, "mailbox")) {
01542                      strncpy(mailbox, v->value, sizeof(mailbox) -1);
01543             } else if (!strcasecmp(v->name, "callreturn")) {
01544             callreturn = ast_true(v->value);
01545                   } else if (!strcasecmp(v->name, "callwaiting")) {
01546                      callwaiting = ast_true(v->value);
01547                   } else if (!strcasecmp(v->name, "transfer")) {
01548                      transfer = ast_true(v->value);
01549                   } else if (!strcasecmp(v->name, "threewaycalling")) {
01550                      threewaycalling = ast_true(v->value);
01551                   } else if (!strcasecmp(v->name, "mwiblink")) {
01552                      mwiblink = ast_true(v->value);
01553             } else if (!strcasecmp(v->name, "linelabel")) {
01554                   strncpy(linelabel, v->value, sizeof(linelabel)-1);
01555                   } else if (!strcasecmp(v->name, "trunk") || !strcasecmp(v->name, "line")) {
01556             l = malloc(sizeof(struct skinny_line));;
01557             if (l) {
01558                memset(l, 0, sizeof(struct skinny_line));
01559                                         ast_mutex_init(&l->lock);
01560                strncpy(l->name, v->value, sizeof(l->name) - 1);
01561                
01562                /* XXX Should we check for uniqueness?? XXX */
01563                strncpy(l->context, context, sizeof(l->context) - 1);
01564                strncpy(l->cid_num, cid_num, sizeof(l->cid_num) - 1);
01565                strncpy(l->cid_name, cid_name, sizeof(l->cid_name) - 1);
01566                strncpy(l->label, linelabel, sizeof(l->label) - 1);
01567                strncpy(l->language, language, sizeof(l->language) - 1);
01568                   strncpy(l->musicclass, musicclass, sizeof(l->musicclass)-1);
01569                strncpy(l->mailbox, mailbox, sizeof(l->mailbox)-1);
01570                strncpy(l->mailbox, mailbox, sizeof(l->mailbox)-1);
01571                if (!ast_strlen_zero(mailbox)) {
01572                   ast_verbose(VERBOSE_PREFIX_3 "Setting mailbox '%s' on %s@%s\n", mailbox, d->name, l->name);
01573                }
01574                l->msgstate = -1;
01575                l->capability = capability;
01576                l->parent = d;
01577                if (!strcasecmp(v->name, "trunk")) {
01578                   l->type = TYPE_TRUNK;
01579                } else {
01580                   l->type = TYPE_LINE;
01581                }
01582                l->immediate = immediate;
01583                l->callgroup = cur_callergroup;
01584                l->pickupgroup = cur_pickupgroup;
01585                l->callreturn = callreturn;
01586                   l->cancallforward = cancallforward;
01587                   l->callwaiting = callwaiting;
01588                   l->transfer = transfer; 
01589                   l->threewaycalling = threewaycalling;
01590                   l->mwiblink = mwiblink;
01591                   l->onhooktime = time(NULL);
01592                l->instance = 1;
01593                   /* ASSUME we're onhook at this point*/
01594                         l->hookstate = SKINNY_ONHOOK;
01595 
01596                      for (i = 0; i < MAX_SUBS; i++) {
01597                                  sub = malloc(sizeof(struct skinny_subchannel));
01598                                  if (sub) {
01599                                        ast_verbose(VERBOSE_PREFIX_3 "Allocating Skinny subchannel '%d' on %s@%s\n", i, l->name, d->name);
01600                                        memset(sub, 0, sizeof(struct skinny_subchannel));
01601                                                         ast_mutex_init(&sub->lock);
01602                                     sub->parent = l;
01603                                     /* Make a call*ID */
01604                      sub->callid = callnums;
01605                      callnums++;
01606                                     sub->cxmode = SKINNY_CX_INACTIVE;
01607                                     sub->nat = nat;
01608                                     sub->next = l->sub;
01609                                     l->sub = sub;
01610                                  } else {
01611                                     /* XXX Should find a way to clean up our memory */
01612                                     ast_log(LOG_WARNING, "Out of memory allocating subchannel");
01613                                     return NULL;
01614                                  }
01615                            }
01616                   l->next = d->lines;
01617                d->lines = l;        
01618                } else {
01619                   /* XXX Should find a way to clean up our memory */
01620                            ast_log(LOG_WARNING, "Out of memory allocating line");
01621                            return NULL;
01622             }
01623          } else {
01624             ast_log(LOG_WARNING, "Don't know keyword '%s' at line %d\n", v->name, v->lineno);
01625          }
01626          v = v->next;
01627       }
01628    
01629       if (!d->lines) {
01630          ast_log(LOG_ERROR, "A Skinny device must have at least one line!\n");
01631          return NULL;
01632       }
01633       if (d->addr.sin_addr.s_addr && !ntohs(d->addr.sin_port)) {
01634          d->addr.sin_port = htons(DEFAULT_SKINNY_PORT);
01635       }
01636       if (d->addr.sin_addr.s_addr) {
01637          if (ast_ouraddrfor(&d->addr.sin_addr, &d->ourip)) {
01638             memcpy(&d->ourip, &__ourip, sizeof(d->ourip));
01639          }
01640       } else {
01641          memcpy(&d->ourip, &__ourip, sizeof(d->ourip));
01642       }
01643    }
01644    return d;
01645 }
01646 
01647 static int skinny_register(skinny_req *req, struct skinnysession *s)
01648 {
01649    struct skinny_device *d;
01650    
01651    ast_mutex_lock(&devicelock);
01652    d = devices;
01653    while (d) {
01654       if (!strcasecmp(req->data.reg.name, d->id) 
01655             && ast_apply_ha(d->ha, &(s->sin))) {
01656          s->device = d;
01657          d->type = letohl(req->data.reg.type);
01658          if (ast_strlen_zero(d->version_id)) {
01659             strncpy(d->version_id, version_id, sizeof(d->version_id) - 1);
01660          }
01661          d->registered = 1;
01662          d->session = s;
01663          break;
01664       }
01665       d = d->next;
01666    }
01667    ast_mutex_unlock(&devicelock);
01668    if (!d) {
01669       return 0;
01670    }
01671    return 1;
01672 }     
01673 
01674 static void start_rtp(struct skinny_subchannel *sub)
01675 {
01676    ast_mutex_lock(&sub->lock);
01677    /* Allocate the RTP */
01678    sub->rtp = ast_rtp_new(sched, io, 1, 0);
01679    if (sub->rtp && sub->owner) {
01680       sub->owner->fds[0] = ast_rtp_fd(sub->rtp);
01681    }
01682    if (sub->rtp) {
01683       ast_rtp_setnat(sub->rtp, sub->nat);
01684    }
01685    /* Create the RTP connection */
01686    transmit_connect(sub->parent->parent->session);
01687    ast_mutex_unlock(&sub->lock);
01688 }
01689 
01690 static void *skinny_ss(void *data)
01691 {
01692    struct ast_channel *chan = data;
01693    struct skinny_subchannel *sub = chan->tech_pvt;
01694    struct skinny_line *l = sub->parent;
01695    struct skinnysession *s = l->parent->session;
01696    char exten[AST_MAX_EXTENSION] = "";
01697    int len = 0;
01698    int timeout = firstdigittimeout;
01699    int res;
01700    int getforward=0;
01701     
01702    if (option_verbose > 2) {
01703       ast_verbose( VERBOSE_PREFIX_3 "Starting simple switch on '%s@%s'\n", l->name, l->parent->name);
01704    }
01705    while(len < AST_MAX_EXTENSION-1) {
01706          res = ast_waitfordigit(chan, timeout);
01707          timeout = 0;
01708          if (res < 0) {
01709          if (skinnydebug) {
01710             ast_verbose("Skinny(%s@%s): waitfordigit returned < 0\n", l->name, l->parent->name);
01711             }
01712          ast_indicate(chan, -1);
01713          ast_hangup(chan);
01714                   return NULL;
01715          } else if (res)  {
01716                   exten[len++]=res;
01717                   exten[len] = '\0';
01718          }
01719          if (!ast_ignore_pattern(chan->context, exten)) {
01720          transmit_tone(s, SKINNY_SILENCE);
01721          } 
01722          if (ast_exists_extension(chan, chan->context, exten, 1, l->cid_num)) {
01723                   if (!res || !ast_matchmore_extension(chan, chan->context, exten, 1, l->cid_num)) {
01724                      if (getforward) {
01725                            /* Record this as the forwarding extension */
01726                            strncpy(l->call_forward, exten, sizeof(l->call_forward) - 1); 
01727                            if (option_verbose > 2) {
01728                                  ast_verbose(VERBOSE_PREFIX_3 "Setting call forward to '%s' on channel %s\n", 
01729                                        l->call_forward, chan->name);
01730                            }
01731                            transmit_tone(s, SKINNY_DIALTONE); 
01732                   if (res) {
01733                         break;
01734                   }
01735                ast_safe_sleep(chan, 500);
01736                         ast_indicate(chan, -1);
01737                ast_safe_sleep(chan, 1000);
01738                            memset(exten, 0, sizeof(exten));
01739                   transmit_tone(s, SKINNY_DIALTONE); 
01740                            len = 0;
01741                            getforward = 0;
01742                      } else  {
01743                            strncpy(chan->exten, exten, sizeof(chan->exten)-1);
01744 
01745                            if (!ast_strlen_zero(l->cid_num)) {
01746                   ast_set_callerid(chan,
01747                      l->hidecallerid ? "" : l->cid_num,
01748                      l->hidecallerid ? "" : l->cid_name,
01749                      NULL);
01750                               ast_setstate(chan, AST_STATE_RING);
01751                               res = ast_pbx_run(chan);
01752                               if (res) {
01753                                     ast_log(LOG_WARNING, "PBX exited non-zero\n");
01754                      transmit_tone(s, SKINNY_REORDER); 
01755                               }
01756                               return NULL;
01757                         }
01758             }
01759                   } else {
01760                      /* It's a match, but they just typed a digit, and there is an ambiguous match,
01761                            so just set the timeout to matchdigittimeout and wait some more */
01762                         timeout = matchdigittimeout;
01763               }
01764       } else if (res == 0) {
01765                   ast_log(LOG_DEBUG, "Not enough digits (and no ambiguous match)...\n");
01766             transmit_tone(s, SKINNY_REORDER); 
01767                ast_hangup(chan);
01768                return NULL;
01769          } else if (l->callwaiting && !strcmp(exten, "*70")) {
01770                   if (option_verbose > 2) {
01771                         ast_verbose(VERBOSE_PREFIX_3 "Disabling call waiting on %s\n", chan->name);
01772                   }
01773                /* Disable call waiting if enabled */
01774                l->callwaiting = 0;
01775                transmit_tone(s, SKINNY_DIALTONE);
01776          len = 0;
01777                memset(exten, 0, sizeof(exten));\
01778                timeout = firstdigittimeout;
01779           } else if (!strcmp(exten,ast_pickup_ext())) {
01780                /* Scan all channels and see if any there
01781                       * ringing channqels with that have call groups
01782                    * that equal this channels pickup group  
01783                    */
01784                   if (ast_pickup_call(chan)) {
01785                      ast_log(LOG_WARNING, "No call pickup possible...\n");
01786             transmit_tone(s, SKINNY_REORDER);
01787                   }
01788               ast_hangup(chan);
01789                   return NULL;
01790                } else if (!l->hidecallerid && !strcmp(exten, "*67")) {
01791                if (option_verbose > 2) {
01792                    ast_verbose(VERBOSE_PREFIX_3 "Disabling Caller*ID on %s\n", chan->name);
01793                   }
01794                /* Disable Caller*ID if enabled */
01795               l->hidecallerid = 1;
01796          ast_set_callerid(chan, "", "", NULL);
01797                   transmit_tone(s, SKINNY_DIALTONE);
01798                len = 0;
01799                memset(exten, 0, sizeof(exten));
01800                   timeout = firstdigittimeout;
01801          } else if (l->callreturn && !strcmp(exten, "*69")) {
01802                res = 0;
01803                if (!ast_strlen_zero(l->lastcallerid)) {
01804                      res = ast_say_digit_str(chan, l->lastcallerid, "", chan->language);
01805                }
01806                if (!res) {
01807                      transmit_tone(s, SKINNY_DIALTONE);
01808          }
01809                break;
01810          } else if (!strcmp(exten, "*78")) {
01811                   /* Do not disturb */
01812                   if (option_verbose > 2) {
01813                         ast_verbose(VERBOSE_PREFIX_3 "Enabled DND on channel %s\n", chan->name);
01814                   }
01815                transmit_tone(s, SKINNY_DIALTONE);
01816                   l->dnd = 1;
01817                   getforward = 0;
01818                   memset(exten, 0, sizeof(exten));
01819                   len = 0;
01820          } else if (!strcmp(exten, "*79")) {
01821                /* Do not disturb */
01822                if (option_verbose > 2) {
01823                      ast_verbose(VERBOSE_PREFIX_3 "Disabled DND on channel %s\n", chan->name);
01824                   }
01825          transmit_tone(s, SKINNY_DIALTONE);
01826               l->dnd = 0;
01827                   getforward = 0;
01828                   memset(exten, 0, sizeof(exten));
01829                   len = 0;
01830          } else if (l->cancallforward && !strcmp(exten, "*72")) {
01831                transmit_tone(s, SKINNY_DIALTONE);
01832                getforward = 1;
01833                memset(exten, 0, sizeof(exten));
01834                len = 0;
01835             } else if (l->cancallforward && !strcmp(exten, "*73")) {
01836                if (option_verbose > 2) {
01837                   ast_verbose(VERBOSE_PREFIX_3 "Cancelling call forwarding on channel %s\n", chan->name);
01838                }
01839                transmit_tone(s, SKINNY_DIALTONE); 
01840                memset(l->call_forward, 0, sizeof(l->call_forward));
01841                getforward = 0;
01842                memset(exten, 0, sizeof(exten));
01843                len = 0;
01844             } else if (!strcmp(exten, ast_parking_ext()) && 
01845                         sub->next->owner &&
01846                         ast_bridged_channel(sub->next->owner)) {
01847                /* This is a three way call, the main call being a real channel, 
01848                         and we're parking the first call. */
01849                      ast_masq_park_call(ast_bridged_channel(sub->next->owner), chan, 0, NULL);
01850                if (option_verbose > 2) {
01851                         ast_verbose(VERBOSE_PREFIX_3 "Parking call to '%s'\n", chan->name);
01852                }
01853                break;
01854             } else if (!ast_strlen_zero(l->lastcallerid) && !strcmp(exten, "*60")) {
01855                if (option_verbose > 2) {
01856                      ast_verbose(VERBOSE_PREFIX_3 "Blacklisting number %s\n", l->lastcallerid);
01857                }
01858                   res = ast_db_put("blacklist", l->lastcallerid, "1");
01859                   if (!res) {
01860                         transmit_tone(s, SKINNY_DIALTONE);     
01861                   memset(exten, 0, sizeof(exten));
01862                         len = 0;
01863                }
01864             } else if (l->hidecallerid && !strcmp(exten, "*82")) {
01865                if (option_verbose > 2) {
01866                      ast_verbose(VERBOSE_PREFIX_3 "Enabling Caller*ID on %s\n", chan->name);
01867                }
01868                /* Enable Caller*ID if enabled */
01869                l->hidecallerid = 0;
01870          ast_set_callerid(chan, l->cid_num, l->cid_name, NULL);
01871                   transmit_tone(s, SKINNY_DIALTONE);
01872                   len = 0;
01873                   memset(exten, 0, sizeof(exten));
01874                timeout = firstdigittimeout;
01875             } else if (!ast_canmatch_extension(chan, chan->context, exten, 1, chan->cid.cid_num) &&
01876                            ((exten[0] != '*') || (!ast_strlen_zero(exten) > 2))) {
01877                   ast_log(LOG_WARNING, "Can't match [%s] from '%s' in context %s\n", exten, chan->cid.cid_num ? chan->cid.cid_num : "<Unknown Caller>", chan->context);
01878                   transmit_tone(s, SKINNY_REORDER);   
01879          /* hang out for 3 seconds to let congestion play */
01880             ast_safe_sleep(chan, 3000); 
01881             break;
01882             }
01883             if (!timeout) {
01884                   timeout = gendigittimeout;
01885       }
01886          if (len && !ast_ignore_pattern(chan->context, exten)) {
01887          ast_indicate(chan, -1);
01888       }
01889       }  
01890    ast_hangup(chan);
01891    return NULL;
01892 }
01893 
01894 
01895 
01896 static int skinny_call(struct ast_channel *ast, char *dest, int timeout)
01897 {
01898    int res = 0;
01899    int tone = 0;
01900    struct skinny_line *l;
01901         struct skinny_subchannel *sub;
01902    struct skinnysession *session;
01903    
01904    sub = ast->tech_pvt;
01905         l = sub->parent;
01906    session = l->parent->session;
01907 
01908    if (!l->parent->registered) {
01909       ast_log(LOG_ERROR, "Device not registered, cannot call %s\n", dest);
01910       return -1;
01911    }
01912    
01913    if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
01914       ast_log(LOG_WARNING, "skinny_call called on %s, neither down nor reserved\n", ast->name);
01915       return -1;
01916    }
01917 
01918         if (skinnydebug) {
01919          ast_verbose(VERBOSE_PREFIX_3 "skinny_call(%s)\n", ast->name);
01920       }
01921 
01922    if (l->dnd) {
01923       ast_queue_control(ast, AST_CONTROL_BUSY);
01924       return -1;
01925    }
01926    
01927    switch (l->hookstate) {
01928         case SKINNY_OFFHOOK:
01929                tone = SKINNY_CALLWAITTONE;
01930                break;
01931         case SKINNY_ONHOOK:
01932       tone = SKINNY_ALERT;
01933       break;
01934         default:
01935                ast_log(LOG_ERROR, "Don't know how to deal with hookstate %d\n", l->hookstate);
01936                break;
01937       }
01938 
01939    transmit_lamp_indication(session, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK);
01940    transmit_ringer_mode(session, SKINNY_RING_INSIDE);
01941    
01942    if (ast->cid.cid_num) { 
01943       char ciddisplay[41];
01944       char *work;
01945       size_t size = sizeof(ciddisplay);
01946 
01947       /* For now, we'll assume that if it is 10 numbers, it is a standard NANPA number */
01948       if (strlen(ast->cid.cid_num) == 10) {
01949          ast_build_string(&work, &size, "(xxx)xxx-xxxx      %s",
01950                 ast->cid.cid_name ? ast->cid.cid_name : "");
01951          memcpy(&ciddisplay[1], ast->cid.cid_num, 3);
01952          memcpy(&ciddisplay[5], &ast->cid.cid_num[3], 3);
01953          memcpy(&ciddisplay[9], &ast->cid.cid_num[6], 4);
01954       } else {
01955          if (strlen(ast->cid.cid_num) < 41) {
01956             ast_build_string(&work, &size, "%s -- %s", ast->cid.cid_num,
01957                    ast->cid.cid_name ? ast->cid.cid_name : "");
01958          } else {
01959             strncpy(ciddisplay, "Number too long!", 15);
01960          }
01961       }
01962       if (skinnydebug) {
01963          ast_verbose("Trying to send: '%s'\n",ciddisplay);
01964       }
01965       transmit_displaymessage(session, ciddisplay);
01966    } else {
01967       transmit_displaymessage(session, "Unknown Name");
01968    }
01969    transmit_tone(session, tone);
01970    transmit_callstate(session, l->instance, SKINNY_RINGIN, sub->callid);
01971    transmit_displaypromptstatus(session, "Ring-In", 0, l->instance, sub->callid);
01972    transmit_callinfo(session, ast->cid.cid_name, ast->cid.cid_num, l->cid_name, l->cid_num, l->instance, sub->callid, 1); 
01973 
01974    /* XXX need to deal with softkeys */
01975 
01976    ast_setstate(ast, AST_STATE_RINGING);
01977    ast_queue_control(ast, AST_CONTROL_RINGING);
01978    sub->outgoing = 1;
01979    return res;
01980 }
01981 
01982 static int skinny_hangup(struct ast_channel *ast)
01983 {
01984     struct skinny_subchannel *sub = ast->tech_pvt;
01985     struct skinny_line *l = sub->parent;
01986     struct skinnysession *s = l->parent->session;
01987 
01988     if (skinnydebug) {
01989         ast_verbose("skinny_hangup(%s) on %s@%s\n", ast->name, l->name, l->parent->name);
01990     }
01991     if (!ast->tech_pvt) {
01992         ast_log(LOG_DEBUG, "Asked to hangup channel not connected\n");
01993         return 0;
01994     }
01995 
01996     if (l->parent->registered) {
01997    if ((sub->parent->type = TYPE_LINE) && (sub->parent->hookstate == SKINNY_OFFHOOK)) {
01998          sub->parent->hookstate = SKINNY_ONHOOK;
01999          transmit_callstate(s, l->instance, SKINNY_ONHOOK, sub->callid);
02000          transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF);
02001          transmit_speaker_mode(s, SKINNY_SPEAKEROFF); 
02002       } else if ((sub->parent->type = TYPE_LINE) && (sub->parent->hookstate == SKINNY_ONHOOK)) {
02003          transmit_callstate(s, l->instance, SKINNY_ONHOOK, sub->callid);
02004          transmit_speaker_mode(s, SKINNY_SPEAKEROFF); 
02005          transmit_ringer_mode(s, SKINNY_RING_OFF);
02006          transmit_tone(s, SKINNY_SILENCE);
02007          transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF);
02008          do_housekeeping(s);
02009       } 
02010     }
02011     ast_mutex_lock(&sub->lock);
02012     sub->owner = NULL;
02013     ast->tech_pvt = NULL;
02014     sub->alreadygone = 0;
02015     sub->outgoing = 0;
02016     if (sub->rtp) {
02017         ast_rtp_destroy(sub->rtp);
02018         sub->rtp = NULL;
02019     }
02020     ast_mutex_unlock(&sub->lock);
02021     return 0;
02022 }
02023 
02024 static int skinny_answer(struct ast_channel *ast)
02025 {
02026     int res = 0;
02027     struct skinny_subchannel *sub = ast->tech_pvt;
02028     struct skinny_line *l = sub->parent;
02029     struct skinnysession *s = l->parent->session;
02030 
02031     sub->cxmode = SKINNY_CX_SENDRECV;
02032     if (!sub->rtp) {
02033       start_rtp(sub);
02034     } 
02035     ast_verbose("skinny_answer(%s) on %s@%s-%d\n", ast->name, l->name, l->parent->name, sub->callid);
02036     if (ast->_state != AST_STATE_UP) {
02037    ast_setstate(ast, AST_STATE_UP);
02038     }
02039     transmit_tone(s, SKINNY_NOTONE);
02040     transmit_callstate(s, l->instance, SKINNY_CONNECTED, sub->callid);
02041     transmit_displaypromptstatus(s, "Connected", 0, l->instance, sub->callid);
02042     return res;
02043 }
02044 
02045 static struct ast_frame *skinny_rtp_read(struct skinny_subchannel *sub)
02046 {
02047    /* Retrieve audio/etc from channel.  Assumes sub->lock is already held. */
02048    struct ast_frame *f;
02049    f = ast_rtp_read(sub->rtp);
02050    if (sub->owner) {
02051       /* We already hold the channel lock */
02052       if (f->frametype == AST_FRAME_VOICE) {
02053          if (f->subclass != sub->owner->nativeformats) {
02054             ast_log(LOG_DEBUG, "Oooh, format changed to %d\n", f->subclass);
02055             sub->owner->nativeformats = f->subclass;
02056             ast_set_read_format(sub->owner, sub->owner->readformat);
02057             ast_set_write_format(sub->owner, sub->owner->writeformat);
02058          }
02059       }
02060    }
02061    return f;
02062 }
02063 
02064 static struct ast_frame  *skinny_read(struct ast_channel *ast)
02065 {
02066    struct ast_frame *fr;
02067    struct skinny_subchannel *sub = ast->tech_pvt;
02068    ast_mutex_lock(&sub->lock);
02069    fr = skinny_rtp_read(sub);
02070    ast_mutex_unlock(&sub->lock);
02071    return fr;
02072 }
02073 
02074 static int skinny_write(struct ast_channel *ast, struct ast_frame *frame)
02075 {
02076    struct skinny_subchannel *sub = ast->tech_pvt;
02077    int res = 0;
02078    if (frame->frametype != AST_FRAME_VOICE) {
02079       if (frame->frametype == AST_FRAME_IMAGE) {
02080          return 0;
02081       } else {
02082          ast_log(LOG_WARNING, "Can't send %d type frames with skinny_write\n", frame->frametype);
02083          return 0;
02084       }
02085    } else {
02086       if (!(frame->subclass & ast->nativeformats)) {
02087          ast_log(LOG_WARNING, "Asked to transmit frame type %d, while native formats is %d (read/write = %d/%d)\n",
02088             frame->subclass, ast->nativeformats, ast->readformat, ast->writeformat);
02089          return -1;
02090       }
02091    }
02092    if (sub) {
02093       ast_mutex_lock(&sub->lock);
02094       if (sub->rtp) {
02095          res =  ast_rtp_write(sub->rtp, frame);
02096       }
02097       ast_mutex_unlock(&sub->lock);
02098    }
02099    return res;
02100 }
02101 
02102 static int skinny_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
02103 {
02104    struct skinny_subchannel *sub = newchan->tech_pvt;
02105       ast_log(LOG_NOTICE, "skinny_fixup(%s, %s)\n", oldchan->name, newchan->name);
02106    if (sub->owner != oldchan) {
02107       ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, sub->owner);
02108       return -1;
02109    }
02110    sub->owner = newchan;
02111    return 0;
02112 }
02113 
02114 static int skinny_senddigit(struct ast_channel *ast, char digit)
02115 {
02116 #if 0
02117    struct skinny_subchannel *sub = ast->tech_pvt;
02118    int tmp;
02119    /* not right */
02120    sprintf(tmp, "%d", digit);  
02121    transmit_tone(sub->parent->parent->session, digit);
02122 #endif
02123    return -1;
02124 }
02125 
02126 static char *control2str(int ind) {
02127     static char tmp[100];
02128 
02129     switch (ind) {
02130         case AST_CONTROL_HANGUP:
02131             return "Other end has hungup";
02132         case AST_CONTROL_RING:
02133             return "Local ring";
02134         case AST_CONTROL_RINGING:
02135             return "Remote end is ringing";
02136         case AST_CONTROL_ANSWER:
02137             return "Remote end has answered";
02138         case AST_CONTROL_BUSY:
02139             return "Remote end is busy";
02140         case AST_CONTROL_TAKEOFFHOOK:
02141             return "Make it go off hook";
02142         case AST_CONTROL_OFFHOOK:
02143             return "Line is off hook";
02144         case AST_CONTROL_CONGESTION:
02145             return "Congestion (circuits busy)";
02146         case AST_CONTROL_FLASH:
02147             return "Flash hook";
02148         case AST_CONTROL_WINK:
02149             return "Wink";
02150         case AST_CONTROL_OPTION:
02151             return "Set a low-level option";
02152         case AST_CONTROL_RADIO_KEY:
02153             return "Key Radio";
02154         case AST_CONTROL_RADIO_UNKEY:
02155             return "Un-Key Radio";
02156         case AST_CONTROL_PROGRESS:
02157             return "Remote end is making Progress";
02158         case AST_CONTROL_PROCEEDING:
02159             return "Remote end is proceeding";
02160         case AST_CONTROL_HOLD:
02161             return "Hold";
02162         case AST_CONTROL_UNHOLD:
02163             return "Unhold";
02164    case -1:
02165        return "Stop tone";
02166     }
02167     snprintf(tmp, 100, "UNKNOWN-%d", ind);
02168     return tmp;
02169 }
02170 
02171 
02172 static int skinny_indicate(struct ast_channel *ast, int ind)
02173 {
02174    struct skinny_subchannel *sub = ast->tech_pvt;
02175    struct skinny_line *l = sub->parent;
02176    struct skinnysession *s = l->parent->session;
02177 
02178       if (skinnydebug) {
02179          ast_verbose(VERBOSE_PREFIX_3 "Asked to indicate '%s' condition on channel %s\n", control2str(ind), ast->name);
02180       }
02181    switch(ind) {
02182    case AST_CONTROL_RINGING:
02183       if (ast->_state != AST_STATE_UP) {
02184          if (!sub->progress) {      
02185             transmit_tone(s, SKINNY_ALERT);
02186             transmit_callstate(s, l->instance, SKINNY_RINGOUT, sub->callid);
02187             transmit_diallednumber(s, ast->exten, l->instance, sub->callid);
02188             transmit_displaypromptstatus(s, "Ring Out", 0, l->instance, sub->callid);
02189             transmit_callinfo(s, ast->cid.cid_name, ast->cid.cid_num, ast->exten, ast->exten, l->instance, sub->callid, 2); /* 2 = outgoing from phone */
02190             sub->ringing = 1;
02191             break;
02192          }
02193       }
02194       return -1;
02195    case AST_CONTROL_BUSY:
02196       if (ast->_state != AST_STATE_UP) {     
02197          transmit_tone(s, SKINNY_BUSYTONE);
02198          transmit_callstate(s, l->instance, SKINNY_BUSY, sub->callid);
02199          sub->alreadygone = 1;
02200          ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
02201                         break;
02202                 }
02203                 return -1;
02204    case AST_CONTROL_CONGESTION:
02205       if (ast->_state != AST_STATE_UP) {     
02206          transmit_tone(s, SKINNY_REORDER);
02207          transmit_callstate(s, l->instance, SKINNY_CONGESTION, sub->callid);
02208          sub->alreadygone = 1;
02209                         ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
02210                         break;
02211                 }
02212                 return -1;
02213    case AST_CONTROL_PROGRESS:
02214                 if ((ast->_state != AST_STATE_UP) && !sub->progress && !sub->outgoing) {
02215          transmit_tone(s, SKINNY_ALERT);
02216          transmit_callstate(s, l->instance, SKINNY_PROGRESS, sub->callid);
02217          transmit_displaypromptstatus(s, "Call Progress", 0, l->instance, sub->callid);
02218          transmit_callinfo(s, ast->cid.cid_name, ast->cid.cid_num, ast->exten, ast->exten, l->instance, sub->callid, 2); /* 2 = outgoing from phone */
02219                         sub->progress = 1;
02220                         break;
02221                 }
02222                 return -1;  
02223    case -1:
02224       transmit_tone(s, SKINNY_SILENCE);
02225       break;      
02226    case AST_CONTROL_PROCEEDING:
02227       break;
02228    default:
02229       ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", ind);
02230       return -1;
02231    }
02232    return 0;
02233 }
02234    
02235 static struct ast_channel *skinny_new(struct skinny_subchannel *sub, int state)
02236 {
02237    struct ast_channel *tmp;
02238    struct skinny_line *l = sub->parent;
02239    int fmt;
02240    l = sub->parent;
02241    tmp = ast_channel_alloc(1);
02242    if (tmp) {
02243       tmp->tech = &skinny_tech;
02244       tmp->nativeformats = l->capability;
02245       if (!tmp->nativeformats)
02246          tmp->nativeformats = capability;
02247       fmt = ast_best_codec(tmp->nativeformats);
02248       ast_verbose("skinny_new: tmp->nativeformats=%d fmt=%d\n", tmp->nativeformats, fmt);
02249       snprintf(tmp->name, sizeof(tmp->name), "Skinny/%s@%s-%d", l->name, l->parent->name, sub->callid);
02250       if (sub->rtp) {
02251          tmp->fds[0] = ast_rtp_fd(sub->rtp);
02252       }
02253       tmp->type = type;
02254       ast_setstate(tmp, state);
02255       if (state == AST_STATE_RING) {
02256          tmp->rings = 1;
02257       }
02258       tmp->writeformat = fmt;
02259       tmp->rawwriteformat = fmt;
02260       tmp->readformat = fmt;
02261       tmp->rawreadformat = fmt;
02262       tmp->tech_pvt = sub;
02263       if (!ast_strlen_zero(l->language)) {
02264          strncpy(tmp->language, l->language, sizeof(tmp->language)-1);
02265       }
02266       if (!ast_strlen_zero(l->accountcode)) {
02267          strncpy(tmp->accountcode, l->accountcode, sizeof(tmp->accountcode)-1);
02268       }
02269       if (l->amaflags) {
02270          tmp->amaflags = l->amaflags;
02271       }
02272       sub->owner = tmp;
02273       ast_mutex_lock(&usecnt_lock);
02274       usecnt++;
02275       ast_mutex_unlock(&usecnt_lock);
02276       ast_update_use_count();
02277       tmp->callgroup = l->callgroup;
02278       tmp->pickupgroup = l->pickupgroup;
02279       strncpy(tmp->call_forward, l->call_forward, sizeof(tmp->call_forward) - 1);
02280       strncpy(tmp->context, l->context, sizeof(tmp->context)-1);
02281       strncpy(tmp->exten,l->exten, sizeof(tmp->exten)-1);
02282 
02283       if (!ast_strlen_zero(l->cid_num))
02284          tmp->cid.cid_num = strdup(l->cid_num);
02285       if (!ast_strlen_zero(l->cid_name))
02286          tmp->cid.cid_name = strdup(l->cid_name);
02287 
02288       tmp->priority = 1;
02289       tmp->adsicpe = AST_ADSI_UNAVAILABLE;
02290 
02291       if (state != AST_STATE_DOWN) {
02292          if (ast_pbx_start(tmp)) {
02293             ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
02294             ast_hangup(tmp);
02295             tmp = NULL;
02296          }
02297       }
02298    } else {
02299       ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
02300     }
02301     return tmp;
02302 }
02303 
02304 static int handle_message(skinny_req *req, struct skinnysession *s)
02305 {
02306    struct skinny_subchannel *sub;
02307    struct ast_channel *c;
02308    struct ast_frame f = { 0, };  
02309    struct sockaddr_in sin;
02310    struct sockaddr_in us;
02311    struct skinny_line *lines;
02312    char name[16];
02313    char addr[4];
02314    char d;
02315    char iabuf[INET_ADDRSTRLEN];
02316    int digit;
02317    int res=0;
02318    int speedDialNum;
02319    int lineNumber;
02320    int stimulus;
02321    int stimulusInstance;
02322    int status;
02323    int port;
02324    int i;
02325    time_t timer;
02326    struct tm *cmtime;
02327    pthread_t t;
02328    button_defs_t *b, *buse;
02329    
02330    if ((!s->device) && (letohl(req->e) != REGISTER_MESSAGE && letohl(req->e) != ALARM_MESSAGE)) {
02331       ast_log(LOG_WARNING, "Client sent message #%d without first registering.\n", req->e);
02332       free(req);
02333       return 0;
02334    }
02335 
02336    switch(letohl(req->e))  {
02337    case ALARM_MESSAGE:
02338       /* no response necessary */
02339       break;
02340    case REGISTER_MESSAGE:
02341       if (skinnydebug) {
02342          ast_verbose("Device %s is attempting to register\n", req->data.reg.name);
02343       }
02344       res = skinny_register(req, s);   
02345       if (!res) {
02346          ast_log(LOG_ERROR, "Rejecting Device %s: Device not found\n", req->data.reg.name);
02347          memcpy(&name, req->data.reg.name, sizeof(req->data.reg.name));
02348          memset(req, 0, sizeof(skinny_req));
02349          req->len = htolel(sizeof(register_rej_message)+4);
02350          req->e = htolel(REGISTER_REJ_MESSAGE);
02351          snprintf(req->data.regrej.errMsg, sizeof(req->data.regrej.errMsg), "No Authority: %s", name);
02352          transmit_response(s, req);
02353          break;
02354       }
02355       if (option_verbose > 2) {
02356          ast_verbose(VERBOSE_PREFIX_3 "Device '%s' successfuly registered\n", s->device->name); 
02357       }
02358       memset(req, 0, SKINNY_MAX_PACKET);
02359       req->len = htolel(sizeof(register_ack_message)+4);
02360       req->e = htolel(REGISTER_ACK_MESSAGE);
02361       req->data.regack.res[0] = '0';
02362       req->data.regack.res[1] = '\0';
02363       req->data.regack.keepAlive = htolel(keep_alive);
02364       strncpy(req->data.regack.dateTemplate, date_format, sizeof(req->data.regack.dateTemplate) - 1); 
02365       req->data.regack.res2[0] = '0';
02366       req->data.regack.res2[1] = '\0';
02367       req->data.regack.secondaryKeepAlive = htolel(keep_alive);
02368       transmit_response(s, req);
02369       if (skinnydebug) {
02370          ast_verbose("Requesting capabilities\n");
02371       }
02372       memset(req, 0, SKINNY_MAX_PACKET);
02373       req->len = htolel(4);
02374       req->e = htolel(CAPABILITIES_REQ_MESSAGE);
02375       transmit_response(s, req);
02376       break;
02377    case UNREGISTER_MESSAGE:
02378       /* XXX Acutally unregister the device */
02379       break;
02380    case IP_PORT_MESSAGE:
02381       /* no response necessary */
02382       break;
02383    case STIMULUS_MESSAGE:
02384       stimulus = letohl(req->data.stimulus.stimulus);
02385       stimulusInstance = letohl(req->data.stimulus.stimulusInstance);
02386       
02387       switch(stimulus) {
02388       case STIMULUS_REDIAL:
02389          /* If we can keep an array of dialed frames we can implement a quick 
02390             and dirty redial, feeding the frames we last got into the queue
02391             function */
02392          if (skinnydebug) {
02393             ast_verbose("Recieved Stimulus: Redial(%d)\n", stimulusInstance);
02394          }
02395          break;
02396       case STIMULUS_SPEEDDIAL:
02397          if (skinnydebug) {
02398             ast_verbose("Recieved Stimulus: SpeedDial(%d)\n", stimulusInstance);
02399          }
02400          break;
02401       case STIMULUS_HOLD:
02402          /* start moh? set RTP to 0.0.0.0? */
02403          if (skinnydebug) {
02404             ast_verbose("Recieved Stimulus: Hold(%d)\n", stimulusInstance);
02405          }
02406          break;
02407       case STIMULUS_TRANSFER:
02408          if (skinnydebug) {
02409             ast_verbose("Recieved Stimulus: Transfer(%d)\n", stimulusInstance);
02410          }
02411          transmit_tone(s, SKINNY_DIALTONE);  
02412          /* XXX figure out how to transfer */
02413          break;
02414       case STIMULUS_CONFERENCE:
02415          if (skinnydebug) {
02416             ast_verbose("Recieved Stimulus: Transfer(%d)\n", stimulusInstance);
02417          }
02418          transmit_tone(s, SKINNY_DIALTONE);
02419          /* XXX determine the best way to pull off a conference.  Meetme?  */
02420          break;
02421       case STIMULUS_VOICEMAIL:
02422          if (skinnydebug) {
02423             ast_verbose("Recieved Stimulus: Voicemail(%d)\n", stimulusInstance);
02424          }
02425          /* XXX Find and dial voicemail extension */
02426          break;
02427       case STIMULUS_CALLPARK:
02428          if (skinnydebug) {
02429             ast_verbose("Recieved Stimulus: Park Call(%d)\n", stimulusInstance);
02430          }
02431          /* XXX Park the call */
02432          break;
02433       case STIMULUS_FORWARDALL:
02434          /* Why is DND under FORWARDALL ? */
02435 
02436          /* Do not disturb */
02437          transmit_tone(s, SKINNY_DIALTONE);
02438          if (s->device->lines->dnd != 0){
02439             if (option_verbose > 2) {
02440                ast_verbose(VERBOSE_PREFIX_3 "Disabling DND on %s@%s\n",find_subchannel_by_line(s->device->lines)->parent->name,find_subchannel_by_line(s->device->lines)->parent->name);
02441             }
02442             s->device->lines->dnd = 0;
02443             transmit_lamp_indication(s, STIMULUS_FORWARDALL, 1, SKINNY_LAMP_ON);
02444             transmit_displaynotify(s, "DnD disabled",10);
02445          } else {
02446             if (option_verbose > 2) {
02447                ast_verbose(VERBOSE_PREFIX_3 "Enabling DND on %s@%s\n",find_subchannel_by_line(s->device->lines)->parent->name,find_subchannel_by_line(s->device->lines)->parent->name);
02448             }
02449             s->device->lines->dnd = 1;
02450             transmit_lamp_indication(s, STIMULUS_FORWARDALL, 1, SKINNY_LAMP_OFF);
02451             transmit_displaynotify(s, "DnD enabled",10);
02452          }
02453          break;
02454       case STIMULUS_FORWARDBUSY:
02455       case STIMULUS_FORWARDNOANSWER:
02456          /* Gonna be fun, not */
02457          if (skinnydebug) {
02458             ast_verbose("Recieved Stimulus: Forward (%d)\n", stimulusInstance);
02459          }
02460          break;
02461       case STIMULUS_DISPLAY:
02462          /* Not sure what this is */
02463          if (skinnydebug) {
02464             ast_verbose("Recieved Stimulus: Display(%d)\n", stimulusInstance);
02465          }
02466          break;
02467       case STIMULUS_LINE:
02468          if (skinnydebug) {
02469             ast_verbose("Recieved Stimulus: Line(%d)\n", stimulusInstance);
02470          }     
02471          sub = find_subchannel_by_line(s->device->lines);
02472          /* turn the speaker on */
02473          transmit_speaker_mode(s, 1);  
02474       break;
02475       default:
02476          ast_verbose("RECEIVED UNKNOWN STIMULUS:  %d(%d)\n", stimulus, stimulusInstance);       
02477          break;
02478       }
02479       break;
02480    case VERSION_REQ_MESSAGE:
02481       if (skinnydebug) {
02482          ast_verbose("Version Request\n");
02483       }
02484       memset(req, 0, SKINNY_MAX_PACKET);
02485       req->len = htolel(sizeof(version_res_message)+4);
02486       req->e = htolel(VERSION_RES_MESSAGE);
02487       snprintf(req->data.version.version, sizeof(req->data.version.version), s->device->version_id);
02488       transmit_response(s, req);
02489       break;
02490    case SERVER_REQUEST_MESSAGE:
02491       if (skinnydebug) {
02492          ast_verbose("Recieved Server Request\n");
02493       }
02494       memset(req, 0, SKINNY_MAX_PACKET);
02495       req->len = htolel(sizeof(server_res_message)+4);
02496       req->e = htolel(SERVER_RES_MESSAGE);
02497       memcpy(req->data.serverres.server[0].serverName, ourhost, 
02498             sizeof(req->data.serverres.server[0].serverName));
02499       req->data.serverres.serverListenPort[0] = htolel(ourport);
02500       req->data.serverres.serverIpAddr[0] = htolel(__ourip.s_addr);
02501       transmit_response(s, req); 
02502       break;
02503    case BUTTON_TEMPLATE_REQ_MESSAGE:
02504       if (skinnydebug) {
02505          ast_verbose("Buttontemplate requested\n");
02506       }
02507       sub = find_subchannel_by_line(s->device->lines);
02508       memset(req, 0, SKINNY_MAX_PACKET);
02509       req->e = htolel(BUTTON_TEMPLATE_RES_MESSAGE);   
02510       req->len = htolel(sizeof(button_template_res_message)+4);
02511 
02512       /* Find a matching button definition, default to first in the
02513          list */
02514       buse = button_defs;
02515       for(b=button_defs; b->type; b++) {
02516          if (!strcmp(s->device->model, b->type)) {
02517             buse = b;
02518          }
02519       }
02520       req->data.buttontemplate.buttonOffset = 0;
02521       req->data.buttontemplate.buttonCount  = htolel(buse->num_buttons);
02522       req->data.buttontemplate.totalButtonCount = htolel(buse->num_buttons);
02523       for (i=0; i<42; i++) {
02524          if (i < buse->num_buttons) {
02525             memcpy(&(req->data.buttontemplate.definition[i]),
02526                &(buse->button_def[i]),
02527                sizeof(button_definition));
02528          } else {
02529             memcpy(&(req->data.buttontemplate.definition[i]),
02530                &(button_def_none),
02531                sizeof(button_definition));
02532          }
02533       }
02534 
02535       if (skinnydebug) {         
02536          ast_verbose("Sending %s template to %s@%s (%s)\n",
02537                   buse->type, 
02538                   sub->parent->name, 
02539                   sub->parent->parent->name, 
02540                   s->device->model);
02541       }
02542       transmit_response(s, req);
02543       break;
02544    case SOFT_KEY_SET_REQ_MESSAGE:
02545       if (skinnydebug)  {
02546          ast_verbose("Received SoftKeySetReq\n");
02547       }
02548       memset(req, 0, SKINNY_MAX_PACKET);
02549       req->len = htolel(sizeof(soft_key_sets)+4);
02550       req->e = htolel(SOFT_KEY_SET_RES_MESSAGE);
02551       req->data.softkeysets.softKeySetOffset = 0;
02552       req->data.softkeysets.softKeySetCount = htolel(11);
02553       req->data.softkeysets.totalSoftKeySetCount = htolel(11); 
02554       /* XXX Wicked hack XXX */
02555       memcpy(req->data.softkeysets.softKeySetDefinition, 
02556             soft_key_set_hack, 
02557             sizeof(req->data.softkeysets.softKeySetDefinition));
02558       transmit_response(s,req);
02559       break;
02560    case SOFT_KEY_TEMPLATE_REQ_MESSAGE:
02561       if (skinnydebug) {
02562          ast_verbose("Recieved SoftKey Template Request\n");
02563       }
02564       memset(req, 0, SKINNY_MAX_PACKET);
02565       req->len = htolel(sizeof(soft_key_template)+4);
02566       req->e = htolel(SOFT_KEY_TEMPLATE_RES_MESSAGE);
02567       req->data.softkeytemplate.softKeyOffset = 0;
02568       req->data.softkeytemplate.softKeyCount = htolel(sizeof(soft_key_template_default) / sizeof(soft_key_template_definition));
02569       req->data.softkeytemplate.totalSoftKeyCount = htolel(sizeof(soft_key_template_default) / sizeof(soft_key_template_definition)); 
02570       memcpy(req->data.softkeytemplate.softKeyTemplateDefinition,
02571             soft_key_template_default,
02572             sizeof(soft_key_template_default));
02573       transmit_response(s,req);
02574       break;
02575    case TIME_DATE_REQ_MESSAGE:
02576       if (skinnydebug) {
02577          ast_verbose("Received Time/Date Request\n");
02578       }
02579       memset(req, 0, SKINNY_MAX_PACKET);
02580       req->len = htolel(sizeof(definetimedate_message)+4);
02581       req->e = htolel(DEFINETIMEDATE_MESSAGE);
02582       timer=time(NULL);
02583       cmtime = localtime(&timer);
02584       req->data.definetimedate.year = htolel(cmtime->tm_year+1900);
02585       req->data.definetimedate.month = htolel(cmtime->tm_mon+1);
02586       req->data.definetimedate.dayofweek = htolel(cmtime->tm_wday);
02587       req->data.definetimedate.day = htolel(cmtime->tm_mday);
02588       req->data.definetimedate.hour = htolel(cmtime->tm_hour);
02589       req->data.definetimedate.minute = htolel(cmtime->tm_min);
02590       req->data.definetimedate.seconds = htolel(cmtime->tm_sec);
02591       transmit_response(s, req);
02592       break;
02593    case SPEED_DIAL_STAT_REQ_MESSAGE:
02594       /* Not really sure how Speed Dial's are different than the 
02595          Softkey templates */
02596       speedDialNum = letohl(req->data.speeddialreq.speedDialNumber);
02597       memset(req, 0, SKINNY_MAX_PACKET);
02598       req->len = htolel(sizeof(speed_dial_stat_res_message)+4);
02599       req->e = htolel(SPEED_DIAL_STAT_RES_MESSAGE);
02600 #if 0
02601       /* XXX Do this right XXX */   
02602       /* If the redial function works the way I think it will, a modification of it
02603          can work here was well. Yikes. */
02604       req->data.speeddialreq.speedDialNumber = speedDialNum;
02605       snprintf(req->data.speeddial.speedDialDirNumber, sizeof(req->data.speeddial.speedDialDirNumber), "31337");
02606       snprintf(req->data.speeddial.speedDialDisplayName,  sizeof(req->data.speeddial.speedDialDisplayName),"Asterisk Rules!");
02607 #endif   
02608       transmit_response(s, req);
02609       break;
02610    case LINE_STATE_REQ_MESSAGE:
02611       lineNumber = letohl(req->data.line.lineNumber);
02612       if (skinnydebug) {
02613          ast_verbose("Received LineStateReq\n");
02614       }
02615       memset(req, 0, SKINNY_MAX_PACKET);
02616       req->len = htolel(sizeof(line_stat_res_message)+4);
02617       req->e = htolel(LINE_STAT_RES_MESSAGE);   
02618       sub = find_subchannel_by_line(s->device->lines);
02619       if (!sub) {
02620          ast_log(LOG_NOTICE, "No available lines on: %s\n", s->device->name);
02621          return 0;
02622       }
02623       lines = sub->parent;
02624       ast_mutex_lock(&devicelock);
02625       for (i=1; i < lineNumber; i++) {
02626          lines = lines->next;
02627       }
02628       ast_mutex_unlock(&devicelock);
02629       req->data.linestat.linenumber = letohl(lineNumber);      
02630       memcpy(req->data.linestat.lineDirNumber, lines->name,
02631             sizeof(req->data.linestat.lineDirNumber));
02632       memcpy(req->data.linestat.lineDisplayName, lines->label,
02633             sizeof(req->data.linestat.lineDisplayName)); 
02634       transmit_response(s,req);
02635       break;
02636    case CAPABILITIES_RES_MESSAGE:
02637       if (skinnydebug) {
02638          ast_verbose("Received CapabilitiesRes\n");   
02639       }
02640       /* XXX process the capabilites  */
02641       break;
02642    case KEEP_ALIVE_MESSAGE:
02643       memset(req, 0, SKINNY_MAX_PACKET);
02644       req->len = htolel(4);
02645       req->e = htolel(KEEP_ALIVE_ACK_MESSAGE);
02646       transmit_response(s, req);
02647       do_housekeeping(s);
02648       break;
02649    case OFFHOOK_MESSAGE:
02650       transmit_ringer_mode(s,SKINNY_RING_OFF);
02651       transmit_lamp_indication(s, STIMULUS_LINE, s->device->lines->instance, SKINNY_LAMP_ON); 
02652       sub = find_subchannel_by_line(s->device->lines);
02653       if (!sub) {
02654          ast_log(LOG_NOTICE, "No available lines on: %s\n", s->device->name);
02655          return 0;
02656       }
02657       sub->parent->hookstate = SKINNY_OFFHOOK;
02658       
02659       if (sub->outgoing) {
02660          /* We're answering a ringing call */
02661          ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
02662          transmit_callstate(s, s->device->lines->instance, SKINNY_OFFHOOK, sub->callid);
02663          transmit_tone(s, SKINNY_SILENCE);
02664          transmit_callstate(s, s->device->lines->instance, SKINNY_CONNECTED, sub->callid);
02665          start_rtp(sub);
02666          ast_setstate(sub->owner, AST_STATE_UP);
02667          /* XXX select the appropriate soft key here */
02668       } else {    
02669          if (!sub->owner) {   
02670             transmit_callstate(s, s->device->lines->instance, SKINNY_OFFHOOK, sub->callid);
02671             if (skinnydebug) {
02672                ast_verbose("Attempting to Clear display on Skinny %s@%s\n",sub->parent->name, sub->parent->parent->name);
02673             }
02674             transmit_displaymessage(s, NULL); /* clear display */ 
02675             transmit_tone(s, SKINNY_DIALTONE);
02676             c = skinny_new(sub, AST_STATE_DOWN);         
02677             if(c) {
02678                /* start the switch thread */
02679                if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
02680                   ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
02681                   ast_hangup(c);
02682                }
02683             } else {
02684                ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", sub->parent->name, s->device->name);
02685             }
02686          } else {
02687             ast_log(LOG_DEBUG, "Current sub [%s] already has owner\n", sub->owner->name);
02688          }
02689       }
02690       break;
02691    case ONHOOK_MESSAGE:
02692       sub = find_subchannel_by_line(s->device->lines);
02693       if (sub->parent->hookstate == SKINNY_ONHOOK) {
02694          /* Somthing else already put us back on hook */ 
02695          break;
02696       }
02697       sub->cxmode = SKINNY_CX_RECVONLY;
02698       sub->parent->hookstate = SKINNY_ONHOOK;
02699       transmit_callstate(s, s->device->lines->instance, sub->parent->hookstate,sub->callid);
02700       if (skinnydebug) {
02701          ast_verbose("Skinny %s@%s went on hook\n",sub->parent->name, sub->parent->parent->name);
02702          }
02703                if (sub->parent->transfer && (sub->owner && sub->next->owner) && ((!sub->outgoing) || (!sub->next->outgoing))) {
02704          /* We're allowed to transfer, we have two active calls and */
02705          /* we made at least one of the calls.  Let's try and transfer */
02706 
02707 #if 0
02708                if ((res = attempt_transfer(p)) < 0) {
02709              if (p->sub->next->owner) {
02710                sub->next->alreadygone = 1;
02711                ast_queue_hangup(sub->next->owner,1);
02712             }
02713          } else if (res) {
02714             ast_log(LOG_WARNING, "Transfer attempt failed\n");
02715             return -1;
02716                   }
02717 #endif
02718       } else {
02719                /* Hangup the current call */
02720                /* If there is another active call, skinny_hangup will ring the phone with the other call */
02721                if (sub->owner) {
02722                   sub->alreadygone = 1;
02723                   ast_queue_hangup(sub->owner);
02724                } else {
02725                   ast_log(LOG_WARNING, "Skinny(%s@%s-%d) channel already destroyed\n", 
02726                              sub->parent->name, sub->parent->parent->name, sub->callid);
02727                }
02728             }
02729             if ((sub->parent->hookstate == SKINNY_ONHOOK) && (!sub->next->rtp)) {
02730          do_housekeeping(s);
02731          }
02732       break;
02733    case KEYPAD_BUTTON_MESSAGE:
02734       digit = letohl(req->data.keypad.button);
02735       if (skinnydebug) {
02736          ast_verbose("Collected digit: [%d]\n", digit);
02737       }
02738       f.frametype = AST_FRAME_DTMF;
02739       if (digit == 14) {
02740          d = '*';
02741       } else if (digit == 15) {
02742          d = '#';
02743       } else if (digit >=0 && digit <= 9) {
02744          d = '0' + digit;
02745       } else {
02746          /* digit=10-13 (A,B,C,D ?), or
02747           * digit is bad value
02748           * 
02749           * probably should not end up here, but set
02750           * value for backward compatibility, and log
02751           * a warning.
02752           */
02753          d = '0' + digit;
02754          ast_log(LOG_WARNING, "Unsupported digit %d\n", digit);
02755       }
02756       f.subclass  = d;  
02757       f.src = "skinny";
02758       sub = find_subchannel_by_line(s->device->lines);      
02759       if (sub->owner) {
02760          /* XXX MUST queue this frame to all subs in threeway call if threeway call is active */
02761          ast_queue_frame(sub->owner, &f);
02762                   if (sub->next->owner) {
02763             ast_queue_frame(sub->next->owner, &f);
02764                   }
02765          } else {
02766          ast_verbose("No owner: %s\n", s->device->lines->name);
02767       }
02768       break;
02769    case OPEN_RECIEVE_CHANNEL_ACK_MESSAGE:
02770       ast_verbose("Recieved Open Recieve Channel Ack\n");
02771       status = letohl(req->data.openrecievechannelack.status);
02772       if (status) {
02773          ast_log(LOG_ERROR, "Open Recieve Channel Failure\n");
02774          break;
02775       }
02776       /* ENDIAN */
02777       memcpy(addr, req->data.openrecievechannelack.ipAddr, sizeof(addr));
02778       port = htolel(req->data.openrecievechannelack.port);
02779       sin.sin_family = AF_INET;
02780       /* I smell endian problems */
02781       memcpy(&sin.sin_addr, addr, sizeof(sin.sin_addr));  
02782       sin.sin_port = htons(port);
02783       if (skinnydebug) {
02784          ast_verbose("ipaddr = %s:%d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port));
02785       }
02786       sub = find_subchannel_by_line(s->device->lines);
02787       if (sub->rtp) {
02788          ast_rtp_set_peer(sub->rtp, &sin);
02789          ast_rtp_get_us(sub->rtp, &us);   
02790       } else {
02791          ast_log(LOG_ERROR, "No RTP structure, this is very bad\n");
02792          break;
02793       }
02794       memset(req, 0, SKINNY_MAX_PACKET);
02795          req->len = htolel(sizeof(start_media_transmission_message)+4);
02796          req->e = htolel(START_MEDIA_TRANSMISSION_MESSAGE);
02797          req->data.startmedia.conferenceId = 0;
02798          req->data.startmedia.passThruPartyId = 0;
02799          memcpy(req->data.startmedia.remoteIp, &s->device->ourip, 4); /* Endian? */
02800          req->data.startmedia.remotePort = htolel(ntohs(us.sin_port));
02801          req->data.startmedia.packetSize = htolel(20);
02802          req->data.startmedia.payloadType = htolel(convert_cap(s->device->lines->capability));
02803          req->data.startmedia.qualifier.precedence = htolel(127);
02804          req->data.startmedia.qualifier.vad = 0;
02805          req->data.startmedia.qualifier.packets = 0;
02806          req->data.startmedia.qualifier.bitRate = 0;
02807          transmit_response(s, req);
02808       break;   
02809    default:
02810       ast_verbose("RECEIVED UNKNOWN MESSAGE TYPE:  %x\n", letohl(req->e));
02811       break;
02812    }
02813    free(req);
02814    return 1;
02815 }
02816 
02817 static void destroy_session(struct skinnysession *s)
02818 {
02819    struct skinnysession *cur, *prev = NULL;
02820    ast_mutex_lock(&sessionlock);
02821    cur = sessions;
02822    while(cur) {
02823       if (cur == s) {
02824          break;
02825       }
02826       prev = cur;
02827       cur = cur->next;
02828    }
02829    if (cur) {
02830       if (prev) {
02831          prev->next = cur->next;
02832       } else {
02833          sessions = cur->next;
02834       }
02835       if (s->fd > -1) {
02836          close(s->fd);
02837       }
02838       ast_mutex_destroy(&s->lock);
02839       free(s);
02840    } else {
02841       ast_log(LOG_WARNING, "Trying to delete nonexistent session %p?\n", s);
02842    }
02843    ast_mutex_unlock(&sessionlock);
02844 }
02845 
02846 static int get_input(struct skinnysession *s)  
02847 {  
02848    int res;  
02849    int dlen = 0;
02850    struct pollfd fds[1];  
02851  
02852    fds[0].fd = s->fd;
02853    fds[0].events = POLLIN;
02854    res = poll(fds, 1, -1);
02855  
02856    if (res < 0) {
02857       ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
02858    } else if (res > 0) {
02859       memset(s->inbuf,0,sizeof(s->inbuf));
02860       res = read(s->fd, s->inbuf, 4);
02861       if (res != 4) {
02862          ast_log(LOG_WARNING, "Skinny Client sent less data than expected.\n");
02863          return -1;
02864       }
02865       dlen = letohl(*(int *)s->inbuf);
02866       if (dlen+8 > sizeof(s->inbuf)) {
02867          dlen = sizeof(s->inbuf) - 8;
02868       }
02869       *(int *)s->inbuf = htolel(dlen);
02870       res = read(s->fd, s->inbuf+4, dlen+4);
02871       ast_mutex_unlock(&s->lock);
02872       if (res != (dlen+4)) {
02873          ast_log(LOG_WARNING, "Skinny Client sent less data than expected.\n");
02874          return -1;
02875       } 
02876    }  
02877    return res;  
02878 }   
02879 
02880 static skinny_req *skinny_req_parse(struct skinnysession *s)
02881 {
02882    skinny_req *req;
02883    
02884    req = malloc(SKINNY_MAX_PACKET);
02885    if (!req) {
02886       ast_log(LOG_ERROR, "Unable to allocate skinny_request, this is bad\n");
02887       return NULL;
02888    }
02889    memset(req, 0, sizeof(skinny_req));
02890    /* +8 to account for reserved and length fields */
02891    memcpy(req, s->inbuf, letohl(*(int*)(s->inbuf))+8); 
02892    if (letohl(req->e) < 0) {
02893       ast_log(LOG_ERROR, "Event Message is NULL from socket %d, This is bad\n", s->fd);
02894       free(req);
02895       return NULL;
02896    }
02897    return req;
02898 }
02899 
02900 static void *skinny_session(void *data)
02901 {
02902    int res;
02903    skinny_req *req;
02904    struct skinnysession *s = data;
02905    char iabuf[INET_ADDRSTRLEN];
02906    
02907    ast_verbose(VERBOSE_PREFIX_3 "Starting Skinny session from %s\n",  ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
02908    for (;;) {
02909       res = 0;
02910       res = get_input(s);
02911       if (res < 0) {
02912          break;
02913       }
02914       req = skinny_req_parse(s);
02915       if (!req) {
02916          return NULL;
02917       }
02918       res = handle_message(req, s);
02919       if (res < 0) {
02920          destroy_session(s);
02921          return NULL;
02922       } 
02923    }
02924    ast_log(LOG_NOTICE, "Skinny Session returned: %s\n", strerror(errno));
02925    destroy_session(s);
02926    return 0;
02927 }
02928 
02929 static void *accept_thread(void *ignore)
02930 {
02931    int as;
02932    struct sockaddr_in sin;
02933    socklen_t sinlen;
02934    struct skinnysession *s;
02935    struct protoent *p;
02936    int arg = 1;
02937    pthread_attr_t attr;
02938 
02939    pthread_attr_init(&attr);
02940    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
02941 
02942    for (;;) {
02943       sinlen = sizeof(sin);
02944       as = accept(skinnysock, (struct sockaddr *)&sin, &sinlen);
02945       if (as < 0) {
02946          ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
02947          continue;
02948       }
02949       p = getprotobyname("tcp");
02950       if(p) {
02951          if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
02952             ast_log(LOG_WARNING, "Failed to set Skinny tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
02953          }
02954       }
02955       s = malloc(sizeof(struct skinnysession));
02956       if (!s) {
02957          ast_log(LOG_WARNING, "Failed to allocate Skinny session: %s\n", strerror(errno));
02958          continue;
02959       } 
02960       memset(s, 0, sizeof(struct skinnysession));
02961       memcpy(&s->sin, &sin, sizeof(sin));
02962       ast_mutex_init(&s->lock);
02963       s->fd = as;
02964       ast_mutex_lock(&sessionlock);
02965       s->next = sessions;
02966       sessions = s;
02967       ast_mutex_unlock(&sessionlock);
02968       
02969       if (ast_pthread_create(&tcp_thread, NULL, skinny_session, s)) {
02970          destroy_session(s);
02971       }
02972    }
02973    if (skinnydebug) {
02974       ast_verbose("killing accept thread\n");
02975    }
02976    close(as);
02977    return 0;
02978 }
02979 
02980 static void *do_monitor(void *data)
02981 {
02982    int res;
02983 
02984    /* This thread monitors all the interfaces which are not yet in use
02985       (and thus do not have a separate thread) indefinitely */
02986    /* From here on out, we die whenever asked */
02987    for(;;) {
02988       pthread_testcancel();
02989       /* Wait for sched or io */
02990       res = ast_sched_wait(sched);
02991       if ((res < 0) || (res > 1000)) {
02992          res = 1000;
02993       }
02994       res = ast_io_wait(io, res);
02995       ast_mutex_lock(&monlock);
02996       if (res >= 0) {
02997          ast_sched_runq(sched);
02998       }
02999       ast_mutex_unlock(&monlock);
03000    }
03001    /* Never reached */
03002    return NULL;
03003    
03004 }
03005 
03006 static int restart_monitor(void)
03007 {
03008    /* If we're supposed to be stopped -- stay stopped */
03009    if (monitor_thread == AST_PTHREADT_STOP)
03010       return 0;
03011    if (ast_mutex_lock(&monlock)) {
03012       ast_log(LOG_WARNING, "Unable to lock monitor\n");
03013       return -1;
03014    }
03015    if (monitor_thread == pthread_self()) {
03016       ast_mutex_unlock(&monlock);
03017       ast_log(LOG_WARNING, "Cannot kill myself\n");
03018       return -1;
03019    }
03020    if (monitor_thread != AST_PTHREADT_NULL) {
03021       /* Wake up the thread */
03022       pthread_kill(monitor_thread, SIGURG);
03023    } else {
03024       /* Start a new monitor */
03025       if (ast_pthread_create(&monitor_thread, NULL, do_monitor, NULL) < 0) {
03026          ast_mutex_unlock(&monlock);
03027          ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
03028          return -1;
03029       }
03030    }
03031    ast_mutex_unlock(&monlock);
03032    return 0;
03033 }
03034 
03035 static struct ast_channel *skinny_request(const char *type, int format, void *data, int *cause)
03036 {
03037    int oldformat;
03038    struct skinny_subchannel *sub;
03039    struct ast_channel *tmpc = NULL;
03040    char tmp[256];
03041    char *dest = data;
03042 
03043    oldformat = format;
03044    format &= capability;
03045    if (!format) {
03046       ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", format);
03047       return NULL;
03048    }
03049    strncpy(tmp, dest, sizeof(tmp) - 1);
03050    if (ast_strlen_zero(tmp)) {
03051       ast_log(LOG_NOTICE, "Skinny channels require a device\n");
03052       return NULL;
03053    }
03054    sub = find_subchannel_by_name(tmp);  
03055    if (!sub) {
03056       ast_log(LOG_NOTICE, "No available lines on: %s\n", dest);
03057       return NULL;
03058    }
03059       if (option_verbose > 2) {
03060          ast_verbose(VERBOSE_PREFIX_3 "skinny_request(%s)\n", tmp);
03061          ast_verbose(VERBOSE_PREFIX_3 "Skinny cw: %d, dnd: %d, so: %d, sno: %d\n", 
03062                      sub->parent->callwaiting, sub->parent->dnd, sub->owner ? 1 : 0, sub->next->owner ? 1: 0);
03063       }
03064    tmpc = skinny_new(sub->owner ? sub->next : sub, AST_STATE_DOWN);
03065    if (!tmpc) {
03066       ast_log(LOG_WARNING, "Unable to make channel for '%s'\n", tmp);
03067    }
03068    restart_monitor();
03069    return tmpc;
03070 }
03071 
03072 static int reload_config(void)
03073 {
03074    int on = 1;
03075    struct ast_config *cfg;
03076    struct ast_variable *v;
03077    int format;
03078    char *cat;
03079    char iabuf[INET_ADDRSTRLEN];
03080    struct skinny_device *d;
03081    int oldport = ntohs(bindaddr.sin_port);
03082 
03083    if (gethostname(ourhost, sizeof(ourhost))) {
03084       ast_log(LOG_WARNING, "Unable to get hostname, Skinny disabled\n");
03085       return 0;
03086    }
03087    cfg = ast_config_load(config);
03088 
03089    /* We *must* have a config file otherwise stop immediately */
03090    if (!cfg) {
03091       ast_log(LOG_NOTICE, "Unable to load config %s, Skinny disabled\n", config);
03092       return 0;
03093    }
03094    /* load the general section */
03095    memset(&bindaddr, 0, sizeof(bindaddr));
03096    v = ast_variable_browse(cfg, "general");
03097    while(v) {
03098       /* Create the interface list */
03099       if (!strcasecmp(v->name, "bindaddr")) {
03100          if (!(hp = ast_gethostbyname(v->value, &ahp))) {
03101             ast_log(LOG_WARNING, "Invalid address: %s\n", v->value);
03102          } else {
03103             memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr));
03104          }
03105       } else if (!strcasecmp(v->name, "keepAlive")) {
03106          keep_alive = atoi(v->value);     
03107       } else if (!strcasecmp(v->name, "dateFormat")) {
03108          strncpy(date_format, v->value, sizeof(date_format) - 1); 
03109       } else if (!strcasecmp(v->name, "allow")) {
03110          format = ast_getformatbyname(v->value);
03111          if (format < 1) {
03112             ast_log(LOG_WARNING, "Cannot allow unknown format '%s'\n", v->value);
03113          } else {
03114             capability |= format;
03115          }  
03116       } else if (!strcasecmp(v->name, "disallow")) {
03117          format = ast_getformatbyname(v->value);
03118          if (format < 1) {
03119             ast_log(LOG_WARNING, "Cannot disallow unknown format '%s'\n", v->value);
03120          } else {
03121             capability &= ~format;
03122          }
03123       } else if (!strcasecmp(v->name, "port")) {
03124          if (sscanf(v->value, "%d", &ourport) == 1) {
03125             bindaddr.sin_port = htons(ourport);
03126          } else {
03127             ast_log(LOG_WARNING, "Invalid port number '%s' at line %d of %s\n", v->value, v->lineno, config);
03128          }
03129       }
03130       v = v->next;
03131    }
03132    if (ntohl(bindaddr.sin_addr.s_addr)) {
03133       memcpy(&__ourip, &bindaddr.sin_addr, sizeof(__ourip));
03134    } else {
03135       hp = ast_gethostbyname(ourhost, &ahp);
03136       if (!hp) {
03137          ast_log(LOG_WARNING, "Unable to get our IP address, Skinny disabled\n");
03138          ast_config_destroy(cfg);
03139          return 0;
03140       }
03141       memcpy(&__ourip, hp->h_addr, sizeof(__ourip));
03142    }
03143    if (!ntohs(bindaddr.sin_port)) {
03144       bindaddr.sin_port = ntohs(DEFAULT_SKINNY_PORT);
03145    }
03146    bindaddr.sin_family = AF_INET;
03147    
03148    /* load the device sections */
03149    cat = ast_category_browse(cfg, NULL);
03150    while(cat) {
03151       if (!strcasecmp(cat, "general")) {
03152         /* Nothing to do */
03153 #if 0
03154       } else if (!strncasecmp(cat, "paging-", 7)) {
03155          p = build_paging_device(cat, ast_variable_browse(cfg, cat));
03156          if (p) {
03157          }
03158 #endif
03159       } else {
03160          d = build_device(cat, ast_variable_browse(cfg, cat));
03161          if (d) {
03162             if (option_verbose > 2) {
03163                ast_verbose(VERBOSE_PREFIX_3 "Added device '%s'\n", d->name);
03164                      }
03165             ast_mutex_lock(&devicelock);
03166             d->next = devices;
03167             devices = d;
03168             ast_mutex_unlock(&devicelock);
03169          }
03170       }
03171       cat = ast_category_browse(cfg, cat);
03172    }
03173    ast_mutex_lock(&netlock);
03174    if ((skinnysock > -1) && (ntohs(bindaddr.sin_port) != oldport)) {
03175       close(skinnysock);
03176       skinnysock = -1;
03177    }
03178    if (skinnysock < 0) {
03179       skinnysock = socket(AF_INET, SOCK_STREAM, 0);
03180       if(setsockopt(skinnysock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
03181          ast_log(LOG_ERROR, "Set Socket Options failed: errno %d, %s", errno, strerror(errno));
03182          ast_config_destroy(cfg);
03183          return 0;
03184       }
03185       if (skinnysock < 0) {
03186          ast_log(LOG_WARNING, "Unable to create Skinny socket: %s\n", strerror(errno));
03187       } else {
03188          if (bind(skinnysock, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) {
03189             ast_log(LOG_WARNING, "Failed to bind to %s:%d: %s\n",
03190                   ast_inet_ntoa(iabuf, sizeof(iabuf), bindaddr.sin_addr), ntohs(bindaddr.sin_port),
03191                      strerror(errno));
03192             close(skinnysock);
03193             skinnysock = -1;
03194             ast_config_destroy(cfg);
03195             return 0;
03196          } 
03197          if (listen(skinnysock,DEFAULT_SKINNY_BACKLOG)) {
03198                ast_log(LOG_WARNING, "Failed to start listening to %s:%d: %s\n",
03199                   ast_inet_ntoa(iabuf, sizeof(iabuf), bindaddr.sin_addr), ntohs(bindaddr.sin_port),
03200                      strerror(errno));
03201                close(skinnysock);
03202                skinnysock = -1;
03203                ast_config_destroy(cfg);
03204                return 0;
03205          }
03206          if (option_verbose > 1) {
03207             ast_verbose(VERBOSE_PREFIX_2 "Skinny listening on %s:%d\n", 
03208                ast_inet_ntoa(iabuf, sizeof(iabuf), bindaddr.sin_addr), ntohs(bindaddr.sin_port));
03209          }
03210          ast_pthread_create(&accept_t,NULL, accept_thread, NULL);
03211       }
03212    }
03213    ast_mutex_unlock(&netlock);
03214    ast_config_destroy(cfg);
03215    return 0;
03216 }
03217 
03218 void delete_devices(void)
03219 {
03220    struct skinny_device *d, *dlast;
03221    struct skinny_line *l, *llast;
03222    struct skinny_subchannel *sub, *slast;
03223    
03224    ast_mutex_lock(&devicelock);
03225    
03226    /* Delete all devices */
03227    for (d=devices;d;) {    
03228       /* Delete all lines for this device */
03229       for (l=d->lines;l;) {
03230          /* Delete all subchannels for this line */
03231          for (sub=l->sub;sub;) {
03232             slast = sub;
03233             sub = sub->next;
03234             ast_mutex_destroy(&slast->lock);
03235             free(slast);
03236          }
03237          llast = l;
03238          l = l->next;
03239          ast_mutex_destroy(&llast->lock);
03240          free(llast);
03241       }
03242       dlast = d;
03243       d = d->next;
03244       free(dlast);
03245    }
03246    devices=NULL;
03247    ast_mutex_unlock(&devicelock);
03248 }
03249 
03250 int reload(void)
03251 {
03252    delete_devices();
03253    reload_config();
03254    restart_monitor();
03255    return 0;
03256 }
03257 
03258 
03259 int load_module()
03260 {
03261    int res = 0;
03262 
03263    for (; res < (sizeof(soft_key_template_default) / sizeof(soft_key_template_default[0])); res++) {
03264       soft_key_template_default[res].softKeyEvent = htolel(soft_key_template_default[res].softKeyEvent);
03265    }
03266    /* load and parse config */
03267    res = reload_config();
03268    
03269    ast_rtp_proto_register(&skinny_rtp);
03270    ast_cli_register(&cli_show_devices);
03271    ast_cli_register(&cli_show_lines);
03272    ast_cli_register(&cli_debug);
03273    ast_cli_register(&cli_no_debug);
03274    sched = sched_context_create();
03275    if (!sched) {
03276       ast_log(LOG_WARNING, "Unable to create schedule context\n");
03277    }
03278    io = io_context_create();
03279    if (!io) {
03280       ast_log(LOG_WARNING, "Unable to create I/O context\n");
03281    }
03282    /* And start the monitor for the first time */
03283    restart_monitor();
03284 
03285    /* Announce our presence to Asterisk */   
03286    if (!res) {
03287       /* Make sure we can register our skinny channel type */
03288       if (ast_channel_register(&skinny_tech)) {
03289          ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
03290          return -1;
03291       }
03292    }
03293    return res;
03294 }
03295 
03296 int unload_module()
03297 {
03298 #if 0
03299    struct skinny_session *session, s;
03300    struct skinny_subchannel *sub;
03301    struct skinny_line *line = session;
03302 
03303    /* close all IP connections */
03304    if (!ast_mutex_lock(&devicelock)) {
03305       /* Terminate tcp listener thread */
03306    } else {
03307       ast_log(LOG_WARNING, "Unable to lock the monitor\n");
03308       return -1;
03309    }
03310    if (!ast_mutex_lock(&monlock)) {
03311       if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP)) {
03312          pthread_cancel(monitor_thread);
03313          pthread_kill(monitor_thread, SIGURG);
03314          pthread_join(monitor_thread, NULL);
03315       }
03316       monitor_thread = AST_PTHREADT_STOP;
03317       ast_mutex_unlock(&monlock);
03318    } else {
03319       ast_log(LOG_WARNING, "Unable to lock the monitor\n");
03320       return -1;
03321    }
03322    if (!ast_mutex_lock(&iflock)) {
03323       /* Destroy all the interfaces and free their memory */
03324       p = iflist;
03325       while(p) {
03326          pl = p;
03327          p = p->next;
03328          /* Free associated memory */
03329          ast_mutex_destroy(&pl->lock);
03330          free(pl);
03331       }
03332       iflist = NULL;
03333       ast_mutex_unlock(&iflock);
03334    } else {
03335       ast_log(LOG_WARNING, "Unable to lock the monitor\n");
03336       return -1;
03337    }
03338 
03339         ast_rtp_proto_register(&skinny_rtp);
03340    ast_channel_unregister(&skinny_tech);
03341         ast_cli_register(&cli_show_devices);
03342         ast_cli_register(&cli_show_lines);
03343         ast_cli_register(&cli_debug);
03344         ast_cli_register(&cli_no_debug);
03345 
03346    return 0;
03347 #endif
03348    return -1;
03349 }
03350 
03351 int usecount()
03352 {
03353    return usecnt;
03354 }
03355 
03356 char *key()
03357 {
03358    return ASTERISK_GPL_KEY;
03359 }
03360 
03361 char *description()
03362 {
03363    return (char *) desc;
03364 }

Generated on Fri Sep 29 11:12:27 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.7