Fri May 26 01:45:31 2006

Asterisk developer's documentation


chan_skinny.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * chan_skinny was developed by Jeremy McNamara & Florian Overkamp
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Implementation of the Skinny protocol
00022  * 
00023  * \author Jeremy McNamara & Florian Overkamp
00024  * \ingroup channel_drivers
00025  */
00026 
00027 
00028 #include <stdio.h>
00029 #include <stdlib.h>
00030 #include <string.h>
00031 #include <unistd.h>
00032 #include <sys/socket.h>
00033 #include <netinet/in.h>
00034 #include <netinet/tcp.h>
00035 #include <sys/ioctl.h>
00036 #include <net/if.h>
00037 #include <errno.h>
00038 #include <fcntl.h>
00039 #include <netdb.h>
00040 #include <arpa/inet.h>
00041 #include <sys/signal.h>
00042 #include <signal.h>
00043 #include <ctype.h>
00044 
00045 #include "asterisk.h"
00046 
00047 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 7221 $")
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                            if (!ast_strlen_zero(l->cid_num)) {
01745                                  if (!l->hidecallerid) {
01746                                        chan->cid.cid_num = strdup(l->cid_num);
01747                                     chan->cid.cid_ani = strdup(l->cid_num);
01748                               }
01749                               ast_setstate(chan, AST_STATE_RING);
01750                               res = ast_pbx_run(chan);
01751                               if (res) {
01752                                     ast_log(LOG_WARNING, "PBX exited non-zero\n");
01753                      transmit_tone(s, SKINNY_REORDER); 
01754                               }
01755                               return NULL;
01756                         }
01757             }
01758                   } else {
01759                      /* It's a match, but they just typed a digit, and there is an ambiguous match,
01760                            so just set the timeout to matchdigittimeout and wait some more */
01761                         timeout = matchdigittimeout;
01762               }
01763       } else if (res == 0) {
01764                   ast_log(LOG_DEBUG, "Not enough digits (and no ambiguous match)...\n");
01765             transmit_tone(s, SKINNY_REORDER); 
01766                ast_hangup(chan);
01767                return NULL;
01768          } else if (l->callwaiting && !strcmp(exten, "*70")) {
01769                   if (option_verbose > 2) {
01770                         ast_verbose(VERBOSE_PREFIX_3 "Disabling call waiting on %s\n", chan->name);
01771                   }
01772                /* Disable call waiting if enabled */
01773                l->callwaiting = 0;
01774                transmit_tone(s, SKINNY_DIALTONE);
01775          len = 0;
01776                memset(exten, 0, sizeof(exten));\
01777                timeout = firstdigittimeout;
01778           } else if (!strcmp(exten,ast_pickup_ext())) {
01779                /* Scan all channels and see if any there
01780                       * ringing channqels with that have call groups
01781                    * that equal this channels pickup group  
01782                    */
01783                   if (ast_pickup_call(chan)) {
01784                      ast_log(LOG_WARNING, "No call pickup possible...\n");
01785             transmit_tone(s, SKINNY_REORDER);
01786                   }
01787               ast_hangup(chan);
01788                   return NULL;
01789                } else if (!l->hidecallerid && !strcmp(exten, "*67")) {
01790                if (option_verbose > 2) {
01791                    ast_verbose(VERBOSE_PREFIX_3 "Disabling Caller*ID on %s\n", chan->name);
01792                   }
01793                /* Disable Caller*ID if enabled */
01794               l->hidecallerid = 1;
01795                if (chan->cid.cid_num) {
01796                         free(chan->cid.cid_num);
01797             }
01798                   chan->cid.cid_num = NULL;
01799             if (chan->cid.cid_name) {
01800                         free(chan->cid.cid_name);
01801             }
01802                chan->cid.cid_name = NULL;
01803                   transmit_tone(s, SKINNY_DIALTONE);
01804                len = 0;
01805                memset(exten, 0, sizeof(exten));
01806                   timeout = firstdigittimeout;
01807          } else if (l->callreturn && !strcmp(exten, "*69")) {
01808                res = 0;
01809                if (!ast_strlen_zero(l->lastcallerid)) {
01810                      res = ast_say_digit_str(chan, l->lastcallerid, "", chan->language);
01811                }
01812                if (!res) {
01813                      transmit_tone(s, SKINNY_DIALTONE);
01814          }
01815                break;
01816          } else if (!strcmp(exten, "*78")) {
01817                   /* Do not disturb */
01818                   if (option_verbose > 2) {
01819                         ast_verbose(VERBOSE_PREFIX_3 "Enabled DND on channel %s\n", chan->name);
01820                   }
01821                transmit_tone(s, SKINNY_DIALTONE);
01822                   l->dnd = 1;
01823                   getforward = 0;
01824                   memset(exten, 0, sizeof(exten));
01825                   len = 0;
01826          } else if (!strcmp(exten, "*79")) {
01827                /* Do not disturb */
01828                if (option_verbose > 2) {
01829                      ast_verbose(VERBOSE_PREFIX_3 "Disabled DND on channel %s\n", chan->name);
01830                   }
01831          transmit_tone(s, SKINNY_DIALTONE);
01832               l->dnd = 0;
01833                   getforward = 0;
01834                   memset(exten, 0, sizeof(exten));
01835                   len = 0;
01836          } else if (l->cancallforward && !strcmp(exten, "*72")) {
01837                transmit_tone(s, SKINNY_DIALTONE);
01838                getforward = 1;
01839                memset(exten, 0, sizeof(exten));
01840                len = 0;
01841             } else if (l->cancallforward && !strcmp(exten, "*73")) {
01842                if (option_verbose > 2) {
01843                   ast_verbose(VERBOSE_PREFIX_3 "Cancelling call forwarding on channel %s\n", chan->name);
01844                }
01845                transmit_tone(s, SKINNY_DIALTONE); 
01846                memset(l->call_forward, 0, sizeof(l->call_forward));
01847                getforward = 0;
01848                memset(exten, 0, sizeof(exten));
01849                len = 0;
01850             } else if (!strcmp(exten, ast_parking_ext()) && 
01851                         sub->next->owner &&
01852                         ast_bridged_channel(sub->next->owner)) {
01853                /* This is a three way call, the main call being a real channel, 
01854                         and we're parking the first call. */
01855                      ast_masq_park_call(ast_bridged_channel(sub->next->owner), chan, 0, NULL);
01856                if (option_verbose > 2) {
01857                         ast_verbose(VERBOSE_PREFIX_3 "Parking call to '%s'\n", chan->name);
01858                }
01859                break;
01860             } else if (!ast_strlen_zero(l->lastcallerid) && !strcmp(exten, "*60")) {
01861                if (option_verbose > 2) {
01862                      ast_verbose(VERBOSE_PREFIX_3 "Blacklisting number %s\n", l->lastcallerid);
01863                }
01864                   res = ast_db_put("blacklist", l->lastcallerid, "1");
01865                   if (!res) {
01866                         transmit_tone(s, SKINNY_DIALTONE);     
01867                   memset(exten, 0, sizeof(exten));
01868                         len = 0;
01869                }
01870             } else if (l->hidecallerid && !strcmp(exten, "*82")) {
01871                if (option_verbose > 2) {
01872                      ast_verbose(VERBOSE_PREFIX_3 "Enabling Caller*ID on %s\n", chan->name);
01873                }
01874                /* Enable Caller*ID if enabled */
01875                l->hidecallerid = 0;
01876                if (chan->cid.cid_num) {
01877                      free(chan->cid.cid_num);   
01878             }
01879                   if (!ast_strlen_zero(l->cid_num)) {
01880                      chan->cid.cid_num = strdup(l->cid_num);
01881             }
01882                if (chan->cid.cid_name) {
01883                         free(chan->cid.cid_name);
01884             }
01885                   if (!ast_strlen_zero(l->cid_name)) {
01886                         chan->cid.cid_name = strdup(l->cid_name); 
01887          }
01888                   transmit_tone(s, SKINNY_DIALTONE);
01889                   len = 0;
01890                   memset(exten, 0, sizeof(exten));
01891                timeout = firstdigittimeout;
01892             } else if (!ast_canmatch_extension(chan, chan->context, exten, 1, chan->cid.cid_num) &&
01893                            ((exten[0] != '*') || (!ast_strlen_zero(exten) > 2))) {
01894                   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);
01895                   transmit_tone(s, SKINNY_REORDER);   
01896          /* hang out for 3 seconds to let congestion play */
01897             ast_safe_sleep(chan, 3000); 
01898             break;
01899             }
01900             if (!timeout) {
01901                   timeout = gendigittimeout;
01902       }
01903          if (len && !ast_ignore_pattern(chan->context, exten)) {
01904          ast_indicate(chan, -1);
01905       }
01906       }  
01907    ast_hangup(chan);
01908    return NULL;
01909 }
01910 
01911 
01912 
01913 static int skinny_call(struct ast_channel *ast, char *dest, int timeout)
01914 {
01915    int res = 0;
01916    int tone = 0;
01917    struct skinny_line *l;
01918         struct skinny_subchannel *sub;
01919    struct skinnysession *session;
01920    
01921    sub = ast->tech_pvt;
01922         l = sub->parent;
01923    session = l->parent->session;
01924 
01925    if (!l->parent->registered) {
01926       ast_log(LOG_ERROR, "Device not registered, cannot call %s\n", dest);
01927       return -1;
01928    }
01929    
01930    if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
01931       ast_log(LOG_WARNING, "skinny_call called on %s, neither down nor reserved\n", ast->name);
01932       return -1;
01933    }
01934 
01935         if (skinnydebug) {
01936          ast_verbose(VERBOSE_PREFIX_3 "skinny_call(%s)\n", ast->name);
01937       }
01938 
01939    if (l->dnd) {
01940       ast_queue_control(ast, AST_CONTROL_BUSY);
01941       return -1;
01942    }
01943    
01944    switch (l->hookstate) {
01945         case SKINNY_OFFHOOK:
01946                tone = SKINNY_CALLWAITTONE;
01947                break;
01948         case SKINNY_ONHOOK:
01949       tone = SKINNY_ALERT;
01950       break;
01951         default:
01952                ast_log(LOG_ERROR, "Don't know how to deal with hookstate %d\n", l->hookstate);
01953                break;
01954       }
01955 
01956    transmit_lamp_indication(session, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK);
01957    transmit_ringer_mode(session, SKINNY_RING_INSIDE);
01958    
01959    if (ast->cid.cid_num) { 
01960       char ciddisplay[41];
01961       char *work;
01962       size_t size = sizeof(ciddisplay);
01963 
01964       /* For now, we'll assume that if it is 10 numbers, it is a standard NANPA number */
01965       if (strlen(ast->cid.cid_num) == 10) {
01966          ast_build_string(&work, &size, "(xxx)xxx-xxxx      %s",
01967                 ast->cid.cid_name ? ast->cid.cid_name : "");
01968          memcpy(&ciddisplay[1], ast->cid.cid_num, 3);
01969          memcpy(&ciddisplay[5], &ast->cid.cid_num[3], 3);
01970          memcpy(&ciddisplay[9], &ast->cid.cid_num[6], 4);
01971       } else {
01972          if (strlen(ast->cid.cid_num) < 41) {
01973             ast_build_string(&work, &size, "%s -- %s", ast->cid.cid_num,
01974                    ast->cid.cid_name ? ast->cid.cid_name : "");
01975          } else {
01976             strncpy(ciddisplay, "Number too long!", 15);
01977          }
01978       }
01979       if (skinnydebug) {
01980          ast_verbose("Trying to send: '%s'\n",ciddisplay);
01981       }
01982       transmit_displaymessage(session, ciddisplay);
01983    } else {
01984       transmit_displaymessage(session, "Unknown Name");
01985    }
01986    transmit_tone(session, tone);
01987    transmit_callstate(session, l->instance, SKINNY_RINGIN, sub->callid);
01988    transmit_displaypromptstatus(session, "Ring-In", 0, l->instance, sub->callid);
01989    transmit_callinfo(session, ast->cid.cid_name, ast->cid.cid_num, l->cid_name, l->cid_num, l->instance, sub->callid, 1); 
01990 
01991    /* XXX need to deal with softkeys */
01992 
01993    ast_setstate(ast, AST_STATE_RINGING);
01994    ast_queue_control(ast, AST_CONTROL_RINGING);
01995    sub->outgoing = 1;
01996    return res;
01997 }
01998 
01999 static int skinny_hangup(struct ast_channel *ast)
02000 {
02001     struct skinny_subchannel *sub = ast->tech_pvt;
02002     struct skinny_line *l = sub->parent;
02003     struct skinnysession *s = l->parent->session;
02004 
02005     if (skinnydebug) {
02006         ast_verbose("skinny_hangup(%s) on %s@%s\n", ast->name, l->name, l->parent->name);
02007     }
02008     if (!ast->tech_pvt) {
02009         ast_log(LOG_DEBUG, "Asked to hangup channel not connected\n");
02010         return 0;
02011     }
02012 
02013     if (l->parent->registered) {
02014    if ((sub->parent->type = TYPE_LINE) && (sub->parent->hookstate == SKINNY_OFFHOOK)) {
02015          sub->parent->hookstate = SKINNY_ONHOOK;
02016          transmit_callstate(s, l->instance, SKINNY_ONHOOK, sub->callid);
02017          transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF);
02018          transmit_speaker_mode(s, SKINNY_SPEAKEROFF); 
02019       } else if ((sub->parent->type = TYPE_LINE) && (sub->parent->hookstate == SKINNY_ONHOOK)) {
02020          transmit_callstate(s, l->instance, SKINNY_ONHOOK, sub->callid);
02021          transmit_speaker_mode(s, SKINNY_SPEAKEROFF); 
02022          transmit_ringer_mode(s, SKINNY_RING_OFF);
02023          transmit_tone(s, SKINNY_SILENCE);
02024          transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF);
02025          do_housekeeping(s);
02026       } 
02027     }
02028     ast_mutex_lock(&sub->lock);
02029     sub->owner = NULL;
02030     ast->tech_pvt = NULL;
02031     sub->alreadygone = 0;
02032     sub->outgoing = 0;
02033     if (sub->rtp) {
02034         ast_rtp_destroy(sub->rtp);
02035         sub->rtp = NULL;
02036     }
02037     ast_mutex_unlock(&sub->lock);
02038     return 0;
02039 }
02040 
02041 static int skinny_answer(struct ast_channel *ast)
02042 {
02043     int res = 0;
02044     struct skinny_subchannel *sub = ast->tech_pvt;
02045     struct skinny_line *l = sub->parent;
02046     struct skinnysession *s = l->parent->session;
02047 
02048     sub->cxmode = SKINNY_CX_SENDRECV;
02049     if (!sub->rtp) {
02050       start_rtp(sub);
02051     } 
02052     ast_verbose("skinny_answer(%s) on %s@%s-%d\n", ast->name, l->name, l->parent->name, sub->callid);
02053     if (ast->_state != AST_STATE_UP) {
02054    ast_setstate(ast, AST_STATE_UP);
02055     }
02056     transmit_tone(s, SKINNY_NOTONE);
02057     transmit_callstate(s, l->instance, SKINNY_CONNECTED, sub->callid);
02058     transmit_displaypromptstatus(s, "Connected", 0, l->instance, sub->callid);
02059     return res;
02060 }
02061 
02062 static struct ast_frame *skinny_rtp_read(struct skinny_subchannel *sub)
02063 {
02064    /* Retrieve audio/etc from channel.  Assumes sub->lock is already held. */
02065    struct ast_frame *f;
02066    f = ast_rtp_read(sub->rtp);
02067    if (sub->owner) {
02068       /* We already hold the channel lock */
02069       if (f->frametype == AST_FRAME_VOICE) {
02070          if (f->subclass != sub->owner->nativeformats) {
02071             ast_log(LOG_DEBUG, "Oooh, format changed to %d\n", f->subclass);
02072             sub->owner->nativeformats = f->subclass;
02073             ast_set_read_format(sub->owner, sub->owner->readformat);
02074             ast_set_write_format(sub->owner, sub->owner->writeformat);
02075          }
02076       }
02077    }
02078    return f;
02079 }
02080 
02081 static struct ast_frame  *skinny_read(struct ast_channel *ast)
02082 {
02083    struct ast_frame *fr;
02084    struct skinny_subchannel *sub = ast->tech_pvt;
02085    ast_mutex_lock(&sub->lock);
02086    fr = skinny_rtp_read(sub);
02087    ast_mutex_unlock(&sub->lock);
02088    return fr;
02089 }
02090 
02091 static int skinny_write(struct ast_channel *ast, struct ast_frame *frame)
02092 {
02093    struct skinny_subchannel *sub = ast->tech_pvt;
02094    int res = 0;
02095    if (frame->frametype != AST_FRAME_VOICE) {
02096       if (frame->frametype == AST_FRAME_IMAGE) {
02097          return 0;
02098       } else {
02099          ast_log(LOG_WARNING, "Can't send %d type frames with skinny_write\n", frame->frametype);
02100          return 0;
02101       }
02102    } else {
02103       if (!(frame->subclass & ast->nativeformats)) {
02104          ast_log(LOG_WARNING, "Asked to transmit frame type %d, while native formats is %d (read/write = %d/%d)\n",
02105             frame->subclass, ast->nativeformats, ast->readformat, ast->writeformat);
02106          return -1;
02107       }
02108    }
02109    if (sub) {
02110       ast_mutex_lock(&sub->lock);
02111       if (sub->rtp) {
02112          res =  ast_rtp_write(sub->rtp, frame);
02113       }
02114       ast_mutex_unlock(&sub->lock);
02115    }
02116    return res;
02117 }
02118 
02119 static int skinny_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
02120 {
02121    struct skinny_subchannel *sub = newchan->tech_pvt;
02122       ast_log(LOG_NOTICE, "skinny_fixup(%s, %s)\n", oldchan->name, newchan->name);
02123    if (sub->owner != oldchan) {
02124       ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, sub->owner);
02125       return -1;
02126    }
02127    sub->owner = newchan;
02128    return 0;
02129 }
02130 
02131 static int skinny_senddigit(struct ast_channel *ast, char digit)
02132 {
02133 #if 0
02134    struct skinny_subchannel *sub = ast->tech_pvt;
02135    int tmp;
02136    /* not right */
02137    sprintf(tmp, "%d", digit);  
02138    transmit_tone(sub->parent->parent->session, digit);
02139 #endif
02140    return -1;
02141 }
02142 
02143 static char *control2str(int ind) {
02144     static char tmp[100];
02145 
02146     switch (ind) {
02147         case AST_CONTROL_HANGUP:
02148             return "Other end has hungup";
02149         case AST_CONTROL_RING:
02150             return "Local ring";
02151         case AST_CONTROL_RINGING:
02152             return "Remote end is ringing";
02153         case AST_CONTROL_ANSWER:
02154             return "Remote end has answered";
02155         case AST_CONTROL_BUSY:
02156             return "Remote end is busy";
02157         case AST_CONTROL_TAKEOFFHOOK:
02158             return "Make it go off hook";
02159         case AST_CONTROL_OFFHOOK:
02160             return "Line is off hook";
02161         case AST_CONTROL_CONGESTION:
02162             return "Congestion (circuits busy)";
02163         case AST_CONTROL_FLASH:
02164             return "Flash hook";
02165         case AST_CONTROL_WINK:
02166             return "Wink";
02167         case AST_CONTROL_OPTION:
02168             return "Set a low-level option";
02169         case AST_CONTROL_RADIO_KEY:
02170             return "Key Radio";
02171         case AST_CONTROL_RADIO_UNKEY:
02172             return "Un-Key Radio";
02173         case AST_CONTROL_PROGRESS:
02174             return "Remote end is making Progress";
02175         case AST_CONTROL_PROCEEDING:
02176             return "Remote end is proceeding";
02177         case AST_CONTROL_HOLD:
02178             return "Hold";
02179         case AST_CONTROL_UNHOLD:
02180             return "Unhold";
02181    case -1:
02182        return "Stop tone";
02183     }
02184     snprintf(tmp, 100, "UNKNOWN-%d", ind);
02185     return tmp;
02186 }
02187 
02188 
02189 static int skinny_indicate(struct ast_channel *ast, int ind)
02190 {
02191    struct skinny_subchannel *sub = ast->tech_pvt;
02192    struct skinny_line *l = sub->parent;
02193    struct skinnysession *s = l->parent->session;
02194 
02195       if (skinnydebug) {
02196          ast_verbose(VERBOSE_PREFIX_3 "Asked to indicate '%s' condition on channel %s\n", control2str(ind), ast->name);
02197       }
02198    switch(ind) {
02199    case AST_CONTROL_RINGING:
02200       if (ast->_state != AST_STATE_UP) {
02201          if (!sub->progress) {      
02202             transmit_tone(s, SKINNY_ALERT);
02203             transmit_callstate(s, l->instance, SKINNY_RINGOUT, sub->callid);
02204             transmit_diallednumber(s, ast->exten, l->instance, sub->callid);
02205             transmit_displaypromptstatus(s, "Ring Out", 0, l->instance, sub->callid);
02206             transmit_callinfo(s, ast->cid.cid_name, ast->cid.cid_num, ast->exten, ast->exten, l->instance, sub->callid, 2); /* 2 = outgoing from phone */
02207             sub->ringing = 1;
02208             break;
02209          }
02210       }
02211       return -1;
02212    case AST_CONTROL_BUSY:
02213       if (ast->_state != AST_STATE_UP) {     
02214          transmit_tone(s, SKINNY_BUSYTONE);
02215          transmit_callstate(s, l->instance, SKINNY_BUSY, sub->callid);
02216          sub->alreadygone = 1;
02217          ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
02218                         break;
02219                 }
02220                 return -1;
02221    case AST_CONTROL_CONGESTION:
02222       if (ast->_state != AST_STATE_UP) {     
02223          transmit_tone(s, SKINNY_REORDER);
02224          transmit_callstate(s, l->instance, SKINNY_CONGESTION, sub->callid);
02225          sub->alreadygone = 1;
02226                         ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
02227                         break;
02228                 }
02229                 return -1;
02230    case AST_CONTROL_PROGRESS:
02231                 if ((ast->_state != AST_STATE_UP) && !sub->progress && !sub->outgoing) {
02232          transmit_tone(s, SKINNY_ALERT);
02233          transmit_callstate(s, l->instance, SKINNY_PROGRESS, sub->callid);
02234          transmit_displaypromptstatus(s, "Call Progress", 0, l->instance, sub->callid);
02235          transmit_callinfo(s, ast->cid.cid_name, ast->cid.cid_num, ast->exten, ast->exten, l->instance, sub->callid, 2); /* 2 = outgoing from phone */
02236                         sub->progress = 1;
02237                         break;
02238                 }
02239                 return -1;  
02240    case -1:
02241       transmit_tone(s, SKINNY_SILENCE);
02242       break;      
02243    case AST_CONTROL_PROCEEDING:
02244       break;
02245    default:
02246       ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", ind);
02247       return -1;
02248    }
02249    return 0;
02250 }
02251    
02252 static struct ast_channel *skinny_new(struct skinny_subchannel *sub, int state)
02253 {
02254    struct ast_channel *tmp;
02255    struct skinny_line *l = sub->parent;
02256    int fmt;
02257    l = sub->parent;
02258    tmp = ast_channel_alloc(1);
02259    if (tmp) {
02260       tmp->tech = &skinny_tech;
02261       tmp->nativeformats = l->capability;
02262       if (!tmp->nativeformats)
02263          tmp->nativeformats = capability;
02264       fmt = ast_best_codec(tmp->nativeformats);
02265       ast_verbose("skinny_new: tmp->nativeformats=%d fmt=%d\n", tmp->nativeformats, fmt);
02266       snprintf(tmp->name, sizeof(tmp->name), "Skinny/%s@%s-%d", l->name, l->parent->name, sub->callid);
02267       if (sub->rtp) {
02268          tmp->fds[0] = ast_rtp_fd(sub->rtp);
02269       }
02270       tmp->type = type;
02271       ast_setstate(tmp, state);
02272       if (state == AST_STATE_RING) {
02273          tmp->rings = 1;
02274       }
02275       tmp->writeformat = fmt;
02276       tmp->rawwriteformat = fmt;
02277       tmp->readformat = fmt;
02278       tmp->rawreadformat = fmt;
02279       tmp->tech_pvt = sub;
02280       if (!ast_strlen_zero(l->language)) {
02281          strncpy(tmp->language, l->language, sizeof(tmp->language)-1);
02282       }
02283       if (!ast_strlen_zero(l->accountcode)) {
02284          strncpy(tmp->accountcode, l->accountcode, sizeof(tmp->accountcode)-1);
02285       }
02286       if (l->amaflags) {
02287          tmp->amaflags = l->amaflags;
02288       }
02289       sub->owner = tmp;
02290       ast_mutex_lock(&usecnt_lock);
02291       usecnt++;
02292       ast_mutex_unlock(&usecnt_lock);
02293       ast_update_use_count();
02294       tmp->callgroup = l->callgroup;
02295       tmp->pickupgroup = l->pickupgroup;
02296       strncpy(tmp->call_forward, l->call_forward, sizeof(tmp->call_forward) - 1);
02297       strncpy(tmp->context, l->context, sizeof(tmp->context)-1);
02298       strncpy(tmp->exten,l->exten, sizeof(tmp->exten)-1);
02299       if (!ast_strlen_zero(l->cid_num)) {
02300          tmp->cid.cid_num = strdup(l->cid_num);
02301       }
02302       if (!ast_strlen_zero(l->cid_name)) {
02303          tmp->cid.cid_name = strdup(l->cid_name);
02304       }
02305       tmp->priority = 1;
02306       tmp->adsicpe = AST_ADSI_UNAVAILABLE;
02307 
02308       if (state != AST_STATE_DOWN) {
02309          if (ast_pbx_start(tmp)) {
02310             ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
02311             ast_hangup(tmp);
02312             tmp = NULL;
02313          }
02314       }
02315    } else {
02316       ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
02317     }
02318     return tmp;
02319 }
02320 
02321 static int handle_message(skinny_req *req, struct skinnysession *s)
02322 {
02323    struct skinny_subchannel *sub;
02324    struct ast_channel *c;
02325    struct ast_frame f = { 0, };  
02326    struct sockaddr_in sin;
02327    struct sockaddr_in us;
02328    struct skinny_line *lines;
02329    char name[16];
02330    char addr[4];
02331    char d;
02332    char iabuf[INET_ADDRSTRLEN];
02333    int digit;
02334    int res=0;
02335    int speedDialNum;
02336    int lineNumber;
02337    int stimulus;
02338    int stimulusInstance;
02339    int status;
02340    int port;
02341    int i;
02342    time_t timer;
02343    struct tm *cmtime;
02344    pthread_t t;
02345    button_defs_t *b, *buse;
02346    
02347    if ((!s->device) && (letohl(req->e) != REGISTER_MESSAGE && letohl(req->e) != ALARM_MESSAGE)) {
02348       ast_log(LOG_WARNING, "Client sent message #%d without first registering.\n", req->e);
02349       free(req);
02350       return 0;
02351    }
02352 
02353    switch(letohl(req->e))  {
02354    case ALARM_MESSAGE:
02355       /* no response necessary */
02356       break;
02357    case REGISTER_MESSAGE:
02358       if (skinnydebug) {
02359          ast_verbose("Device %s is attempting to register\n", req->data.reg.name);
02360       }
02361       res = skinny_register(req, s);   
02362       if (!res) {
02363          ast_log(LOG_ERROR, "Rejecting Device %s: Device not found\n", req->data.reg.name);
02364          memcpy(&name, req->data.reg.name, sizeof(req->data.reg.name));
02365          memset(req, 0, sizeof(skinny_req));
02366          req->len = htolel(sizeof(register_rej_message)+4);
02367          req->e = htolel(REGISTER_REJ_MESSAGE);
02368          snprintf(req->data.regrej.errMsg, sizeof(req->data.regrej.errMsg), "No Authority: %s", name);
02369          transmit_response(s, req);
02370          break;
02371       }
02372       if (option_verbose > 2) {
02373          ast_verbose(VERBOSE_PREFIX_3 "Device '%s' successfuly registered\n", s->device->name); 
02374       }
02375       memset(req, 0, SKINNY_MAX_PACKET);
02376       req->len = htolel(sizeof(register_ack_message)+4);
02377       req->e = htolel(REGISTER_ACK_MESSAGE);
02378       req->data.regack.res[0] = '0';
02379       req->data.regack.res[1] = '\0';
02380       req->data.regack.keepAlive = htolel(keep_alive);
02381       strncpy(req->data.regack.dateTemplate, date_format, sizeof(req->data.regack.dateTemplate) - 1); 
02382       req->data.regack.res2[0] = '0';
02383       req->data.regack.res2[1] = '\0';
02384       req->data.regack.secondaryKeepAlive = htolel(keep_alive);
02385       transmit_response(s, req);
02386       if (skinnydebug) {
02387          ast_verbose("Requesting capabilities\n");
02388       }
02389       memset(req, 0, SKINNY_MAX_PACKET);
02390       req->len = htolel(4);
02391       req->e = htolel(CAPABILITIES_REQ_MESSAGE);
02392       transmit_response(s, req);
02393       break;
02394    case UNREGISTER_MESSAGE:
02395       /* XXX Acutally unregister the device */
02396       break;
02397    case IP_PORT_MESSAGE:
02398       /* no response necessary */
02399       break;
02400    case STIMULUS_MESSAGE:
02401       stimulus = letohl(req->data.stimulus.stimulus);
02402       stimulusInstance = letohl(req->data.stimulus.stimulusInstance);
02403       
02404       switch(stimulus) {
02405       case STIMULUS_REDIAL:
02406          /* If we can keep an array of dialed frames we can implement a quick 
02407             and dirty redial, feeding the frames we last got into the queue
02408             function */
02409          if (skinnydebug) {
02410             ast_verbose("Recieved Stimulus: Redial(%d)\n", stimulusInstance);
02411          }
02412          break;
02413       case STIMULUS_SPEEDDIAL:
02414          if (skinnydebug) {
02415             ast_verbose("Recieved Stimulus: SpeedDial(%d)\n", stimulusInstance);
02416          }
02417          break;
02418       case STIMULUS_HOLD:
02419          /* start moh? set RTP to 0.0.0.0? */
02420          if (skinnydebug) {
02421             ast_verbose("Recieved Stimulus: Hold(%d)\n", stimulusInstance);
02422          }
02423          break;
02424       case STIMULUS_TRANSFER:
02425          if (skinnydebug) {
02426             ast_verbose("Recieved Stimulus: Transfer(%d)\n", stimulusInstance);
02427          }
02428          transmit_tone(s, SKINNY_DIALTONE);  
02429          /* XXX figure out how to transfer */
02430          break;
02431       case STIMULUS_CONFERENCE:
02432          if (skinnydebug) {
02433             ast_verbose("Recieved Stimulus: Transfer(%d)\n", stimulusInstance);
02434          }
02435          transmit_tone(s, SKINNY_DIALTONE);
02436          /* XXX determine the best way to pull off a conference.  Meetme?  */
02437          break;
02438       case STIMULUS_VOICEMAIL:
02439          if (skinnydebug) {
02440             ast_verbose("Recieved Stimulus: Voicemail(%d)\n", stimulusInstance);
02441          }
02442          /* XXX Find and dial voicemail extension */
02443          break;
02444       case STIMULUS_CALLPARK:
02445          if (skinnydebug) {
02446             ast_verbose("Recieved Stimulus: Park Call(%d)\n", stimulusInstance);
02447          }
02448          /* XXX Park the call */
02449          break;
02450       case STIMULUS_FORWARDALL:
02451          /* Why is DND under FORWARDALL ? */
02452 
02453          /* Do not disturb */
02454          transmit_tone(s, SKINNY_DIALTONE);
02455          if (s->device->lines->dnd != 0){
02456             if (option_verbose > 2) {
02457                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);
02458             }
02459             s->device->lines->dnd = 0;
02460             transmit_lamp_indication(s, STIMULUS_FORWARDALL, 1, SKINNY_LAMP_ON);
02461             transmit_displaynotify(s, "DnD disabled",10);
02462          } else {
02463             if (option_verbose > 2) {
02464                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);
02465             }
02466             s->device->lines->dnd = 1;
02467             transmit_lamp_indication(s, STIMULUS_FORWARDALL, 1, SKINNY_LAMP_OFF);
02468             transmit_displaynotify(s, "DnD enabled",10);
02469          }
02470          break;
02471       case STIMULUS_FORWARDBUSY:
02472       case STIMULUS_FORWARDNOANSWER:
02473          /* Gonna be fun, not */
02474          if (skinnydebug) {
02475             ast_verbose("Recieved Stimulus: Forward (%d)\n", stimulusInstance);
02476          }
02477          break;
02478       case STIMULUS_DISPLAY:
02479          /* Not sure what this is */
02480          if (skinnydebug) {
02481             ast_verbose("Recieved Stimulus: Display(%d)\n", stimulusInstance);
02482          }
02483          break;
02484       case STIMULUS_LINE:
02485          if (skinnydebug) {
02486             ast_verbose("Recieved Stimulus: Line(%d)\n", stimulusInstance);
02487          }     
02488          sub = find_subchannel_by_line(s->device->lines);
02489          /* turn the speaker on */
02490          transmit_speaker_mode(s, 1);  
02491       break;
02492       default:
02493          ast_verbose("RECEIVED UNKNOWN STIMULUS:  %d(%d)\n", stimulus, stimulusInstance);       
02494          break;
02495       }
02496       break;
02497    case VERSION_REQ_MESSAGE:
02498       if (skinnydebug) {
02499          ast_verbose("Version Request\n");
02500       }
02501       memset(req, 0, SKINNY_MAX_PACKET);
02502       req->len = htolel(sizeof(version_res_message)+4);
02503       req->e = htolel(VERSION_RES_MESSAGE);
02504       snprintf(req->data.version.version, sizeof(req->data.version.version), s->device->version_id);
02505       transmit_response(s, req);
02506       break;
02507    case SERVER_REQUEST_MESSAGE:
02508       if (skinnydebug) {
02509          ast_verbose("Recieved Server Request\n");
02510       }
02511       memset(req, 0, SKINNY_MAX_PACKET);
02512       req->len = htolel(sizeof(server_res_message)+4);
02513       req->e = htolel(SERVER_RES_MESSAGE);
02514       memcpy(req->data.serverres.server[0].serverName, ourhost, 
02515             sizeof(req->data.serverres.server[0].serverName));
02516       req->data.serverres.serverListenPort[0] = htolel(ourport);
02517       req->data.serverres.serverIpAddr[0] = htolel(__ourip.s_addr);
02518       transmit_response(s, req); 
02519       break;
02520    case BUTTON_TEMPLATE_REQ_MESSAGE:
02521       if (skinnydebug) {
02522          ast_verbose("Buttontemplate requested\n");
02523       }
02524       sub = find_subchannel_by_line(s->device->lines);
02525       memset(req, 0, SKINNY_MAX_PACKET);
02526       req->e = htolel(BUTTON_TEMPLATE_RES_MESSAGE);   
02527       req->len = htolel(sizeof(button_template_res_message)+4);
02528 
02529       /* Find a matching button definition, default to first in the
02530          list */
02531       buse = button_defs;
02532       for(b=button_defs; b->type; b++) {
02533          if (!strcmp(s->device->model, b->type)) {
02534             buse = b;
02535          }
02536       }
02537       req->data.buttontemplate.buttonOffset = 0;
02538       req->data.buttontemplate.buttonCount  = htolel(buse->num_buttons);
02539       req->data.buttontemplate.totalButtonCount = htolel(buse->num_buttons);
02540       for (i=0; i<42; i++) {
02541          if (i < buse->num_buttons) {
02542             memcpy(&(req->data.buttontemplate.definition[i]),
02543                &(buse->button_def[i]),
02544                sizeof(button_definition));
02545          } else {
02546             memcpy(&(req->data.buttontemplate.definition[i]),
02547                &(button_def_none),
02548                sizeof(button_definition));
02549          }
02550       }
02551 
02552       if (skinnydebug) {         
02553          ast_verbose("Sending %s template to %s@%s (%s)\n",
02554                   buse->type, 
02555                   sub->parent->name, 
02556                   sub->parent->parent->name, 
02557                   s->device->model);
02558       }
02559       transmit_response(s, req);
02560       break;
02561    case SOFT_KEY_SET_REQ_MESSAGE:
02562       if (skinnydebug)  {
02563          ast_verbose("Received SoftKeySetReq\n");
02564       }
02565       memset(req, 0, SKINNY_MAX_PACKET);
02566       req->len = htolel(sizeof(soft_key_sets)+4);
02567       req->e = htolel(SOFT_KEY_SET_RES_MESSAGE);
02568       req->data.softkeysets.softKeySetOffset = 0;
02569       req->data.softkeysets.softKeySetCount = htolel(11);
02570       req->data.softkeysets.totalSoftKeySetCount = htolel(11); 
02571       /* XXX Wicked hack XXX */
02572       memcpy(req->data.softkeysets.softKeySetDefinition, 
02573             soft_key_set_hack, 
02574             sizeof(req->data.softkeysets.softKeySetDefinition));
02575       transmit_response(s,req);
02576       break;
02577    case SOFT_KEY_TEMPLATE_REQ_MESSAGE:
02578       if (skinnydebug) {
02579          ast_verbose("Recieved SoftKey Template Request\n");
02580       }
02581       memset(req, 0, SKINNY_MAX_PACKET);
02582       req->len = htolel(sizeof(soft_key_template)+4);
02583       req->e = htolel(SOFT_KEY_TEMPLATE_RES_MESSAGE);
02584       req->data.softkeytemplate.softKeyOffset = 0;
02585       req->data.softkeytemplate.softKeyCount = htolel(sizeof(soft_key_template_default) / sizeof(soft_key_template_definition));
02586       req->data.softkeytemplate.totalSoftKeyCount = htolel(sizeof(soft_key_template_default) / sizeof(soft_key_template_definition)); 
02587       memcpy(req->data.softkeytemplate.softKeyTemplateDefinition,
02588             soft_key_template_default,
02589             sizeof(soft_key_template_default));
02590       transmit_response(s,req);
02591       break;
02592    case TIME_DATE_REQ_MESSAGE:
02593       if (skinnydebug) {
02594          ast_verbose("Received Time/Date Request\n");
02595       }
02596       memset(req, 0, SKINNY_MAX_PACKET);
02597       req->len = htolel(sizeof(definetimedate_message)+4);
02598       req->e = htolel(DEFINETIMEDATE_MESSAGE);
02599       timer=time(NULL);
02600       cmtime = localtime(&timer);
02601       req->data.definetimedate.year = htolel(cmtime->tm_year+1900);
02602       req->data.definetimedate.month = htolel(cmtime->tm_mon+1);
02603       req->data.definetimedate.dayofweek = htolel(cmtime->tm_wday);
02604       req->data.definetimedate.day = htolel(cmtime->tm_mday);
02605       req->data.definetimedate.hour = htolel(cmtime->tm_hour);
02606       req->data.definetimedate.minute = htolel(cmtime->tm_min);
02607       req->data.definetimedate.seconds = htolel(cmtime->tm_sec);
02608       transmit_response(s, req);
02609       break;
02610    case SPEED_DIAL_STAT_REQ_MESSAGE:
02611       /* Not really sure how Speed Dial's are different than the 
02612          Softkey templates */
02613       speedDialNum = letohl(req->data.speeddialreq.speedDialNumber);
02614       memset(req, 0, SKINNY_MAX_PACKET);
02615       req->len = htolel(sizeof(speed_dial_stat_res_message)+4);
02616       req->e = htolel(SPEED_DIAL_STAT_RES_MESSAGE);
02617 #if 0
02618       /* XXX Do this right XXX */   
02619       /* If the redial function works the way I think it will, a modification of it
02620          can work here was well. Yikes. */
02621       req->data.speeddialreq.speedDialNumber = speedDialNum;
02622       snprintf(req->data.speeddial.speedDialDirNumber, sizeof(req->data.speeddial.speedDialDirNumber), "31337");
02623       snprintf(req->data.speeddial.speedDialDisplayName,  sizeof(req->data.speeddial.speedDialDisplayName),"Asterisk Rules!");
02624 #endif   
02625       transmit_response(s, req);
02626       break;
02627    case LINE_STATE_REQ_MESSAGE:
02628       lineNumber = letohl(req->data.line.lineNumber);
02629       if (skinnydebug) {
02630          ast_verbose("Received LineStateReq\n");
02631       }
02632       memset(req, 0, SKINNY_MAX_PACKET);
02633       req->len = htolel(sizeof(line_stat_res_message)+4);
02634       req->e = htolel(LINE_STAT_RES_MESSAGE);   
02635       sub = find_subchannel_by_line(s->device->lines);
02636       if (!sub) {
02637          ast_log(LOG_NOTICE, "No available lines on: %s\n", s->device->name);
02638          return 0;
02639       }
02640       lines = sub->parent;
02641       ast_mutex_lock(&devicelock);
02642       for (i=1; i < lineNumber; i++) {
02643          lines = lines->next;
02644       }
02645       ast_mutex_unlock(&devicelock);
02646       req->data.linestat.linenumber = letohl(lineNumber);      
02647       memcpy(req->data.linestat.lineDirNumber, lines->name,
02648             sizeof(req->data.linestat.lineDirNumber));
02649       memcpy(req->data.linestat.lineDisplayName, lines->label,
02650             sizeof(req->data.linestat.lineDisplayName)); 
02651       transmit_response(s,req);
02652       break;
02653    case CAPABILITIES_RES_MESSAGE:
02654       if (skinnydebug) {
02655          ast_verbose("Received CapabilitiesRes\n");   
02656       }
02657       /* XXX process the capabilites  */
02658       break;
02659    case KEEP_ALIVE_MESSAGE:
02660       memset(req, 0, SKINNY_MAX_PACKET);
02661       req->len = htolel(4);
02662       req->e = htolel(KEEP_ALIVE_ACK_MESSAGE);
02663       transmit_response(s, req);
02664       do_housekeeping(s);
02665       break;
02666    case OFFHOOK_MESSAGE:
02667       transmit_ringer_mode(s,SKINNY_RING_OFF);
02668       transmit_lamp_indication(s, STIMULUS_LINE, s->device->lines->instance, SKINNY_LAMP_ON); 
02669       sub = find_subchannel_by_line(s->device->lines);
02670       if (!sub) {
02671          ast_log(LOG_NOTICE, "No available lines on: %s\n", s->device->name);
02672          return 0;
02673       }
02674       sub->parent->hookstate = SKINNY_OFFHOOK;
02675       
02676       if (sub->outgoing) {
02677          /* We're answering a ringing call */
02678          ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
02679          transmit_callstate(s, s->device->lines->instance, SKINNY_OFFHOOK, sub->callid);
02680          transmit_tone(s, SKINNY_SILENCE);
02681          transmit_callstate(s, s->device->lines->instance, SKINNY_CONNECTED, sub->callid);
02682          start_rtp(sub);
02683          ast_setstate(sub->owner, AST_STATE_UP);
02684          /* XXX select the appropriate soft key here */
02685       } else {    
02686          if (!sub->owner) {   
02687             transmit_callstate(s, s->device->lines->instance, SKINNY_OFFHOOK, sub->callid);
02688             if (skinnydebug) {
02689                ast_verbose("Attempting to Clear display on Skinny %s@%s\n",sub->parent->name, sub->parent->parent->name);
02690             }
02691             transmit_displaymessage(s, NULL); /* clear display */ 
02692             transmit_tone(s, SKINNY_DIALTONE);
02693             c = skinny_new(sub, AST_STATE_DOWN);         
02694             if(c) {
02695                /* start the switch thread */
02696                if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
02697                   ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
02698                   ast_hangup(c);
02699                }
02700             } else {
02701                ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", sub->parent->name, s->device->name);
02702             }
02703          } else {
02704             ast_log(LOG_DEBUG, "Current sub [%s] already has owner\n", sub->owner->name);
02705          }
02706       }
02707       break;
02708    case ONHOOK_MESSAGE:
02709       sub = find_subchannel_by_line(s->device->lines);
02710       if (sub->parent->hookstate == SKINNY_ONHOOK) {
02711          /* Somthing else already put us back on hook */ 
02712          break;
02713       }
02714       sub->cxmode = SKINNY_CX_RECVONLY;
02715       sub->parent->hookstate = SKINNY_ONHOOK;
02716       transmit_callstate(s, s->device->lines->instance, sub->parent->hookstate,sub->callid);
02717       if (skinnydebug) {
02718          ast_verbose("Skinny %s@%s went on hook\n",sub->parent->name, sub->parent->parent->name);
02719          }
02720                if (sub->parent->transfer && (sub->owner && sub->next->owner) && ((!sub->outgoing) || (!sub->next->outgoing))) {
02721          /* We're allowed to transfer, we have two active calls and */
02722          /* we made at least one of the calls.  Let's try and transfer */
02723 
02724 #if 0
02725                if ((res = attempt_transfer(p)) < 0) {
02726              if (p->sub->next->owner) {
02727                sub->next->alreadygone = 1;
02728                ast_queue_hangup(sub->next->owner,1);
02729             }
02730          } else if (res) {
02731             ast_log(LOG_WARNING, "Transfer attempt failed\n");
02732             return -1;
02733                   }
02734 #endif
02735       } else {
02736                /* Hangup the current call */
02737                /* If there is another active call, skinny_hangup will ring the phone with the other call */
02738                if (sub->owner) {
02739                   sub->alreadygone = 1;
02740                   ast_queue_hangup(sub->owner);
02741                } else {
02742                   ast_log(LOG_WARNING, "Skinny(%s@%s-%d) channel already destroyed\n", 
02743                              sub->parent->name, sub->parent->parent->name, sub->callid);
02744                }
02745             }
02746             if ((sub->parent->hookstate == SKINNY_ONHOOK) && (!sub->next->rtp)) {
02747          do_housekeeping(s);
02748          }
02749       break;
02750    case KEYPAD_BUTTON_MESSAGE:
02751       digit = letohl(req->data.keypad.button);
02752       if (skinnydebug) {
02753          ast_verbose("Collected digit: [%d]\n", digit);
02754       }
02755       f.frametype = AST_FRAME_DTMF;
02756       if (digit == 14) {
02757          d = '*';
02758       } else if (digit == 15) {
02759          d = '#';
02760       } else if (digit >=0 && digit <= 9) {
02761          d = '0' + digit;
02762       } else {
02763          /* digit=10-13 (A,B,C,D ?), or
02764           * digit is bad value
02765           * 
02766           * probably should not end up here, but set
02767           * value for backward compatibility, and log
02768           * a warning.
02769           */
02770          d = '0' + digit;
02771          ast_log(LOG_WARNING, "Unsupported digit %d\n", digit);
02772       }
02773       f.subclass  = d;  
02774       f.src = "skinny";
02775       sub = find_subchannel_by_line(s->device->lines);      
02776       if (sub->owner) {
02777          /* XXX MUST queue this frame to all subs in threeway call if threeway call is active */
02778          ast_queue_frame(sub->owner, &f);
02779                   if (sub->next->owner) {
02780             ast_queue_frame(sub->next->owner, &f);
02781                   }
02782          } else {
02783          ast_verbose("No owner: %s\n", s->device->lines->name);
02784       }
02785       break;
02786    case OPEN_RECIEVE_CHANNEL_ACK_MESSAGE:
02787       ast_verbose("Recieved Open Recieve Channel Ack\n");
02788       status = letohl(req->data.openrecievechannelack.status);
02789       if (status) {
02790          ast_log(LOG_ERROR, "Open Recieve Channel Failure\n");
02791          break;
02792       }
02793       /* ENDIAN */
02794       memcpy(addr, req->data.openrecievechannelack.ipAddr, sizeof(addr));
02795       port = htolel(req->data.openrecievechannelack.port);
02796       sin.sin_family = AF_INET;
02797       /* I smell endian problems */
02798       memcpy(&sin.sin_addr, addr, sizeof(sin.sin_addr));  
02799       sin.sin_port = htons(port);
02800       if (skinnydebug) {
02801          ast_verbose("ipaddr = %s:%d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port));
02802       }
02803       sub = find_subchannel_by_line(s->device->lines);
02804       if (sub->rtp) {
02805          ast_rtp_set_peer(sub->rtp, &sin);
02806          ast_rtp_get_us(sub->rtp, &us);   
02807       } else {
02808          ast_log(LOG_ERROR, "No RTP structure, this is very bad\n");
02809          break;
02810       }
02811       memset(req, 0, SKINNY_MAX_PACKET);
02812          req->len = htolel(sizeof(start_media_transmission_message)+4);
02813          req->e = htolel(START_MEDIA_TRANSMISSION_MESSAGE);
02814          req->data.startmedia.conferenceId = 0;
02815          req->data.startmedia.passThruPartyId = 0;
02816          memcpy(req->data.startmedia.remoteIp, &s->device->ourip, 4); /* Endian? */
02817          req->data.startmedia.remotePort = htolel(ntohs(us.sin_port));
02818          req->data.startmedia.packetSize = htolel(20);
02819          req->data.startmedia.payloadType = htolel(convert_cap(s->device->lines->capability));
02820          req->data.startmedia.qualifier.precedence = htolel(127);
02821          req->data.startmedia.qualifier.vad = 0;
02822          req->data.startmedia.qualifier.packets = 0;
02823          req->data.startmedia.qualifier.bitRate = 0;
02824          transmit_response(s, req);
02825       break;   
02826    default:
02827       ast_verbose("RECEIVED UNKNOWN MESSAGE TYPE:  %x\n", letohl(req->e));
02828       break;
02829    }
02830    free(req);
02831    return 1;
02832 }
02833 
02834 static void destroy_session(struct skinnysession *s)
02835 {
02836    struct skinnysession *cur, *prev = NULL;
02837    ast_mutex_lock(&sessionlock);
02838    cur = sessions;
02839    while(cur) {
02840       if (cur == s) {
02841          break;
02842       }
02843       prev = cur;
02844       cur = cur->next;
02845    }
02846    if (cur) {
02847       if (prev) {
02848          prev->next = cur->next;
02849       } else {
02850          sessions = cur->next;
02851       }
02852       if (s->fd > -1) {
02853          close(s->fd);
02854       }
02855       ast_mutex_destroy(&s->lock);
02856       free(s);
02857    } else {
02858       ast_log(LOG_WARNING, "Trying to delete nonexistent session %p?\n", s);
02859    }
02860    ast_mutex_unlock(&sessionlock);
02861 }
02862 
02863 static int get_input(struct skinnysession *s)  
02864 {  
02865    int res;  
02866    int dlen = 0;
02867    struct pollfd fds[1];  
02868  
02869    fds[0].fd = s->fd;
02870    fds[0].events = POLLIN;
02871    res = poll(fds, 1, -1);
02872  
02873    if (res < 0) {
02874       ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
02875    } else if (res > 0) {
02876       memset(s->inbuf,0,sizeof(s->inbuf));
02877       res = read(s->fd, s->inbuf, 4);
02878       if (res != 4) {
02879          ast_log(LOG_WARNING, "Skinny Client sent less data than expected.\n");
02880          return -1;
02881       }
02882       dlen = letohl(*(int *)s->inbuf);
02883       if (dlen+8 > sizeof(s->inbuf)) {
02884          dlen = sizeof(s->inbuf) - 8;
02885       }
02886       *(int *)s->inbuf = htolel(dlen);
02887       res = read(s->fd, s->inbuf+4, dlen+4);
02888       ast_mutex_unlock(&s->lock);
02889       if (res != (dlen+4)) {
02890          ast_log(LOG_WARNING, "Skinny Client sent less data than expected.\n");
02891          return -1;
02892       } 
02893    }  
02894    return res;  
02895 }   
02896 
02897 static skinny_req *skinny_req_parse(struct skinnysession *s)
02898 {
02899    skinny_req *req;
02900    
02901    req = malloc(SKINNY_MAX_PACKET);
02902    if (!req) {
02903       ast_log(LOG_ERROR, "Unable to allocate skinny_request, this is bad\n");
02904       return NULL;
02905    }
02906    memset(req, 0, sizeof(skinny_req));
02907    /* +8 to account for reserved and length fields */
02908    memcpy(req, s->inbuf, letohl(*(int*)(s->inbuf))+8); 
02909    if (letohl(req->e) < 0) {
02910       ast_log(LOG_ERROR, "Event Message is NULL from socket %d, This is bad\n", s->fd);
02911       free(req);
02912       return NULL;
02913    }
02914    return req;
02915 }
02916 
02917 static void *skinny_session(void *data)
02918 {
02919    int res;
02920    skinny_req *req;
02921    struct skinnysession *s = data;
02922    char iabuf[INET_ADDRSTRLEN];
02923    
02924    ast_verbose(VERBOSE_PREFIX_3 "Starting Skinny session from %s\n",  ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
02925    for (;;) {
02926       res = 0;
02927       res = get_input(s);
02928       if (res < 0) {
02929          break;
02930       }
02931       req = skinny_req_parse(s);
02932       if (!req) {
02933          return NULL;
02934       }
02935       res = handle_message(req, s);
02936       if (res < 0) {
02937          destroy_session(s);
02938          return NULL;
02939       } 
02940    }
02941    ast_log(LOG_NOTICE, "Skinny Session returned: %s\n", strerror(errno));
02942    destroy_session(s);
02943    return 0;
02944 }
02945 
02946 static void *accept_thread(void *ignore)
02947 {
02948    int as;
02949    struct sockaddr_in sin;
02950    socklen_t sinlen;
02951    struct skinnysession *s;
02952    struct protoent *p;
02953    int arg = 1;
02954    pthread_attr_t attr;
02955 
02956    pthread_attr_init(&attr);
02957    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
02958 
02959    for (;;) {
02960       sinlen = sizeof(sin);
02961       as = accept(skinnysock, (struct sockaddr *)&sin, &sinlen);
02962       if (as < 0) {
02963          ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
02964          continue;
02965       }
02966       p = getprotobyname("tcp");
02967       if(p) {
02968          if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
02969             ast_log(LOG_WARNING, "Failed to set Skinny tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
02970          }
02971       }
02972       s = malloc(sizeof(struct skinnysession));
02973       if (!s) {
02974          ast_log(LOG_WARNING, "Failed to allocate Skinny session: %s\n", strerror(errno));
02975          continue;
02976       } 
02977       memset(s, 0, sizeof(struct skinnysession));
02978       memcpy(&s->sin, &sin, sizeof(sin));
02979       ast_mutex_init(&s->lock);
02980       s->fd = as;
02981       ast_mutex_lock(&sessionlock);
02982       s->next = sessions;
02983       sessions = s;
02984       ast_mutex_unlock(&sessionlock);
02985       
02986       if (ast_pthread_create(&tcp_thread, NULL, skinny_session, s)) {
02987          destroy_session(s);
02988       }
02989    }
02990    if (skinnydebug) {
02991       ast_verbose("killing accept thread\n");
02992    }
02993    close(as);
02994    return 0;
02995 }
02996 
02997 static void *do_monitor(void *data)
02998 {
02999    int res;
03000 
03001    /* This thread monitors all the interfaces which are not yet in use
03002       (and thus do not have a separate thread) indefinitely */
03003    /* From here on out, we die whenever asked */
03004    for(;;) {
03005       pthread_testcancel();
03006       /* Wait for sched or io */
03007       res = ast_sched_wait(sched);
03008       if ((res < 0) || (res > 1000)) {
03009          res = 1000;
03010       }
03011       res = ast_io_wait(io, res);
03012       ast_mutex_lock(&monlock);
03013       if (res >= 0) {
03014          ast_sched_runq(sched);
03015       }
03016       ast_mutex_unlock(&monlock);
03017    }
03018    /* Never reached */
03019    return NULL;
03020    
03021 }
03022 
03023 static int restart_monitor(void)
03024 {
03025    /* If we're supposed to be stopped -- stay stopped */
03026    if (monitor_thread == AST_PTHREADT_STOP)
03027       return 0;
03028    if (ast_mutex_lock(&monlock)) {
03029       ast_log(LOG_WARNING, "Unable to lock monitor\n");
03030       return -1;
03031    }
03032    if (monitor_thread == pthread_self()) {
03033       ast_mutex_unlock(&monlock);
03034       ast_log(LOG_WARNING, "Cannot kill myself\n");
03035       return -1;
03036    }
03037    if (monitor_thread != AST_PTHREADT_NULL) {
03038       /* Wake up the thread */
03039       pthread_kill(monitor_thread, SIGURG);
03040    } else {
03041       /* Start a new monitor */
03042       if (ast_pthread_create(&monitor_thread, NULL, do_monitor, NULL) < 0) {
03043          ast_mutex_unlock(&monlock);
03044          ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
03045          return -1;
03046       }
03047    }
03048    ast_mutex_unlock(&monlock);
03049    return 0;
03050 }
03051 
03052 static struct ast_channel *skinny_request(const char *type, int format, void *data, int *cause)
03053 {
03054    int oldformat;
03055    struct skinny_subchannel *sub;
03056    struct ast_channel *tmpc = NULL;
03057    char tmp[256];
03058    char *dest = data;
03059 
03060    oldformat = format;
03061    format &= capability;
03062    if (!format) {
03063       ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", format);
03064       return NULL;
03065    }
03066    strncpy(tmp, dest, sizeof(tmp) - 1);
03067    if (ast_strlen_zero(tmp)) {
03068       ast_log(LOG_NOTICE, "Skinny channels require a device\n");
03069       return NULL;
03070    }
03071    sub = find_subchannel_by_name(tmp);  
03072    if (!sub) {
03073       ast_log(LOG_NOTICE, "No available lines on: %s\n", dest);
03074       return NULL;
03075    }
03076       if (option_verbose > 2) {
03077          ast_verbose(VERBOSE_PREFIX_3 "skinny_request(%s)\n", tmp);
03078          ast_verbose(VERBOSE_PREFIX_3 "Skinny cw: %d, dnd: %d, so: %d, sno: %d\n", 
03079                      sub->parent->callwaiting, sub->parent->dnd, sub->owner ? 1 : 0, sub->next->owner ? 1: 0);
03080       }
03081    tmpc = skinny_new(sub->owner ? sub->next : sub, AST_STATE_DOWN);
03082    if (!tmpc) {
03083       ast_log(LOG_WARNING, "Unable to make channel for '%s'\n", tmp);
03084    }
03085    restart_monitor();
03086    return tmpc;
03087 }
03088 
03089 static int reload_config(void)
03090 {
03091    int on = 1;
03092    struct ast_config *cfg;
03093    struct ast_variable *v;
03094    int format;
03095    char *cat;
03096    char iabuf[INET_ADDRSTRLEN];
03097    struct skinny_device *d;
03098    int oldport = ntohs(bindaddr.sin_port);
03099 
03100    if (gethostname(ourhost, sizeof(ourhost))) {
03101       ast_log(LOG_WARNING, "Unable to get hostname, Skinny disabled\n");
03102       return 0;
03103    }
03104    cfg = ast_config_load(config);
03105 
03106    /* We *must* have a config file otherwise stop immediately */
03107    if (!cfg) {
03108       ast_log(LOG_NOTICE, "Unable to load config %s, Skinny disabled\n", config);
03109       return 0;
03110    }
03111    /* load the general section */
03112    memset(&bindaddr, 0, sizeof(bindaddr));
03113    v = ast_variable_browse(cfg, "general");
03114    while(v) {
03115       /* Create the interface list */
03116       if (!strcasecmp(v->name, "bindaddr")) {
03117          if (!(hp = ast_gethostbyname(v->value, &ahp))) {
03118             ast_log(LOG_WARNING, "Invalid address: %s\n", v->value);
03119          } else {
03120             memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr));
03121          }
03122       } else if (!strcasecmp(v->name, "keepAlive")) {
03123          keep_alive = atoi(v->value);     
03124       } else if (!strcasecmp(v->name, "dateFormat")) {
03125          strncpy(date_format, v->value, sizeof(date_format) - 1); 
03126       } else if (!strcasecmp(v->name, "allow")) {
03127          format = ast_getformatbyname(v->value);
03128          if (format < 1) {
03129             ast_log(LOG_WARNING, "Cannot allow unknown format '%s'\n", v->value);
03130          } else {
03131             capability |= format;
03132          }  
03133       } else if (!strcasecmp(v->name, "disallow")) {
03134          format = ast_getformatbyname(v->value);
03135          if (format < 1) {
03136             ast_log(LOG_WARNING, "Cannot disallow unknown format '%s'\n", v->value);
03137          } else {
03138             capability &= ~format;
03139          }
03140       } else if (!strcasecmp(v->name, "port")) {
03141          if (sscanf(v->value, "%d", &ourport) == 1) {
03142             bindaddr.sin_port = htons(ourport);
03143          } else {
03144             ast_log(LOG_WARNING, "Invalid port number '%s' at line %d of %s\n", v->value, v->lineno, config);
03145          }
03146       }
03147       v = v->next;
03148    }
03149    if (ntohl(bindaddr.sin_addr.s_addr)) {
03150       memcpy(&__ourip, &bindaddr.sin_addr, sizeof(__ourip));
03151    } else {
03152       hp = ast_gethostbyname(ourhost, &ahp);
03153       if (!hp) {
03154          ast_log(LOG_WARNING, "Unable to get our IP address, Skinny disabled\n");
03155          ast_config_destroy(cfg);
03156          return 0;
03157       }
03158       memcpy(&__ourip, hp->h_addr, sizeof(__ourip));
03159    }
03160    if (!ntohs(bindaddr.sin_port)) {
03161       bindaddr.sin_port = ntohs(DEFAULT_SKINNY_PORT);
03162    }
03163    bindaddr.sin_family = AF_INET;
03164    
03165    /* load the device sections */
03166    cat = ast_category_browse(cfg, NULL);
03167    while(cat) {
03168       if (!strcasecmp(cat, "general")) {
03169         /* Nothing to do */
03170 #if 0
03171       } else if (!strncasecmp(cat, "paging-", 7)) {
03172          p = build_paging_device(cat, ast_variable_browse(cfg, cat));
03173          if (p) {
03174          }
03175 #endif
03176       } else {
03177          d = build_device(cat, ast_variable_browse(cfg, cat));
03178          if (d) {
03179             if (option_verbose > 2) {
03180                ast_verbose(VERBOSE_PREFIX_3 "Added device '%s'\n", d->name);
03181                      }
03182             ast_mutex_lock(&devicelock);
03183             d->next = devices;
03184             devices = d;
03185             ast_mutex_unlock(&devicelock);
03186          }
03187       }
03188       cat = ast_category_browse(cfg, cat);
03189    }
03190    ast_mutex_lock(&netlock);
03191    if ((skinnysock > -1) && (ntohs(bindaddr.sin_port) != oldport)) {
03192       close(skinnysock);
03193       skinnysock = -1;
03194    }
03195    if (skinnysock < 0) {
03196       skinnysock = socket(AF_INET, SOCK_STREAM, 0);
03197       if(setsockopt(skinnysock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
03198          ast_log(LOG_ERROR, "Set Socket Options failed: errno %d, %s", errno, strerror(errno));
03199          ast_config_destroy(cfg);
03200          return 0;
03201       }
03202       if (skinnysock < 0) {
03203          ast_log(LOG_WARNING, "Unable to create Skinny socket: %s\n", strerror(errno));
03204       } else {
03205          if (bind(skinnysock, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) {
03206             ast_log(LOG_WARNING, "Failed to bind to %s:%d: %s\n",
03207                   ast_inet_ntoa(iabuf, sizeof(iabuf), bindaddr.sin_addr), ntohs(bindaddr.sin_port),
03208                      strerror(errno));
03209             close(skinnysock);
03210             skinnysock = -1;
03211             ast_config_destroy(cfg);
03212             return 0;
03213          } 
03214          if (listen(skinnysock,DEFAULT_SKINNY_BACKLOG)) {
03215                ast_log(LOG_WARNING, "Failed to start listening to %s:%d: %s\n",
03216                   ast_inet_ntoa(iabuf, sizeof(iabuf), bindaddr.sin_addr), ntohs(bindaddr.sin_port),
03217                      strerror(errno));
03218                close(skinnysock);
03219                skinnysock = -1;
03220                ast_config_destroy(cfg);
03221                return 0;
03222          }
03223          if (option_verbose > 1) {
03224             ast_verbose(VERBOSE_PREFIX_2 "Skinny listening on %s:%d\n", 
03225                ast_inet_ntoa(iabuf, sizeof(iabuf), bindaddr.sin_addr), ntohs(bindaddr.sin_port));
03226          }
03227          ast_pthread_create(&accept_t,NULL, accept_thread, NULL);
03228       }
03229    }
03230    ast_mutex_unlock(&netlock);
03231    ast_config_destroy(cfg);
03232    return 0;
03233 }
03234 
03235 void delete_devices(void)
03236 {
03237    struct skinny_device *d, *dlast;
03238    struct skinny_line *l, *llast;
03239    struct skinny_subchannel *sub, *slast;
03240    
03241    ast_mutex_lock(&devicelock);
03242    
03243    /* Delete all devices */
03244    for (d=devices;d;) {    
03245       /* Delete all lines for this device */
03246       for (l=d->lines;l;) {
03247          /* Delete all subchannels for this line */
03248          for (sub=l->sub;sub;) {
03249             slast = sub;
03250             sub = sub->next;
03251             ast_mutex_destroy(&slast->lock);
03252             free(slast);
03253          }
03254          llast = l;
03255          l = l->next;
03256          ast_mutex_destroy(&llast->lock);
03257          free(llast);
03258       }
03259       dlast = d;
03260       d = d->next;
03261       free(dlast);
03262    }
03263    devices=NULL;
03264    ast_mutex_unlock(&devicelock);
03265 }
03266 
03267 int reload(void)
03268 {
03269    delete_devices();
03270    reload_config();
03271    restart_monitor();
03272    return 0;
03273 }
03274 
03275 
03276 int load_module()
03277 {
03278    int res = 0;
03279 
03280    for (; res < (sizeof(soft_key_template_default) / sizeof(soft_key_template_default[0])); res++) {
03281       soft_key_template_default[res].softKeyEvent = htolel(soft_key_template_default[res].softKeyEvent);
03282    }
03283    /* load and parse config */
03284    res = reload_config();
03285    
03286    ast_rtp_proto_register(&skinny_rtp);
03287    ast_cli_register(&cli_show_devices);
03288    ast_cli_register(&cli_show_lines);
03289    ast_cli_register(&cli_debug);
03290    ast_cli_register(&cli_no_debug);
03291    sched = sched_context_create();
03292    if (!sched) {
03293       ast_log(LOG_WARNING, "Unable to create schedule context\n");
03294    }
03295    io = io_context_create();
03296    if (!io) {
03297       ast_log(LOG_WARNING, "Unable to create I/O context\n");
03298    }
03299    /* And start the monitor for the first time */
03300    restart_monitor();
03301 
03302    /* Announce our presence to Asterisk */   
03303    if (!res) {
03304       /* Make sure we can register our skinny channel type */
03305       if (ast_channel_register(&skinny_tech)) {
03306          ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
03307          return -1;
03308       }
03309    }
03310    return res;
03311 }
03312 
03313 int unload_module()
03314 {
03315 #if 0
03316    struct skinny_session *session, s;
03317    struct skinny_subchannel *sub;
03318    struct skinny_line *line = session;
03319 
03320    /* close all IP connections */
03321    if (!ast_mutex_lock(&devicelock)) {
03322       /* Terminate tcp listener thread */
03323    } else {
03324       ast_log(LOG_WARNING, "Unable to lock the monitor\n");
03325       return -1;
03326    }
03327    if (!ast_mutex_lock(&monlock)) {
03328       if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP)) {
03329          pthread_cancel(monitor_thread);
03330          pthread_kill(monitor_thread, SIGURG);
03331          pthread_join(monitor_thread, NULL);
03332       }
03333       monitor_thread = AST_PTHREADT_STOP;
03334       ast_mutex_unlock(&monlock);
03335    } else {
03336       ast_log(LOG_WARNING, "Unable to lock the monitor\n");
03337       return -1;
03338    }
03339    if (!ast_mutex_lock(&iflock)) {
03340       /* Destroy all the interfaces and free their memory */
03341       p = iflist;
03342       while(p) {
03343          pl = p;
03344          p = p->next;
03345          /* Free associated memory */
03346          ast_mutex_destroy(&pl->lock);
03347          free(pl);
03348       }
03349       iflist = NULL;
03350       ast_mutex_unlock(&iflock);
03351    } else {
03352       ast_log(LOG_WARNING, "Unable to lock the monitor\n");
03353       return -1;
03354    }
03355 
03356         ast_rtp_proto_register(&skinny_rtp);
03357    ast_channel_unregister(&skinny_tech);
03358         ast_cli_register(&cli_show_devices);
03359         ast_cli_register(&cli_show_lines);
03360         ast_cli_register(&cli_debug);
03361         ast_cli_register(&cli_no_debug);
03362 
03363    return 0;
03364 #endif
03365    return -1;
03366 }
03367 
03368 int usecount()
03369 {
03370    return usecnt;
03371 }
03372 
03373 char *key()
03374 {
03375    return ASTERISK_GPL_KEY;
03376 }
03377 
03378 char *description()
03379 {
03380    return (char *) desc;
03381 }

Generated on Fri May 26 01:45:32 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.6