Asterisk.org: Developer Documentation (28 Oct 2009)

Wed Oct 28 15:47:52 2009

## Asterisk developer's documentation

# chan_skinny.c

Go to the documentation of this file.

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