don't use '%i' at all, since we have no current use cases that need non base-10 parsi...
[asterisk/asterisk.git] / channels / chan_skinny.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Implementation of the Skinny protocol
5  * 
6  * Copyright (C) 1999 - 2005 Digium, inc
7  *
8  * chan_skinny was developed by Jeremy McNamara & Florian Overkamp
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  *
13  */
14
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include "asterisk/lock.h"
20 #include "asterisk/channel.h"
21 #include "asterisk/config.h"
22 #include "asterisk/logger.h"
23 #include "asterisk/module.h"
24 #include "asterisk/pbx.h"
25 #include "asterisk/options.h"
26 #include "asterisk/lock.h"
27 #include "asterisk/sched.h"
28 #include "asterisk/io.h"
29 #include "asterisk/rtp.h"
30 #include "asterisk/acl.h"
31 #include "asterisk/callerid.h"
32 #include "asterisk/cli.h"
33 #include "asterisk/say.h"
34 #include "asterisk/cdr.h"
35 #include "asterisk/astdb.h"
36 #include "asterisk/features.h"
37 #include "asterisk/app.h"
38 #include "asterisk/musiconhold.h"
39 #include "asterisk/utils.h"
40 #include <sys/socket.h>
41 #include <netinet/in.h>
42 #include <netinet/tcp.h>
43 #include <sys/ioctl.h>
44 #include <net/if.h>
45 #include <errno.h>
46 #include <unistd.h>
47 #include <fcntl.h>
48 #include <netdb.h>
49 #include <arpa/inet.h>
50 #include <sys/signal.h>
51 #include <signal.h>
52 #include "asterisk/dsp.h"
53 #include <ctype.h>
54
55 /************************************************************************************/
56 /*                         Skinny/Asterisk Protocol Settings                        */
57 /************************************************************************************/
58 static const char desc[] = "Skinny Client Control Protocol (Skinny)";
59 static const char tdesc[] = "Skinny Client Control Protocol (Skinny)";
60 static const char type[] = "Skinny";
61 static const char config[] = "skinny.conf";
62
63 /* Just about everybody seems to support ulaw, so make it a nice default */
64 static int capability = AST_FORMAT_ULAW;
65
66 #define DEFAULT_SKINNY_PORT     2000
67 #define DEFAULT_SKINNY_BACKLOG  2
68 #define SKINNY_MAX_PACKET       1000
69
70 static int  keep_alive = 120;
71 static char date_format[6] = "D-M-Y";
72 static char version_id[16] = "P002F202";
73
74 /* these should be in an include file, but dunno what to include */
75 typedef unsigned char  UINT8;
76 typedef unsigned short UINT16;
77 typedef unsigned int   UINT32;
78
79 /************************************************************************************/
80 /*                                Protocol Messages                                 */
81 /************************************************************************************/
82 /* message types */
83 #define KEEP_ALIVE_MESSAGE 0x0000
84 /* no additional struct */
85
86 #define REGISTER_MESSAGE 0x0001
87 typedef struct register_message {
88         char name[16];
89         int userId;
90         int instance;
91         char ip[4];
92         int type;
93         int maxStreams;
94 } register_message;
95
96 #define IP_PORT_MESSAGE 0x0002
97
98 #define KEYPAD_BUTTON_MESSAGE 0x0003
99 typedef struct keypad_button_message {
100         int button;
101 } keypad_button_message;
102
103 #define STIMULUS_MESSAGE 0x0005
104 typedef struct stimulus_message {
105         int stimulus;
106         int stimulusInstance;
107 } stimulus_message;
108                 
109 #define OFFHOOK_MESSAGE 0x0006
110 #define ONHOOK_MESSAGE 0x0007
111
112 #define CAPABILITIES_RES_MESSAGE 0x0010
113 typedef struct station_capabilities {   
114         int codec;
115         int frames;
116         union {
117                 char res[8];
118                 long rate;
119         } payloads;     
120 } station_capabilities;
121
122 typedef struct capabilities_res_message {
123         int count;
124         struct station_capabilities caps[18];
125 } capabilities_res_message;
126
127 #define SPEED_DIAL_STAT_REQ_MESSAGE 0x000A
128 typedef struct speed_dial_stat_req_message {
129         int speedDialNumber;
130 } speed_dial_stat_req_message;
131
132 #define LINE_STATE_REQ_MESSAGE 0x000B
133 typedef struct line_state_req_message {
134         int lineNumber;
135 } line_state_req_message;
136
137 #define TIME_DATE_REQ_MESSAGE 0x000D
138 #define VERSION_REQ_MESSAGE 0x000F
139 #define BUTTON_TEMPLATE_REQ_MESSAGE 0x000E
140 #define SERVER_REQUEST_MESSAGE 0x0012
141 #define ALARM_MESSAGE 0x0020
142
143 #define OPEN_RECIEVE_CHANNEL_ACK_MESSAGE 0x0022 
144 typedef struct open_recieve_channel_ack_message {
145         int status;
146         char ipAddr[4];
147         int port;
148         int passThruId;
149 } open_recieve_channel_ack_message;
150
151 #define SOFT_KEY_SET_REQ_MESSAGE 0x0025
152 #define UNREGISTER_MESSAGE 0x0027
153 #define SOFT_KEY_TEMPLATE_REQ_MESSAGE 0x0028
154
155 #define REGISTER_ACK_MESSAGE 0x0081
156 typedef struct register_ack_message {
157         int keepAlive;
158         char dateTemplate[6];
159         char res[2];
160         int secondaryKeepAlive;
161         char res2[4];
162 } register_ack_message;
163
164 #define START_TONE_MESSAGE 0x0082
165 typedef struct start_tone_message {
166         int tone;
167 } start_tone_message;
168
169 #define STOP_TONE_MESSAGE 0x0083
170
171 #define SET_RINGER_MESSAGE 0x0085
172 typedef struct set_ringer_message {
173         int ringerMode;
174 } set_ringer_message;
175
176 #define SET_LAMP_MESSAGE 0x0086
177 typedef struct set_lamp_message {
178         int stimulus;
179         int stimulusInstance;
180         int deviceStimulus;
181 } set_lamp_message;
182
183 #define SET_SPEAKER_MESSAGE 0x0088 
184 typedef struct set_speaker_message {
185         int mode;
186 } set_speaker_message;
187
188 #define START_MEDIA_TRANSMISSION_MESSAGE 0x008A
189 typedef struct media_qualifier {
190         int precedence;
191         int vad;
192         int packets;
193         int bitRate;
194 } media_qualifier;
195
196 typedef struct start_media_transmission_message {
197         int conferenceId;
198         int passThruPartyId;
199         char remoteIp[4];
200         int remotePort;
201         int packetSize;
202         int payloadType;
203         media_qualifier qualifier;
204 } start_media_transmission_message;
205
206 #define STOP_MEDIA_TRANSMISSION_MESSAGE 0x008B
207 typedef struct stop_media_transmission_message {
208         int conferenceId;
209         int passThruPartyId;
210 } stop_media_transmission_message;
211
212 #define CALL_INFO_MESSAGE 0x008F
213 typedef struct call_info_message {
214         char callingPartyName[40];
215         char callingParty[24];
216         char calledPartyName[40];
217         char calledParty[24];
218         int  instance;
219         int  reference;
220         int  type;
221         char originalCalledPartyName[40];
222         char originalCalledParty[24];
223 } call_info_message;
224
225 #define SPEED_DIAL_STAT_RES_MESSAGE 0x0091
226 typedef struct speed_dial_stat_res_message {
227         int speedDialNumber;
228         char speedDialDirNumber[24];
229         char speedDialDisplayName[40];
230 } speed_dial_stat_res_message;
231
232 #define LINE_STAT_RES_MESSAGE 0x0092
233 typedef struct line_stat_res_message {
234         int  linenumber;
235         char lineDirNumber[24];
236         char lineDisplayName[42];
237         int  space;
238 } line_stat_res_message;
239
240 #define DEFINETIMEDATE_MESSAGE 0x0094
241 typedef struct definetimedate_message {
242         int year;       /* since 1900 */
243         int month;
244         int dayofweek;  /* monday = 1 */
245         int day;
246         int hour;
247         int minute;
248         int seconds;
249         int milliseconds;
250         int timestamp;
251 } definetimedate_message;
252  
253 #define DISPLAYTEXT_MESSAGE 0x0099
254 typedef struct displaytext_message {
255         char text[40];
256 } displaytext_message;
257
258 #define CLEAR_DISPLAY_MESSAGE 0x009A
259
260 #define REGISTER_REJ_MESSAGE 0x009D
261 typedef struct register_rej_message {
262         char errMsg[33];
263 } register_rej_message;
264
265 #define CAPABILITIES_REQ_MESSAGE 0x009B
266
267 #define SERVER_RES_MESSAGE 0x009E
268 typedef struct server_identifier {
269         char serverName[48];
270 } server_identifier;
271
272 typedef struct server_res_message {
273         server_identifier server[5];
274         int serverListenPort[5];
275         int serverIpAddr[5];
276 } server_res_message;
277
278 #define BUTTON_TEMPLATE_RES_MESSAGE 0x0097
279 /* 
280    Define the template for a 30VIP phone, this is the older Selsius systems
281    model that has 30 buttons for your button-pushing enjoyment. Each 
282    softbutton defition goes by a 'index' of a button followed by that buttons
283    assignment for example "\x01\x09" means Line 1, while "\x02\x09" means Line 2
284    comments on what each assignment means is below 
285 */
286 static const char *thirtyvip_button_definition_hack = {
287         "\x01\x09"      /* Line 1 */
288         "\x02\x09"      /* Line 2 */
289         "\x03\x09"      /* Line 3 */
290         "\x04\x09"      /* Line 4 */
291         "\x01\x7e"
292         "\x01\x01"      /* Redial */
293         "\x00\xff"      
294         "\x01\x02"      /* Speeddial 1 */
295         "\x02\x02"      /* Speeddial 2 */
296         "\x03\x02"      /* Speeddial 3 */
297         "\x04\x02"      /* Speeddial 4 */
298         "\x05\x02"      /* Speeddial 5 */
299         "\x06\x02"      /* Speeddial 6 */
300         "\x01\x0f"      /* Voicemail */
301         "\x01\x05"      /* Forward all */
302         "\x01\x7d"      /* Conference */
303         "\x00\xff"
304         "\x00\xff"
305         "\x00\xff"
306         "\x00\xff"
307         "\x00\xff"
308         "\x07\x02"      /* Speeddial 7 */
309         "\x08\x02"      /* Speeddial 8 */
310         "\x09\x02"      /* Speeddial 9 */
311         "\x0A\x02"      /* Speeddial 10 */
312         "\x00\xff"
313         "\x00\xff"
314         "\x00\xff"
315         "\x00\xff"
316         "\x00\xff"
317         "\x00\xff"
318         "\x00\xff"
319         "\x00\xff"
320         "\x00\xff"
321         "\x00\xff"
322         "\x00\xff"
323         "\x00\xff"
324         "\x00\xff"
325         "\x00\xff"
326         "\x00\xff"
327         "\x00\xff"
328         "\x00\xff"
329 };
330
331 /* 
332    Now, define the buttons on the 12SP, you 79XX series folks, you get this 
333    too, as I don't know how the buttons are laid out, and I don't care as I 
334    don't have one. Code your own damn buttons or send me a 7960
335 */
336
337 static const char *twelvesp_button_definition_hack = {
338         "\x01\x09"      /* Line 1 */
339         "\x01\x09"      /* Line 2 */    
340         "\x01\x02"      /* Speeddial 1 */
341         "\x02\x02"      /* Speeddial 2 */
342         "\x03\x02"      /* Speeddial 3 */
343         "\x04\x02"      /* Speeddial 4 */
344         "\x01\x0f"      /* Voicemail */
345         "\x05\x02"      /* Speeddial 5 */
346         "\x06\x02"      /* Speeddial 6 */
347         "\x07\x02"      /* Speeddial 7 */
348         "\x08\x02"      /* Speeddial 8 */
349         "\x09\x02"      /* Speeddial 9 */
350         "\x00\xff"
351         "\x00\xff"
352         "\x00\xff"
353         "\x00\xff"
354         "\x00\xff"
355         "\x00\xff"
356         "\x00\xff"
357         "\x00\xff"
358         "\x00\xff"
359         "\x00\xff"
360         "\x00\xff"
361         "\x00\xff"
362         "\x00\xff"
363         "\x00\xff"
364         "\x00\xff"
365         "\x00\xff"
366         "\x00\xff"
367         "\x00\xff"
368         "\x00\xff"
369         "\x00\xff"
370         "\x00\xff"
371         "\x00\xff"
372         "\x00\xff"
373         "\x00\xff"
374         "\x00\xff"
375         "\x00\xff"
376         "\x00\xff"
377         "\x00\xff"
378         "\x00\xff"
379         "\x00\xff"
380 };
381
382 typedef struct buttondefinition {
383         UINT8 instanceNumber;
384         UINT8 buttonDefinition;
385 } button_definition;
386
387 typedef struct button_template_res_message {
388         UINT32 buttonOffset;
389         UINT32 buttonCount;
390         UINT32 totalButtonCount;
391         button_definition definition[42];
392 } button_template_res_message;
393
394 #define VERSION_RES_MESSAGE 0x0098
395 typedef struct version_res_message {
396         char version[16];
397 } version_res_message;
398
399 #define KEEP_ALIVE_ACK_MESSAGE 0x0100
400
401 #define OPEN_RECIEVE_CHANNEL_MESSAGE 0x0105
402 typedef struct open_recieve_channel_message {
403         int conferenceId;
404         int partyId;
405         int packets;
406         int capability;
407         int echo;
408         int bitrate;
409 } open_recieve_channel_message;
410
411 #define CLOSE_RECIEVE_CHANNEL_MESSAGE 0x0106
412 typedef struct close_recieve_channel_message {
413         int conferenceId;
414         int partyId;
415 } close_recieve_channel_message;
416
417 #define SOFT_KEY_TEMPLATE_RES_MESSAGE 0x0108
418 static const char *soft_key_template_hack = {
419         "\x52\x65\x64\x69\x61\x6c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
420         "\x01\x00\x00\x00\x4e\x65\x77\x43\x61\x6c\6c\\x00\x00\x00\x00\x00"
421         "\x00\x00\x00\x00\x02\x00\x00\x00\x48\x6f\x6c\x64\x00\x00\x00\x00"
422         "\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x54\x72\x6e\x73"
423         "\x66\x65\x72\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00"
424         "\x43\x46\x77\x64\x41\x6c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05"
425         "\x00\x00\x00\x43\x46\x77\x64\x20\x42\x75\x73\x79\x00\x00\x00\x00"
426         "\x00\x00\x00\x06\x00\x00\x00\x43\x46\x77\x64\x4e\x6f\x41\x6e\x73"
427         "\x77\x65\x72\x00\x00\x00\x00\x07\x00\x00\x00\x3c\x3c\x00\x00\x00"
428         "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x45"
429         "\x6e\x64\x43\x61\x6c\x6c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09"
430         "\x00\x00\x00\x52\x65\x73\x75\x6d\x65\x00\x00\x00\x00\x00\x00\x00"
431         "\x00\x00\x0a\x00\x00\x00\x41\x6e\x73\x77\x65\x72\x00\x00\x00\x00"
432         "\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x49\x6e\x66\x6f\x00\x00"
433         "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x43\x6f"
434         "\x6e\x66\x72\x6e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0d\x00\x00"
435         "\x00\x50\x61\x72\x6b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
436         "\x00\x0e\x00\x00\x00\x4a\x6f\x69\x6e\x00\x00\x00\x00\x00\x00\x00"
437         "\x00\x00\x00\x00\x0f\x00\x00\x00\x4d\x65\x65\x74\x4d\x65\x00\x00"
438         "\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x50\x69\x63\x6b"
439         "\x55\x70\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00"
440         "\x47\x50\x69\x63\x6b\x55\x70\x00\x00\x00\x00\x00\x00\x00\x00\x00"
441         "\x12\x00\x00\x00\x52\x6d\x4c\x73\x43\x00\x00\x00\x00\x00\x00\x00"
442         "\x00\x00\x00\x13\x00\x00\x00\x42\x61\x72\x67\x65\x00\x00\x00\x00"
443         "\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x42\x61\x72\x67\x65"
444         "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x15\x00\x00\x00"
445 };
446
447 typedef struct soft_key_template_definition {
448         char softKeyLabel[16];
449         int softKeyEvent;
450 } soft_key_template_definition;
451
452 typedef struct soft_key_template {
453         int softKeyOffset;
454         int softKeyCount;
455         int totalSoftKeyCount;
456     soft_key_template_definition softKeyTemplateDefinition[32];
457 } soft_key_template;
458
459 #define SOFT_KEY_SET_RES_MESSAGE 0x0109
460 static const char *soft_key_set_hack = {
461         "\x01\x02\x05\x03\x09\x0a\x0b\x10\x11\x12\x04\x0e\x0d\x00\x00\x00"
462         "\x2d\x01\x2e\x01\x31\x01\x2f\x01\x35\x01\x36\x01\x37\x01\x3c\x01"
463         "\x3d\x01\x3e\x01\x30\x01\x3a\x01\x39\x01\x00\x00\x00\x00\x00\x00"
464         "\x03\x09\x04\x0e\x0d\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
465         "\x2f\x01\x35\x01\x30\x01\x3a\x01\x39\x01\x3f\x01\x00\x00\x00\x00"
466         "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
467         "\x0a\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
468         "\x36\x01\x2e\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
469         "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
470         "\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
471         "\x37\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
472         "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
473         "\x01\x09\x05\x10\x11\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
474         "\x2d\x01\x35\x01\x31\x01\x3c\x01\x3d\x01\x3e\x01\x00\x00\x00\x00"
475         "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
476         "\x00\x09\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
477         "\x00\x00\x35\x01\x30\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
478         "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
479         "\x08\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
480         "\x34\x01\x35\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
481         "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
482         "\x00\x09\x0d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
483         "\x00\x00\x35\x01\x39\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
484         "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
485         "\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
486         "\x00\x00\x35\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
487         "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
488         "\x01\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
489         "\x2d\x01\x35\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
490         "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
491         "\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
492         "\x41\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
493         "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
494 };
495
496 typedef struct soft_key_set_definition {
497         UINT8  softKeyTemplateIndex[16];
498         UINT16 softKeyInfoIndex[16];
499 } soft_key_set_definition;
500
501 typedef struct soft_key_sets {
502         UINT32 softKeySetOffset;
503         UINT32 softKeySetCount;
504         UINT32 totalSoftKeySetCount;
505         soft_key_set_definition softKeySetDefinition[16];
506         UINT32 res;
507 } soft_key_sets;
508
509 #define SELECT_SOFT_KEYS_MESSAGE 0x0110
510 typedef struct select_soft_keys_message {
511         int instance;
512         int reference;
513         int softKeySetIndex;
514         int validKeyMask;
515 } select_soft_keys_message;
516
517 #define CALL_STATE_MESSAGE 0x0111
518 typedef struct call_state_message {
519         int callState;
520         int lineInstance;
521         int callReference;
522 } call_state_message;
523
524 #define ACTIVATE_CALL_PLANE_MESSAGE 0x0116
525 typedef struct activate_call_plane_message {
526         int lineInstance;
527 } activate_call_plane_message;
528
529 /* packet composition */
530 typedef struct {
531         int len;
532         int res;
533         int e;
534         union {
535                 speed_dial_stat_req_message speeddialreq;
536                 register_message reg;
537                 register_ack_message regack;
538                 register_rej_message regrej;
539                 capabilities_res_message caps;
540                 version_res_message version;
541                 button_template_res_message buttontemplate;
542                 displaytext_message displaytext;
543                 definetimedate_message definetimedate;
544                 start_tone_message starttone;
545                 speed_dial_stat_res_message speeddial;
546                 line_state_req_message line;
547                 line_stat_res_message linestat;
548                 soft_key_sets softkeysets;
549                 soft_key_template softkeytemplate;
550                 server_res_message serverres;
551                 set_lamp_message setlamp;
552                 set_ringer_message setringer;
553                 call_state_message callstate;
554                 keypad_button_message keypad;
555                 select_soft_keys_message selectsoftkey;
556                 activate_call_plane_message activatecallplane;
557                 stimulus_message stimulus;
558                 set_speaker_message setspeaker;
559                 call_info_message callinfo;
560                 start_media_transmission_message startmedia;
561                 stop_media_transmission_message stopmedia;
562                 open_recieve_channel_message openrecievechannel;
563                 open_recieve_channel_ack_message openrecievechannelack;
564                 close_recieve_channel_message closerecievechannel;
565         } data;
566 } skinny_req;
567
568 /************************************************************************************/
569 /*                            Asterisk specific globals                             */
570 /************************************************************************************/
571
572 static int skinnydebug = 1;     /* XXX for now, enable debugging default */
573
574 /* a hostname, portnumber, socket and such is usefull for VoIP protocols */
575 static struct sockaddr_in bindaddr;
576 static char ourhost[256];
577 static int ourport;
578 static struct in_addr __ourip;
579 struct ast_hostent ahp; struct hostent *hp;
580 static int skinnysock  = -1;
581 static pthread_t tcp_thread;
582 static pthread_t accept_t;
583 static char context[AST_MAX_EXTENSION] = "default";
584 static char language[MAX_LANGUAGE] = "";
585 static char musicclass[MAX_LANGUAGE] = "";
586 static char cid_num[AST_MAX_EXTENSION] = "";
587 static char cid_name[AST_MAX_EXTENSION] = "";
588 static char linelabel[AST_MAX_EXTENSION] ="";
589 static int nat = 0;
590 static ast_group_t cur_callergroup = 0;
591 static ast_group_t cur_pickupgroup = 0;
592 static int immediate = 0;
593 static int callwaiting = 0;
594 static int callreturn = 0;
595 static int threewaycalling = 0;
596 /* This is for flashhook transfers */
597 static int transfer = 0;
598 static int cancallforward = 0;
599 /* static int busycount = 3;*/
600 static char accountcode[20] = "";
601 static char mailbox[AST_MAX_EXTENSION];
602 static int amaflags = 0;
603 static int callnums = 1;
604
605 #define SUB_REAL 0
606 #define SUB_ALT  1
607 #define MAX_SUBS 2
608
609 #define SKINNY_SPEAKERON 1
610 #define SKINNY_SPEAKEROFF 2
611
612 #define SKINNY_OFFHOOK 1
613 #define SKINNY_ONHOOK 2
614 #define SKINNY_RINGOUT 3
615 #define SKINNY_RINGIN 4
616 #define SKINNY_CONNECTED 5
617 #define SKINNY_BUSY 6
618 #define SKINNY_CONGESTION 7
619 #define SKINNY_HOLD 8
620 #define SKINNY_CALLWAIT 9
621 #define SKINNY_TRANSFER 10
622 #define SKINNY_PARK 11
623 #define SKINNY_PROGRESS 12
624 #define SKINNY_INVALID 14
625
626 #define SKINNY_SILENCE 0
627 #define SKINNY_DIALTONE 33
628 #define SKINNY_BUSYTONE 35
629 #define SKINNY_ALERT 36
630 #define SKINNY_REORDER 37
631 #define SKINNY_CALLWAITTONE 45
632
633 #define SKINNY_LAMP_OFF 1
634 #define SKINNY_LAMP_ON  2
635 #define SKINNY_LAMP_WINK 3
636 #define SKINNY_LAMP_FLASH 4
637 #define SKINNY_LAMP_BLINK 5
638
639 #define SKINNY_RING_OFF 1
640 #define SKINNY_RING_INSIDE 2
641 #define SKINNY_RING_OUTSIDE 3
642 #define SKINNY_RING_FEATURE 4
643
644 #define TYPE_TRUNK 1
645 #define TYPE_LINE 2
646
647 #define STIMULUS_REDIAL 1
648 #define STIMULUS_SPEEDDIAL 2
649 #define STIMULUS_HOLD 3
650 #define STIMULUS_TRANSFER 4
651 #define STIMULUS_FORWARDALL 5
652 #define STIMULUS_FORWARDBUSY 6
653 #define STIMULUS_FORWARDNOANSWER 7
654 #define STIMULUS_DISPLAY 8
655 #define STIMULUS_LINE 9
656 #define STIMULUS_VOICEMAIL 15
657 #define STIMULUS_AUTOANSWER 17
658 #define STIMULUS_CONFERENCE 125
659 #define STIMULUS_CALLPARK 126
660 #define STIMULUS_CALLPICKUP 127
661
662
663 /* Skinny rtp stream modes. Do we really need this? */
664 #define SKINNY_CX_SENDONLY 0
665 #define SKINNY_CX_RECVONLY 1
666 #define SKINNY_CX_SENDRECV 2
667 #define SKINNY_CX_CONF     3
668 #define SKINNY_CX_CONFERENCE 3
669 #define SKINNY_CX_MUTE     4
670 #define SKINNY_CX_INACTIVE 4
671
672 #if 0
673 static char *skinny_cxmodes[] = {
674     "sendonly",
675     "recvonly",
676     "sendrecv",
677     "confrnce",
678     "inactive"
679 };
680 #endif
681
682 /* driver scheduler */
683 static struct sched_context *sched;
684 static struct io_context *io;
685
686 /* usage count and locking */
687 static int usecnt = 0;
688 AST_MUTEX_DEFINE_STATIC(usecnt_lock);
689
690 /* Protect the monitoring thread, so only one process can kill or start it, and not
691    when it's doing something critical. */
692 AST_MUTEX_DEFINE_STATIC(monlock);
693 /* Protect the network socket */
694 AST_MUTEX_DEFINE_STATIC(netlock);
695 /* Protect the session list */
696 AST_MUTEX_DEFINE_STATIC(sessionlock);
697 /* Protect the device list */
698 AST_MUTEX_DEFINE_STATIC(devicelock);
699
700 /* This is the thread for the monitor which checks for input on the channels
701    which are not currently in use.  */
702 static pthread_t monitor_thread = AST_PTHREADT_NULL;
703
704 /* Wait up to 16 seconds for first digit */
705 static int firstdigittimeout = 16000;
706
707 /* How long to wait for following digits */
708 static int gendigittimeout = 8000;
709
710 /* How long to wait for an extra digit, if there is an ambiguous match */
711 static int matchdigittimeout = 3000;
712
713 struct skinny_subchannel {
714         ast_mutex_t lock;
715         unsigned int callid;
716         struct ast_channel *owner;
717         struct skinny_line *parent;
718         struct ast_rtp *rtp;
719         time_t lastouttime;
720         int progress;
721         int ringing;
722         int lastout;
723         int cxmode;
724         int nat;
725         int outgoing;
726         int alreadygone;
727         struct skinny_subchannel *next; 
728 };
729
730 struct skinny_line {
731         ast_mutex_t lock;
732         char name[80];
733         char label[42];                                 /* Label that shows next to the line buttons */
734         struct skinny_subchannel *sub;                  /* pointer to our current connection, channel and stuff */
735         char accountcode[80];
736         char exten[AST_MAX_EXTENSION];                  /* Extention where to start */
737         char context[AST_MAX_EXTENSION];
738         char language[MAX_LANGUAGE];
739         char cid_num[AST_MAX_EXTENSION];                /* Caller*ID */
740         char cid_name[AST_MAX_EXTENSION];               /* Caller*ID */
741         char lastcallerid[AST_MAX_EXTENSION];           /* Last Caller*ID */
742         char call_forward[AST_MAX_EXTENSION];   
743         char mailbox[AST_MAX_EXTENSION];
744         char musicclass[MAX_LANGUAGE];
745         int curtone;                                    /* Current tone being played */
746         ast_group_t callgroup;
747         ast_group_t pickupgroup;
748         int callwaiting;
749         int transfer;
750         int threewaycalling;
751         int cancallforward;
752         int callreturn;
753         int dnd; /* How does this affect callwait?  Do we just deny a skinny_request if we're dnd? */
754         int hascallerid;
755         int hidecallerid;
756         int amaflags;
757         int type;
758         int instance;
759         int group;
760         int needdestroy;
761         int capability;
762         int nonCodecCapability;
763         int onhooktime;
764         int msgstate;           /* voicemail message state */
765         int immediate;
766         int hookstate;
767         int progress;
768         struct skinny_line *next;
769         struct skinny_device *parent;
770 };
771
772 static struct skinny_device {
773         /* A device containing one or more lines */
774         char name[80];
775         char id[16];
776         char version_id[16];    
777         int type;
778         int registered;
779         char model[6];
780         struct sockaddr_in addr;
781         struct in_addr ourip;
782         struct skinny_line *lines;
783         struct ast_ha *ha;
784         struct skinnysession *session;
785         struct skinny_device *next;
786 } *devices = NULL;
787
788 static struct skinnysession {
789         pthread_t t;
790         ast_mutex_t lock;
791         struct sockaddr_in sin;
792         int fd;
793         char inbuf[SKINNY_MAX_PACKET];
794         struct skinny_device *device;
795         struct skinnysession *next;
796 } *sessions = NULL;
797
798 static struct ast_channel *skinny_request(const char *type, int format, void *data, int *cause);
799 static int skinny_call(struct ast_channel *ast, char *dest, int timeout);
800 static int skinny_hangup(struct ast_channel *ast);
801 static int skinny_answer(struct ast_channel *ast);
802 static struct ast_frame *skinny_read(struct ast_channel *ast);
803 static int skinny_write(struct ast_channel *ast, struct ast_frame *frame);
804 static int skinny_indicate(struct ast_channel *ast, int ind);
805 static int skinny_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
806 static int skinny_senddigit(struct ast_channel *ast, char digit);
807
808 static const struct ast_channel_tech skinny_tech = {
809         .type = type,
810         .description = tdesc,
811         .capabilities = AST_FORMAT_ULAW,
812         .properties = AST_CHAN_TP_WANTSJITTER,
813         .requester = skinny_request,
814         .call = skinny_call,
815         .hangup = skinny_hangup,
816         .answer = skinny_answer,
817         .read = skinny_read,
818         .write = skinny_write,
819         .indicate = skinny_indicate,
820         .fixup = skinny_fixup,
821         .send_digit = skinny_senddigit,
822 /*      .bridge = ast_rtp_bridge, */
823 };
824
825 static skinny_req *req_alloc(size_t size)
826 {
827         skinny_req *req;
828         req = malloc(size+12);
829         if (!req) {
830                 return NULL;
831         }       
832         memset(req, 0, size+12);
833         return req;
834 }
835
836 static struct skinny_subchannel *find_subchannel_by_line(struct skinny_line *l)
837 {
838         /* XXX Need to figure out how to determine which sub we want */
839         struct skinny_subchannel *sub = l->sub;
840         return sub;
841 }
842
843 static struct skinny_subchannel *find_subchannel_by_name(char *dest)
844 {
845         struct skinny_line *l;
846         struct skinny_device *d;
847         char line[256];
848         char *at;
849         char *device;
850         
851         strncpy(line, dest, sizeof(line) - 1);
852         at = strchr(line, '@');
853         if (!at) {
854                 ast_log(LOG_NOTICE, "Device '%s' has no @ (at) sign!\n", dest);
855                 return NULL;
856         }
857         *at = '\0';
858         at++;
859         device = at;
860         ast_mutex_lock(&devicelock);
861         d = devices;
862         while(d) {
863                 if (!strcasecmp(d->name, device)) {
864                         if (skinnydebug) {
865                                 ast_verbose("Found device: %s\n", d->name);
866                         }
867                         /* Found the device */
868                         l = d->lines;
869                         while (l) {
870                                 /* Search for the right line */
871                                 if (!strcasecmp(l->name, line)) {
872                                         ast_mutex_unlock(&devicelock);
873                                         return l->sub;
874                                 }
875                                 l = l->next;
876                         }
877                 }
878                 d = d->next;
879         }
880         /* Device not found*/
881         ast_mutex_unlock(&devicelock);
882         return NULL;
883 }
884
885 static int transmit_response(struct skinnysession *s, skinny_req *req)
886 {
887         int res = 0;
888         ast_mutex_lock(&s->lock);
889 #if 0
890         ast_verbose("writing packet type %d (%d bytes) to socket %d\n", req->e, req->len+8, s->fd);
891 #endif
892         res = write(s->fd, req, req->len+8);
893         if (res != req->len+8) {
894                 ast_log(LOG_WARNING, "Transmit: write only sent %d out of %d bytes: %s\n", res, req->len+8, strerror(errno));
895         }
896         ast_mutex_unlock(&s->lock);
897         return 1;
898 }
899
900 /* XXX Do this right */
901 static int convert_cap(int capability)
902 {
903         return 4; /* ulaw (this is not the same as asterisk's '4'  */
904
905 }
906
907 static void transmit_speaker_mode(struct skinnysession *s, int mode)
908 {
909         skinny_req *req;
910
911         req = req_alloc(sizeof(struct set_speaker_message));
912         if (!req) {
913                 ast_log(LOG_ERROR, "Unable to allocate skinny_request, this is bad\n");
914                 return;
915         }
916         req->len = sizeof(set_speaker_message)+4;
917         req->e = SET_SPEAKER_MESSAGE;
918         req->data.setspeaker.mode = mode; 
919         transmit_response(s, req);
920 }
921
922 static void transmit_callstate(struct skinnysession *s, int instance, int state, unsigned callid)
923
924         skinny_req *req;
925         int memsize = sizeof(struct call_state_message);
926
927         req = req_alloc(memsize);
928         if (!req) {
929                 ast_log(LOG_ERROR, "Unable to allocate skinny_request, this is bad\n");
930                 return;
931         }       
932         if (state == SKINNY_ONHOOK) {
933                 transmit_speaker_mode(s, SKINNY_SPEAKEROFF);
934         }
935         req->len = sizeof(call_state_message)+4;
936         req->e = CALL_STATE_MESSAGE;
937         req->data.callstate.callState = state;
938         req->data.callstate.lineInstance = instance;
939         req->data.callstate.callReference = callid;
940         transmit_response(s, req);
941         if (state == SKINNY_OFFHOOK) {
942                 memset(req, 0, memsize);
943                 req->len = sizeof(activate_call_plane_message)+4;
944                 req->e = ACTIVATE_CALL_PLANE_MESSAGE;
945                 req->data.activatecallplane.lineInstance = instance;
946                 transmit_response(s, req);
947         } else if (state == SKINNY_ONHOOK) {
948                 memset(req, 0, memsize);
949                 req->len = sizeof(activate_call_plane_message)+4;
950                 req->e = ACTIVATE_CALL_PLANE_MESSAGE;
951                 req->data.activatecallplane.lineInstance = 0;
952                 transmit_response(s, req);
953                 memset(req, 0, memsize);
954                 req->len = sizeof(close_recieve_channel_message)+4;
955                 req->e = CLOSE_RECIEVE_CHANNEL_MESSAGE;
956                 req->data.closerecievechannel.conferenceId = 0;
957                 req->data.closerecievechannel.partyId = 0;
958                 transmit_response(s, req);
959                 memset(req, 0, memsize);
960                 req->len = sizeof(stop_media_transmission_message)+4;
961                 req->e = STOP_MEDIA_TRANSMISSION_MESSAGE;
962                 req->data.stopmedia.conferenceId = 0;   
963                 req->data.stopmedia.passThruPartyId = 0;
964                 transmit_response(s, req);      
965         }
966 }       
967
968 static void transmit_connect(struct skinnysession *s)
969 {
970         skinny_req *req;
971         struct skinny_line *l = s->device->lines;
972
973         req = req_alloc(sizeof(struct open_recieve_channel_message));
974         if (!req) {
975                 ast_log(LOG_ERROR, "Unable to allocate skinny_request, this is bad\n");
976                 return;
977         }       
978         req->len = sizeof(struct call_info_message);
979         req->e = OPEN_RECIEVE_CHANNEL_MESSAGE;
980         req->data.openrecievechannel.conferenceId = 0;
981         req->data.openrecievechannel.partyId = 0;
982         req->data.openrecievechannel.packets = 20;
983         req->data.openrecievechannel.capability = convert_cap(l->capability); 
984         req->data.openrecievechannel.echo = 0;
985         req->data.openrecievechannel.bitrate = 0;
986         transmit_response(s, req);
987 }       
988
989 static void transmit_tone(struct skinnysession *s, int tone)
990 {
991         skinny_req *req;
992
993         if (tone > 0)
994                 req = req_alloc(sizeof(struct start_tone_message));
995         else 
996                 req = req_alloc(4);
997         if (!req) {
998                 ast_log(LOG_ERROR, "Unable to allocate skinny_request, this is bad\n");
999                 return;
1000         }       
1001         if (tone > 0) {
1002                 req->len = sizeof(start_tone_message)+4;
1003                 req->e = START_TONE_MESSAGE;
1004                 req->data.starttone.tone = tone; 
1005         } else {
1006                 req->len = 4;
1007                 req->e = STOP_TONE_MESSAGE;
1008         }
1009         transmit_response(s, req);
1010 }
1011
1012 #if 0
1013 /* XXX need to properly deal with softkeys */
1014 static void transmit_selectsoftkeys(struct skinnysession *s, int instance, int callid, int softkey)
1015 {
1016         skinny_req *req;
1017         int memsize = sizeof(struct select_soft_keys_message);
1018
1019         req = req_alloc(memsize);
1020         if (!req) {
1021                 ast_log(LOG_ERROR, "Unable to allocate skinny_request, this is bad\n");
1022                 return;
1023         }       
1024         memset(req, 0, memsize);
1025         req->len = sizeof(select_soft_keys_message)+4;
1026         req->e = SELECT_SOFT_KEYS_MESSAGE;
1027         req->data.selectsoftkey.instance = instance;
1028         req->data.selectsoftkey.reference = callid;
1029         req->data.selectsoftkey.softKeySetIndex = softkey;
1030         transmit_response(s, req);
1031 }
1032 #endif
1033
1034 static void transmit_lamp_indication(struct skinnysession *s, int stimulus, int instance, int indication)
1035 {
1036         skinny_req *req;
1037
1038         req = req_alloc(sizeof(struct set_lamp_message));
1039         if (!req) {
1040                 ast_log(LOG_ERROR, "Unable to allocate skinny_request, this is bad\n");
1041                 return;
1042         }       
1043         req->len = sizeof(set_lamp_message)+4;
1044         req->e = SET_LAMP_MESSAGE;
1045         req->data.setlamp.stimulus = stimulus;
1046         req->data.setlamp.stimulusInstance = instance;
1047         req->data.setlamp.deviceStimulus = indication;
1048         transmit_response(s, req);
1049 }
1050
1051 static void transmit_ringer_mode(struct skinnysession *s, int mode)
1052 {
1053         skinny_req *req;
1054
1055         req = req_alloc(sizeof(struct set_ringer_message));
1056         if (!req) {
1057                 ast_log(LOG_ERROR, "Unable to allocate skinny_request, this is bad\n");
1058                 return;
1059         }
1060         req->len = sizeof(set_ringer_message)+4;
1061         req->e = SET_RINGER_MESSAGE; 
1062         req->data.setringer.ringerMode = mode; 
1063         transmit_response(s, req);
1064 }
1065
1066 static void transmit_displaymessage(struct skinnysession *s, char *text)
1067 {
1068         skinny_req *req;
1069
1070         if (text == 0) {
1071                 req = req_alloc(4);
1072                 req->len = 4;
1073                 req->e = CLEAR_DISPLAY_MESSAGE;
1074         } else {
1075                 req = req_alloc(sizeof(struct displaytext_message));
1076
1077                 strncpy(req->data.displaytext.text, text, sizeof(req->data.displaytext.text)-1);
1078                 req->len = sizeof(displaytext_message) + 4;
1079                 req->e = DISPLAYTEXT_MESSAGE;
1080                 if (skinnydebug) {
1081                         ast_verbose("Displaying message '%s'\n", req->data.displaytext.text);
1082                 }
1083         }
1084
1085         if (!req) {
1086                 ast_log(LOG_ERROR, "Unable to allocate skinny_request, this is bad\n");
1087                 return;
1088         }
1089         transmit_response(s, req);
1090 }
1091
1092 static int has_voicemail(struct skinny_line *l)
1093 {
1094         return ast_app_has_voicemail(l->mailbox, NULL);
1095 }
1096
1097
1098 static void do_housekeeping(struct skinnysession *s)
1099 {
1100         struct skinny_subchannel *sub;
1101
1102         sub = find_subchannel_by_line(s->device->lines);
1103         transmit_displaymessage(s, 0);
1104
1105         if (skinnydebug) {
1106                 ast_verbose("Checking for voicemail Skinny %s@%s\n", sub->parent->name, sub->parent->parent->name);
1107         }
1108         if (has_voicemail(sub->parent)) {
1109                 int new;
1110                 int old;
1111                 ast_app_messagecount(sub->parent->mailbox, &new, &old);
1112                 if (skinnydebug) {
1113                         ast_verbose("Skinny %s@%s has voicemail! Yay!\n", sub->parent->name, sub->parent->parent->name);
1114                 }
1115                 transmit_lamp_indication(s, STIMULUS_VOICEMAIL, s->device->lines->instance, SKINNY_LAMP_BLINK);
1116         } else {
1117                 transmit_lamp_indication(s, STIMULUS_VOICEMAIL, s->device->lines->instance, SKINNY_LAMP_OFF);
1118         }
1119
1120 }
1121
1122 /* I do not believe skinny can deal with video. 
1123    Anyone know differently? */
1124 static struct ast_rtp *skinny_get_vrtp_peer(struct ast_channel *chan)
1125 {
1126         return NULL;
1127 }
1128
1129 static struct ast_rtp *skinny_get_rtp_peer(struct ast_channel *chan)
1130 {
1131         struct skinny_subchannel *sub;
1132         sub = chan->tech_pvt;
1133         if (sub && sub->rtp)
1134                 return sub->rtp;
1135         return NULL;
1136 }
1137
1138 static int skinny_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, int codecs)
1139 {
1140         struct skinny_subchannel *sub;
1141         sub = chan->tech_pvt;
1142         if (sub) {
1143                 /* transmit_modify_with_sdp(sub, rtp); @@FIXME@@ if needed */
1144                 return 0;
1145         }
1146         return -1;
1147 }
1148
1149 static struct ast_rtp_protocol skinny_rtp = {
1150         .type = type,
1151         .get_rtp_info = skinny_get_rtp_peer,
1152         .get_vrtp_info =  skinny_get_vrtp_peer,
1153         .set_rtp_peer = skinny_set_rtp_peer,
1154 };
1155
1156 static int skinny_do_debug(int fd, int argc, char *argv[])
1157 {
1158         if (argc != 2)
1159                 return RESULT_SHOWUSAGE;
1160         skinnydebug = 1;
1161         ast_cli(fd, "Skinny Debugging Enabled\n");
1162         return RESULT_SUCCESS;
1163 }
1164
1165 static int skinny_no_debug(int fd, int argc, char *argv[])
1166 {
1167         if (argc != 3)
1168                 return RESULT_SHOWUSAGE;
1169         skinnydebug = 0;
1170         ast_cli(fd, "Skinny Debugging Disabled\n");
1171         return RESULT_SUCCESS;
1172 }
1173
1174 static int skinny_show_lines(int fd, int argc, char *argv[])
1175 {
1176         struct skinny_device  *d;
1177         struct skinny_line *l;
1178         int haslines = 0;
1179         char iabuf[INET_ADDRSTRLEN];
1180         if (argc != 3) 
1181                 return RESULT_SHOWUSAGE;
1182         ast_mutex_lock(&devicelock);
1183         d = devices;
1184         while(d) {
1185                 l = d->lines;
1186                 ast_cli(fd, "Device '%s' at %s\n", d->name, ast_inet_ntoa(iabuf, sizeof(iabuf), d->addr.sin_addr));
1187                 while(l) {
1188                         ast_cli(fd, "   -- '%s@%s in '%s' is %s\n", l->name, d->name, l->context, l->sub->owner ? "active" : "idle");
1189                         haslines = 1;
1190                         l = l->next;
1191                 }
1192                 if (!haslines) {
1193                         ast_cli(fd, "   << No Lines Defined >>     ");
1194                 }
1195                 d = d->next;
1196         }
1197         ast_mutex_unlock(&devicelock);
1198         return RESULT_SUCCESS;
1199 }
1200
1201 static char show_lines_usage[] = 
1202 "Usage: skinny show lines\n"
1203 "       Lists all lines known to the Skinny subsystem.\n";
1204
1205 static char debug_usage[] = 
1206 "Usage: skinny debug\n"
1207 "       Enables dumping of Skinny packets for debugging purposes\n";
1208
1209 static char no_debug_usage[] = 
1210 "Usage: skinny no debug\n"
1211 "       Disables dumping of Skinny packets for debugging purposes\n";
1212
1213 static struct ast_cli_entry  cli_show_lines =
1214         { { "skinny", "show", "lines", NULL }, skinny_show_lines, "Show defined Skinny lines per device", show_lines_usage };
1215 static struct ast_cli_entry  cli_debug =
1216         { { "skinny", "debug", NULL }, skinny_do_debug, "Enable Skinny debugging", debug_usage };
1217 static struct ast_cli_entry  cli_no_debug =
1218         { { "skinny", "no", "debug", NULL }, skinny_no_debug, "Disable Skinny debugging", no_debug_usage };
1219
1220 static struct skinny_device *build_device(char *cat, struct ast_variable *v)
1221 {
1222         struct skinny_device *d;
1223         struct skinny_line *l;
1224         struct skinny_subchannel *sub;
1225         int i=0, y=0;
1226         
1227         d = malloc(sizeof(struct skinny_device));
1228         if (d) {
1229                 memset(d, 0, sizeof(struct skinny_device));
1230                 strncpy(d->name, cat, sizeof(d->name) - 1);
1231                 while(v) {
1232                         if (!strcasecmp(v->name, "host")) {
1233                                         if (ast_get_ip(&d->addr, v->value)) {
1234                                                 free(d);
1235                                                 return NULL;
1236                                         }                               
1237                         } else if (!strcasecmp(v->name, "port")) {
1238                                 d->addr.sin_port = htons(atoi(v->value));
1239                         } else if (!strcasecmp(v->name, "device")) {
1240                         strncpy(d->id, v->value, sizeof(d->id)-1);
1241                         } else if (!strcasecmp(v->name, "permit") || !strcasecmp(v->name, "deny")) {
1242                                 d->ha = ast_append_ha(v->name, v->value, d->ha);
1243                         } else if (!strcasecmp(v->name, "context")) {
1244                                 strncpy(context, v->value, sizeof(context) - 1);
1245                         } else if (!strcasecmp(v->name, "version")) {
1246                                 strncpy(d->version_id, v->value, sizeof(d->version_id) -1); 
1247                         } else if (!strcasecmp(v->name, "nat")) {
1248                                 nat = ast_true(v->value);
1249                 } else if (!strcasecmp(v->name, "model")) {
1250                                 strncpy(d->model, v->value, sizeof(d->model) - 1);
1251                         } else if (!strcasecmp(v->name, "callerid")) {
1252                                 if (!strcasecmp(v->value, "asreceived")) {
1253                                         cid_num[0] = '\0';
1254                                         cid_name[0] = '\0';
1255                                 } else {
1256                                         ast_callerid_split(v->value, cid_name, sizeof(cid_name), cid_num, sizeof(cid_num));
1257                                 }
1258                         } else if (!strcasecmp(v->name, "language")) {
1259                                 strncpy(language, v->value, sizeof(language)-1);
1260                         } else if (!strcasecmp(v->name, "accountcode")) {
1261                                 strncpy(accountcode, v->value, sizeof(accountcode)-1);
1262                         } else if (!strcasecmp(v->name, "amaflags")) {
1263                                 y = ast_cdr_amaflags2int(v->value);
1264                                 if (y < 0) {
1265                                         ast_log(LOG_WARNING, "Invalid AMA flags: %s at line %d\n", v->value, v->lineno);
1266                                 } else {
1267                                 amaflags = y;
1268                                 }
1269                         } else if (!strcasecmp(v->name, "musiconhold")) {
1270                                 strncpy(musicclass, v->value, sizeof(musicclass)-1);
1271                         } else if (!strcasecmp(v->name, "callgroup")) {
1272                                 cur_callergroup = ast_get_group(v->value);
1273                         } else if (!strcasecmp(v->name, "pickupgroup")) {
1274                                 cur_pickupgroup = ast_get_group(v->value);
1275                         } else if (!strcasecmp(v->name, "immediate")) {
1276                                 immediate = ast_true(v->value);
1277                         } else if (!strcasecmp(v->name, "cancallforward")) {
1278                                 cancallforward = ast_true(v->value);
1279                         } else if (!strcasecmp(v->name, "mailbox")) {
1280                                 strncpy(mailbox, v->value, sizeof(mailbox) -1);
1281                         } else if (!strcasecmp(v->name, "callreturn")) {
1282                                 callreturn = ast_true(v->value);
1283                         } else if (!strcasecmp(v->name, "immediate")) {
1284                                 immediate = ast_true(v->value);
1285                         } else if (!strcasecmp(v->name, "callwaiting")) {
1286                                 callwaiting = ast_true(v->value);
1287                         } else if (!strcasecmp(v->name, "transfer")) {
1288                                 transfer = ast_true(v->value);
1289                         } else if (!strcasecmp(v->name, "threewaycalling")) {
1290                                 threewaycalling = ast_true(v->value);
1291                         } else if (!strcasecmp(v->name, "linelabel")) {
1292                                 strncpy(linelabel, v->value, sizeof(linelabel)-1);
1293                         } else if (!strcasecmp(v->name, "trunk") || !strcasecmp(v->name, "line")) {
1294                                 l = malloc(sizeof(struct skinny_line));;
1295                                 if (l) {
1296                                         memset(l, 0, sizeof(struct skinny_line));
1297                                         ast_mutex_init(&l->lock);
1298                                         strncpy(l->name, v->value, sizeof(l->name) - 1);
1299                                         
1300                                         /* XXX Should we check for uniqueness?? XXX */
1301
1302                                         strncpy(l->context, context, sizeof(l->context) - 1);
1303                                         strncpy(l->cid_num, cid_num, sizeof(l->cid_num) - 1);
1304                                         strncpy(l->cid_name, cid_name, sizeof(l->cid_name) - 1);
1305                                         strncpy(l->label, linelabel, sizeof(l->label) - 1);
1306                                         strncpy(l->language, language, sizeof(l->language) - 1);
1307                                         strncpy(l->musicclass, musicclass, sizeof(l->musicclass)-1);
1308                                         strncpy(l->mailbox, mailbox, sizeof(l->mailbox)-1);
1309                                         strncpy(l->mailbox, mailbox, sizeof(l->mailbox)-1);
1310                                         if (!ast_strlen_zero(mailbox)) {
1311                                                 ast_verbose(VERBOSE_PREFIX_3 "Setting mailbox '%s' on %s@%s\n", mailbox, d->name, l->name);
1312                                         }
1313                                 
1314                                         l->msgstate = -1;
1315                                         l->capability = capability;
1316                                         l->parent = d;
1317                                         if (!strcasecmp(v->name, "trunk")) {
1318                                                 l->type = TYPE_TRUNK;
1319                                         } else {
1320                                                 l->type = TYPE_LINE;
1321                                         }
1322                                         l->immediate = immediate;
1323                                         l->callgroup = cur_callergroup;
1324                                         l->pickupgroup = cur_pickupgroup;
1325                                         l->callreturn = callreturn;
1326                                         l->cancallforward = cancallforward;
1327                                         l->callwaiting = callwaiting;
1328                                         l->transfer = transfer; 
1329                                         l->threewaycalling = threewaycalling;
1330                                         l->onhooktime = time(NULL);
1331                                         l->instance = 1;
1332                                         /* ASSUME we're onhook at this point*/
1333                                         l->hookstate = SKINNY_ONHOOK;
1334
1335                                         for (i = 0; i < MAX_SUBS; i++) {
1336                                                 sub = malloc(sizeof(struct skinny_subchannel));
1337                                                 if (sub) {
1338                                                         ast_verbose(VERBOSE_PREFIX_3 "Allocating Skinny subchannel '%d' on %s@%s\n", i, l->name, d->name);
1339                                                         memset(sub, 0, sizeof(struct skinny_subchannel));
1340                                                         ast_mutex_init(&sub->lock);
1341                                                         sub->parent = l;
1342                                                         /* Make a call*ID */
1343                                                         sub->callid = callnums;
1344                                                         callnums++;
1345                                                         sub->cxmode = SKINNY_CX_INACTIVE;
1346                                                         sub->nat = nat;
1347                                                         sub->next = l->sub;
1348                                                         l->sub = sub;
1349                                                 } else {
1350                                                         /* XXX Should find a way to clean up our memory */
1351                                                         ast_log(LOG_WARNING, "Out of memory allocating subchannel");
1352                                                         return NULL;
1353                                                 }
1354                                         }
1355                                         l->next = d->lines;
1356                                         d->lines = l;                   
1357                                 } else {
1358                                         /* XXX Should find a way to clean up our memory */
1359                                         ast_log(LOG_WARNING, "Out of memory allocating line");
1360                                         return NULL;
1361                                 }
1362                         } else {
1363                                 ast_log(LOG_WARNING, "Don't know keyword '%s' at line %d\n", v->name, v->lineno);
1364                         }
1365                         v = v->next;
1366                 }
1367         
1368                 if (!d->lines) {
1369                         ast_log(LOG_ERROR, "A Skinny device must have at least one line!\n");
1370                         return NULL;
1371                 }
1372
1373                 if (d->addr.sin_addr.s_addr && !ntohs(d->addr.sin_port))
1374                         d->addr.sin_port = htons(DEFAULT_SKINNY_PORT);
1375                 if (d->addr.sin_addr.s_addr) {
1376                         if (ast_ouraddrfor(&d->addr.sin_addr, &d->ourip)) {
1377                                 memcpy(&d->ourip, &__ourip, sizeof(d->ourip));
1378                         }
1379                 } else {
1380                         memcpy(&d->ourip, &__ourip, sizeof(d->ourip));
1381                 }
1382         }
1383         return d;
1384 }
1385
1386 static int skinny_register(skinny_req *req, struct skinnysession *s)
1387 {
1388         struct skinny_device *d;
1389         
1390         ast_mutex_lock(&devicelock);
1391         d = devices;
1392         while (d) {
1393                 if (!strcasecmp(req->data.reg.name, d->id)) {
1394                         /* XXX Deal with IP authentication */
1395                         s->device = d;
1396                         d->type = req->data.reg.type;
1397                         if (ast_strlen_zero(d->version_id)) {
1398                                 strncpy(d->version_id, version_id, sizeof(d->version_id) - 1);
1399                         }
1400                         d->registered = 1;
1401                         d->session = s;
1402                         break;
1403                 }
1404                 d = d->next;
1405         }
1406         ast_mutex_unlock(&devicelock);
1407
1408         if (!d)
1409                 return 0;
1410         
1411         return 1;
1412 }               
1413
1414 static void start_rtp(struct skinny_subchannel *sub)
1415 {
1416                 ast_mutex_lock(&sub->lock);
1417                 /* Allocate the RTP */
1418                 sub->rtp = ast_rtp_new(sched, io, 1, 0);
1419                 if (sub->rtp && sub->owner)
1420                         sub->owner->fds[0] = ast_rtp_fd(sub->rtp);
1421                 if (sub->rtp)
1422                         ast_rtp_setnat(sub->rtp, sub->nat);
1423                 
1424                 /* Create the RTP connection */
1425                 transmit_connect(sub->parent->parent->session);
1426                 ast_mutex_unlock(&sub->lock);
1427 }
1428
1429
1430 static void *skinny_ss(void *data)
1431 {
1432         struct ast_channel *chan = data;
1433         struct skinny_subchannel *sub = chan->tech_pvt;
1434         struct skinny_line *l = sub->parent;
1435         struct skinnysession *s = l->parent->session;
1436         char exten[AST_MAX_EXTENSION] = "";
1437         int len = 0;
1438         int timeout = firstdigittimeout;
1439         int res;
1440         int getforward=0;
1441     
1442         if (option_verbose > 2) {
1443                 ast_verbose( VERBOSE_PREFIX_3 "Starting simple switch on '%s@%s'\n", l->name, l->parent->name);
1444         }
1445         while(len < AST_MAX_EXTENSION-1) {
1446         res = ast_waitfordigit(chan, timeout);
1447         timeout = 0;
1448         if (res < 0) {
1449                 if (skinnydebug) {
1450                         ast_verbose("Skinny(%s@%s): waitfordigit returned < 0\n", l->name, l->parent->name);
1451                 }
1452                 ast_indicate(chan, -1);
1453                 ast_hangup(chan);
1454                 return NULL;
1455         } else if (res)  {
1456             exten[len++]=res;
1457             exten[len] = '\0';
1458         }
1459         if (!ast_ignore_pattern(chan->context, exten)) {
1460                         transmit_tone(s, SKINNY_SILENCE);
1461         } 
1462         if (ast_exists_extension(chan, chan->context, exten, 1, l->cid_num)) {
1463             if (!res || !ast_matchmore_extension(chan, chan->context, exten, 1, l->cid_num)) {
1464                 if (getforward) {
1465                     /* Record this as the forwarding extension */
1466                     strncpy(l->call_forward, exten, sizeof(l->call_forward) - 1); 
1467                     if (option_verbose > 2) {
1468                         ast_verbose(VERBOSE_PREFIX_3 "Setting call forward to '%s' on channel %s\n", 
1469                                 l->call_forward, chan->name);
1470                     }
1471                     transmit_tone(s, SKINNY_DIALTONE); 
1472                     if (res) {
1473                             break;
1474                     }
1475                     usleep(500000);
1476                     ast_indicate(chan, -1);
1477                     sleep(1);
1478                     memset(exten, 0, sizeof(exten));
1479                     transmit_tone(s, SKINNY_DIALTONE); 
1480                     len = 0;
1481                     getforward = 0;
1482                 } else  {
1483                     strncpy(chan->exten, exten, sizeof(chan->exten)-1);
1484                     if (!ast_strlen_zero(l->cid_num)) {
1485                         if (!l->hidecallerid)
1486                             chan->cid.cid_num = strdup(l->cid_num);
1487                         chan->cid.cid_ani = strdup(l->cid_num);
1488                     }
1489                     ast_setstate(chan, AST_STATE_RING);
1490                     res = ast_pbx_run(chan);
1491                     if (res) {
1492                         ast_log(LOG_WARNING, "PBX exited non-zero\n");
1493                                                 transmit_tone(s, SKINNY_REORDER); 
1494                     }
1495                     return NULL;
1496                 }
1497             } else {
1498                 /* It's a match, but they just typed a digit, and there is an ambiguous match,
1499                    so just set the timeout to matchdigittimeout and wait some more */
1500                 timeout = matchdigittimeout;
1501             }
1502         } else if (res == 0) {
1503             ast_log(LOG_DEBUG, "Not enough digits (and no ambiguous match)...\n");
1504                 transmit_tone(s, SKINNY_REORDER); 
1505             ast_hangup(chan);
1506             return NULL;
1507         } else if (l->callwaiting && !strcmp(exten, "*70")) {
1508             if (option_verbose > 2) {
1509                 ast_verbose(VERBOSE_PREFIX_3 "Disabling call waiting on %s\n", chan->name);
1510             }
1511             /* Disable call waiting if enabled */
1512             l->callwaiting = 0;
1513             transmit_tone(s, SKINNY_DIALTONE);
1514                         len = 0;
1515             memset(exten, 0, sizeof(exten));
1516             timeout = firstdigittimeout;
1517                 
1518         } else if (!strcmp(exten,ast_pickup_ext())) {
1519             /* Scan all channels and see if any there
1520              * ringing channqels with that have call groups
1521              * that equal this channels pickup group  
1522              */
1523             if (ast_pickup_call(chan)) {
1524                 ast_log(LOG_WARNING, "No call pickup possible...\n");
1525                                 transmit_tone(s, SKINNY_REORDER);
1526             }
1527             ast_hangup(chan);
1528             return NULL;
1529             
1530         } else if (!l->hidecallerid && !strcmp(exten, "*67")) {
1531             if (option_verbose > 2) {
1532                 ast_verbose(VERBOSE_PREFIX_3 "Disabling Caller*ID on %s\n", chan->name);
1533             }
1534             /* Disable Caller*ID if enabled */
1535             l->hidecallerid = 1;
1536             if (chan->cid.cid_num)
1537                 free(chan->cid.cid_num);
1538             chan->cid.cid_num = NULL;
1539             
1540                         if (chan->cid.cid_name)
1541                 free(chan->cid.cid_name);
1542             chan->cid.cid_name = NULL;
1543                         
1544             transmit_tone(s, SKINNY_DIALTONE);
1545             len = 0;
1546             memset(exten, 0, sizeof(exten));
1547             timeout = firstdigittimeout;
1548         } else if (l->callreturn && !strcmp(exten, "*69")) {
1549             res = 0;
1550             if (!ast_strlen_zero(l->lastcallerid)) {
1551                 res = ast_say_digit_str(chan, l->lastcallerid, "", chan->language);
1552             }
1553             if (!res) {
1554                 transmit_tone(s, SKINNY_DIALTONE);
1555                         }
1556             break;
1557         } else if (!strcmp(exten, "*78")) {
1558             /* Do not disturb */
1559             if (option_verbose > 2) {
1560                 ast_verbose(VERBOSE_PREFIX_3 "Enabled DND on channel %s\n", chan->name);
1561             }
1562             transmit_tone(s, SKINNY_DIALTONE);
1563             l->dnd = 1;
1564             getforward = 0;
1565             memset(exten, 0, sizeof(exten));
1566             len = 0;
1567         } else if (!strcmp(exten, "*79")) {
1568             /* Do not disturb */
1569             if (option_verbose > 2) {
1570                 ast_verbose(VERBOSE_PREFIX_3 "Disabled DND on channel %s\n", chan->name);
1571             }
1572                         transmit_tone(s, SKINNY_DIALTONE);
1573             l->dnd = 0;
1574             getforward = 0;
1575             memset(exten, 0, sizeof(exten));
1576             len = 0;
1577         } else if (l->cancallforward && !strcmp(exten, "*72")) {
1578             transmit_tone(s, SKINNY_DIALTONE);
1579             getforward = 1;
1580             memset(exten, 0, sizeof(exten));
1581             len = 0;
1582         } else if (l->cancallforward && !strcmp(exten, "*73")) {
1583             if (option_verbose > 2) {
1584                 ast_verbose(VERBOSE_PREFIX_3 "Cancelling call forwarding on channel %s\n", chan->name);
1585             }
1586             transmit_tone(s, SKINNY_DIALTONE); 
1587             memset(l->call_forward, 0, sizeof(l->call_forward));
1588             getforward = 0;
1589             memset(exten, 0, sizeof(exten));
1590             len = 0;
1591         } else if (!strcmp(exten, ast_parking_ext()) && 
1592                     sub->next->owner &&
1593                     ast_bridged_channel(sub->next->owner)) {
1594             /* This is a three way call, the main call being a real channel, 
1595                 and we're parking the first call. */
1596             ast_masq_park_call(ast_bridged_channel(sub->next->owner), chan, 0, NULL);
1597             if (option_verbose > 2) {
1598                 ast_verbose(VERBOSE_PREFIX_3 "Parking call to '%s'\n", chan->name);
1599             }
1600             break;
1601         } else if (!ast_strlen_zero(l->lastcallerid) && !strcmp(exten, "*60")) {
1602             if (option_verbose > 2) {
1603                 ast_verbose(VERBOSE_PREFIX_3 "Blacklisting number %s\n", l->lastcallerid);
1604             }
1605             res = ast_db_put("blacklist", l->lastcallerid, "1");
1606             if (!res) {
1607                 transmit_tone(s, SKINNY_DIALTONE);              
1608                 memset(exten, 0, sizeof(exten));
1609                 len = 0;
1610             }
1611         } else if (l->hidecallerid && !strcmp(exten, "*82")) {
1612             if (option_verbose > 2) {
1613                 ast_verbose(VERBOSE_PREFIX_3 "Enabling Caller*ID on %s\n", chan->name);
1614             }
1615             /* Enable Caller*ID if enabled */
1616             l->hidecallerid = 0;
1617             if (chan->cid.cid_num)
1618                 free(chan->cid.cid_num);
1619             if (!ast_strlen_zero(l->cid_num))
1620                 chan->cid.cid_num = strdup(l->cid_num);
1621
1622             if (chan->cid.cid_name)
1623                 free(chan->cid.cid_name);
1624             if (!ast_strlen_zero(l->cid_name))
1625                 chan->cid.cid_name = strdup(l->cid_name);
1626
1627             transmit_tone(s, SKINNY_DIALTONE);
1628             len = 0;
1629             memset(exten, 0, sizeof(exten));
1630             timeout = firstdigittimeout;
1631         } else if (!ast_canmatch_extension(chan, chan->context, exten, 1, chan->cid.cid_num) &&
1632                         ((exten[0] != '*') || (!ast_strlen_zero(exten) > 2))) {
1633             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);
1634             transmit_tone(s, SKINNY_REORDER); 
1635                         sleep(3); /* hang out for 3 seconds to let congestion play */
1636                         break;
1637         }
1638         if (!timeout)
1639             timeout = gendigittimeout;
1640         if (len && !ast_ignore_pattern(chan->context, exten))
1641                         ast_indicate(chan, -1);
1642     }
1643         ast_hangup(chan);
1644         return NULL;
1645 }
1646
1647
1648
1649 static int skinny_call(struct ast_channel *ast, char *dest, int timeout)
1650 {
1651         int res = 0;
1652         int tone = 0;
1653         struct skinny_line *l;
1654         struct skinny_subchannel *sub;
1655         struct skinnysession *session;
1656         
1657         sub = ast->tech_pvt;
1658         l = sub->parent;
1659         session = l->parent->session;
1660
1661         if (!l->parent->registered) {
1662                 ast_log(LOG_ERROR, "Device not registered, cannot call %s\n", dest);
1663                 return -1;
1664         }
1665         
1666         if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
1667                 ast_log(LOG_WARNING, "skinny_call called on %s, neither down nor reserved\n", ast->name);
1668                 return -1;
1669         }
1670
1671         if (skinnydebug) {
1672                 ast_verbose(VERBOSE_PREFIX_3 "skinny_call(%s)\n", ast->name);
1673         }
1674
1675         if (l->dnd) {
1676                 ast_queue_control(ast, AST_CONTROL_BUSY);
1677                 return -1;
1678         }
1679    
1680         switch (l->hookstate) {
1681         case SKINNY_OFFHOOK:
1682             tone = SKINNY_CALLWAITTONE;
1683             break;
1684         case SKINNY_ONHOOK:
1685                         tone = SKINNY_ALERT;
1686                         break;
1687         default:
1688             ast_log(LOG_ERROR, "Don't know how to deal with hookstate %d\n", l->hookstate);
1689             break;
1690         }
1691
1692         transmit_lamp_indication(session, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK);
1693         transmit_ringer_mode(session, SKINNY_RING_INSIDE);
1694         
1695         if (ast->cid.cid_num){ 
1696
1697                 char ciddisplay[41] = "";
1698
1699                 /* We'll assume that if it is 10 numbers, it is a standard NANPA number
1700                    Why? Because I am bloody American, and I'm bigoted that way. */
1701
1702                 if (strlen(ast->cid.cid_num) == 10) {
1703
1704                         strcat (ciddisplay, "(");
1705                         strncat (ciddisplay, ast->cid.cid_num,3);
1706                         strcat (ciddisplay, ") ");
1707
1708                         strncat (ciddisplay, &ast->cid.cid_num[3],3);
1709                         strcat (ciddisplay,"-");
1710                         strncat (ciddisplay, &ast->cid.cid_num[6],4);
1711
1712                         strncat (ciddisplay, "      ", 6);
1713
1714                         strncat (ciddisplay, ast->cid.cid_name,17);
1715                 } else {
1716                         if (strlen(ast->cid.cid_num) < 40) {
1717                                 strncpy(ciddisplay,ast->cid.cid_num,strlen(ast->cid.cid_num));
1718                                 strcat (ciddisplay," -- ");
1719                                 
1720                                 if (sizeof(ast->cid.cid_name) > (40 - (strlen(ast->cid.cid_num) + 4))) {
1721                                         strncat (ciddisplay, ast->cid.cid_name, (40 - (strlen(ast->cid.cid_num) + 4)));
1722                                 } else {
1723                                         strncat (ciddisplay, ast->cid.cid_name, strlen(ast->cid.cid_name));
1724                                 }
1725                         } else {
1726                                 strncpy(ciddisplay, "Number too long!", 15);
1727                         }
1728                 }
1729
1730                 if (skinnydebug) {
1731                         ast_verbose("Trying to send: '%s'\n",ciddisplay);
1732                 }
1733
1734                 transmit_displaymessage(session, ciddisplay);
1735         }else{
1736                 transmit_displaymessage(session, "Unknown Name");
1737         }
1738
1739         transmit_tone(session, tone);
1740         transmit_callstate(session, l->instance, SKINNY_RINGIN, sub->callid);
1741
1742         /* XXX need to set the prompt */
1743         /* XXX need to deal with softkeys */
1744
1745         ast_setstate(ast, AST_STATE_RINGING);
1746         ast_queue_control(ast, AST_CONTROL_RINGING);
1747
1748         sub->outgoing = 1;
1749         if (l->type == TYPE_LINE) {
1750         if (!sub->rtp) {
1751             start_rtp(sub);
1752         } else {
1753                 /* do/should we need to anything if there already is an RTP allocated? */
1754         }
1755
1756         } else {
1757                 ast_log(LOG_ERROR, "I don't know how to dial on trunks, yet\n");
1758                 res = -1;
1759         }
1760         return res;
1761 }
1762
1763
1764 static int skinny_hangup(struct ast_channel *ast)
1765 {
1766     struct skinny_subchannel *sub = ast->tech_pvt;
1767     struct skinny_line *l = sub->parent;
1768     struct skinnysession *s = l->parent->session;
1769
1770     if (skinnydebug) {
1771         ast_verbose("skinny_hangup(%s) on %s@%s\n", ast->name, l->name, l->parent->name);
1772     }
1773     if (!ast->tech_pvt) {
1774         ast_log(LOG_DEBUG, "Asked to hangup channel not connected\n");
1775         return 0;
1776     }
1777
1778     if (l->parent->registered) {
1779         if ((sub->parent->type = TYPE_LINE) && (sub->parent->hookstate == SKINNY_OFFHOOK)) {
1780                         sub->parent->hookstate = SKINNY_ONHOOK;
1781                         transmit_callstate(s, l->instance, SKINNY_ONHOOK, sub->callid);
1782                         transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF);
1783                         transmit_speaker_mode(s, SKINNY_SPEAKEROFF); 
1784                 } else if ((sub->parent->type = TYPE_LINE) && (sub->parent->hookstate == SKINNY_ONHOOK)) {
1785                         transmit_callstate(s, l->instance, SKINNY_ONHOOK, sub->callid);
1786                         transmit_speaker_mode(s, SKINNY_SPEAKEROFF); 
1787                         transmit_ringer_mode(s, SKINNY_RING_OFF);
1788                         transmit_tone(s, SKINNY_SILENCE);
1789                         transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF);
1790                         do_housekeeping(s);
1791                 } 
1792     }
1793     ast_mutex_lock(&sub->lock);
1794     sub->owner = NULL;
1795     ast->tech_pvt = NULL;
1796     sub->alreadygone = 0;
1797     sub->outgoing = 0;
1798     if (sub->rtp) {
1799         ast_rtp_destroy(sub->rtp);
1800         sub->rtp = NULL;
1801     }
1802     ast_mutex_unlock(&sub->lock);
1803     return 0;
1804 }
1805
1806 static int skinny_answer(struct ast_channel *ast)
1807 {
1808     int res = 0;
1809     struct skinny_subchannel *sub = ast->tech_pvt;
1810     struct skinny_line *l = sub->parent;
1811     sub->cxmode = SKINNY_CX_SENDRECV;
1812     if (!sub->rtp) {
1813                 start_rtp(sub);
1814     } 
1815     ast_verbose("skinny_answer(%s) on %s@%s-%d\n", ast->name, l->name, l->parent->name, sub->callid);
1816     if (ast->_state != AST_STATE_UP) {
1817         ast_setstate(ast, AST_STATE_UP);
1818     }
1819     return res;
1820 }
1821
1822 static struct ast_frame *skinny_rtp_read(struct skinny_subchannel *sub)
1823 {
1824         /* Retrieve audio/etc from channel.  Assumes sub->lock is already held. */
1825         struct ast_frame *f;
1826         f = ast_rtp_read(sub->rtp);
1827         if (sub->owner) {
1828                 /* We already hold the channel lock */
1829                 if (f->frametype == AST_FRAME_VOICE) {
1830                         if (f->subclass != sub->owner->nativeformats) {
1831                                 ast_log(LOG_DEBUG, "Oooh, format changed to %d\n", f->subclass);
1832                                 sub->owner->nativeformats = f->subclass;
1833                                 ast_set_read_format(sub->owner, sub->owner->readformat);
1834                                 ast_set_write_format(sub->owner, sub->owner->writeformat);
1835                         }
1836                 }
1837         }
1838         return f;
1839 }
1840
1841 static struct ast_frame  *skinny_read(struct ast_channel *ast)
1842 {
1843         struct ast_frame *fr;
1844         struct skinny_subchannel *sub = ast->tech_pvt;
1845         ast_mutex_lock(&sub->lock);
1846         fr = skinny_rtp_read(sub);
1847         ast_mutex_unlock(&sub->lock);
1848         return fr;
1849 }
1850
1851 static int skinny_write(struct ast_channel *ast, struct ast_frame *frame)
1852 {
1853         struct skinny_subchannel *sub = ast->tech_pvt;
1854         int res = 0;
1855         if (frame->frametype != AST_FRAME_VOICE) {
1856                 if (frame->frametype == AST_FRAME_IMAGE)
1857                         return 0;
1858                 else {
1859                         ast_log(LOG_WARNING, "Can't send %d type frames with skinny_write\n", frame->frametype);
1860                         return 0;
1861                 }
1862         } else {
1863                 if (!(frame->subclass & ast->nativeformats)) {
1864                         ast_log(LOG_WARNING, "Asked to transmit frame type %d, while native formats is %d (read/write = %d/%d)\n",
1865                                 frame->subclass, ast->nativeformats, ast->readformat, ast->writeformat);
1866                         return -1;
1867                 }
1868         }
1869         if (sub) {
1870                 ast_mutex_lock(&sub->lock);
1871                 if (sub->rtp) {
1872                         res =  ast_rtp_write(sub->rtp, frame);
1873                 }
1874                 ast_mutex_unlock(&sub->lock);
1875         }
1876         return res;
1877 }
1878
1879 static int skinny_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
1880 {
1881         struct skinny_subchannel *sub = newchan->tech_pvt;
1882         ast_log(LOG_NOTICE, "skinny_fixup(%s, %s)\n", oldchan->name, newchan->name);
1883         if (sub->owner != oldchan) {
1884                 ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, sub->owner);
1885                 return -1;
1886         }
1887         sub->owner = newchan;
1888         return 0;
1889 }
1890
1891 static int skinny_senddigit(struct ast_channel *ast, char digit)
1892 {
1893 #if 0
1894         struct skinny_subchannel *sub = ast->tech_pvt;
1895         int tmp;
1896         /* not right */
1897         sprintf(tmp, "%d", digit);  
1898         transmit_tone(sub->parent->parent->session, digit);
1899 #endif
1900         return -1;
1901 }
1902
1903 static char *control2str(int ind) {
1904     switch (ind) {
1905         case AST_CONTROL_HANGUP:
1906             return "Other end has hungup";
1907         case AST_CONTROL_RING:
1908             return "Local ring";
1909         case AST_CONTROL_RINGING:
1910             return "Remote end is ringing";
1911         case AST_CONTROL_ANSWER:
1912             return "Remote end has answered";
1913         case AST_CONTROL_BUSY:
1914             return "Remote end is busy";
1915         case AST_CONTROL_TAKEOFFHOOK:
1916             return "Make it go off hook";
1917         case AST_CONTROL_OFFHOOK:
1918             return "Line is off hook";
1919         case AST_CONTROL_CONGESTION:
1920             return "Congestion (circuits busy)";
1921         case AST_CONTROL_FLASH:
1922             return "Flash hook";
1923         case AST_CONTROL_WINK:
1924             return "Wink";
1925         case AST_CONTROL_OPTION:
1926             return "Set a low-level option";
1927         case AST_CONTROL_RADIO_KEY:
1928             return "Key Radio";
1929         case AST_CONTROL_RADIO_UNKEY:
1930             return "Un-Key Radio";
1931                 case -1:
1932                         return "Stop tone";
1933     }
1934     return "UNKNOWN";
1935 }
1936
1937
1938 static int skinny_indicate(struct ast_channel *ast, int ind)
1939 {
1940         struct skinny_subchannel *sub = ast->tech_pvt;
1941         struct skinny_line *l = sub->parent;
1942         struct skinnysession *s = l->parent->session;
1943
1944         if (skinnydebug) {
1945                 ast_verbose(VERBOSE_PREFIX_3 "Asked to indicate '%s' condition on channel %s\n", control2str(ind), ast->name);
1946         }
1947         switch(ind) {
1948         case AST_CONTROL_RINGING:
1949                 if (ast->_state != AST_STATE_UP) {
1950                         if (!sub->progress) {           
1951                                 transmit_tone(s, SKINNY_ALERT);
1952                                 transmit_callstate(s, l->instance, SKINNY_RINGOUT, sub->callid);
1953                                 sub->ringing = 1;
1954                                 break;
1955                         }
1956                 }
1957                 return -1;
1958         case AST_CONTROL_BUSY:
1959                 if (ast->_state != AST_STATE_UP) {              
1960                         transmit_tone(s, SKINNY_BUSYTONE);
1961                         transmit_callstate(s, l->instance, SKINNY_BUSY, sub->callid);
1962                         sub->alreadygone = 1;
1963                         ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
1964                         break;
1965                 }
1966                 return -1;
1967         case AST_CONTROL_CONGESTION:
1968                 if (ast->_state != AST_STATE_UP) {              
1969                         transmit_tone(s, SKINNY_REORDER);
1970                         transmit_callstate(s, l->instance, SKINNY_CONGESTION, sub->callid);
1971                         sub->alreadygone = 1;
1972                         ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
1973                         break;
1974                 }
1975                 return -1;
1976         case AST_CONTROL_PROGRESS:
1977                 if ((ast->_state != AST_STATE_UP) && !sub->progress && !sub->outgoing) {
1978                         transmit_callstate(s, l->instance, SKINNY_PROGRESS, sub->callid);
1979                         sub->progress = 1;
1980                         break;
1981                 }
1982                 return -1;  
1983         case -1:
1984                 transmit_tone(s, SKINNY_SILENCE);
1985                 break;          
1986         case AST_CONTROL_PROCEEDING:
1987                 break;
1988         default:
1989                 ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", ind);
1990                 return -1;
1991         }
1992         return 0;
1993 }
1994         
1995 static struct ast_channel *skinny_new(struct skinny_subchannel *sub, int state)
1996 {
1997         struct ast_channel *tmp;
1998         struct skinny_line *l = sub->parent;
1999         int fmt;
2000         l = sub->parent;
2001         tmp = ast_channel_alloc(1);
2002         if (tmp) {
2003                 tmp->tech = &skinny_tech;
2004                 tmp->nativeformats = l->capability;
2005                 if (!tmp->nativeformats)
2006                         tmp->nativeformats = capability;
2007                 fmt = ast_best_codec(tmp->nativeformats);
2008                 snprintf(tmp->name, sizeof(tmp->name), "Skinny/%s@%s-%d", l->name, l->parent->name, sub->callid);
2009                 if (sub->rtp)
2010                         tmp->fds[0] = ast_rtp_fd(sub->rtp);
2011                 tmp->type = type;
2012                 ast_setstate(tmp, state);
2013                 if (state == AST_STATE_RING)
2014                         tmp->rings = 1;
2015                 tmp->writeformat = fmt;
2016                 tmp->rawwriteformat = fmt;
2017                 tmp->readformat = fmt;
2018                 tmp->rawreadformat = fmt;
2019                 tmp->tech_pvt = sub;
2020                 if (!ast_strlen_zero(l->language))
2021                         strncpy(tmp->language, l->language, sizeof(tmp->language)-1);
2022                 if (!ast_strlen_zero(l->accountcode))
2023                         strncpy(tmp->accountcode, l->accountcode, sizeof(tmp->accountcode)-1);
2024                 if (l->amaflags)
2025                         tmp->amaflags = l->amaflags;
2026                 sub->owner = tmp;
2027                 ast_mutex_lock(&usecnt_lock);
2028                 usecnt++;
2029                 ast_mutex_unlock(&usecnt_lock);
2030                 ast_update_use_count();
2031                 tmp->callgroup = l->callgroup;
2032                 tmp->pickupgroup = l->pickupgroup;
2033                 strncpy(tmp->call_forward, l->call_forward, sizeof(tmp->call_forward) - 1);
2034                 strncpy(tmp->context, l->context, sizeof(tmp->context)-1);
2035                 strncpy(tmp->exten,l->exten, sizeof(tmp->exten)-1);
2036                 if (!ast_strlen_zero(l->cid_num)) {
2037                         tmp->cid.cid_num = strdup(l->cid_num);
2038                 }
2039                 if (!ast_strlen_zero(l->cid_name)) {
2040                         tmp->cid.cid_name = strdup(l->cid_name);
2041                 }
2042                 tmp->priority = 1;
2043                 if (state != AST_STATE_DOWN) {
2044                         if (ast_pbx_start(tmp)) {
2045                                 ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
2046                                 ast_hangup(tmp);
2047                                 tmp = NULL;
2048                         }
2049                 }
2050         } else {
2051                 ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
2052     }
2053
2054         return tmp;
2055 }
2056
2057 static int handle_message(skinny_req *req, struct skinnysession *s)
2058 {
2059         struct skinny_subchannel *sub;
2060         struct ast_channel *c;
2061         struct ast_frame f = { 0, };    
2062         struct sockaddr_in sin;
2063         struct sockaddr_in us;
2064         struct skinny_line *lines;
2065         char name[16];
2066         char addr[4];
2067         char d;
2068         int digit;
2069         int res=0;
2070         int speedDialNum;
2071         int lineNumber;
2072         int stimulus;
2073         int stimulusInstance;
2074         int status;
2075         int port;
2076         int i;
2077         time_t timer;
2078         struct tm *cmtime;
2079         pthread_t t;
2080         
2081         if ( (!s->device) && (req->e != REGISTER_MESSAGE && req->e != ALARM_MESSAGE)) {
2082                 ast_log(LOG_WARNING, "Client sent message #%d without first registering.\n", req->e);
2083                 free(req);
2084                 return 0;
2085         }
2086
2087         switch(req->e)  {
2088         case ALARM_MESSAGE:
2089                 /* no response necessary */
2090                 break;
2091         case REGISTER_MESSAGE:
2092                 if (skinnydebug) {
2093                         ast_verbose("Device %s is attempting to register\n", req->data.reg.name);
2094                 }
2095                 res = skinny_register(req, s);  
2096                 if (!res) {
2097                         ast_log(LOG_ERROR, "Rejecting Device %s: Device not found\n", req->data.reg.name);
2098                         memcpy(&name, req->data.reg.name, sizeof(req->data.reg.name));
2099                         memset(req, 0, sizeof(skinny_req));
2100                         req->len = sizeof(register_rej_message)+4;
2101                         req->e = REGISTER_REJ_MESSAGE;
2102                         snprintf(req->data.regrej.errMsg, sizeof(req->data.regrej.errMsg), "No Authority: %s", name);
2103                         transmit_response(s, req);
2104                         break;
2105                 }
2106                 if (option_verbose > 2) {
2107                         ast_verbose(VERBOSE_PREFIX_3 "Device '%s' successfuly registered\n", s->device->name); 
2108                 }
2109                 memset(req, 0, SKINNY_MAX_PACKET);
2110                 req->len = sizeof(register_ack_message)+4;
2111                 req->e = REGISTER_ACK_MESSAGE;
2112                 req->data.regack.res[0] = '0';
2113                 req->data.regack.res[1] = '\0';
2114                 req->data.regack.keepAlive = keep_alive;
2115                 strncpy(req->data.regack.dateTemplate, date_format, sizeof(req->data.regack.dateTemplate) - 1); 
2116                 req->data.regack.res2[0] = '0';
2117                 req->data.regack.res2[1] = '\0';
2118                 req->data.regack.secondaryKeepAlive = keep_alive;
2119                 transmit_response(s, req);
2120                 if (skinnydebug) {
2121                         ast_verbose("Requesting capabilities\n");
2122                 }
2123                 memset(req, 0, SKINNY_MAX_PACKET);
2124                 req->len = 4;
2125                 req->e = CAPABILITIES_REQ_MESSAGE;
2126                 transmit_response(s, req);
2127                 break;
2128         case UNREGISTER_MESSAGE:
2129                 /* XXX Acutally unregister the device */
2130                 break;
2131         case IP_PORT_MESSAGE:
2132                 /* no response necessary */
2133                 break;
2134         case STIMULUS_MESSAGE:
2135                 stimulus = req->data.stimulus.stimulus;
2136                 stimulusInstance = req->data.stimulus.stimulusInstance;
2137                 
2138                 switch(stimulus) {
2139                 case STIMULUS_REDIAL:
2140                         /* If we can keep an array of dialed frames we can implement a quick 
2141                            and dirty redial, feeding the frames we last got into the queue
2142                            function */
2143
2144                         if (skinnydebug) {
2145                                 ast_verbose("Recieved Stimulus: Redial(%d)\n", stimulusInstance);
2146                         }
2147
2148
2149                         break;
2150                 case STIMULUS_SPEEDDIAL:
2151                         if (skinnydebug) {
2152                                 ast_verbose("Recieved Stimulus: SpeedDial(%d)\n", stimulusInstance);
2153                         }
2154                         break;
2155                 case STIMULUS_HOLD:
2156                         /* start moh? set RTP to 0.0.0.0? */
2157                         if (skinnydebug) {
2158                                 ast_verbose("Recieved Stimulus: Hold(%d)\n", stimulusInstance);
2159                         }
2160                         break;
2161                 case STIMULUS_TRANSFER:
2162                         if (skinnydebug) {
2163                                 ast_verbose("Recieved Stimulus: Transfer(%d)\n", stimulusInstance);
2164                         }
2165                         transmit_tone(s, SKINNY_DIALTONE);
2166                                 
2167                         /* figure out how to transfer */
2168
2169                         break;
2170                 case STIMULUS_CONFERENCE:
2171                         if (skinnydebug) {
2172                                 ast_verbose("Recieved Stimulus: Transfer(%d)\n", stimulusInstance);
2173                         }
2174                         transmit_tone(s, SKINNY_DIALTONE);
2175
2176                         /* figure out how to bridge n' stuff */
2177                                 
2178
2179                         break;
2180                 case STIMULUS_VOICEMAIL:
2181                         if (skinnydebug) {
2182                                 ast_verbose("Recieved Stimulus: Voicemail(%d)\n", stimulusInstance);
2183                         }
2184                                 
2185                         /* Dial Voicemail */
2186
2187                         break;
2188                 case STIMULUS_CALLPARK:
2189                         if (skinnydebug) {
2190                                 ast_verbose("Recieved Stimulus: Park Call(%d)\n", stimulusInstance);
2191                         }
2192
2193                         break;
2194                 case STIMULUS_FORWARDALL:
2195                         /* Do not disturb */
2196                         transmit_tone(s, SKINNY_DIALTONE);
2197                         if(s->device->lines->dnd != 0){
2198                                 if (option_verbose > 2) {
2199                                         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);
2200                                 }
2201                                 s->device->lines->dnd = 0;
2202                                 transmit_lamp_indication(s, STIMULUS_FORWARDALL, 1, SKINNY_LAMP_ON);
2203                         }else{
2204                                 if (option_verbose > 2) {
2205                                         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);
2206                                 }
2207                                 s->device->lines->dnd = 1;
2208                                 transmit_lamp_indication(s, STIMULUS_FORWARDALL, 1, SKINNY_LAMP_OFF);
2209                         }
2210                         break;
2211                 case STIMULUS_FORWARDBUSY:
2212                 case STIMULUS_FORWARDNOANSWER:
2213                         /* Gonna be fun, not */
2214                         if (skinnydebug) {
2215                                 ast_verbose("Recieved Stimulus: Forward (%d)\n", stimulusInstance);
2216                         }
2217                         break;
2218                 case STIMULUS_DISPLAY:
2219                         /* Not sure what this is */
2220                         if (skinnydebug) {
2221                                 ast_verbose("Recieved Stimulus: Display(%d)\n", stimulusInstance);
2222                         }
2223                         break;
2224                 case STIMULUS_LINE:
2225                         if (skinnydebug) {
2226                                 ast_verbose("Recieved Stimulus: Line(%d)\n", stimulusInstance);
2227                         }               
2228                         sub = find_subchannel_by_line(s->device->lines);
2229                         /* turn the speaker on */
2230                         transmit_speaker_mode(s, 1);  
2231                 break;
2232                 default:
2233                         ast_verbose("RECEIVED UNKNOWN STIMULUS:  %d(%d)\n", stimulus, stimulusInstance);                        
2234                         break;
2235                 }
2236                 break;
2237         case VERSION_REQ_MESSAGE:
2238                 if (skinnydebug) {
2239                         ast_verbose("Version Request\n");
2240                 }
2241                 memset(req, 0, SKINNY_MAX_PACKET);
2242                 req->len = sizeof(version_res_message)+4;
2243                 req->e = VERSION_RES_MESSAGE;
2244                 snprintf(req->data.version.version, sizeof(req->data.version.version), s->device->version_id);
2245                 transmit_response(s, req);
2246                 break;
2247         case SERVER_REQUEST_MESSAGE:
2248                 if (skinnydebug) {
2249                         ast_verbose("Recieved Server Request\n");
2250                 }
2251                 memset(req, 0, SKINNY_MAX_PACKET);
2252                 req->len = sizeof(server_res_message)+4;
2253                 req->e = SERVER_RES_MESSAGE;
2254                 memcpy(req->data.serverres.server[0].serverName, ourhost, 
2255                                 sizeof(req->data.serverres.server[0].serverName));
2256                 req->data.serverres.serverListenPort[0] = ourport;
2257                 req->data.serverres.serverIpAddr[0] = __ourip.s_addr;
2258                 transmit_response(s, req);      
2259                 break;
2260         case BUTTON_TEMPLATE_REQ_MESSAGE:
2261                 if (skinnydebug) {
2262                         ast_verbose("Buttontemplate requested\n");
2263                 }
2264                 memset(req, 0, SKINNY_MAX_PACKET);
2265                 req->e = BUTTON_TEMPLATE_RES_MESSAGE;   
2266                 /* XXX Less of a hack, more of a kludge now */
2267                 sub = find_subchannel_by_line(s->device->lines);
2268                 req->len = sizeof(button_template_res_message)+4;
2269                 if (!strcmp(s->device->model,"30VIP")){
2270                         req->data.buttontemplate.buttonOffset = 0;
2271                         req->data.buttontemplate.buttonCount  = 30;
2272                         req->data.buttontemplate.totalButtonCount = 30;
2273                         memcpy(req->data.buttontemplate.definition,
2274                                 thirtyvip_button_definition_hack, 
2275                                 sizeof(req->data.buttontemplate.definition));
2276                         if(skinnydebug){                        
2277                                 ast_verbose("Sending 30VIP template to %s@%s (%s)\n",sub->parent->name, sub->parent->parent->name, s->device->model);
2278                         }
2279                 }else if(!strcmp(s->device->model,"12SP")){
2280                         req->data.buttontemplate.buttonOffset = 0;
2281                         req->data.buttontemplate.buttonCount  = 12;
2282                         req->data.buttontemplate.totalButtonCount = 12;
2283                         memcpy(req->data.buttontemplate.definition,
2284                                 twelvesp_button_definition_hack, 
2285                                 sizeof(req->data.buttontemplate.definition));
2286                         if(skinnydebug){                        
2287                                 ast_verbose("Sending 12SP template to %s@%s (%s)\n",sub->parent->name, sub->parent->parent->name, s->device->model);
2288                         }               
2289                 }else{
2290                         req->data.buttontemplate.buttonOffset = 0;
2291                         req->data.buttontemplate.buttonCount  = 12;
2292                         req->data.buttontemplate.totalButtonCount = 12;
2293                         memcpy(req->data.buttontemplate.definition,
2294                                 twelvesp_button_definition_hack, 
2295                                 sizeof(req->data.buttontemplate.definition));
2296                         if(skinnydebug){                        
2297                                 ast_verbose("Sending default template to %s@%s (%s)\n",sub->parent->name, sub->parent->parent->name, s->device->model);
2298                         }
2299
2300                 }
2301                 
2302                 transmit_response(s, req);
2303                 break;
2304         case SOFT_KEY_SET_REQ_MESSAGE:
2305                 if (skinnydebug)  {
2306                         ast_verbose("Received SoftKeySetReq\n");
2307                 }
2308                 memset(req, 0, SKINNY_MAX_PACKET);
2309                 req->len = sizeof(soft_key_sets)+4;
2310                 req->e = SOFT_KEY_SET_RES_MESSAGE;
2311                 req->data.softkeysets.softKeySetOffset          = 0;
2312                 req->data.softkeysets.softKeySetCount           = 11;
2313                 req->data.softkeysets.totalSoftKeySetCount  = 11;       
2314                 /* XXX Wicked hack XXX */
2315                 memcpy(req->data.softkeysets.softKeySetDefinition, 
2316                            soft_key_set_hack, 
2317                            sizeof(req->data.softkeysets.softKeySetDefinition));
2318                 transmit_response(s,req);
2319                 break;
2320         case SOFT_KEY_TEMPLATE_REQ_MESSAGE:
2321                 if (skinnydebug) {
2322                         ast_verbose("Recieved SoftKey Template Request\n");
2323                 }
2324                 memset(req, 0, SKINNY_MAX_PACKET);
2325                 req->len = sizeof(soft_key_template)+4;
2326                 req->e = SOFT_KEY_TEMPLATE_RES_MESSAGE;
2327                 req->data.softkeytemplate.softKeyOffset         = 0;
2328                 req->data.softkeytemplate.softKeyCount          = 21;
2329                 req->data.softkeytemplate.totalSoftKeyCount = 21; 
2330                 /* XXX Another wicked hack XXX */
2331                 memcpy(req->data.softkeytemplate.softKeyTemplateDefinition,
2332                            soft_key_template_hack,
2333                            sizeof(req->data.softkeytemplate.softKeyTemplateDefinition));
2334                 transmit_response(s,req);
2335                 break;
2336         case TIME_DATE_REQ_MESSAGE:
2337                 if (skinnydebug) {
2338                         ast_verbose("Received Time/Date Request\n");
2339                 }
2340                 memset(req, 0, SKINNY_MAX_PACKET);
2341                 req->len = sizeof(definetimedate_message)+4;
2342                 req->e = DEFINETIMEDATE_MESSAGE;
2343                 timer=time(NULL);
2344                 cmtime = localtime(&timer);
2345                 req->data.definetimedate.year = cmtime->tm_year+1900;
2346                 req->data.definetimedate.month = cmtime->tm_mon+1;
2347                 req->data.definetimedate.dayofweek = cmtime->tm_wday;
2348                 req->data.definetimedate.day = cmtime->tm_mday;
2349                 req->data.definetimedate.hour = cmtime->tm_hour;
2350                 req->data.definetimedate.minute = cmtime->tm_min;
2351                 req->data.definetimedate.seconds = cmtime->tm_sec;
2352                 transmit_response(s, req);
2353                 break;
2354         case SPEED_DIAL_STAT_REQ_MESSAGE:
2355                 /* Not really sure how Speed Dial's are different than the 
2356                    Softkey templates */
2357                 speedDialNum = req->data.speeddialreq.speedDialNumber;
2358                 memset(req, 0, SKINNY_MAX_PACKET);
2359                 req->len = sizeof(speed_dial_stat_res_message)+4;
2360                 req->e = SPEED_DIAL_STAT_RES_MESSAGE;
2361 #if 0
2362                 /* XXX Do this right XXX */     
2363                 /* If the redial function works the way I think it will, a modification of it
2364                    can work here was well. Yikes. */
2365                 req->data.speeddialreq.speedDialNumber = speedDialNum;
2366                 snprintf(req->data.speeddial.speedDialDirNumber, sizeof(req->data.speeddial.speedDialDirNumber), "31337");
2367                 snprintf(req->data.speeddial.speedDialDisplayName,  sizeof(req->data.speeddial.speedDialDisplayName),"Asterisk Rules!");
2368 #endif  
2369                 transmit_response(s, req);
2370                 break;
2371         case LINE_STATE_REQ_MESSAGE:
2372                 lineNumber = req->data.line.lineNumber;
2373                 if (skinnydebug) {
2374                         ast_verbose("Received LineStateReq\n");
2375                 }
2376                 memset(req, 0, SKINNY_MAX_PACKET);
2377                 req->len = sizeof(line_stat_res_message)+4;
2378                 req->e = LINE_STAT_RES_MESSAGE; 
2379                 sub = find_subchannel_by_line(s->device->lines);
2380                 if (!sub) {
2381                         ast_log(LOG_NOTICE, "No available lines on: %s\n", s->device->name);
2382                         return 0;
2383                 }
2384                 lines = sub->parent;
2385                 ast_mutex_lock(&devicelock);
2386                 for (i=1; i < lineNumber; i++) {
2387                         lines = lines->next;
2388                 }
2389                 ast_mutex_unlock(&devicelock);
2390                 req->data.linestat.linenumber = lineNumber;             
2391                 memcpy(req->data.linestat.lineDirNumber, lines->name,
2392                                 sizeof(req->data.linestat.lineDirNumber));
2393                 memcpy(req->data.linestat.lineDisplayName, lines->label,
2394                                 sizeof(req->data.linestat.lineDisplayName)); 
2395                 transmit_response(s,req);
2396                 break;
2397         case CAPABILITIES_RES_MESSAGE:
2398                 if (skinnydebug) {
2399                         ast_verbose("Received CapabilitiesRes\n");      
2400                 }
2401                 /* XXX process the capabilites  */
2402                 break;
2403         case KEEP_ALIVE_MESSAGE:
2404                 memset(req, 0, SKINNY_MAX_PACKET);
2405                 req->len = 4;
2406                 req->e = KEEP_ALIVE_ACK_MESSAGE;
2407                 transmit_response(s, req);
2408                 do_housekeeping(s);
2409
2410                 break;
2411         case OFFHOOK_MESSAGE:
2412                 transmit_ringer_mode(s,SKINNY_RING_OFF);
2413                 transmit_lamp_indication(s, STIMULUS_LINE, s->device->lines->instance, SKINNY_LAMP_ON); 
2414                 
2415                 sub = find_subchannel_by_line(s->device->lines);
2416                 if (!sub) {
2417                         ast_log(LOG_NOTICE, "No available lines on: %s\n", s->device->name);
2418                         return 0;
2419                 }
2420                 sub->parent->hookstate = SKINNY_OFFHOOK;
2421                 
2422                 if (sub->outgoing) {
2423                         transmit_callstate(s, s->device->lines->instance, SKINNY_OFFHOOK, sub->callid);
2424                         transmit_tone(s, SKINNY_SILENCE);
2425                         ast_setstate(sub->owner, AST_STATE_UP);
2426                         /* XXX select the appropriate soft key here */
2427                 } else {        
2428                         if (!sub->owner) {      
2429                                 transmit_callstate(s, s->device->lines->instance, SKINNY_OFFHOOK, sub->callid);
2430                                 if (skinnydebug) {
2431                                         ast_verbose("Attempting to Clear display on Skinny %s@%s\n",sub->parent->name, sub->parent->parent->name);
2432                                 }
2433                                 transmit_displaymessage(s, 0); /* clear display */ 
2434                                 transmit_tone(s, SKINNY_DIALTONE);
2435                                 c = skinny_new(sub, AST_STATE_DOWN);                    
2436                                 if(c) {
2437                                         /* start switch */
2438                                         if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
2439                                                 ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
2440                                                 ast_hangup(c);
2441                                         }
2442                                 } else {
2443                                         ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", sub->parent->name, s->device->name);
2444                                 }
2445                                 
2446                         } else {
2447                                 ast_log(LOG_DEBUG, "Current sub [%s] already has owner\n", sub->owner->name);
2448                         }
2449                 }
2450                 break;
2451         case ONHOOK_MESSAGE:
2452                 sub = find_subchannel_by_line(s->device->lines);
2453                 if (sub->parent->hookstate == SKINNY_ONHOOK) {
2454                         /* Somthing else already put us back on hook */ 
2455                         break;
2456                 }
2457                 sub->cxmode = SKINNY_CX_RECVONLY;
2458                 sub->parent->hookstate = SKINNY_ONHOOK;
2459                 transmit_callstate(s, s->device->lines->instance, sub->parent->hookstate,sub->callid);
2460             if (skinnydebug) {
2461                         ast_verbose("Skinny %s@%s went on hook\n",sub->parent->name, sub->parent->parent->name);
2462             }
2463             if (sub->parent->transfer && (sub->owner && sub->next->owner) && ((!sub->outgoing) || (!sub->next->outgoing))) {
2464                         /* We're allowed to transfer, we have two active calls and */
2465                         /* we made at least one of the calls.  Let's try and transfer */
2466 #if 0
2467              if ((res = attempt_transfer(p)) < 0) {
2468                                  if (p->sub->next->owner) {
2469                                         sub->next->alreadygone = 1;
2470                                         ast_queue_hangup(sub->next->owner,1);
2471                                 }
2472                          } else if (res) {
2473                                     ast_log(LOG_WARNING, "Transfer attempt failed\n");
2474                                         return -1;
2475              }
2476 #endif
2477         } else {
2478            /* Hangup the current call */
2479            /* If there is another active call, skinny_hangup will ring the phone with the other call */
2480            if (sub->owner) {
2481                sub->alreadygone = 1;
2482                ast_queue_hangup(sub->owner);
2483            } else {
2484                ast_log(LOG_WARNING, "Skinny(%s@%s-%d) channel already destroyed\n", 
2485                            sub->parent->name, sub->parent->parent->name, sub->callid);
2486            }
2487         }
2488
2489         if ((sub->parent->hookstate == SKINNY_ONHOOK) && (!sub->next->rtp)) {
2490                         do_housekeeping(s);
2491         }
2492
2493         break;
2494         case KEYPAD_BUTTON_MESSAGE:
2495                 digit = req->data.keypad.button;
2496                 if (skinnydebug) {
2497                         ast_verbose("Collected digit: [%d]\n", digit);
2498                 }
2499                 f.frametype = AST_FRAME_DTMF;
2500                 if (digit == 14) {
2501                         d = '*';
2502                 } else if (digit == 15) {
2503                         d = '#';
2504                 } else if (digit >=0 && digit <= 9) {
2505                         d = '0' + digit;
2506                 } else {
2507                         /* digit=10-13 (A,B,C,D ?), or
2508                          * digit is bad value
2509                          * 
2510                          * probably should not end up here, but set
2511                          * value for backward compatibility, and log
2512                          * a warning.
2513                          */
2514                         d = '0' + digit;
2515                         ast_log(LOG_WARNING, "Unsupported digit %d\n", digit);
2516                 }
2517                 f.subclass  = d;  
2518                 f.src = "skinny";
2519
2520                 sub = find_subchannel_by_line(s->device->lines);                
2521
2522                 if (sub->owner) {
2523                         /* XXX MUST queue this frame to all subs in threeway call if threeway call is active */
2524                         ast_queue_frame(sub->owner, &f);
2525                         if (sub->next->owner) {
2526                                 ast_queue_frame(sub->next->owner, &f);
2527                         }
2528                 } else {
2529                         ast_verbose("No owner: %s\n", s->device->lines->name);
2530                 }
2531                 break;
2532         case OPEN_RECIEVE_CHANNEL_ACK_MESSAGE:
2533                 ast_verbose("Recieved Open Recieve Channel Ack\n");
2534                 status = req->data.openrecievechannelack.status;
2535                 if (status) {
2536                         ast_log(LOG_ERROR, "Open Recieve Channel Failure\n");
2537                         break;
2538                 }
2539                 memcpy(addr, req->data.openrecievechannelack.ipAddr, sizeof(addr));
2540                 port = req->data.openrecievechannelack.port;
2541                                 
2542                 sin.sin_family = AF_INET;
2543                 /* I smell endian problems */
2544                 memcpy(&sin.sin_addr, addr, sizeof(sin.sin_addr));  
2545                 sin.sin_port = htons(port);
2546         
2547                 sub = find_subchannel_by_line(s->device->lines);
2548                 if (sub->rtp) {
2549                         ast_rtp_set_peer(sub->rtp, &sin);
2550                         ast_rtp_get_us(sub->rtp, &us);  
2551                 } else {
2552                         ast_log(LOG_ERROR, "No RTP structure, this is very bad\n");
2553                         break;
2554                 }
2555                 memset(req, 0, SKINNY_MAX_PACKET);
2556                 req->len = sizeof(start_media_transmission_message)+4;
2557                 req->e = START_MEDIA_TRANSMISSION_MESSAGE;
2558                 req->data.startmedia.conferenceId = 0;
2559                 req->data.startmedia.passThruPartyId = 0;
2560                 memcpy(req->data.startmedia.remoteIp, &s->device->ourip, 4); /* Endian? */
2561                 req->data.startmedia.remotePort = ntohs(us.sin_port);
2562                 req->data.startmedia.packetSize = 20;
2563                 req->data.startmedia.payloadType = convert_cap(s->device->lines->capability);
2564                 req->data.startmedia.qualifier.precedence = 127;
2565                 req->data.startmedia.qualifier.vad = 0;
2566                 req->data.startmedia.qualifier.packets = 0;
2567                 req->data.startmedia.qualifier.bitRate = 0;
2568                 transmit_response(s, req);
2569                 break;  
2570         default:
2571                 ast_verbose("RECEIVED UNKNOWN MESSAGE TYPE:  %x\n", req->e);
2572                 break;
2573         }
2574
2575         free(req);
2576         return 1;
2577
2578 }
2579
2580 static void destroy_session(struct skinnysession *s)
2581 {
2582         struct skinnysession *cur, *prev = NULL;
2583         ast_mutex_lock(&sessionlock);
2584         cur = sessions;
2585         while(cur) {
2586                 if (cur == s)
2587                         break;
2588                 prev = cur;
2589                 cur = cur->next;
2590         }
2591         if (cur) {
2592                 if (prev)
2593                         prev->next = cur->next;
2594                 else
2595                         sessions = cur->next;
2596                 if (s->fd > -1)
2597                         close(s->fd);
2598                 ast_mutex_destroy(&s->lock);
2599                 free(s);
2600         } else
2601                 ast_log(LOG_WARNING, "Trying to delete nonexistent session %p?\n", s);
2602         ast_mutex_unlock(&sessionlock);
2603 }
2604
2605 static int get_input(struct skinnysession *s)  
2606 {  
2607         int res;  
2608         int dlen = 0;
2609         struct pollfd fds[1];  
2610  
2611         fds[0].fd = s->fd;
2612         fds[0].events = POLLIN;
2613         res = poll(fds, 1, -1);
2614  
2615         if (res < 0) {
2616                 ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
2617         } else if (res > 0) {
2618                 memset(s->inbuf,0,sizeof(s->inbuf));
2619                 res = read(s->fd, s->inbuf, 4);
2620                 if (res != 4) {
2621                         ast_log(LOG_WARNING, "Skinny Client sent less data than expected.\n");
2622                         return -1;
2623                 }
2624                 dlen = *(int *)s->inbuf;
2625                 if (dlen+8 > sizeof(s->inbuf))
2626                         dlen = sizeof(s->inbuf) - 8;
2627                 *(int *)s->inbuf = dlen;
2628                 res = read(s->fd, s->inbuf+4, dlen+4);
2629                 ast_mutex_unlock(&s->lock);
2630                 if (res != (dlen+4)) {
2631                         ast_log(LOG_WARNING, "Skinny Client sent less data than expected.\n");
2632                         return -1;
2633                 } 
2634         }  
2635         return res;  
2636 }   
2637
2638 static skinny_req *skinny_req_parse(struct skinnysession *s)
2639 {
2640         skinny_req *req;
2641         
2642         req = malloc(SKINNY_MAX_PACKET);
2643         if (!req) {
2644                 ast_log(LOG_ERROR, "Unable to allocate skinny_request, this is bad\n");
2645                 return NULL;
2646         }
2647         memset(req, 0, sizeof(skinny_req));
2648         /* +8 to account for reserved and length fields */
2649         memcpy(req, s->inbuf, *(int*)(s->inbuf)+8); 
2650         if (req->e < 0) {
2651                 ast_log(LOG_ERROR, "Event Message is NULL from socket %d, This is bad\n", s->fd);
2652                 free(req);
2653                 return NULL;
2654         }
2655         return req;
2656 }
2657
2658 static void *skinny_session(void *data)
2659 {
2660         int res;
2661         skinny_req *req;
2662         struct skinnysession *s = data;
2663         char iabuf[INET_ADDRSTRLEN];
2664         
2665         ast_verbose(VERBOSE_PREFIX_3 "Starting Skinny session from %s\n",  ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
2666
2667         for (;;) {
2668                 res = 0;
2669                 res = get_input(s);
2670                 if (res < 0)
2671                         break;
2672                 req = skinny_req_parse(s);
2673                 if (!req) {
2674                         return NULL;
2675                 }
2676                 res = handle_message(req, s);
2677                 if (res < 0) {
2678                         destroy_session(s);
2679                         return NULL;
2680                 } 
2681         }
2682         ast_log(LOG_NOTICE, "Skinny Session returned: %s\n", strerror(errno));
2683         destroy_session(s);
2684         return 0;
2685 }
2686
2687 static void *accept_thread(void *ignore)
2688 {
2689         int as;
2690         struct sockaddr_in sin;
2691         int sinlen;
2692         struct skinnysession *s;
2693         struct protoent *p;
2694         int arg = 1;
2695         pthread_attr_t attr;
2696
2697         pthread_attr_init(&attr);
2698         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
2699
2700         for (;;) {
2701                 sinlen = sizeof(sin);
2702                 as = accept(skinnysock, (struct sockaddr *)&sin, &sinlen);
2703                 if (as < 0) {
2704                         ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
2705                         continue;
2706                 }
2707                 p = getprotobyname("tcp");
2708                 if(p) {
2709                         if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
2710                                 ast_log(LOG_WARNING, "Failed to set Skinny tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
2711                         }
2712                 }
2713                 s = malloc(sizeof(struct skinnysession));
2714                 if (!s) {
2715                         ast_log(LOG_WARNING, "Failed to allocate Skinny session: %s\n", strerror(errno));
2716                         continue;
2717                 } 
2718                 memset(s, 0, sizeof(struct skinnysession));
2719                 memcpy(&s->sin, &sin, sizeof(sin));
2720                 ast_mutex_init(&s->lock);
2721                 s->fd = as;
2722                 ast_mutex_lock(&sessionlock);
2723                 s->next = sessions;
2724                 sessions = s;
2725                 ast_mutex_unlock(&sessionlock);
2726                 
2727                 if (ast_pthread_create(&tcp_thread, NULL, skinny_session, s)) {
2728                         destroy_session(s);
2729                 }
2730         }
2731         
2732         
2733         if (skinnydebug) {
2734                 ast_verbose("killing accept thread\n");
2735         }
2736         close(as);
2737         return 0;
2738 }
2739
2740 static void *do_monitor(void *data)
2741 {
2742         int res;
2743
2744         /* This thread monitors all the interfaces which are not yet in use
2745            (and thus do not have a separate thread) indefinitely */
2746         /* From here on out, we die whenever asked */
2747         for(;;) {
2748                 pthread_testcancel();
2749                 /* Wait for sched or io */
2750                 res = ast_sched_wait(sched);
2751                 if ((res < 0) || (res > 1000)) {
2752                         res = 1000;
2753                 }
2754                 res = ast_io_wait(io, res);
2755                 ast_mutex_lock(&monlock);
2756                 if (res >= 0) {
2757                         ast_sched_runq(sched);
2758                 }
2759                 ast_mutex_unlock(&monlock);
2760         }
2761         /* Never reached */
2762         return NULL;
2763         
2764 }
2765
2766 static int restart_monitor(void)
2767 {
2768         /* If we're supposed to be stopped -- stay stopped */
2769         if (monitor_thread == AST_PTHREADT_STOP)
2770                 return 0;
2771         if (ast_mutex_lock(&monlock)) {
2772                 ast_log(LOG_WARNING, "Unable to lock monitor\n");
2773                 return -1;
2774         }
2775         if (monitor_thread == pthread_self()) {
2776                 ast_mutex_unlock(&monlock);
2777                 ast_log(LOG_WARNING, "Cannot kill myself\n");
2778                 return -1;
2779         }
2780         if (monitor_thread != AST_PTHREADT_NULL) {
2781                 /* Wake up the thread */
2782                 pthread_kill(monitor_thread, SIGURG);
2783         } else {
2784                 /* Start a new monitor */
2785                 if (ast_pthread_create(&monitor_thread, NULL, do_monitor, NULL) < 0) {
2786                         ast_mutex_unlock(&monlock);
2787                         ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
2788                         return -1;
2789                 }
2790         }
2791         ast_mutex_unlock(&monlock);
2792         return 0;
2793 }
2794
2795 static struct ast_channel *skinny_request(const char *type, int format, void *data, int *cause)
2796 {
2797         int oldformat;
2798         struct skinny_subchannel *sub;
2799         struct ast_channel *tmpc = NULL;
2800         char tmp[256];
2801         char *dest = data;
2802
2803         oldformat = format;
2804         format &= capability;
2805         if (!format) {
2806                 ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", format);
2807                 return NULL;
2808         }
2809         
2810         strncpy(tmp, dest, sizeof(tmp) - 1);
2811         if (ast_strlen_zero(tmp)) {
2812                 ast_log(LOG_NOTICE, "Skinny channels require a device\n");
2813                 return NULL;
2814         }
2815         
2816         sub = find_subchannel_by_name(tmp);  
2817         if (!sub) {
2818                 ast_log(LOG_NOTICE, "No available lines on: %s\n", dest);
2819                 return NULL;
2820         }
2821         
2822         if (option_verbose > 2) {
2823                 ast_verbose(VERBOSE_PREFIX_3 "skinny_request(%s)\n", tmp);
2824                 ast_verbose(VERBOSE_PREFIX_3 "Skinny cw: %d, dnd: %d, so: %d, sno: %d\n", 
2825                         sub->parent->callwaiting, sub->parent->dnd, sub->owner ? 1 : 0, sub->next->owner ? 1: 0);
2826         }
2827         tmpc = skinny_new(sub->owner ? sub->next : sub, AST_STATE_DOWN);
2828         if (!tmpc) {
2829                 ast_log(LOG_WARNING, "Unable to make channel for '%s'\n", tmp);
2830         }
2831         restart_monitor();
2832
2833         /* and finish */        
2834         return tmpc;
2835 }
2836
2837 static int reload_config(void)
2838 {
2839         int on=1;
2840         struct ast_config *cfg;
2841         struct ast_variable *v;
2842         int format;
2843         char *cat;
2844         char iabuf[INET_ADDRSTRLEN];
2845         struct skinny_device *d;
2846         int oldport = ntohs(bindaddr.sin_port);
2847
2848 #if 0           
2849         hp = ast_gethostbyname(ourhost, &ahp);
2850         if (!hp) {
2851                 ast_log(LOG_WARNING, "Unable to get hostname, Skinny disabled\n");
2852                 return 0;
2853         }
2854 #endif
2855         cfg = ast_config_load(config);
2856
2857         /* We *must* have a config file otherwise stop immediately */
2858         if (!cfg) {
2859                 ast_log(LOG_NOTICE, "Unable to load config %s, Skinny disabled\n", config);
2860                 return 0;
2861         }
2862
2863         /* load the general section */
2864         memset(&bindaddr, 0, sizeof(bindaddr));
2865         v = ast_variable_browse(cfg, "general");
2866         while(v) {
2867                 /* Create the interface list */
2868                 if (!strcasecmp(v->name, "bindaddr")) {
2869                         if (!(hp = ast_gethostbyname(v->value, &ahp))) {
2870                                 ast_log(LOG_WARNING, "Invalid address: %s\n", v->value);
2871                         } else {
2872                                 memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr));
2873                         }
2874                 } else if (!strcasecmp(v->name, "keepAlive")) {
2875                         keep_alive = atoi(v->value);            
2876                 } else if (!strcasecmp(v->name, "dateFormat")) {
2877                         strncpy(date_format, v->value, sizeof(date_format) - 1);        
2878                 } else if (!strcasecmp(v->name, "allow")) {
2879                         format = ast_getformatbyname(v->value);
2880                         if (format < 1) 
2881                                 ast_log(LOG_WARNING, "Cannot allow unknown format '%s'\n", v->value);
2882                         else
2883                                 capability |= format;
2884                 } else if (!strcasecmp(v->name, "disallow")) {
2885                         format = ast_getformatbyname(v->value);
2886                         if (format < 1) 
2887                                 ast_log(LOG_WARNING, "Cannot disallow unknown format '%s'\n", v->value);
2888                         else
2889                                 capability &= ~format;
2890                 } else if (!strcasecmp(v->name, "port")) {
2891                         if (sscanf(v->value, "%d", &ourport) == 1) {
2892                                 bindaddr.sin_port = htons(ourport);
2893                         } else {
2894                                 ast_log(LOG_WARNING, "Invalid port number '%s' at line %d of %s\n", v->value, v->lineno, config);
2895                         }
2896                 }
2897                 v = v->next;
2898         }
2899
2900         if (ntohl(bindaddr.sin_addr.s_addr)) {
2901                 memcpy(&__ourip, &bindaddr.sin_addr, sizeof(__ourip));
2902         } else {
2903                 hp = ast_gethostbyname(ourhost, &ahp);
2904                 if (!hp) {
2905                         ast_log(LOG_WARNING, "Unable to get our IP address, Skinny disabled\n");
2906                         ast_config_destroy(cfg);
2907                         return 0;
2908                 }
2909                 memcpy(&__ourip, hp->h_addr, sizeof(__ourip));
2910         }
2911         if (!ntohs(bindaddr.sin_port)) {
2912                 bindaddr.sin_port = ntohs(DEFAULT_SKINNY_PORT);
2913         }
2914         bindaddr.sin_family = AF_INET;
2915         
2916         /* load the device sections */
2917         cat = ast_category_browse(cfg, NULL);
2918         while(cat) {
2919                 if (strcasecmp(cat, "general")) {
2920                         d = build_device(cat, ast_variable_browse(cfg, cat));
2921                         if (d) {
2922                                 if (option_verbose > 2) {
2923                                         ast_verbose(VERBOSE_PREFIX_3 "Added device '%s'\n", d->name);
2924                 }
2925                                 ast_mutex_lock(&devicelock);
2926                                 d->next = devices;
2927                                 devices = d;
2928                                 ast_mutex_unlock(&devicelock);
2929                         }
2930                 }
2931                 cat = ast_category_browse(cfg, cat);
2932         }
2933         ast_mutex_lock(&netlock);
2934         if ((skinnysock > -1) && (ntohs(bindaddr.sin_port) != oldport)) {
2935                 close(skinnysock);
2936                 skinnysock = -1;
2937         }
2938         if (skinnysock < 0) {
2939                 skinnysock = socket(AF_INET, SOCK_STREAM, 0);
2940                 if(setsockopt(skinnysock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
2941                         ast_log(LOG_ERROR, "Set Socket Options failed: errno %d, %s", errno, strerror(errno));
2942                         ast_config_destroy(cfg);
2943                         return 0;
2944                 }
2945
2946                 if (skinnysock < 0) {
2947                         ast_log(LOG_WARNING, "Unable to create Skinny socket: %s\n", strerror(errno));
2948                 } else {
2949                         if (bind(skinnysock, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) {
2950                                 ast_log(LOG_WARNING, "Failed to bind to %s:%d: %s\n",
2951                                                 ast_inet_ntoa(iabuf, sizeof(iabuf), bindaddr.sin_addr), ntohs(bindaddr.sin_port),
2952                                                         strerror(errno));
2953                                 close(skinnysock);
2954                                 skinnysock = -1;
2955                                 ast_config_destroy(cfg);
2956                                 return 0;
2957                         } 
2958
2959                         if (listen(skinnysock,DEFAULT_SKINNY_BACKLOG)) {
2960                                         ast_log(LOG_WARNING, "Failed to start listening to %s:%d: %s\n",
2961                                                 ast_inet_ntoa(iabuf, sizeof(iabuf), bindaddr.sin_addr), ntohs(bindaddr.sin_port),
2962                                                         strerror(errno));
2963                                         close(skinnysock);
2964                                         skinnysock = -1;
2965                                         ast_config_destroy(cfg);
2966                                         return 0;
2967                         }
2968                 
2969                         if (option_verbose > 1)
2970                                 ast_verbose(VERBOSE_PREFIX_2 "Skinny listening on %s:%d\n", 
2971                                         ast_inet_ntoa(iabuf, sizeof(iabuf), bindaddr.sin_addr), ntohs(bindaddr.sin_port));
2972
2973                         ast_pthread_create(&accept_t,NULL, accept_thread, NULL);
2974                 }
2975         }
2976         ast_mutex_unlock(&netlock);
2977
2978         /* and unload the configuration when were done */
2979         ast_config_destroy(cfg);
2980
2981         return 0;
2982 }
2983
2984 #if 0
2985 void delete_devices(void)
2986 {
2987         struct skinny_device *d, *dlast;
2988         struct skinny_line *l, *llast;
2989         struct skinny_subchannel *sub, *slast;
2990         
2991         ast_mutex_lock(&devicelock);
2992         
2993         /* Delete all devices */
2994         for (d=devices;d;) {
2995                 
2996                 /* Delete all lines for this device */
2997                 for (l=d->lines;l;) {
2998                         /* Delete all subchannels for this line */
2999                         for (sub=l->sub;sub;) {
3000                                 slast = sub;
3001                                 sub = sub->next;
3002                                 ast_mutex_destroy(&slast->lock);
3003                                 free(slast);
3004                         }
3005                         llast = l;
3006                         l = l->next;
3007                         ast_mutex_destroy(&llast->lock);
3008                         free(llast);
3009                 }
3010                 dlast = d;
3011                 d = d->next;
3012                 free(dlast);
3013         }
3014         devices=NULL;
3015         ast_mutex_unlock(&devicelock);
3016 }
3017 #endif
3018
3019 int reload(void)
3020 {
3021 #if 0
3022 /* XXX Causes Seg - needs to be fixed, or? */
3023
3024         delete_devices();
3025         reload_config();
3026         restart_monitor();
3027 #endif
3028         return 0;
3029 }
3030
3031
3032 int load_module()
3033 {
3034         int res = 0;
3035
3036         /* load and parse config */
3037         res = reload_config();
3038         
3039         /* Announce our presence to Asterisk */ 
3040         if (!res) {
3041                 /* Make sure we can register our skinny channel type */
3042                 if (ast_channel_register(&skinny_tech)) {
3043                         ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
3044                         return -1;
3045                 }
3046         }
3047         ast_rtp_proto_register(&skinny_rtp);
3048         ast_cli_register(&cli_show_lines);
3049         ast_cli_register(&cli_debug);
3050         ast_cli_register(&cli_no_debug);
3051         sched = sched_context_create();
3052         if (!sched) {
3053                 ast_log(LOG_WARNING, "Unable to create schedule context\n");
3054         }
3055         io = io_context_create();
3056         if (!io) {
3057                 ast_log(LOG_WARNING, "Unable to create I/O context\n");
3058         }
3059         /* And start the monitor for the first time */
3060         restart_monitor();
3061         return res;
3062 }
3063
3064 int unload_module()
3065 {
3066 #if 0
3067         struct skinny_session *session, s;
3068         struct skinny_subchannel *sub;
3069         struct skinny_line *line = session;
3070
3071         /* close all IP connections */
3072         if (!ast_mutex_lock(&devicelock)) {
3073                 /* Terminate tcp listener thread */
3074                 
3075
3076         } else {
3077                 ast_log(LOG_WARNING, "Unable to lock the monitor\n");
3078                 return -1;
3079         }
3080         if (!ast_mutex_lock(&monlock)) {
3081                 if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP)) {
3082                         pthread_cancel(monitor_thread);
3083                         pthread_kill(monitor_thread, SIGURG);
3084                         pthread_join(monitor_thread, NULL);
3085                 }