Sun Aug 6 15:02:35 2006

Asterisk developer's documentation


Main Page | Modules | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

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

Generated on Sun Aug 6 15:02:35 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.2