Wed Oct 28 15:47:52 2009

Asterisk developer's documentation


chan_skinny.c

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

Generated on Wed Oct 28 15:47:52 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6