00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include "asterisk.h"
00030
00031 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 253158 $")
00032
00033 #include <sys/socket.h>
00034 #include <netinet/in.h>
00035 #include <netinet/tcp.h>
00036 #include <sys/ioctl.h>
00037 #include <net/if.h>
00038 #include <fcntl.h>
00039 #include <netdb.h>
00040 #include <arpa/inet.h>
00041 #include <sys/signal.h>
00042 #include <signal.h>
00043 #include <ctype.h>
00044
00045 #include "asterisk/lock.h"
00046 #include "asterisk/channel.h"
00047 #include "asterisk/config.h"
00048 #include "asterisk/module.h"
00049 #include "asterisk/pbx.h"
00050 #include "asterisk/sched.h"
00051 #include "asterisk/io.h"
00052 #include "asterisk/rtp.h"
00053 #include "asterisk/netsock.h"
00054 #include "asterisk/acl.h"
00055 #include "asterisk/callerid.h"
00056 #include "asterisk/cli.h"
00057 #include "asterisk/manager.h"
00058 #include "asterisk/say.h"
00059 #include "asterisk/cdr.h"
00060 #include "asterisk/astdb.h"
00061 #include "asterisk/features.h"
00062 #include "asterisk/app.h"
00063 #include "asterisk/musiconhold.h"
00064 #include "asterisk/utils.h"
00065 #include "asterisk/dsp.h"
00066 #include "asterisk/stringfields.h"
00067 #include "asterisk/abstract_jb.h"
00068 #include "asterisk/threadstorage.h"
00069 #include "asterisk/devicestate.h"
00070 #include "asterisk/event.h"
00071 #include "asterisk/indications.h"
00072 #include "asterisk/linkedlists.h"
00073
00074 #ifdef SKINNY_DEVMODE
00075 #define SKINNY_DEVONLY(code) \
00076 code
00077 #else
00078 #define SKINNY_DEVONLY(code)
00079 #endif
00080
00081
00082
00083
00084 static const char tdesc[] = "Skinny Client Control Protocol (Skinny)";
00085 static const char config[] = "skinny.conf";
00086
00087 static int default_capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW;
00088 static struct ast_codec_pref default_prefs;
00089
00090 enum skinny_codecs {
00091 SKINNY_CODEC_ALAW = 2,
00092 SKINNY_CODEC_ULAW = 4,
00093 SKINNY_CODEC_G723_1 = 9,
00094 SKINNY_CODEC_G729A = 12,
00095 SKINNY_CODEC_G726_32 = 82,
00096 SKINNY_CODEC_H261 = 100,
00097 SKINNY_CODEC_H263 = 101
00098 };
00099
00100 #define DEFAULT_SKINNY_PORT 2000
00101 #define DEFAULT_SKINNY_BACKLOG 2
00102 #define SKINNY_MAX_PACKET 1000
00103
00104 static struct {
00105 unsigned int tos;
00106 unsigned int tos_audio;
00107 unsigned int tos_video;
00108 unsigned int cos;
00109 unsigned int cos_audio;
00110 unsigned int cos_video;
00111 } qos = { 0, 0, 0, 0, 0, 0 };
00112
00113 static int keep_alive = 120;
00114 static char global_vmexten[AST_MAX_EXTENSION];
00115 static char used_context[AST_MAX_EXTENSION];
00116 static char regcontext[AST_MAX_CONTEXT];
00117 static char date_format[6] = "D-M-Y";
00118 static char version_id[16] = "P002F202";
00119
00120 #if __BYTE_ORDER == __LITTLE_ENDIAN
00121 #define letohl(x) (x)
00122 #define letohs(x) (x)
00123 #define htolel(x) (x)
00124 #define htoles(x) (x)
00125 #else
00126 #if defined(HAVE_BYTESWAP_H)
00127 #include <byteswap.h>
00128 #define letohl(x) bswap_32(x)
00129 #define letohs(x) bswap_16(x)
00130 #define htolel(x) bswap_32(x)
00131 #define htoles(x) bswap_16(x)
00132 #elif defined(HAVE_SYS_ENDIAN_SWAP16)
00133 #include <sys/endian.h>
00134 #define letohl(x) __swap32(x)
00135 #define letohs(x) __swap16(x)
00136 #define htolel(x) __swap32(x)
00137 #define htoles(x) __swap16(x)
00138 #elif defined(HAVE_SYS_ENDIAN_BSWAP16)
00139 #include <sys/endian.h>
00140 #define letohl(x) bswap32(x)
00141 #define letohs(x) bswap16(x)
00142 #define htolel(x) bswap32(x)
00143 #define htoles(x) bswap16(x)
00144 #else
00145 #define __bswap_16(x) \
00146 ((((x) & 0xff00) >> 8) | \
00147 (((x) & 0x00ff) << 8))
00148 #define __bswap_32(x) \
00149 ((((x) & 0xff000000) >> 24) | \
00150 (((x) & 0x00ff0000) >> 8) | \
00151 (((x) & 0x0000ff00) << 8) | \
00152 (((x) & 0x000000ff) << 24))
00153 #define letohl(x) __bswap_32(x)
00154 #define letohs(x) __bswap_16(x)
00155 #define htolel(x) __bswap_32(x)
00156 #define htoles(x) __bswap_16(x)
00157 #endif
00158 #endif
00159
00160
00161 static struct ast_jb_conf default_jbconf =
00162 {
00163 .flags = 0,
00164 .max_size = -1,
00165 .resync_threshold = -1,
00166 .impl = "",
00167 .target_extra = -1,
00168 };
00169 static struct ast_jb_conf global_jbconf;
00170
00171 #ifdef SKINNY_DEVMODE
00172 AST_THREADSTORAGE(message2str_threadbuf);
00173 #define MESSAGE2STR_BUFSIZE 35
00174 #endif
00175
00176 AST_THREADSTORAGE(device2str_threadbuf);
00177 #define DEVICE2STR_BUFSIZE 15
00178
00179 AST_THREADSTORAGE(control2str_threadbuf);
00180 #define CONTROL2STR_BUFSIZE 100
00181
00182
00183
00184
00185
00186 #define KEEP_ALIVE_MESSAGE 0x0000
00187
00188
00189 #define REGISTER_MESSAGE 0x0001
00190 struct register_message {
00191 char name[16];
00192 uint32_t userId;
00193 uint32_t instance;
00194 uint32_t ip;
00195 uint32_t type;
00196 uint32_t maxStreams;
00197 };
00198
00199 #define IP_PORT_MESSAGE 0x0002
00200
00201 #define KEYPAD_BUTTON_MESSAGE 0x0003
00202 struct keypad_button_message {
00203 uint32_t button;
00204 uint32_t lineInstance;
00205 uint32_t callReference;
00206 };
00207
00208
00209 #define ENBLOC_CALL_MESSAGE 0x0004
00210 struct enbloc_call_message {
00211 char calledParty[24];
00212 };
00213
00214 #define STIMULUS_MESSAGE 0x0005
00215 struct stimulus_message {
00216 uint32_t stimulus;
00217 uint32_t stimulusInstance;
00218 uint32_t callreference;
00219 };
00220
00221 #define OFFHOOK_MESSAGE 0x0006
00222 struct offhook_message {
00223 uint32_t instance;
00224 uint32_t reference;
00225 };
00226
00227 #define ONHOOK_MESSAGE 0x0007
00228 struct onhook_message {
00229 uint32_t instance;
00230 uint32_t reference;
00231 };
00232
00233 #define CAPABILITIES_RES_MESSAGE 0x0010
00234 struct station_capabilities {
00235 uint32_t codec;
00236 uint32_t frames;
00237 union {
00238 char res[8];
00239 uint32_t rate;
00240 } payloads;
00241 };
00242
00243 #define SKINNY_MAX_CAPABILITIES 18
00244
00245 struct capabilities_res_message {
00246 uint32_t count;
00247 struct station_capabilities caps[SKINNY_MAX_CAPABILITIES];
00248 };
00249
00250 #define SPEED_DIAL_STAT_REQ_MESSAGE 0x000A
00251 struct speed_dial_stat_req_message {
00252 uint32_t speedDialNumber;
00253 };
00254
00255 #define LINE_STATE_REQ_MESSAGE 0x000B
00256 struct line_state_req_message {
00257 uint32_t lineNumber;
00258 };
00259
00260 #define TIME_DATE_REQ_MESSAGE 0x000D
00261 #define BUTTON_TEMPLATE_REQ_MESSAGE 0x000E
00262 #define VERSION_REQ_MESSAGE 0x000F
00263 #define SERVER_REQUEST_MESSAGE 0x0012
00264
00265 #define ALARM_MESSAGE 0x0020
00266 struct alarm_message {
00267 uint32_t alarmSeverity;
00268 char displayMessage[80];
00269 uint32_t alarmParam1;
00270 uint32_t alarmParam2;
00271 };
00272
00273 #define OPEN_RECEIVE_CHANNEL_ACK_MESSAGE 0x0022
00274 struct open_receive_channel_ack_message {
00275 uint32_t status;
00276 uint32_t ipAddr;
00277 uint32_t port;
00278 uint32_t passThruId;
00279 };
00280
00281 #define SOFT_KEY_SET_REQ_MESSAGE 0x0025
00282
00283 #define SOFT_KEY_EVENT_MESSAGE 0x0026
00284 struct soft_key_event_message {
00285 uint32_t softKeyEvent;
00286 uint32_t instance;
00287 uint32_t callreference;
00288 };
00289
00290 #define UNREGISTER_MESSAGE 0x0027
00291 #define SOFT_KEY_TEMPLATE_REQ_MESSAGE 0x0028
00292 #define HEADSET_STATUS_MESSAGE 0x002B
00293 #define REGISTER_AVAILABLE_LINES_MESSAGE 0x002D
00294
00295 #define REGISTER_ACK_MESSAGE 0x0081
00296 struct register_ack_message {
00297 uint32_t keepAlive;
00298 char dateTemplate[6];
00299 char res[2];
00300 uint32_t secondaryKeepAlive;
00301 char res2[4];
00302 };
00303
00304 #define START_TONE_MESSAGE 0x0082
00305 struct start_tone_message {
00306 uint32_t tone;
00307 uint32_t space;
00308 uint32_t instance;
00309 uint32_t reference;
00310 };
00311
00312 #define STOP_TONE_MESSAGE 0x0083
00313 struct stop_tone_message {
00314 uint32_t instance;
00315 uint32_t reference;
00316 };
00317
00318 #define SET_RINGER_MESSAGE 0x0085
00319 struct set_ringer_message {
00320 uint32_t ringerMode;
00321 uint32_t unknown1;
00322 uint32_t unknown2;
00323 uint32_t space[2];
00324 };
00325
00326 #define SET_LAMP_MESSAGE 0x0086
00327 struct set_lamp_message {
00328 uint32_t stimulus;
00329 uint32_t stimulusInstance;
00330 uint32_t deviceStimulus;
00331 };
00332
00333 #define SET_SPEAKER_MESSAGE 0x0088
00334 struct set_speaker_message {
00335 uint32_t mode;
00336 };
00337
00338
00339 #define SET_MICROPHONE_MESSAGE 0x0089
00340 struct set_microphone_message {
00341 uint32_t mode;
00342 };
00343
00344 #define START_MEDIA_TRANSMISSION_MESSAGE 0x008A
00345 struct media_qualifier {
00346 uint32_t precedence;
00347 uint32_t vad;
00348 uint16_t packets;
00349 uint32_t bitRate;
00350 };
00351
00352 struct start_media_transmission_message {
00353 uint32_t conferenceId;
00354 uint32_t passThruPartyId;
00355 uint32_t remoteIp;
00356 uint32_t remotePort;
00357 uint32_t packetSize;
00358 uint32_t payloadType;
00359 struct media_qualifier qualifier;
00360 uint32_t space[16];
00361 };
00362
00363 #define STOP_MEDIA_TRANSMISSION_MESSAGE 0x008B
00364 struct stop_media_transmission_message {
00365 uint32_t conferenceId;
00366 uint32_t passThruPartyId;
00367 uint32_t space[3];
00368 };
00369
00370 #define CALL_INFO_MESSAGE 0x008F
00371 struct call_info_message {
00372 char callingPartyName[40];
00373 char callingParty[24];
00374 char calledPartyName[40];
00375 char calledParty[24];
00376 uint32_t instance;
00377 uint32_t reference;
00378 uint32_t type;
00379 char originalCalledPartyName[40];
00380 char originalCalledParty[24];
00381 char lastRedirectingPartyName[40];
00382 char lastRedirectingParty[24];
00383 uint32_t originalCalledPartyRedirectReason;
00384 uint32_t lastRedirectingReason;
00385 char callingPartyVoiceMailbox[24];
00386 char calledPartyVoiceMailbox[24];
00387 char originalCalledPartyVoiceMailbox[24];
00388 char lastRedirectingVoiceMailbox[24];
00389 uint32_t space[3];
00390 };
00391
00392 #define FORWARD_STAT_MESSAGE 0x0090
00393 struct forward_stat_message {
00394 uint32_t activeforward;
00395 uint32_t lineNumber;
00396 uint32_t fwdall;
00397 char fwdallnum[24];
00398 uint32_t fwdbusy;
00399 char fwdbusynum[24];
00400 uint32_t fwdnoanswer;
00401 char fwdnoanswernum[24];
00402 };
00403
00404 #define SPEED_DIAL_STAT_RES_MESSAGE 0x0091
00405 struct speed_dial_stat_res_message {
00406 uint32_t speedDialNumber;
00407 char speedDialDirNumber[24];
00408 char speedDialDisplayName[40];
00409 };
00410
00411 #define LINE_STAT_RES_MESSAGE 0x0092
00412 struct line_stat_res_message {
00413 uint32_t lineNumber;
00414 char lineDirNumber[24];
00415 char lineDisplayName[24];
00416 uint32_t space[15];
00417 };
00418
00419 #define DEFINETIMEDATE_MESSAGE 0x0094
00420 struct definetimedate_message {
00421 uint32_t year;
00422 uint32_t month;
00423 uint32_t dayofweek;
00424 uint32_t day;
00425 uint32_t hour;
00426 uint32_t minute;
00427 uint32_t seconds;
00428 uint32_t milliseconds;
00429 uint32_t timestamp;
00430 };
00431
00432 #define BUTTON_TEMPLATE_RES_MESSAGE 0x0097
00433 struct button_definition {
00434 uint8_t instanceNumber;
00435 uint8_t buttonDefinition;
00436 };
00437
00438 struct button_definition_template {
00439 uint8_t buttonDefinition;
00440
00441
00442 };
00443
00444 #define STIMULUS_REDIAL 0x01
00445 #define STIMULUS_SPEEDDIAL 0x02
00446 #define STIMULUS_HOLD 0x03
00447 #define STIMULUS_TRANSFER 0x04
00448 #define STIMULUS_FORWARDALL 0x05
00449 #define STIMULUS_FORWARDBUSY 0x06
00450 #define STIMULUS_FORWARDNOANSWER 0x07
00451 #define STIMULUS_DISPLAY 0x08
00452 #define STIMULUS_LINE 0x09
00453 #define STIMULUS_VOICEMAIL 0x0F
00454 #define STIMULUS_AUTOANSWER 0x11
00455 #define STIMULUS_DND 0x3F
00456 #define STIMULUS_CONFERENCE 0x7D
00457 #define STIMULUS_CALLPARK 0x7E
00458 #define STIMULUS_CALLPICKUP 0x7F
00459 #define STIMULUS_NONE 0xFF
00460
00461
00462 #define BT_REDIAL STIMULUS_REDIAL
00463 #define BT_SPEEDDIAL STIMULUS_SPEEDDIAL
00464 #define BT_HOLD STIMULUS_HOLD
00465 #define BT_TRANSFER STIMULUS_TRANSFER
00466 #define BT_FORWARDALL STIMULUS_FORWARDALL
00467 #define BT_FORWARDBUSY STIMULUS_FORWARDBUSY
00468 #define BT_FORWARDNOANSWER STIMULUS_FORWARDNOANSWER
00469 #define BT_DISPLAY STIMULUS_DISPLAY
00470 #define BT_LINE STIMULUS_LINE
00471 #define BT_VOICEMAIL STIMULUS_VOICEMAIL
00472 #define BT_AUTOANSWER STIMULUS_AUTOANSWER
00473 #define BT_DND STIMULUS_DND
00474 #define BT_CONFERENCE STIMULUS_CONFERENCE
00475 #define BT_CALLPARK STIMULUS_CALLPARK
00476 #define BT_CALLPICKUP STIMULUS_CALLPICKUP
00477 #define BT_NONE 0x00
00478
00479
00480
00481
00482 #define BT_CUST_LINESPEEDDIAL 0xB0
00483 #define BT_CUST_LINE 0xB1
00484
00485 struct button_template_res_message {
00486 uint32_t buttonOffset;
00487 uint32_t buttonCount;
00488 uint32_t totalButtonCount;
00489 struct button_definition definition[42];
00490 };
00491
00492 #define VERSION_RES_MESSAGE 0x0098
00493 struct version_res_message {
00494 char version[16];
00495 };
00496
00497 #define DISPLAYTEXT_MESSAGE 0x0099
00498 struct displaytext_message {
00499 char text[40];
00500 };
00501
00502 #define CLEAR_NOTIFY_MESSAGE 0x0115
00503 #define CLEAR_DISPLAY_MESSAGE 0x009A
00504
00505 #define CAPABILITIES_REQ_MESSAGE 0x009B
00506
00507 #define REGISTER_REJ_MESSAGE 0x009D
00508 struct register_rej_message {
00509 char errMsg[33];
00510 };
00511
00512 #define SERVER_RES_MESSAGE 0x009E
00513 struct server_identifier {
00514 char serverName[48];
00515 };
00516
00517 struct server_res_message {
00518 struct server_identifier server[5];
00519 uint32_t serverListenPort[5];
00520 uint32_t serverIpAddr[5];
00521 };
00522
00523 #define RESET_MESSAGE 0x009F
00524 struct reset_message {
00525 uint32_t resetType;
00526 };
00527
00528 #define KEEP_ALIVE_ACK_MESSAGE 0x0100
00529
00530 #define OPEN_RECEIVE_CHANNEL_MESSAGE 0x0105
00531 struct open_receive_channel_message {
00532 uint32_t conferenceId;
00533 uint32_t partyId;
00534 uint32_t packets;
00535 uint32_t capability;
00536 uint32_t echo;
00537 uint32_t bitrate;
00538 uint32_t space[16];
00539 };
00540
00541 #define CLOSE_RECEIVE_CHANNEL_MESSAGE 0x0106
00542 struct close_receive_channel_message {
00543 uint32_t conferenceId;
00544 uint32_t partyId;
00545 uint32_t space[2];
00546 };
00547
00548 #define SOFT_KEY_TEMPLATE_RES_MESSAGE 0x0108
00549
00550 struct soft_key_template_definition {
00551 char softKeyLabel[16];
00552 uint32_t softKeyEvent;
00553 };
00554
00555 #define KEYDEF_ONHOOK 0
00556 #define KEYDEF_CONNECTED 1
00557 #define KEYDEF_ONHOLD 2
00558 #define KEYDEF_RINGIN 3
00559 #define KEYDEF_OFFHOOK 4
00560 #define KEYDEF_CONNWITHTRANS 5
00561 #define KEYDEF_DADFD 6
00562 #define KEYDEF_CONNWITHCONF 7
00563 #define KEYDEF_RINGOUT 8
00564 #define KEYDEF_OFFHOOKWITHFEAT 9
00565 #define KEYDEF_UNKNOWN 10
00566
00567 #define SOFTKEY_NONE 0x00
00568 #define SOFTKEY_REDIAL 0x01
00569 #define SOFTKEY_NEWCALL 0x02
00570 #define SOFTKEY_HOLD 0x03
00571 #define SOFTKEY_TRNSFER 0x04
00572 #define SOFTKEY_CFWDALL 0x05
00573 #define SOFTKEY_CFWDBUSY 0x06
00574 #define SOFTKEY_CFWDNOANSWER 0x07
00575 #define SOFTKEY_BKSPC 0x08
00576 #define SOFTKEY_ENDCALL 0x09
00577 #define SOFTKEY_RESUME 0x0A
00578 #define SOFTKEY_ANSWER 0x0B
00579 #define SOFTKEY_INFO 0x0C
00580 #define SOFTKEY_CONFRN 0x0D
00581 #define SOFTKEY_PARK 0x0E
00582 #define SOFTKEY_JOIN 0x0F
00583 #define SOFTKEY_MEETME 0x10
00584 #define SOFTKEY_PICKUP 0x11
00585 #define SOFTKEY_GPICKUP 0x12
00586 #define SOFTKEY_DND 0x13
00587 #define SOFTKEY_IDIVERT 0x14
00588
00589 struct soft_key_template_definition soft_key_template_default[] = {
00590 { "\200\001", SOFTKEY_REDIAL },
00591 { "\200\002", SOFTKEY_NEWCALL },
00592 { "\200\003", SOFTKEY_HOLD },
00593 { "\200\004", SOFTKEY_TRNSFER },
00594 { "\200\005", SOFTKEY_CFWDALL },
00595 { "\200\006", SOFTKEY_CFWDBUSY },
00596 { "\200\007", SOFTKEY_CFWDNOANSWER },
00597 { "\200\010", SOFTKEY_BKSPC },
00598 { "\200\011", SOFTKEY_ENDCALL },
00599 { "\200\012", SOFTKEY_RESUME },
00600 { "\200\013", SOFTKEY_ANSWER },
00601 { "\200\014", SOFTKEY_INFO },
00602 { "\200\015", SOFTKEY_CONFRN },
00603 { "\200\016", SOFTKEY_PARK },
00604 { "\200\017", SOFTKEY_JOIN },
00605 { "\200\020", SOFTKEY_MEETME },
00606 { "\200\021", SOFTKEY_PICKUP },
00607 { "\200\022", SOFTKEY_GPICKUP },
00608 { "\200\077", SOFTKEY_DND },
00609 { "\200\120", SOFTKEY_IDIVERT },
00610 };
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635
00636
00637
00638
00639
00640
00641
00642
00643
00644
00645
00646
00647
00648
00649
00650
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679
00680
00681
00682
00683
00684
00685
00686
00687
00688
00689
00690
00691
00692
00693
00694
00695
00696
00697
00698
00699
00700
00701
00702
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712
00713
00714
00715
00716
00717
00718
00719
00720
00721
00722
00723
00724
00725
00726
00727
00728
00729
00730
00731
00732
00733
00734
00735
00736
00737
00738
00739
00740
00741
00742
00743
00744
00745
00746 struct soft_key_definitions {
00747 const uint8_t mode;
00748 const uint8_t *defaults;
00749 const int count;
00750 };
00751
00752 static const uint8_t soft_key_default_onhook[] = {
00753 SOFTKEY_REDIAL,
00754 SOFTKEY_NEWCALL,
00755 SOFTKEY_CFWDALL,
00756 SOFTKEY_CFWDBUSY,
00757 SOFTKEY_DND,
00758
00759
00760 };
00761
00762 static const uint8_t soft_key_default_connected[] = {
00763 SOFTKEY_HOLD,
00764 SOFTKEY_ENDCALL,
00765 SOFTKEY_TRNSFER,
00766 SOFTKEY_PARK,
00767 SOFTKEY_CFWDALL,
00768 SOFTKEY_CFWDBUSY,
00769 };
00770
00771 static const uint8_t soft_key_default_onhold[] = {
00772 SOFTKEY_RESUME,
00773 SOFTKEY_NEWCALL,
00774 SOFTKEY_ENDCALL,
00775 SOFTKEY_TRNSFER,
00776 };
00777
00778 static const uint8_t soft_key_default_ringin[] = {
00779 SOFTKEY_ANSWER,
00780 SOFTKEY_ENDCALL,
00781 SOFTKEY_TRNSFER,
00782 };
00783
00784 static const uint8_t soft_key_default_offhook[] = {
00785 SOFTKEY_REDIAL,
00786 SOFTKEY_ENDCALL,
00787 SOFTKEY_CFWDALL,
00788 SOFTKEY_CFWDBUSY,
00789
00790 };
00791
00792 static const uint8_t soft_key_default_connwithtrans[] = {
00793 SOFTKEY_HOLD,
00794 SOFTKEY_ENDCALL,
00795 SOFTKEY_TRNSFER,
00796 SOFTKEY_PARK,
00797 SOFTKEY_CFWDALL,
00798 SOFTKEY_CFWDBUSY,
00799 };
00800
00801 static const uint8_t soft_key_default_dadfd[] = {
00802 SOFTKEY_BKSPC,
00803 SOFTKEY_ENDCALL,
00804 };
00805
00806 static const uint8_t soft_key_default_connwithconf[] = {
00807 SOFTKEY_NONE,
00808 };
00809
00810 static const uint8_t soft_key_default_ringout[] = {
00811 SOFTKEY_NONE,
00812 SOFTKEY_ENDCALL,
00813 };
00814
00815 static const uint8_t soft_key_default_offhookwithfeat[] = {
00816 SOFTKEY_REDIAL,
00817 SOFTKEY_ENDCALL,
00818 SOFTKEY_TRNSFER,
00819 };
00820
00821 static const uint8_t soft_key_default_unknown[] = {
00822 SOFTKEY_NONE,
00823 };
00824
00825 static const struct soft_key_definitions soft_key_default_definitions[] = {
00826 {KEYDEF_ONHOOK, soft_key_default_onhook, sizeof(soft_key_default_onhook) / sizeof(uint8_t)},
00827 {KEYDEF_CONNECTED, soft_key_default_connected, sizeof(soft_key_default_connected) / sizeof(uint8_t)},
00828 {KEYDEF_ONHOLD, soft_key_default_onhold, sizeof(soft_key_default_onhold) / sizeof(uint8_t)},
00829 {KEYDEF_RINGIN, soft_key_default_ringin, sizeof(soft_key_default_ringin) / sizeof(uint8_t)},
00830 {KEYDEF_OFFHOOK, soft_key_default_offhook, sizeof(soft_key_default_offhook) / sizeof(uint8_t)},
00831 {KEYDEF_CONNWITHTRANS, soft_key_default_connwithtrans, sizeof(soft_key_default_connwithtrans) / sizeof(uint8_t)},
00832 {KEYDEF_DADFD, soft_key_default_dadfd, sizeof(soft_key_default_dadfd) / sizeof(uint8_t)},
00833 {KEYDEF_CONNWITHCONF, soft_key_default_connwithconf, sizeof(soft_key_default_connwithconf) / sizeof(uint8_t)},
00834 {KEYDEF_RINGOUT, soft_key_default_ringout, sizeof(soft_key_default_ringout) / sizeof(uint8_t)},
00835 {KEYDEF_OFFHOOKWITHFEAT, soft_key_default_offhookwithfeat, sizeof(soft_key_default_offhookwithfeat) / sizeof(uint8_t)},
00836 {KEYDEF_UNKNOWN, soft_key_default_unknown, sizeof(soft_key_default_unknown) / sizeof(uint8_t)}
00837 };
00838
00839 struct soft_key_template_res_message {
00840 uint32_t softKeyOffset;
00841 uint32_t softKeyCount;
00842 uint32_t totalSoftKeyCount;
00843 struct soft_key_template_definition softKeyTemplateDefinition[32];
00844 };
00845
00846 #define SOFT_KEY_SET_RES_MESSAGE 0x0109
00847
00848 struct soft_key_set_definition {
00849 uint8_t softKeyTemplateIndex[16];
00850 uint16_t softKeyInfoIndex[16];
00851 };
00852
00853 struct soft_key_set_res_message {
00854 uint32_t softKeySetOffset;
00855 uint32_t softKeySetCount;
00856 uint32_t totalSoftKeySetCount;
00857 struct soft_key_set_definition softKeySetDefinition[16];
00858 uint32_t res;
00859 };
00860
00861 #define SELECT_SOFT_KEYS_MESSAGE 0x0110
00862 struct select_soft_keys_message {
00863 uint32_t instance;
00864 uint32_t reference;
00865 uint32_t softKeySetIndex;
00866 uint32_t validKeyMask;
00867 };
00868
00869 #define CALL_STATE_MESSAGE 0x0111
00870 struct call_state_message {
00871 uint32_t callState;
00872 uint32_t lineInstance;
00873 uint32_t callReference;
00874 uint32_t space[3];
00875 };
00876
00877 #define DISPLAY_PROMPT_STATUS_MESSAGE 0x0112
00878 struct display_prompt_status_message {
00879 uint32_t messageTimeout;
00880 char promptMessage[32];
00881 uint32_t lineInstance;
00882 uint32_t callReference;
00883 uint32_t space[3];
00884 };
00885
00886 #define CLEAR_PROMPT_MESSAGE 0x0113
00887 struct clear_prompt_message {
00888 uint32_t lineInstance;
00889 uint32_t callReference;
00890 };
00891
00892 #define DISPLAY_NOTIFY_MESSAGE 0x0114
00893 struct display_notify_message {
00894 uint32_t displayTimeout;
00895 char displayMessage[100];
00896 };
00897
00898 #define ACTIVATE_CALL_PLANE_MESSAGE 0x0116
00899 struct activate_call_plane_message {
00900 uint32_t lineInstance;
00901 };
00902
00903 #define DIALED_NUMBER_MESSAGE 0x011D
00904 struct dialed_number_message {
00905 char dialedNumber[24];
00906 uint32_t lineInstance;
00907 uint32_t callReference;
00908 };
00909
00910 union skinny_data {
00911 struct alarm_message alarm;
00912 struct speed_dial_stat_req_message speeddialreq;
00913 struct register_message reg;
00914 struct register_ack_message regack;
00915 struct register_rej_message regrej;
00916 struct capabilities_res_message caps;
00917 struct version_res_message version;
00918 struct button_template_res_message buttontemplate;
00919 struct displaytext_message displaytext;
00920 struct display_prompt_status_message displaypromptstatus;
00921 struct clear_prompt_message clearpromptstatus;
00922 struct definetimedate_message definetimedate;
00923 struct start_tone_message starttone;
00924 struct stop_tone_message stoptone;
00925 struct speed_dial_stat_res_message speeddial;
00926 struct line_state_req_message line;
00927 struct line_stat_res_message linestat;
00928 struct soft_key_set_res_message softkeysets;
00929 struct soft_key_template_res_message softkeytemplate;
00930 struct server_res_message serverres;
00931 struct reset_message reset;
00932 struct set_lamp_message setlamp;
00933 struct set_ringer_message setringer;
00934 struct call_state_message callstate;
00935 struct keypad_button_message keypad;
00936 struct select_soft_keys_message selectsoftkey;
00937 struct activate_call_plane_message activatecallplane;
00938 struct stimulus_message stimulus;
00939 struct offhook_message offhook;
00940 struct onhook_message onhook;
00941 struct set_speaker_message setspeaker;
00942 struct set_microphone_message setmicrophone;
00943 struct call_info_message callinfo;
00944 struct start_media_transmission_message startmedia;
00945 struct stop_media_transmission_message stopmedia;
00946 struct open_receive_channel_message openreceivechannel;
00947 struct open_receive_channel_ack_message openreceivechannelack;
00948 struct close_receive_channel_message closereceivechannel;
00949 struct display_notify_message displaynotify;
00950 struct dialed_number_message dialednumber;
00951 struct soft_key_event_message softkeyeventmessage;
00952 struct enbloc_call_message enbloccallmessage;
00953 struct forward_stat_message forwardstat;
00954 };
00955
00956
00957 struct skinny_req {
00958 int len;
00959 int res;
00960 int e;
00961 union skinny_data data;
00962 };
00963
00964
00965
00966
00967 int skinny_header_size = 12;
00968
00969
00970
00971
00972
00973 static int skinnydebug = 0;
00974 static int skinnyreload = 0;
00975
00976
00977 static struct sockaddr_in bindaddr;
00978 static char ourhost[256];
00979 static int ourport;
00980 static struct in_addr __ourip;
00981 struct ast_hostent ahp;
00982 struct hostent *hp;
00983 static int skinnysock = -1;
00984 static pthread_t accept_t;
00985 static int callnums = 1;
00986
00987 #define SKINNY_DEVICE_UNKNOWN -1
00988 #define SKINNY_DEVICE_NONE 0
00989 #define SKINNY_DEVICE_30SPPLUS 1
00990 #define SKINNY_DEVICE_12SPPLUS 2
00991 #define SKINNY_DEVICE_12SP 3
00992 #define SKINNY_DEVICE_12 4
00993 #define SKINNY_DEVICE_30VIP 5
00994 #define SKINNY_DEVICE_7910 6
00995 #define SKINNY_DEVICE_7960 7
00996 #define SKINNY_DEVICE_7940 8
00997 #define SKINNY_DEVICE_7935 9
00998 #define SKINNY_DEVICE_ATA186 12
00999 #define SKINNY_DEVICE_7941 115
01000 #define SKINNY_DEVICE_7971 119
01001 #define SKINNY_DEVICE_7914 124
01002 #define SKINNY_DEVICE_7985 302
01003 #define SKINNY_DEVICE_7911 307
01004 #define SKINNY_DEVICE_7961GE 308
01005 #define SKINNY_DEVICE_7941GE 309
01006 #define SKINNY_DEVICE_7931 348
01007 #define SKINNY_DEVICE_7921 365
01008 #define SKINNY_DEVICE_7906 369
01009 #define SKINNY_DEVICE_7962 404
01010 #define SKINNY_DEVICE_7937 431
01011 #define SKINNY_DEVICE_7942 434
01012 #define SKINNY_DEVICE_7945 435
01013 #define SKINNY_DEVICE_7965 436
01014 #define SKINNY_DEVICE_7975 437
01015 #define SKINNY_DEVICE_7905 20000
01016 #define SKINNY_DEVICE_7920 30002
01017 #define SKINNY_DEVICE_7970 30006
01018 #define SKINNY_DEVICE_7912 30007
01019 #define SKINNY_DEVICE_7902 30008
01020 #define SKINNY_DEVICE_CIPC 30016
01021 #define SKINNY_DEVICE_7961 30018
01022 #define SKINNY_DEVICE_7936 30019
01023 #define SKINNY_DEVICE_SCCPGATEWAY_AN 30027
01024 #define SKINNY_DEVICE_SCCPGATEWAY_BRI 30028
01025
01026 #define SKINNY_SPEAKERON 1
01027 #define SKINNY_SPEAKEROFF 2
01028
01029 #define SKINNY_MICON 1
01030 #define SKINNY_MICOFF 2
01031
01032 #define SKINNY_OFFHOOK 1
01033 #define SKINNY_ONHOOK 2
01034 #define SKINNY_RINGOUT 3
01035 #define SKINNY_RINGIN 4
01036 #define SKINNY_CONNECTED 5
01037 #define SKINNY_BUSY 6
01038 #define SKINNY_CONGESTION 7
01039 #define SKINNY_HOLD 8
01040 #define SKINNY_CALLWAIT 9
01041 #define SKINNY_TRANSFER 10
01042 #define SKINNY_PARK 11
01043 #define SKINNY_PROGRESS 12
01044 #define SKINNY_CALLREMOTEMULTILINE 13
01045 #define SKINNY_INVALID 14
01046
01047 #define SKINNY_SILENCE 0x00
01048 #define SKINNY_DIALTONE 0x21
01049 #define SKINNY_BUSYTONE 0x23
01050 #define SKINNY_ALERT 0x24
01051 #define SKINNY_REORDER 0x25
01052 #define SKINNY_CALLWAITTONE 0x2D
01053 #define SKINNY_NOTONE 0x7F
01054
01055 #define SKINNY_LAMP_OFF 1
01056 #define SKINNY_LAMP_ON 2
01057 #define SKINNY_LAMP_WINK 3
01058 #define SKINNY_LAMP_FLASH 4
01059 #define SKINNY_LAMP_BLINK 5
01060
01061 #define SKINNY_RING_OFF 1
01062 #define SKINNY_RING_INSIDE 2
01063 #define SKINNY_RING_OUTSIDE 3
01064 #define SKINNY_RING_FEATURE 4
01065
01066 #define SKINNY_CFWD_ALL (1 << 0)
01067 #define SKINNY_CFWD_BUSY (1 << 1)
01068 #define SKINNY_CFWD_NOANSWER (1 << 2)
01069
01070
01071 #define SKINNY_CX_SENDONLY 0
01072 #define SKINNY_CX_RECVONLY 1
01073 #define SKINNY_CX_SENDRECV 2
01074 #define SKINNY_CX_CONF 3
01075 #define SKINNY_CX_CONFERENCE 3
01076 #define SKINNY_CX_MUTE 4
01077 #define SKINNY_CX_INACTIVE 4
01078
01079 #if 0
01080 static char *skinny_cxmodes[] = {
01081 "sendonly",
01082 "recvonly",
01083 "sendrecv",
01084 "confrnce",
01085 "inactive"
01086 };
01087 #endif
01088
01089
01090 static struct sched_context *sched = NULL;
01091 static struct io_context *io;
01092
01093
01094
01095 AST_MUTEX_DEFINE_STATIC(monlock);
01096
01097 AST_MUTEX_DEFINE_STATIC(netlock);
01098
01099
01100
01101 static pthread_t monitor_thread = AST_PTHREADT_NULL;
01102
01103
01104 static int firstdigittimeout = 16000;
01105
01106
01107 static int gendigittimeout = 8000;
01108
01109
01110 static int matchdigittimeout = 3000;
01111
01112 struct skinny_subchannel {
01113 ast_mutex_t lock;
01114 struct ast_channel *owner;
01115 struct ast_rtp *rtp;
01116 struct ast_rtp *vrtp;
01117 unsigned int callid;
01118
01119 int progress;
01120 int ringing;
01121 int onhold;
01122
01123 int cxmode;
01124 int nat;
01125 int outgoing;
01126 int alreadygone;
01127 int blindxfer;
01128 int xferor;
01129
01130
01131 AST_LIST_ENTRY(skinny_subchannel) list;
01132 struct skinny_subchannel *related;
01133 struct skinny_line *parent;
01134 };
01135
01136 #define SKINNY_LINE_OPTIONS \
01137 char name[80]; \
01138 char label[24]; \
01139 char accountcode[AST_MAX_ACCOUNT_CODE]; \
01140 char exten[AST_MAX_EXTENSION]; \
01141 char context[AST_MAX_CONTEXT]; \
01142 char language[MAX_LANGUAGE]; \
01143 char cid_num[AST_MAX_EXTENSION]; \
01144 char cid_name[AST_MAX_EXTENSION]; \
01145 char lastcallerid[AST_MAX_EXTENSION]; \
01146 int cfwdtype; \
01147 char call_forward_all[AST_MAX_EXTENSION]; \
01148 char call_forward_busy[AST_MAX_EXTENSION]; \
01149 char call_forward_noanswer[AST_MAX_EXTENSION]; \
01150 char mailbox[AST_MAX_EXTENSION]; \
01151 char vmexten[AST_MAX_EXTENSION]; \
01152 char regexten[AST_MAX_EXTENSION]; \
01153 char regcontext[AST_MAX_CONTEXT]; \
01154 char parkinglot[AST_MAX_CONTEXT]; \
01155 char mohinterpret[MAX_MUSICCLASS]; \
01156 char mohsuggest[MAX_MUSICCLASS]; \
01157 char lastnumberdialed[AST_MAX_EXTENSION]; \
01158 int curtone; \
01159 ast_group_t callgroup; \
01160 ast_group_t pickupgroup; \
01161 int callwaiting; \
01162 int transfer; \
01163 int threewaycalling; \
01164 int mwiblink; \
01165 int cancallforward; \
01166 int getforward; \
01167 int callreturn; \
01168 int dnd; \
01169 int hascallerid; \
01170 int hidecallerid; \
01171 int amaflags; \
01172 int type; \
01173 int instance; \
01174 int group; \
01175 int needdestroy; \
01176 int confcapability; \
01177 struct ast_codec_pref confprefs; \
01178 int capability; \
01179 struct ast_codec_pref prefs; \
01180 int nonCodecCapability; \
01181 int onhooktime; \
01182 int msgstate; \
01183 int immediate; \
01184 int hookstate; \
01185 int nat; \
01186 int directmedia; \
01187 int prune;
01188
01189 struct skinny_line {
01190 SKINNY_LINE_OPTIONS
01191 ast_mutex_t lock;
01192 struct ast_event_sub *mwi_event_sub;
01193 struct skinny_subchannel *activesub;
01194 AST_LIST_HEAD(, skinny_subchannel) sub;
01195 AST_LIST_ENTRY(skinny_line) list;
01196 AST_LIST_ENTRY(skinny_line) all;
01197 struct skinny_device *device;
01198 struct ast_variable *chanvars;
01199 int newmsgs;
01200 };
01201
01202 struct skinny_line_options{
01203 SKINNY_LINE_OPTIONS
01204 } default_line_struct = {
01205 .callwaiting = 1,
01206 .transfer = 1,
01207 .mwiblink = 0,
01208 .dnd = 0,
01209 .hidecallerid = 0,
01210 .amaflags = 0,
01211 .instance = 0,
01212 .directmedia = 0,
01213 .nat = 0,
01214 .confcapability = AST_FORMAT_ULAW | AST_FORMAT_ALAW,
01215 .capability = 0,
01216 .getforward = 0,
01217 .needdestroy = 0,
01218 .prune = 0,
01219 .hookstate = SKINNY_ONHOOK,
01220 };
01221 struct skinny_line_options *default_line = &default_line_struct;
01222
01223 static AST_LIST_HEAD_STATIC(lines, skinny_line);
01224
01225 struct skinny_speeddial {
01226 ast_mutex_t lock;
01227 char label[42];
01228 char context[AST_MAX_CONTEXT];
01229 char exten[AST_MAX_EXTENSION];
01230 int instance;
01231 int stateid;
01232 int laststate;
01233 int isHint;
01234
01235 AST_LIST_ENTRY(skinny_speeddial) list;
01236 struct skinny_device *parent;
01237 };
01238
01239 struct skinny_addon {
01240 ast_mutex_t lock;
01241 char type[10];
01242 AST_LIST_ENTRY(skinny_addon) list;
01243 struct skinny_device *parent;
01244 };
01245
01246 #define SKINNY_DEVICE_OPTIONS \
01247 char name[80]; \
01248 char id[16]; \
01249 char version_id[16]; \
01250 char exten[AST_MAX_EXTENSION]; \
01251 char vmexten[AST_MAX_EXTENSION]; \
01252 int type; \
01253 int registered; \
01254 int lastlineinstance; \
01255 int lastcallreference; \
01256 int confcapability; \
01257 struct ast_codec_pref confprefs; \
01258 int capability; \
01259 int earlyrtp; \
01260 int transfer; \
01261 int callwaiting; \
01262 int mwiblink; \
01263 int dnd; \
01264 int prune;
01265
01266 struct skinny_device {
01267 SKINNY_DEVICE_OPTIONS
01268 struct type *first;
01269 struct type *last;
01270 ast_mutex_t lock;
01271 struct sockaddr_in addr;
01272 struct in_addr ourip;
01273 struct ast_ha *ha;
01274 struct skinnysession *session;
01275 struct skinny_line *activeline;
01276 AST_LIST_HEAD(, skinny_line) lines;
01277 AST_LIST_HEAD(, skinny_speeddial) speeddials;
01278 AST_LIST_HEAD(, skinny_addon) addons;
01279 AST_LIST_ENTRY(skinny_device) list;
01280 };
01281
01282 struct skinny_device_options{
01283 SKINNY_DEVICE_OPTIONS
01284 } default_device_struct = {
01285 .transfer = 1,
01286 .earlyrtp = 1,
01287 .callwaiting = 1,
01288 .mwiblink = 0,
01289 .dnd = 0,
01290 .confcapability = AST_FORMAT_ULAW | AST_FORMAT_ALAW,
01291 .capability = 0,
01292 .prune = 0,
01293 };
01294 struct skinny_device_options *default_device = &default_device_struct;
01295
01296 static AST_LIST_HEAD_STATIC(devices, skinny_device);
01297
01298
01299
01300
01301
01302
01303
01304
01305
01306
01307 struct skinnysession {
01308 pthread_t t;
01309 ast_mutex_t lock;
01310 struct sockaddr_in sin;
01311 int fd;
01312 char inbuf[SKINNY_MAX_PACKET];
01313 char outbuf[SKINNY_MAX_PACKET];
01314 struct skinny_device *device;
01315 AST_LIST_ENTRY(skinnysession) list;
01316 };
01317
01318 static AST_LIST_HEAD_STATIC(sessions, skinnysession);
01319
01320 static struct ast_channel *skinny_request(const char *type, int format, void *data, int *cause);
01321 static int skinny_devicestate(void *data);
01322 static int skinny_call(struct ast_channel *ast, char *dest, int timeout);
01323 static int skinny_hangup(struct ast_channel *ast);
01324 static int skinny_answer(struct ast_channel *ast);
01325 static struct ast_frame *skinny_read(struct ast_channel *ast);
01326 static int skinny_write(struct ast_channel *ast, struct ast_frame *frame);
01327 static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, size_t datalen);
01328 static int skinny_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
01329 static int skinny_senddigit_begin(struct ast_channel *ast, char digit);
01330 static int skinny_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration);
01331 static int handle_time_date_req_message(struct skinny_req *req, struct skinnysession *s);
01332 static void mwi_event_cb(const struct ast_event *event, void *userdata);
01333 static int skinny_reload(void);
01334
01335 static const struct ast_channel_tech skinny_tech = {
01336 .type = "Skinny",
01337 .description = tdesc,
01338 .capabilities = AST_FORMAT_AUDIO_MASK,
01339 .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER,
01340 .requester = skinny_request,
01341 .devicestate = skinny_devicestate,
01342 .call = skinny_call,
01343 .hangup = skinny_hangup,
01344 .answer = skinny_answer,
01345 .read = skinny_read,
01346 .write = skinny_write,
01347 .indicate = skinny_indicate,
01348 .fixup = skinny_fixup,
01349 .send_digit_begin = skinny_senddigit_begin,
01350 .send_digit_end = skinny_senddigit_end,
01351 .bridge = ast_rtp_bridge,
01352 };
01353
01354 static int skinny_extensionstate_cb(char *context, char* exten, int state, void *data);
01355 static int skinny_transfer(struct skinny_subchannel *sub);
01356
01357 static void *get_button_template(struct skinnysession *s, struct button_definition_template *btn)
01358 {
01359 struct skinny_device *d = s->device;
01360 struct skinny_addon *a;
01361 int i;
01362
01363 switch (d->type) {
01364 case SKINNY_DEVICE_30SPPLUS:
01365 case SKINNY_DEVICE_30VIP:
01366
01367 for (i = 0; i < 4; i++)
01368 (btn++)->buttonDefinition = BT_CUST_LINE;
01369 (btn++)->buttonDefinition = BT_REDIAL;
01370 (btn++)->buttonDefinition = BT_VOICEMAIL;
01371 (btn++)->buttonDefinition = BT_CALLPARK;
01372 (btn++)->buttonDefinition = BT_FORWARDALL;
01373 (btn++)->buttonDefinition = BT_CONFERENCE;
01374 for (i = 0; i < 4; i++)
01375 (btn++)->buttonDefinition = BT_NONE;
01376 for (i = 0; i < 13; i++)
01377 (btn++)->buttonDefinition = BT_SPEEDDIAL;
01378
01379 break;
01380 case SKINNY_DEVICE_12SPPLUS:
01381 case SKINNY_DEVICE_12SP:
01382 case SKINNY_DEVICE_12:
01383
01384 for (i = 0; i < 2; i++)
01385 (btn++)->buttonDefinition = BT_CUST_LINE;
01386 for (i = 0; i < 4; i++)
01387 (btn++)->buttonDefinition = BT_SPEEDDIAL;
01388 (btn++)->buttonDefinition = BT_HOLD;
01389 (btn++)->buttonDefinition = BT_REDIAL;
01390 (btn++)->buttonDefinition = BT_TRANSFER;
01391 (btn++)->buttonDefinition = BT_FORWARDALL;
01392 (btn++)->buttonDefinition = BT_CALLPARK;
01393 (btn++)->buttonDefinition = BT_VOICEMAIL;
01394 break;
01395 case SKINNY_DEVICE_7910:
01396 (btn++)->buttonDefinition = BT_LINE;
01397 (btn++)->buttonDefinition = BT_HOLD;
01398 (btn++)->buttonDefinition = BT_TRANSFER;
01399 (btn++)->buttonDefinition = BT_DISPLAY;
01400 (btn++)->buttonDefinition = BT_VOICEMAIL;
01401 (btn++)->buttonDefinition = BT_CONFERENCE;
01402 (btn++)->buttonDefinition = BT_FORWARDALL;
01403 for (i = 0; i < 2; i++)
01404 (btn++)->buttonDefinition = BT_SPEEDDIAL;
01405 (btn++)->buttonDefinition = BT_REDIAL;
01406 break;
01407 case SKINNY_DEVICE_7960:
01408 case SKINNY_DEVICE_7961:
01409 case SKINNY_DEVICE_7961GE:
01410 case SKINNY_DEVICE_7962:
01411 case SKINNY_DEVICE_7965:
01412 for (i = 0; i < 6; i++)
01413 (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL;
01414 break;
01415 case SKINNY_DEVICE_7940:
01416 case SKINNY_DEVICE_7941:
01417 case SKINNY_DEVICE_7941GE:
01418 case SKINNY_DEVICE_7942:
01419 case SKINNY_DEVICE_7945:
01420 for (i = 0; i < 2; i++)
01421 (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL;
01422 break;
01423 case SKINNY_DEVICE_7935:
01424 case SKINNY_DEVICE_7936:
01425 for (i = 0; i < 2; i++)
01426 (btn++)->buttonDefinition = BT_LINE;
01427 break;
01428 case SKINNY_DEVICE_ATA186:
01429 (btn++)->buttonDefinition = BT_LINE;
01430 break;
01431 case SKINNY_DEVICE_7970:
01432 case SKINNY_DEVICE_7971:
01433 case SKINNY_DEVICE_7975:
01434 case SKINNY_DEVICE_CIPC:
01435 for (i = 0; i < 8; i++)
01436 (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL;
01437 break;
01438 case SKINNY_DEVICE_7985:
01439
01440 ast_log(LOG_WARNING, "Unsupported device type '%d (7985)' found.\n", d->type);
01441 break;
01442 case SKINNY_DEVICE_7912:
01443 case SKINNY_DEVICE_7911:
01444 case SKINNY_DEVICE_7905:
01445 (btn++)->buttonDefinition = BT_LINE;
01446 (btn++)->buttonDefinition = BT_HOLD;
01447 break;
01448 case SKINNY_DEVICE_7920:
01449
01450 for (i = 0; i < 4; i++)
01451 (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL;
01452 break;
01453 case SKINNY_DEVICE_7921:
01454 for (i = 0; i < 6; i++)
01455 (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL;
01456 break;
01457 case SKINNY_DEVICE_7902:
01458 ast_log(LOG_WARNING, "Unsupported device type '%d (7902)' found.\n", d->type);
01459 break;
01460 case SKINNY_DEVICE_7906:
01461 ast_log(LOG_WARNING, "Unsupported device type '%d (7906)' found.\n", d->type);
01462 break;
01463 case SKINNY_DEVICE_7931:
01464 ast_log(LOG_WARNING, "Unsupported device type '%d (7931)' found.\n", d->type);
01465 break;
01466 case SKINNY_DEVICE_7937:
01467 ast_log(LOG_WARNING, "Unsupported device type '%d (7937)' found.\n", d->type);
01468 break;
01469 case SKINNY_DEVICE_7914:
01470 ast_log(LOG_WARNING, "Unsupported device type '%d (7914)' found. Expansion module registered by itself?\n", d->type);
01471 break;
01472 case SKINNY_DEVICE_SCCPGATEWAY_AN:
01473 case SKINNY_DEVICE_SCCPGATEWAY_BRI:
01474 ast_log(LOG_WARNING, "Unsupported device type '%d (SCCP gateway)' found.\n", d->type);
01475 break;
01476 default:
01477 ast_log(LOG_WARNING, "Unknown device type '%d' found.\n", d->type);
01478 break;
01479 }
01480
01481 AST_LIST_LOCK(&d->addons);
01482 AST_LIST_TRAVERSE(&d->addons, a, list) {
01483 if (!strcasecmp(a->type, "7914")) {
01484 for (i = 0; i < 14; i++)
01485 (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL;
01486 } else {
01487 ast_log(LOG_WARNING, "Unknown addon type '%s' found. Skipping.\n", a->type);
01488 }
01489 }
01490 AST_LIST_UNLOCK(&d->addons);
01491
01492 return btn;
01493 }
01494
01495 static struct skinny_req *req_alloc(size_t size, int response_message)
01496 {
01497 struct skinny_req *req;
01498
01499 if (!(req = ast_calloc(1, skinny_header_size + size + 4)))
01500 return NULL;
01501
01502 req->len = htolel(size+4);
01503 req->e = htolel(response_message);
01504
01505 return req;
01506 }
01507
01508 static struct skinny_line *find_line_by_instance(struct skinny_device *d, int instance)
01509 {
01510 struct skinny_line *l;
01511
01512
01513
01514
01515 if (!instance)
01516 instance = 1;
01517
01518 AST_LIST_TRAVERSE(&d->lines, l, list){
01519 if (l->instance == instance)
01520 break;
01521 }
01522
01523 if (!l) {
01524 ast_log(LOG_WARNING, "Could not find line with instance '%d' on device '%s'\n", instance, d->name);
01525 }
01526 return l;
01527 }
01528
01529 static struct skinny_line *find_line_by_name(const char *dest)
01530 {
01531 struct skinny_line *l;
01532 struct skinny_line *tmpl = NULL;
01533 struct skinny_device *d;
01534 char line[256];
01535 char *at;
01536 char *device;
01537 int checkdevice = 0;
01538
01539 ast_copy_string(line, dest, sizeof(line));
01540 at = strchr(line, '@');
01541 if (at)
01542 *at++ = '\0';
01543 device = at;
01544
01545 if (!ast_strlen_zero(device))
01546 checkdevice = 1;
01547
01548 AST_LIST_LOCK(&devices);
01549 AST_LIST_TRAVERSE(&devices, d, list){
01550 if (checkdevice && tmpl)
01551 break;
01552 else if (!checkdevice) {
01553
01554 } else if (!strcasecmp(d->name, device)) {
01555 if (skinnydebug)
01556 ast_verb(2, "Found device: %s\n", d->name);
01557 } else
01558 continue;
01559
01560
01561 AST_LIST_TRAVERSE(&d->lines, l, list){
01562
01563 if (!strcasecmp(l->name, line)) {
01564 if (tmpl) {
01565 ast_verb(2, "Ambiguous line name: %s\n", line);
01566 AST_LIST_UNLOCK(&devices);
01567 return NULL;
01568 } else
01569 tmpl = l;
01570 }
01571 }
01572 }
01573 AST_LIST_UNLOCK(&devices);
01574 return tmpl;
01575 }
01576
01577
01578
01579
01580 static struct ast_variable *add_var(const char *buf, struct ast_variable *list)
01581 {
01582 struct ast_variable *tmpvar = NULL;
01583 char *varname = ast_strdupa(buf), *varval = NULL;
01584
01585 if ((varval = strchr(varname,'='))) {
01586 *varval++ = '\0';
01587 if ((tmpvar = ast_variable_new(varname, varval, ""))) {
01588 tmpvar->next = list;
01589 list = tmpvar;
01590 }
01591 }
01592 return list;
01593 }
01594
01595
01596 static struct skinny_subchannel *find_subchannel_by_instance_reference(struct skinny_device *d, int instance, int reference)
01597 {
01598 struct skinny_line *l = find_line_by_instance(d, instance);
01599 struct skinny_subchannel *sub;
01600
01601 if (!l) {
01602 return NULL;
01603 }
01604
01605
01606
01607
01608 if (!reference)
01609 sub = AST_LIST_FIRST(&l->sub);
01610 else {
01611 AST_LIST_TRAVERSE(&l->sub, sub, list) {
01612 if (sub->callid == reference)
01613 break;
01614 }
01615 }
01616 if (!sub) {
01617 ast_log(LOG_WARNING, "Could not find subchannel with reference '%d' on '%s'\n", reference, d->name);
01618 }
01619 return sub;
01620 }
01621
01622
01623 static struct skinny_subchannel *find_subchannel_by_reference(struct skinny_device *d, int reference)
01624 {
01625 struct skinny_line *l;
01626 struct skinny_subchannel *sub = NULL;
01627
01628 AST_LIST_TRAVERSE(&d->lines, l, list){
01629 AST_LIST_TRAVERSE(&l->sub, sub, list){
01630 if (sub->callid == reference)
01631 break;
01632 }
01633 if (sub)
01634 break;
01635 }
01636
01637 if (!l) {
01638 ast_log(LOG_WARNING, "Could not find any lines that contained a subchannel with reference '%d' on device '%s'\n", reference, d->name);
01639 } else {
01640 if (!sub) {
01641 ast_log(LOG_WARNING, "Could not find subchannel with reference '%d' on '%s@%s'\n", reference, l->name, d->name);
01642 }
01643 }
01644 return sub;
01645 }
01646
01647 static struct skinny_speeddial *find_speeddial_by_instance(struct skinny_device *d, int instance, int isHint)
01648 {
01649 struct skinny_speeddial *sd;
01650
01651 AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
01652 if (sd->isHint == isHint && sd->instance == instance)
01653 break;
01654 }
01655
01656 if (!sd) {
01657 ast_log(LOG_WARNING, "Could not find speeddial with instance '%d' on device '%s'\n", instance, d->name);
01658 }
01659 return sd;
01660 }
01661
01662 static int codec_skinny2ast(enum skinny_codecs skinnycodec)
01663 {
01664 switch (skinnycodec) {
01665 case SKINNY_CODEC_ALAW:
01666 return AST_FORMAT_ALAW;
01667 case SKINNY_CODEC_ULAW:
01668 return AST_FORMAT_ULAW;
01669 case SKINNY_CODEC_G723_1:
01670 return AST_FORMAT_G723_1;
01671 case SKINNY_CODEC_G729A:
01672 return AST_FORMAT_G729A;
01673 case SKINNY_CODEC_G726_32:
01674 return AST_FORMAT_G726_AAL2;
01675 case SKINNY_CODEC_H261:
01676 return AST_FORMAT_H261;
01677 case SKINNY_CODEC_H263:
01678 return AST_FORMAT_H263;
01679 default:
01680 return 0;
01681 }
01682 }
01683
01684 static int codec_ast2skinny(int astcodec)
01685 {
01686 switch (astcodec) {
01687 case AST_FORMAT_ALAW:
01688 return SKINNY_CODEC_ALAW;
01689 case AST_FORMAT_ULAW:
01690 return SKINNY_CODEC_ULAW;
01691 case AST_FORMAT_G723_1:
01692 return SKINNY_CODEC_G723_1;
01693 case AST_FORMAT_G729A:
01694 return SKINNY_CODEC_G729A;
01695 case AST_FORMAT_G726_AAL2:
01696 return SKINNY_CODEC_G726_32;
01697 case AST_FORMAT_H261:
01698 return SKINNY_CODEC_H261;
01699 case AST_FORMAT_H263:
01700 return SKINNY_CODEC_H263;
01701 default:
01702 return 0;
01703 }
01704 }
01705
01706 static int set_callforwards(struct skinny_line *l, const char *cfwd, int cfwdtype)
01707 {
01708 if (!l)
01709 return 0;
01710
01711 if (!ast_strlen_zero(cfwd)) {
01712 if (cfwdtype & SKINNY_CFWD_ALL) {
01713 l->cfwdtype |= SKINNY_CFWD_ALL;
01714 ast_copy_string(l->call_forward_all, cfwd, sizeof(l->call_forward_all));
01715 }
01716 if (cfwdtype & SKINNY_CFWD_BUSY) {
01717 l->cfwdtype |= SKINNY_CFWD_BUSY;
01718 ast_copy_string(l->call_forward_busy, cfwd, sizeof(l->call_forward_busy));
01719 }
01720 if (cfwdtype & SKINNY_CFWD_NOANSWER) {
01721 l->cfwdtype |= SKINNY_CFWD_NOANSWER;
01722 ast_copy_string(l->call_forward_noanswer, cfwd, sizeof(l->call_forward_noanswer));
01723 }
01724 } else {
01725 if (cfwdtype & SKINNY_CFWD_ALL) {
01726 l->cfwdtype &= ~SKINNY_CFWD_ALL;
01727 memset(l->call_forward_all, 0, sizeof(l->call_forward_all));
01728 }
01729 if (cfwdtype & SKINNY_CFWD_BUSY) {
01730 l->cfwdtype &= ~SKINNY_CFWD_BUSY;
01731 memset(l->call_forward_busy, 0, sizeof(l->call_forward_busy));
01732 }
01733 if (cfwdtype & SKINNY_CFWD_NOANSWER) {
01734 l->cfwdtype &= ~SKINNY_CFWD_NOANSWER;
01735 memset(l->call_forward_noanswer, 0, sizeof(l->call_forward_noanswer));
01736 }
01737 }
01738 return l->cfwdtype;
01739 }
01740
01741 static void cleanup_stale_contexts(char *new, char *old)
01742 {
01743 char *oldcontext, *newcontext, *stalecontext, *stringp, newlist[AST_MAX_CONTEXT];
01744
01745 while ((oldcontext = strsep(&old, "&"))) {
01746 stalecontext = '\0';
01747 ast_copy_string(newlist, new, sizeof(newlist));
01748 stringp = newlist;
01749 while ((newcontext = strsep(&stringp, "&"))) {
01750 if (strcmp(newcontext, oldcontext) == 0) {
01751
01752 stalecontext = '\0';
01753 break;
01754 } else if (strcmp(newcontext, oldcontext)) {
01755 stalecontext = oldcontext;
01756 }
01757
01758 }
01759 if (stalecontext)
01760 ast_context_destroy(ast_context_find(stalecontext), "Skinny");
01761 }
01762 }
01763
01764 static void register_exten(struct skinny_line *l)
01765 {
01766 char multi[256];
01767 char *stringp, *ext, *context;
01768
01769 if (ast_strlen_zero(regcontext))
01770 return;
01771
01772 ast_copy_string(multi, S_OR(l->regexten, l->name), sizeof(multi));
01773 stringp = multi;
01774 while ((ext = strsep(&stringp, "&"))) {
01775 if ((context = strchr(ext, '@'))) {
01776 *context++ = '\0';
01777 if (!ast_context_find(context)) {
01778 ast_log(LOG_WARNING, "Context %s must exist in regcontext= in skinny.conf!\n", context);
01779 continue;
01780 }
01781 } else {
01782 context = regcontext;
01783 }
01784 ast_add_extension(context, 1, ext, 1, NULL, NULL, "Noop",
01785 ast_strdup(l->name), ast_free_ptr, "Skinny");
01786 }
01787 }
01788
01789 static void unregister_exten(struct skinny_line *l)
01790 {
01791 char multi[256];
01792 char *stringp, *ext, *context;
01793
01794 if (ast_strlen_zero(regcontext))
01795 return;
01796
01797 ast_copy_string(multi, S_OR(l->regexten, l->name), sizeof(multi));
01798 stringp = multi;
01799 while ((ext = strsep(&stringp, "&"))) {
01800 if ((context = strchr(ext, '@'))) {
01801 *context++ = '\0';
01802 if (!ast_context_find(context)) {
01803 ast_log(LOG_WARNING, "Context %s must exist in regcontext= in skinny.conf!\n", context);
01804 continue;
01805 }
01806 } else {
01807 context = regcontext;
01808 }
01809 ast_context_remove_extension(context, ext, 1, NULL);
01810 }
01811 }
01812
01813 static int skinny_register(struct skinny_req *req, struct skinnysession *s)
01814 {
01815 struct skinny_device *d;
01816 struct skinny_line *l;
01817 struct skinny_speeddial *sd;
01818 struct sockaddr_in sin;
01819 socklen_t slen;
01820 int instance;
01821
01822 AST_LIST_LOCK(&devices);
01823 AST_LIST_TRAVERSE(&devices, d, list){
01824 if (!strcasecmp(req->data.reg.name, d->id)
01825 && ast_apply_ha(d->ha, &(s->sin))) {
01826 s->device = d;
01827 d->type = letohl(req->data.reg.type);
01828 if (ast_strlen_zero(d->version_id)) {
01829 ast_copy_string(d->version_id, version_id, sizeof(d->version_id));
01830 }
01831 d->registered = 1;
01832 d->session = s;
01833
01834 slen = sizeof(sin);
01835 if (getsockname(s->fd, (struct sockaddr *)&sin, &slen)) {
01836 ast_log(LOG_WARNING, "Cannot get socket name\n");
01837 sin.sin_addr = __ourip;
01838 }
01839 d->ourip = sin.sin_addr;
01840
01841 AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
01842 sd->stateid = ast_extension_state_add(sd->context, sd->exten, skinny_extensionstate_cb, sd);
01843 }
01844 instance = 0;
01845 AST_LIST_TRAVERSE(&d->lines, l, list) {
01846 instance++;
01847 }
01848 AST_LIST_TRAVERSE(&d->lines, l, list) {
01849
01850 if (l->device) {
01851 ast_verb(1, "Line %s already connected to %s. Not connecting to %s.\n", l->name, l->device->name, d->name);
01852 } else {
01853 l->device = d;
01854 l->capability = l->confcapability & d->capability;
01855 l->prefs = l->confprefs;
01856 if (!l->prefs.order[0]) {
01857 l->prefs = d->confprefs;
01858 }
01859
01860
01861 l->instance = instance;
01862 l->newmsgs = ast_app_has_voicemail(l->mailbox, NULL);
01863 set_callforwards(l, NULL, 0);
01864 register_exten(l);
01865
01866 mwi_event_cb(0, l);
01867 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
01868 }
01869 --instance;
01870 }
01871 break;
01872 }
01873 }
01874 AST_LIST_UNLOCK(&devices);
01875 if (!d) {
01876 return 0;
01877 }
01878 return 1;
01879 }
01880
01881 static int skinny_unregister(struct skinny_req *req, struct skinnysession *s)
01882 {
01883 struct skinny_device *d;
01884 struct skinny_line *l;
01885 struct skinny_speeddial *sd;
01886
01887 d = s->device;
01888
01889 if (d) {
01890 d->session = NULL;
01891 d->registered = 0;
01892
01893 AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
01894 if (sd->stateid > -1)
01895 ast_extension_state_del(sd->stateid, NULL);
01896 }
01897 AST_LIST_TRAVERSE(&d->lines, l, list) {
01898 if (l->device == d) {
01899 l->device = NULL;
01900 l->capability = 0;
01901 ast_parse_allow_disallow(&l->prefs, &l->capability, "all", 0);
01902 l->instance = 0;
01903 unregister_exten(l);
01904 ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Skinny/%s@%s", l->name, d->name);
01905 }
01906 }
01907 }
01908
01909 return -1;
01910 }
01911
01912 #ifdef SKINNY_DEVMODE
01913 static char *message2str(int type)
01914 {
01915 char *tmp;
01916
01917 switch (type) {
01918 case KEEP_ALIVE_MESSAGE:
01919 return "KEEP_ALIVE_MESSAGE";
01920 case REGISTER_MESSAGE:
01921 return "REGISTER_MESSAGE";
01922 case IP_PORT_MESSAGE:
01923 return "IP_PORT_MESSAGE";
01924 case KEYPAD_BUTTON_MESSAGE:
01925 return "KEYPAD_BUTTON_MESSAGE";
01926 case ENBLOC_CALL_MESSAGE:
01927 return "ENBLOC_CALL_MESSAGE";
01928 case STIMULUS_MESSAGE:
01929 return "STIMULUS_MESSAGE";
01930 case OFFHOOK_MESSAGE:
01931 return "OFFHOOK_MESSAGE";
01932 case ONHOOK_MESSAGE:
01933 return "ONHOOK_MESSAGE";
01934 case CAPABILITIES_RES_MESSAGE:
01935 return "CAPABILITIES_RES_MESSAGE";
01936 case SPEED_DIAL_STAT_REQ_MESSAGE:
01937 return "SPEED_DIAL_STAT_REQ_MESSAGE";
01938 case LINE_STATE_REQ_MESSAGE:
01939 return "LINE_STATE_REQ_MESSAGE";
01940 case TIME_DATE_REQ_MESSAGE:
01941 return "TIME_DATE_REQ_MESSAGE";
01942 case BUTTON_TEMPLATE_REQ_MESSAGE:
01943 return "BUTTON_TEMPLATE_REQ_MESSAGE";
01944 case VERSION_REQ_MESSAGE:
01945 return "VERSION_REQ_MESSAGE";
01946 case SERVER_REQUEST_MESSAGE:
01947 return "SERVER_REQUEST_MESSAGE";
01948 case ALARM_MESSAGE:
01949 return "ALARM_MESSAGE";
01950 case OPEN_RECEIVE_CHANNEL_ACK_MESSAGE:
01951 return "OPEN_RECEIVE_CHANNEL_ACK_MESSAGE";
01952 case SOFT_KEY_SET_REQ_MESSAGE:
01953 return "SOFT_KEY_SET_REQ_MESSAGE";
01954 case SOFT_KEY_EVENT_MESSAGE:
01955 return "SOFT_KEY_EVENT_MESSAGE";
01956 case UNREGISTER_MESSAGE:
01957 return "UNREGISTER_MESSAGE";
01958 case SOFT_KEY_TEMPLATE_REQ_MESSAGE:
01959 return "SOFT_KEY_TEMPLATE_REQ_MESSAGE";
01960 case HEADSET_STATUS_MESSAGE:
01961 return "HEADSET_STATUS_MESSAGE";
01962 case REGISTER_AVAILABLE_LINES_MESSAGE:
01963 return "REGISTER_AVAILABLE_LINES_MESSAGE";
01964 case REGISTER_ACK_MESSAGE:
01965 return "REGISTER_ACK_MESSAGE";
01966 case START_TONE_MESSAGE:
01967 return "START_TONE_MESSAGE";
01968 case STOP_TONE_MESSAGE:
01969 return "STOP_TONE_MESSAGE";
01970 case SET_RINGER_MESSAGE:
01971 return "SET_RINGER_MESSAGE";
01972 case SET_LAMP_MESSAGE:
01973 return "SET_LAMP_MESSAGE";
01974 case SET_SPEAKER_MESSAGE:
01975 return "SET_SPEAKER_MESSAGE";
01976 case SET_MICROPHONE_MESSAGE:
01977 return "SET_MICROPHONE_MESSAGE";
01978 case START_MEDIA_TRANSMISSION_MESSAGE:
01979 return "START_MEDIA_TRANSMISSION_MESSAGE";
01980 case STOP_MEDIA_TRANSMISSION_MESSAGE:
01981 return "STOP_MEDIA_TRANSMISSION_MESSAGE";
01982 case CALL_INFO_MESSAGE:
01983 return "CALL_INFO_MESSAGE";
01984 case FORWARD_STAT_MESSAGE:
01985 return "FORWARD_STAT_MESSAGE";
01986 case SPEED_DIAL_STAT_RES_MESSAGE:
01987 return "SPEED_DIAL_STAT_RES_MESSAGE";
01988 case LINE_STAT_RES_MESSAGE:
01989 return "LINE_STAT_RES_MESSAGE";
01990 case DEFINETIMEDATE_MESSAGE:
01991 return "DEFINETIMEDATE_MESSAGE";
01992 case BUTTON_TEMPLATE_RES_MESSAGE:
01993 return "BUTTON_TEMPLATE_RES_MESSAGE";
01994 case VERSION_RES_MESSAGE:
01995 return "VERSION_RES_MESSAGE";
01996 case DISPLAYTEXT_MESSAGE:
01997 return "DISPLAYTEXT_MESSAGE";
01998 case CLEAR_NOTIFY_MESSAGE:
01999 return "CLEAR_NOTIFY_MESSAGE";
02000 case CLEAR_DISPLAY_MESSAGE:
02001 return "CLEAR_DISPLAY_MESSAGE";
02002 case CAPABILITIES_REQ_MESSAGE:
02003 return "CAPABILITIES_REQ_MESSAGE";
02004 case REGISTER_REJ_MESSAGE:
02005 return "REGISTER_REJ_MESSAGE";
02006 case SERVER_RES_MESSAGE:
02007 return "SERVER_RES_MESSAGE";
02008 case RESET_MESSAGE:
02009 return "RESET_MESSAGE";
02010 case KEEP_ALIVE_ACK_MESSAGE:
02011 return "KEEP_ALIVE_ACK_MESSAGE";
02012 case OPEN_RECEIVE_CHANNEL_MESSAGE:
02013 return "OPEN_RECEIVE_CHANNEL_MESSAGE";
02014 case CLOSE_RECEIVE_CHANNEL_MESSAGE:
02015 return "CLOSE_RECEIVE_CHANNEL_MESSAGE";
02016 case SOFT_KEY_TEMPLATE_RES_MESSAGE:
02017 return "SOFT_KEY_TEMPLATE_RES_MESSAGE";
02018 case SOFT_KEY_SET_RES_MESSAGE:
02019 return "SOFT_KEY_SET_RES_MESSAGE";
02020 case SELECT_SOFT_KEYS_MESSAGE:
02021 return "SELECT_SOFT_KEYS_MESSAGE";
02022 case CALL_STATE_MESSAGE:
02023 return "CALL_STATE_MESSAGE";
02024 case DISPLAY_PROMPT_STATUS_MESSAGE:
02025 return "DISPLAY_PROMPT_STATUS_MESSAGE";
02026 case CLEAR_PROMPT_MESSAGE:
02027 return "CLEAR_PROMPT_MESSAGE";
02028 case DISPLAY_NOTIFY_MESSAGE:
02029 return "DISPLAY_NOTIFY_MESSAGE";
02030 case ACTIVATE_CALL_PLANE_MESSAGE:
02031 return "ACTIVATE_CALL_PLANE_MESSAGE";
02032 case DIALED_NUMBER_MESSAGE:
02033 return "DIALED_NUMBER_MESSAGE";
02034 default:
02035 if (!(tmp = ast_threadstorage_get(&message2str_threadbuf, MESSAGE2STR_BUFSIZE)))
02036 return "Unknown";
02037 snprintf(tmp, MESSAGE2STR_BUFSIZE, "UNKNOWN_MESSAGE-%d", type);
02038 return tmp;
02039 }
02040 }
02041 #endif
02042
02043 static int transmit_response(struct skinny_device *d, struct skinny_req *req)
02044 {
02045 struct skinnysession *s = d->session;
02046 int res = 0;
02047
02048 if (!s) {
02049 ast_log(LOG_WARNING, "Asked to transmit to a non-existent session!\n");
02050 return -1;
02051 }
02052
02053 ast_mutex_lock(&s->lock);
02054
02055 SKINNY_DEVONLY(if (skinnydebug>1) ast_verb(4, "Transmitting %s to %s\n", message2str(req->e), d->name);)
02056
02057 if (letohl(req->len > SKINNY_MAX_PACKET) || letohl(req->len < 0)) {
02058 ast_log(LOG_WARNING, "transmit_response: the length of the request is out of bounds\n");
02059 ast_mutex_unlock(&s->lock);
02060 return -1;
02061 }
02062
02063 memset(s->outbuf, 0, sizeof(s->outbuf));
02064 memcpy(s->outbuf, req, skinny_header_size);
02065 memcpy(s->outbuf+skinny_header_size, &req->data, letohl(req->len));
02066
02067 res = write(s->fd, s->outbuf, letohl(req->len)+8);
02068
02069 if (res != letohl(req->len)+8) {
02070 ast_log(LOG_WARNING, "Transmit: write only sent %d out of %d bytes: %s\n", res, letohl(req->len)+8, strerror(errno));
02071 if (res == -1) {
02072 if (skinnydebug)
02073 ast_log(LOG_WARNING, "Transmit: Skinny Client was lost, unregistering\n");
02074 skinny_unregister(NULL, s);
02075 }
02076
02077 }
02078
02079 ast_free(req);
02080 ast_mutex_unlock(&s->lock);
02081 return 1;
02082 }
02083
02084 static void transmit_speaker_mode(struct skinny_device *d, int mode)
02085 {
02086 struct skinny_req *req;
02087
02088 if (!(req = req_alloc(sizeof(struct set_speaker_message), SET_SPEAKER_MESSAGE)))
02089 return;
02090
02091 req->data.setspeaker.mode = htolel(mode);
02092 transmit_response(d, req);
02093 }
02094
02095
02096
02097
02098
02099
02100
02101
02102
02103
02104
02105
02106
02107 static void transmit_callinfo(struct skinny_device *d, const char *fromname, const char *fromnum, const char *toname, const char *tonum, int instance, int callid, int calltype)
02108 {
02109 struct skinny_req *req;
02110
02111
02112 if (!d)
02113 return;
02114
02115 if (!(req = req_alloc(sizeof(struct call_info_message), CALL_INFO_MESSAGE)))
02116 return;
02117
02118 if (skinnydebug)
02119 ast_verb(1, "Setting Callinfo to %s(%s) from %s(%s) on %s(%d)\n", fromname, fromnum, toname, tonum, d->name, instance);
02120
02121 if (fromname) {
02122 ast_copy_string(req->data.callinfo.callingPartyName, fromname, sizeof(req->data.callinfo.callingPartyName));
02123 }
02124 if (fromnum) {
02125 ast_copy_string(req->data.callinfo.callingParty, fromnum, sizeof(req->data.callinfo.callingParty));
02126 }
02127 if (toname) {
02128 ast_copy_string(req->data.callinfo.calledPartyName, toname, sizeof(req->data.callinfo.calledPartyName));
02129 }
02130 if (tonum) {
02131 ast_copy_string(req->data.callinfo.calledParty, tonum, sizeof(req->data.callinfo.calledParty));
02132 }
02133 req->data.callinfo.instance = htolel(instance);
02134 req->data.callinfo.reference = htolel(callid);
02135 req->data.callinfo.type = htolel(calltype);
02136 transmit_response(d, req);
02137 }
02138
02139 static void transmit_connect(struct skinny_device *d, struct skinny_subchannel *sub)
02140 {
02141 struct skinny_req *req;
02142 struct skinny_line *l = sub->parent;
02143 struct ast_format_list fmt;
02144
02145 if (!(req = req_alloc(sizeof(struct open_receive_channel_message), OPEN_RECEIVE_CHANNEL_MESSAGE)))
02146 return;
02147
02148 fmt = ast_codec_pref_getsize(&l->prefs, ast_best_codec(l->capability));
02149
02150 req->data.openreceivechannel.conferenceId = htolel(sub->callid);
02151 req->data.openreceivechannel.partyId = htolel(sub->callid);
02152 req->data.openreceivechannel.packets = htolel(fmt.cur_ms);
02153 req->data.openreceivechannel.capability = htolel(codec_ast2skinny(fmt.bits));
02154 req->data.openreceivechannel.echo = htolel(0);
02155 req->data.openreceivechannel.bitrate = htolel(0);
02156 transmit_response(d, req);
02157 }
02158
02159 static void transmit_tone(struct skinny_device *d, int tone, int instance, int reference)
02160 {
02161 struct skinny_req *req;
02162
02163 if (tone == SKINNY_NOTONE) {
02164
02165 return;
02166 }
02167
02168 if (tone > 0) {
02169 if (!(req = req_alloc(sizeof(struct start_tone_message), START_TONE_MESSAGE)))
02170 return;
02171 req->data.starttone.tone = htolel(tone);
02172 req->data.starttone.instance = htolel(instance);
02173 req->data.starttone.reference = htolel(reference);
02174 } else {
02175 if (!(req = req_alloc(sizeof(struct stop_tone_message), STOP_TONE_MESSAGE)))
02176 return;
02177 req->data.stoptone.instance = htolel(instance);
02178 req->data.stoptone.reference = htolel(reference);
02179 }
02180
02181
02182
02183
02184
02185
02186 transmit_response(d, req);
02187 }
02188
02189 static void transmit_selectsoftkeys(struct skinny_device *d, int instance, int callid, int softkey)
02190 {
02191 struct skinny_req *req;
02192
02193 if (!(req = req_alloc(sizeof(struct select_soft_keys_message), SELECT_SOFT_KEYS_MESSAGE)))
02194 return;
02195
02196 req->data.selectsoftkey.instance = htolel(instance);
02197 req->data.selectsoftkey.reference = htolel(callid);
02198 req->data.selectsoftkey.softKeySetIndex = htolel(softkey);
02199 req->data.selectsoftkey.validKeyMask = htolel(0xFFFFFFFF);
02200 transmit_response(d, req);
02201 }
02202
02203 static void transmit_lamp_indication(struct skinny_device *d, int stimulus, int instance, int indication)
02204 {
02205 struct skinny_req *req;
02206
02207 if (!(req = req_alloc(sizeof(struct set_lamp_message), SET_LAMP_MESSAGE)))
02208 return;
02209
02210 req->data.setlamp.stimulus = htolel(stimulus);
02211 req->data.setlamp.stimulusInstance = htolel(instance);
02212 req->data.setlamp.deviceStimulus = htolel(indication);
02213 transmit_response(d, req);
02214 }
02215
02216 static void transmit_ringer_mode(struct skinny_device *d, int mode)
02217 {
02218 struct skinny_req *req;
02219
02220 if (skinnydebug)
02221 ast_verb(1, "Setting ringer mode to '%d'.\n", mode);
02222
02223 if (!(req = req_alloc(sizeof(struct set_ringer_message), SET_RINGER_MESSAGE)))
02224 return;
02225
02226 req->data.setringer.ringerMode = htolel(mode);
02227
02228
02229
02230
02231
02232
02233
02234 req->data.setringer.unknown1 = htolel(1);
02235
02236
02237 req->data.setringer.unknown2 = htolel(1);
02238 transmit_response(d, req);
02239 }
02240
02241 static void transmit_displaymessage(struct skinny_device *d, const char *text, int instance, int reference)
02242 {
02243 struct skinny_req *req;
02244
02245 if (text == 0) {
02246 if (!(req = req_alloc(0, CLEAR_DISPLAY_MESSAGE)))
02247 return;
02248
02249
02250
02251
02252
02253
02254
02255 handle_time_date_req_message(NULL, d->session);
02256
02257 if (skinnydebug)
02258 ast_verb(1, "Clearing Display\n");
02259 } else {
02260 if (!(req = req_alloc(sizeof(struct displaytext_message), DISPLAYTEXT_MESSAGE)))
02261 return;
02262
02263 ast_copy_string(req->data.displaytext.text, text, sizeof(req->data.displaytext.text));
02264 if (skinnydebug)
02265 ast_verb(1, "Displaying message '%s'\n", req->data.displaytext.text);
02266 }
02267
02268 transmit_response(d, req);
02269 }
02270
02271 static void transmit_displaynotify(struct skinny_device *d, const char *text, int t)
02272 {
02273 struct skinny_req *req;
02274
02275 if (!(req = req_alloc(sizeof(struct display_notify_message), DISPLAY_NOTIFY_MESSAGE)))
02276 return;
02277
02278 ast_copy_string(req->data.displaynotify.displayMessage, text, sizeof(req->data.displaynotify.displayMessage));
02279 req->data.displaynotify.displayTimeout = htolel(t);
02280
02281 if (skinnydebug)
02282 ast_verb(1, "Displaying notify '%s'\n", text);
02283
02284 transmit_response(d, req);
02285 }
02286
02287 static void transmit_displaypromptstatus(struct skinny_device *d, const char *text, int t, int instance, int callid)
02288 {
02289 struct skinny_req *req;
02290
02291 if (text == 0) {
02292 if (!(req = req_alloc(sizeof(struct clear_prompt_message), CLEAR_PROMPT_MESSAGE)))
02293 return;
02294
02295 req->data.clearpromptstatus.lineInstance = htolel(instance);
02296 req->data.clearpromptstatus.callReference = htolel(callid);
02297
02298 if (skinnydebug)
02299 ast_verb(1, "Clearing Prompt\n");
02300 } else {
02301 if (!(req = req_alloc(sizeof(struct display_prompt_status_message), DISPLAY_PROMPT_STATUS_MESSAGE)))
02302 return;
02303
02304 ast_copy_string(req->data.displaypromptstatus.promptMessage, text, sizeof(req->data.displaypromptstatus.promptMessage));
02305 req->data.displaypromptstatus.messageTimeout = htolel(t);
02306 req->data.displaypromptstatus.lineInstance = htolel(instance);
02307 req->data.displaypromptstatus.callReference = htolel(callid);
02308
02309 if (skinnydebug)
02310 ast_verb(1, "Displaying Prompt Status '%s'\n", text);
02311 }
02312
02313 transmit_response(d, req);
02314 }
02315
02316 static void transmit_dialednumber(struct skinny_device *d, const char *text, int instance, int callid)
02317 {
02318 struct skinny_req *req;
02319
02320 if (!(req = req_alloc(sizeof(struct dialed_number_message), DIALED_NUMBER_MESSAGE)))
02321 return;
02322
02323 ast_copy_string(req->data.dialednumber.dialedNumber, text, sizeof(req->data.dialednumber.dialedNumber));
02324 req->data.dialednumber.lineInstance = htolel(instance);
02325 req->data.dialednumber.callReference = htolel(callid);
02326
02327 transmit_response(d, req);
02328 }
02329
02330 static void transmit_closereceivechannel(struct skinny_device *d, struct skinny_subchannel *sub)
02331 {
02332 struct skinny_req *req;
02333
02334 if (!(req = req_alloc(sizeof(struct close_receive_channel_message), CLOSE_RECEIVE_CHANNEL_MESSAGE)))
02335 return;
02336
02337 req->data.closereceivechannel.conferenceId = htolel(0);
02338 req->data.closereceivechannel.partyId = htolel(sub->callid);
02339 transmit_response(d, req);
02340 }
02341
02342 static void transmit_stopmediatransmission(struct skinny_device *d, struct skinny_subchannel *sub)
02343 {
02344 struct skinny_req *req;
02345
02346 if (!(req = req_alloc(sizeof(struct stop_media_transmission_message), STOP_MEDIA_TRANSMISSION_MESSAGE)))
02347 return;
02348
02349 req->data.stopmedia.conferenceId = htolel(0);
02350 req->data.stopmedia.passThruPartyId = htolel(sub->callid);
02351 transmit_response(d, req);
02352 }
02353
02354 static void transmit_activatecallplane(struct skinny_device *d, struct skinny_line *l)
02355 {
02356 struct skinny_req *req;
02357
02358 if (!(req = req_alloc(sizeof(struct activate_call_plane_message), ACTIVATE_CALL_PLANE_MESSAGE)))
02359 return;
02360
02361 req->data.activatecallplane.lineInstance = htolel(l->instance);
02362 transmit_response(d, req);
02363 }
02364
02365 static void transmit_callstateonly(struct skinny_device *d, struct skinny_subchannel *sub, int state)
02366 {
02367 struct skinny_req *req;
02368
02369 if (!(req = req_alloc(sizeof(struct call_state_message), CALL_STATE_MESSAGE)))
02370 return;
02371
02372 req->data.callstate.callState = htolel(state);
02373 req->data.callstate.lineInstance = htolel(sub->parent->instance);
02374 req->data.callstate.callReference = htolel(sub->callid);
02375 transmit_response(d, req);
02376 }
02377
02378 static void transmit_callstate(struct skinny_device *d, int instance, int state, unsigned callid)
02379 {
02380 struct skinny_req *req;
02381
02382 if (state == SKINNY_ONHOOK) {
02383 if (!(req = req_alloc(sizeof(struct close_receive_channel_message), CLOSE_RECEIVE_CHANNEL_MESSAGE)))
02384 return;
02385
02386 req->data.closereceivechannel.conferenceId = htolel(callid);
02387 req->data.closereceivechannel.partyId = htolel(callid);
02388 transmit_response(d, req);
02389
02390 if (!(req = req_alloc(sizeof(struct stop_media_transmission_message), STOP_MEDIA_TRANSMISSION_MESSAGE)))
02391 return;
02392
02393 req->data.stopmedia.conferenceId = htolel(callid);
02394 req->data.stopmedia.passThruPartyId = htolel(callid);
02395 transmit_response(d, req);
02396
02397 transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
02398
02399 transmit_displaypromptstatus(d, NULL, 0, instance, callid);
02400 }
02401
02402 if (!(req = req_alloc(sizeof(struct call_state_message), CALL_STATE_MESSAGE)))
02403 return;
02404
02405 req->data.callstate.callState = htolel(state);
02406 req->data.callstate.lineInstance = htolel(instance);
02407 req->data.callstate.callReference = htolel(callid);
02408 transmit_response(d, req);
02409
02410 if (state == SKINNY_ONHOOK) {
02411 transmit_selectsoftkeys(d, 0, 0, KEYDEF_ONHOOK);
02412 }
02413
02414 if (state == SKINNY_OFFHOOK || state == SKINNY_ONHOOK) {
02415 if (!(req = req_alloc(sizeof(struct activate_call_plane_message), ACTIVATE_CALL_PLANE_MESSAGE)))
02416 return;
02417
02418 req->data.activatecallplane.lineInstance = htolel(instance);
02419 transmit_response(d, req);
02420 }
02421 }
02422
02423
02424 static void transmit_cfwdstate(struct skinny_device *d, struct skinny_line *l)
02425 {
02426 struct skinny_req *req;
02427 int anyon = 0;
02428
02429 if (!(req = req_alloc(sizeof(struct forward_stat_message), FORWARD_STAT_MESSAGE)))
02430 return;
02431
02432 if (l->cfwdtype & SKINNY_CFWD_ALL) {
02433 if (!ast_strlen_zero(l->call_forward_all)) {
02434 ast_copy_string(req->data.forwardstat.fwdallnum, l->call_forward_all, sizeof(req->data.forwardstat.fwdallnum));
02435 req->data.forwardstat.fwdall = htolel(1);
02436 anyon++;
02437 } else {
02438 req->data.forwardstat.fwdall = htolel(0);
02439 }
02440 }
02441 if (l->cfwdtype & SKINNY_CFWD_BUSY) {
02442 if (!ast_strlen_zero(l->call_forward_busy)) {
02443 ast_copy_string(req->data.forwardstat.fwdbusynum, l->call_forward_busy, sizeof(req->data.forwardstat.fwdbusynum));
02444 req->data.forwardstat.fwdbusy = htolel(1);
02445 anyon++;
02446 } else {
02447 req->data.forwardstat.fwdbusy = htolel(0);
02448 }
02449 }
02450 if (l->cfwdtype & SKINNY_CFWD_NOANSWER) {
02451 if (!ast_strlen_zero(l->call_forward_noanswer)) {
02452 ast_copy_string(req->data.forwardstat.fwdnoanswernum, l->call_forward_noanswer, sizeof(req->data.forwardstat.fwdnoanswernum));
02453 req->data.forwardstat.fwdnoanswer = htolel(1);
02454 anyon++;
02455 } else {
02456 req->data.forwardstat.fwdnoanswer = htolel(0);
02457 }
02458 }
02459 req->data.forwardstat.lineNumber = htolel(l->instance);
02460 if (anyon)
02461 req->data.forwardstat.activeforward = htolel(7);
02462 else
02463 req->data.forwardstat.activeforward = htolel(0);
02464
02465 transmit_response(d, req);
02466 }
02467
02468 static int skinny_extensionstate_cb(char *context, char *exten, int state, void *data)
02469 {
02470 struct skinny_speeddial *sd = data;
02471 struct skinny_device *d = sd->parent;
02472 char hint[AST_MAX_EXTENSION];
02473 int callstate = SKINNY_CALLREMOTEMULTILINE;
02474 int lamp = SKINNY_LAMP_OFF;
02475
02476 switch (state) {
02477 case AST_EXTENSION_DEACTIVATED:
02478 case AST_EXTENSION_REMOVED:
02479 ast_verb(2, "Extension state: Watcher for hint %s %s. Notify Device %s\n", exten, state == AST_EXTENSION_DEACTIVATED ? "deactivated" : "removed", d->name);
02480 sd->stateid = -1;
02481 callstate = SKINNY_ONHOOK;
02482 lamp = SKINNY_LAMP_OFF;
02483 break;
02484 case AST_EXTENSION_RINGING:
02485 case AST_EXTENSION_UNAVAILABLE:
02486 callstate = SKINNY_RINGIN;
02487 lamp = SKINNY_LAMP_BLINK;
02488 break;
02489 case AST_EXTENSION_BUSY:
02490 case AST_EXTENSION_INUSE:
02491 callstate = SKINNY_CALLREMOTEMULTILINE;
02492 lamp = SKINNY_LAMP_ON;
02493 break;
02494 case AST_EXTENSION_ONHOLD:
02495 callstate = SKINNY_HOLD;
02496 lamp = SKINNY_LAMP_WINK;
02497 break;
02498 case AST_EXTENSION_NOT_INUSE:
02499 default:
02500 callstate = SKINNY_ONHOOK;
02501 lamp = SKINNY_LAMP_OFF;
02502 break;
02503 }
02504
02505 if (ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, sd->context, sd->exten)) {
02506
02507 if (ast_device_state(hint) == AST_DEVICE_UNAVAILABLE) {
02508 callstate = SKINNY_ONHOOK;
02509 lamp = SKINNY_LAMP_FLASH;
02510 }
02511 }
02512
02513 transmit_lamp_indication(d, STIMULUS_LINE, sd->instance, lamp);
02514 transmit_callstate(d, sd->instance, callstate, 0);
02515 sd->laststate = state;
02516
02517 return 0;
02518 }
02519
02520 static void mwi_event_cb(const struct ast_event *event, void *userdata)
02521 {
02522 struct skinny_line *l = userdata;
02523 struct skinny_device *d = l->device;
02524 if (d) {
02525 struct skinnysession *s = d->session;
02526 struct skinny_line *l2;
02527 int new_msgs = 0;
02528 int dev_msgs = 0;
02529
02530 if (s) {
02531 if (event) {
02532 l->newmsgs = ast_event_get_ie_uint(event, AST_EVENT_IE_NEWMSGS);
02533 }
02534
02535 if (l->newmsgs) {
02536 transmit_lamp_indication(d, STIMULUS_VOICEMAIL, l->instance, l->mwiblink?SKINNY_LAMP_BLINK:SKINNY_LAMP_ON);
02537 } else {
02538 transmit_lamp_indication(d, STIMULUS_VOICEMAIL, l->instance, SKINNY_LAMP_OFF);
02539 }
02540
02541
02542 AST_LIST_TRAVERSE(&d->lines, l2, list) {
02543 if (l2->newmsgs) {
02544 dev_msgs++;
02545 }
02546 }
02547
02548 if (dev_msgs) {
02549 transmit_lamp_indication(d, STIMULUS_VOICEMAIL, 0, d->mwiblink?SKINNY_LAMP_BLINK:SKINNY_LAMP_ON);
02550 } else {
02551 transmit_lamp_indication(d, STIMULUS_VOICEMAIL, 0, SKINNY_LAMP_OFF);
02552 }
02553 ast_verb(3, "Skinny mwi_event_cb found %d new messages\n", new_msgs);
02554 }
02555 }
02556 }
02557
02558
02559
02560
02561 static enum ast_rtp_get_result skinny_get_vrtp_peer(struct ast_channel *c, struct ast_rtp **rtp)
02562 {
02563 struct skinny_subchannel *sub = NULL;
02564
02565 if (!(sub = c->tech_pvt) || !(sub->vrtp))
02566 return AST_RTP_GET_FAILED;
02567
02568 *rtp = sub->vrtp;
02569
02570 return AST_RTP_TRY_NATIVE;
02571 }
02572
02573 static enum ast_rtp_get_result skinny_get_rtp_peer(struct ast_channel *c, struct ast_rtp **rtp)
02574 {
02575 struct skinny_subchannel *sub = NULL;
02576 struct skinny_line *l;
02577 enum ast_rtp_get_result res = AST_RTP_TRY_NATIVE;
02578
02579 if (skinnydebug)
02580 ast_verb(1, "skinny_get_rtp_peer() Channel = %s\n", c->name);
02581
02582
02583 if (!(sub = c->tech_pvt))
02584 return AST_RTP_GET_FAILED;
02585
02586 ast_mutex_lock(&sub->lock);
02587
02588 if (!(sub->rtp)){
02589 ast_mutex_unlock(&sub->lock);
02590 return AST_RTP_GET_FAILED;
02591 }
02592
02593 *rtp = sub->rtp;
02594
02595 l = sub->parent;
02596
02597 if (!l->directmedia || l->nat){
02598 res = AST_RTP_TRY_PARTIAL;
02599 if (skinnydebug)
02600 ast_verb(1, "skinny_get_rtp_peer() Using AST_RTP_TRY_PARTIAL \n");
02601 }
02602
02603 ast_mutex_unlock(&sub->lock);
02604
02605 return res;
02606
02607 }
02608
02609 static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active)
02610 {
02611 struct skinny_subchannel *sub;
02612 struct skinny_line *l;
02613 struct skinny_device *d;
02614 struct skinnysession *s;
02615 struct ast_format_list fmt;
02616 struct sockaddr_in us;
02617 struct sockaddr_in them;
02618 struct skinny_req *req;
02619
02620 sub = c->tech_pvt;
02621
02622 if (c->_state != AST_STATE_UP)
02623 return 0;
02624
02625 if (!sub) {
02626 return -1;
02627 }
02628
02629 l = sub->parent;
02630 d = l->device;
02631 s = d->session;
02632
02633 if (rtp){
02634 ast_rtp_get_peer(rtp, &them);
02635
02636
02637 if (!(req = req_alloc(sizeof(struct stop_media_transmission_message), STOP_MEDIA_TRANSMISSION_MESSAGE)))
02638 return -1;
02639
02640 req->data.stopmedia.conferenceId = htolel(sub->callid);
02641 req->data.stopmedia.passThruPartyId = htolel(sub->callid);
02642 transmit_response(d, req);
02643
02644 if (skinnydebug)
02645 ast_verb(1, "Peerip = %s:%d\n", ast_inet_ntoa(them.sin_addr), ntohs(them.sin_port));
02646
02647 if (!(req = req_alloc(sizeof(struct start_media_transmission_message), START_MEDIA_TRANSMISSION_MESSAGE)))
02648 return -1;
02649
02650 fmt = ast_codec_pref_getsize(&l->prefs, ast_best_codec(l->capability));
02651
02652 if (skinnydebug)
02653 ast_verb(1, "Setting payloadType to '%d' (%d ms)\n", fmt.bits, fmt.cur_ms);
02654
02655 req->data.startmedia.conferenceId = htolel(sub->callid);
02656 req->data.startmedia.passThruPartyId = htolel(sub->callid);
02657 if (!(l->directmedia) || (l->nat)){
02658 ast_rtp_get_us(rtp, &us);
02659 req->data.startmedia.remoteIp = htolel(d->ourip.s_addr);
02660 req->data.startmedia.remotePort = htolel(ntohs(us.sin_port));
02661 } else {
02662 req->data.startmedia.remoteIp = htolel(them.sin_addr.s_addr);
02663 req->data.startmedia.remotePort = htolel(ntohs(them.sin_port));
02664 }
02665 req->data.startmedia.packetSize = htolel(fmt.cur_ms);
02666 req->data.startmedia.payloadType = htolel(codec_ast2skinny(fmt.bits));
02667 req->data.startmedia.qualifier.precedence = htolel(127);
02668 req->data.startmedia.qualifier.vad = htolel(0);
02669 req->data.startmedia.qualifier.packets = htolel(0);
02670 req->data.startmedia.qualifier.bitRate = htolel(0);
02671 transmit_response(d, req);
02672
02673 return 0;
02674 }
02675
02676 return 0;
02677 }
02678
02679 static struct ast_rtp_protocol skinny_rtp = {
02680 .type = "Skinny",
02681 .get_rtp_info = skinny_get_rtp_peer,
02682 .get_vrtp_info = skinny_get_vrtp_peer,
02683 .set_rtp_peer = skinny_set_rtp_peer,
02684 };
02685
02686 static char *handle_skinny_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02687 {
02688 switch (cmd) {
02689 case CLI_INIT:
02690 #ifdef SKINNY_DEVMODE
02691 e->command = "skinny set debug {off|on|packet}";
02692 e->usage =
02693 "Usage: skinny set debug {off|on|packet}\n"
02694 " Enables/Disables dumping of Skinny packets for debugging purposes\n";
02695 #else
02696 e->command = "skinny set debug {off|on}";
02697 e->usage =
02698 "Usage: skinny set debug {off|on}\n"
02699 " Enables/Disables dumping of Skinny packets for debugging purposes\n";
02700 #endif
02701 return NULL;
02702 case CLI_GENERATE:
02703 return NULL;
02704 }
02705
02706 if (a->argc != e->args)
02707 return CLI_SHOWUSAGE;
02708
02709 if (!strncasecmp(a->argv[e->args - 1], "on", 2)) {
02710 skinnydebug = 1;
02711 ast_cli(a->fd, "Skinny Debugging Enabled\n");
02712 return CLI_SUCCESS;
02713 } else if (!strncasecmp(a->argv[e->args - 1], "off", 3)) {
02714 skinnydebug = 0;
02715 ast_cli(a->fd, "Skinny Debugging Disabled\n");
02716 return CLI_SUCCESS;
02717 #ifdef SKINNY_DEVMODE
02718 } else if (!strncasecmp(a->argv[e->args - 1], "packet", 6)) {
02719 skinnydebug = 2;
02720 ast_cli(a->fd, "Skinny Debugging Enabled including Packets\n");
02721 return CLI_SUCCESS;
02722 #endif
02723 } else {
02724 return CLI_SHOWUSAGE;
02725 }
02726 }
02727
02728 static char *handle_skinny_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02729 {
02730 switch (cmd) {
02731 case CLI_INIT:
02732 e->command = "skinny reload";
02733 e->usage =
02734 "Usage: skinny reload\n"
02735 " Reloads the chan_skinny configuration\n";
02736 return NULL;
02737 case CLI_GENERATE:
02738 return NULL;
02739 }
02740
02741 if (a->argc != e->args)
02742 return CLI_SHOWUSAGE;
02743
02744 skinny_reload();
02745 return CLI_SUCCESS;
02746
02747 }
02748
02749 static char *complete_skinny_devices(const char *word, int state)
02750 {
02751 struct skinny_device *d;
02752 char *result = NULL;
02753 int wordlen = strlen(word), which = 0;
02754
02755 AST_LIST_TRAVERSE(&devices, d, list) {
02756 if (!strncasecmp(word, d->id, wordlen) && ++which > state)
02757 result = ast_strdup(d->id);
02758 }
02759
02760 return result;
02761 }
02762
02763 static char *complete_skinny_show_device(const char *line, const char *word, int pos, int state)
02764 {
02765 return (pos == 3 ? ast_strdup(complete_skinny_devices(word, state)) : NULL);
02766 }
02767
02768 static char *complete_skinny_reset(const char *line, const char *word, int pos, int state)
02769 {
02770 return (pos == 2 ? ast_strdup(complete_skinny_devices(word, state)) : NULL);
02771 }
02772
02773 static char *complete_skinny_show_line(const char *line, const char *word, int pos, int state)
02774 {
02775 struct skinny_device *d;
02776 struct skinny_line *l;
02777 char *result = NULL;
02778 int wordlen = strlen(word), which = 0;
02779
02780 if (pos != 3)
02781 return NULL;
02782
02783 AST_LIST_TRAVERSE(&devices, d, list) {
02784 AST_LIST_TRAVERSE(&d->lines, l, list) {
02785 if (!strncasecmp(word, l->name, wordlen) && ++which > state)
02786 result = ast_strdup(l->name);
02787 }
02788 }
02789
02790 return result;
02791 }
02792
02793 static char *handle_skinny_reset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02794 {
02795 struct skinny_device *d;
02796 struct skinny_req *req;
02797
02798 switch (cmd) {
02799 case CLI_INIT:
02800 e->command = "skinny reset";
02801 e->usage =
02802 "Usage: skinny reset <DeviceId|DeviceName|all> [restart]\n"
02803 " Causes a Skinny device to reset itself, optionally with a full restart\n";
02804 return NULL;
02805 case CLI_GENERATE:
02806 return complete_skinny_reset(a->line, a->word, a->pos, a->n);
02807 }
02808
02809 if (a->argc < 3 || a->argc > 4)
02810 return CLI_SHOWUSAGE;
02811
02812 AST_LIST_LOCK(&devices);
02813 AST_LIST_TRAVERSE(&devices, d, list) {
02814 int fullrestart = 0;
02815 if (!strcasecmp(a->argv[2], d->id) || !strcasecmp(a->argv[2], d->name) || !strcasecmp(a->argv[2], "all")) {
02816 if (!(d->session))
02817 continue;
02818
02819 if (!(req = req_alloc(sizeof(struct reset_message), RESET_MESSAGE)))
02820 continue;
02821
02822 if (a->argc == 4 && !strcasecmp(a->argv[3], "restart"))
02823 fullrestart = 1;
02824
02825 if (fullrestart)
02826 req->data.reset.resetType = 2;
02827 else
02828 req->data.reset.resetType = 1;
02829
02830 ast_verb(3, "%s device %s.\n", (fullrestart) ? "Restarting" : "Resetting", d->id);
02831 transmit_response(d, req);
02832 }
02833 }
02834 AST_LIST_UNLOCK(&devices);
02835 return CLI_SUCCESS;
02836 }
02837
02838 static char *device2str(int type)
02839 {
02840 char *tmp;
02841
02842 switch (type) {
02843 case SKINNY_DEVICE_NONE:
02844 return "No Device";
02845 case SKINNY_DEVICE_30SPPLUS:
02846 return "30SP Plus";
02847 case SKINNY_DEVICE_12SPPLUS:
02848 return "12SP Plus";
02849 case SKINNY_DEVICE_12SP:
02850 return "12SP";
02851 case SKINNY_DEVICE_12:
02852 return "12";
02853 case SKINNY_DEVICE_30VIP:
02854 return "30VIP";
02855 case SKINNY_DEVICE_7910:
02856 return "7910";
02857 case SKINNY_DEVICE_7960:
02858 return "7960";
02859 case SKINNY_DEVICE_7940:
02860 return "7940";
02861 case SKINNY_DEVICE_7935:
02862 return "7935";
02863 case SKINNY_DEVICE_ATA186:
02864 return "ATA186";
02865 case SKINNY_DEVICE_7941:
02866 return "7941";
02867 case SKINNY_DEVICE_7971:
02868 return "7971";
02869 case SKINNY_DEVICE_7914:
02870 return "7914";
02871 case SKINNY_DEVICE_7985:
02872 return "7985";
02873 case SKINNY_DEVICE_7911:
02874 return "7911";
02875 case SKINNY_DEVICE_7961GE:
02876 return "7961GE";
02877 case SKINNY_DEVICE_7941GE:
02878 return "7941GE";
02879 case SKINNY_DEVICE_7931:
02880 return "7931";
02881 case SKINNY_DEVICE_7921:
02882 return "7921";
02883 case SKINNY_DEVICE_7906:
02884 return "7906";
02885 case SKINNY_DEVICE_7962:
02886 return "7962";
02887 case SKINNY_DEVICE_7937:
02888 return "7937";
02889 case SKINNY_DEVICE_7942:
02890 return "7942";
02891 case SKINNY_DEVICE_7945:
02892 return "7945";
02893 case SKINNY_DEVICE_7965:
02894 return "7965";
02895 case SKINNY_DEVICE_7975:
02896 return "7975";
02897 case SKINNY_DEVICE_7905:
02898 return "7905";
02899 case SKINNY_DEVICE_7920:
02900 return "7920";
02901 case SKINNY_DEVICE_7970:
02902 return "7970";
02903 case SKINNY_DEVICE_7912:
02904 return "7912";
02905 case SKINNY_DEVICE_7902:
02906 return "7902";
02907 case SKINNY_DEVICE_CIPC:
02908 return "IP Communicator";
02909 case SKINNY_DEVICE_7961:
02910 return "7961";
02911 case SKINNY_DEVICE_7936:
02912 return "7936";
02913 case SKINNY_DEVICE_SCCPGATEWAY_AN:
02914 return "SCCPGATEWAY_AN";
02915 case SKINNY_DEVICE_SCCPGATEWAY_BRI:
02916 return "SCCPGATEWAY_BRI";
02917 case SKINNY_DEVICE_UNKNOWN:
02918 return "Unknown";
02919 default:
02920 if (!(tmp = ast_threadstorage_get(&device2str_threadbuf, DEVICE2STR_BUFSIZE)))
02921 return "Unknown";
02922 snprintf(tmp, DEVICE2STR_BUFSIZE, "UNKNOWN-%d", type);
02923 return tmp;
02924 }
02925 }
02926
02927
02928 static void print_codec_to_cli(int fd, struct ast_codec_pref *pref)
02929 {
02930 int x, codec;
02931
02932 for(x = 0; x < 32 ; x++) {
02933 codec = ast_codec_pref_index(pref, x);
02934 if (!codec)
02935 break;
02936 ast_cli(fd, "%s", ast_getformatname(codec));
02937 ast_cli(fd, ":%d", pref->framing[x]);
02938 if (x < 31 && ast_codec_pref_index(pref, x + 1))
02939 ast_cli(fd, ",");
02940 }
02941 if (!x)
02942 ast_cli(fd, "none");
02943 }
02944
02945 static char *_skinny_show_devices(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[])
02946 {
02947 struct skinny_device *d;
02948 struct skinny_line *l;
02949 const char *id;
02950 char idtext[256] = "";
02951 int total_devices = 0;
02952
02953 if (s) {
02954 id = astman_get_header(m, "ActionID");
02955 if (!ast_strlen_zero(id))
02956 snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
02957 }
02958
02959 switch (argc) {
02960 case 3:
02961 break;
02962 default:
02963 return CLI_SHOWUSAGE;
02964 }
02965
02966 if (!s) {
02967 ast_cli(fd, "Name DeviceId IP Type R NL\n");
02968 ast_cli(fd, "-------------------- ---------------- --------------- --------------- - --\n");
02969 }
02970
02971 AST_LIST_LOCK(&devices);
02972 AST_LIST_TRAVERSE(&devices, d, list) {
02973 int numlines = 0;
02974 total_devices++;
02975 AST_LIST_TRAVERSE(&d->lines, l, list) {
02976 numlines++;
02977 }
02978 if (!s) {
02979 ast_cli(fd, "%-20s %-16s %-15s %-15s %c %2d\n",
02980 d->name,
02981 d->id,
02982 d->session?ast_inet_ntoa(d->session->sin.sin_addr):"",
02983 device2str(d->type),
02984 d->registered?'Y':'N',
02985 numlines);
02986 } else {
02987 astman_append(s,
02988 "Event: DeviceEntry\r\n%s"
02989 "Channeltype: SKINNY\r\n"
02990 "ObjectName: %s\r\n"
02991 "ChannelObjectType: device\r\n"
02992 "DeviceId: %s\r\n"
02993 "IPaddress: %s\r\n"
02994 "Type: %s\r\n"
02995 "Devicestatus: %s\r\n"
02996 "NumberOfLines: %d\r\n",
02997 idtext,
02998 d->name,
02999 d->id,
03000 d->session?ast_inet_ntoa(d->session->sin.sin_addr):"-none-",
03001 device2str(d->type),
03002 d->registered?"registered":"unregistered",
03003 numlines);
03004 }
03005 }
03006 AST_LIST_UNLOCK(&devices);
03007
03008 if (total)
03009 *total = total_devices;
03010
03011 return CLI_SUCCESS;
03012 }
03013
03014 static char mandescr_show_devices[] =
03015 "Description: Lists Skinny devices in text format with details on current status.\n"
03016 "Devicelist will follow as separate events, followed by a final event called\n"
03017 "DevicelistComplete.\n"
03018 "Variables: \n"
03019 " ActionID: <id> Action ID for this transaction. Will be returned.\n";
03020
03021
03022
03023 static int manager_skinny_show_devices(struct mansession *s, const struct message *m)
03024 {
03025 const char *id = astman_get_header(m, "ActionID");
03026 const char *a[] = {"skinny", "show", "devices"};
03027 char idtext[256] = "";
03028 int total = 0;
03029
03030 if (!ast_strlen_zero(id))
03031 snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
03032
03033 astman_send_listack(s, m, "Device status list will follow", "start");
03034
03035 _skinny_show_devices(-1, &total, s, m, 3, a);
03036
03037 astman_append(s,
03038 "Event: DevicelistComplete\r\n"
03039 "EventList: Complete\r\n"
03040 "ListItems: %d\r\n"
03041 "%s"
03042 "\r\n", total, idtext);
03043 return 0;
03044 }
03045
03046 static char *handle_skinny_show_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03047 {
03048
03049 switch (cmd) {
03050 case CLI_INIT:
03051 e->command = "skinny show devices";
03052 e->usage =
03053 "Usage: skinny show devices\n"
03054 " Lists all devices known to the Skinny subsystem.\n";
03055 return NULL;
03056 case CLI_GENERATE:
03057 return NULL;
03058 }
03059
03060 return _skinny_show_devices(a->fd, NULL, NULL, NULL, a->argc, (const char **) a->argv);
03061 }
03062
03063 static char *_skinny_show_device(int type, int fd, struct mansession *s, const struct message *m, int argc, const char *argv[])
03064 {
03065 struct skinny_device *d;
03066 struct skinny_line *l;
03067 struct skinny_speeddial *sd;
03068 struct skinny_addon *sa;
03069 char codec_buf[512];
03070
03071 if (argc < 4) {
03072 return CLI_SHOWUSAGE;
03073 }
03074
03075 AST_LIST_LOCK(&devices);
03076 AST_LIST_TRAVERSE(&devices, d, list) {
03077 if (!strcasecmp(argv[3], d->id) || !strcasecmp(argv[3], d->name)) {
03078 int numlines = 0, numaddons = 0, numspeeddials = 0;
03079
03080 AST_LIST_TRAVERSE(&d->lines, l, list){
03081 numlines++;
03082 }
03083
03084 AST_LIST_TRAVERSE(&d->addons, sa, list) {
03085 numaddons++;
03086 }
03087
03088 AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
03089 numspeeddials++;
03090 }
03091
03092 if (type == 0) {
03093 ast_cli(fd, "Name: %s\n", d->name);
03094 ast_cli(fd, "Id: %s\n", d->id);
03095 ast_cli(fd, "version: %s\n", S_OR(d->version_id, "Unknown"));
03096 ast_cli(fd, "Ip address: %s\n", (d->session ? ast_inet_ntoa(d->session->sin.sin_addr) : "Unknown"));
03097 ast_cli(fd, "Port: %d\n", (d->session ? ntohs(d->session->sin.sin_port) : 0));
03098 ast_cli(fd, "Device Type: %s\n", device2str(d->type));
03099 ast_cli(fd, "Conf Codecs:");
03100 ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, d->confcapability);
03101 ast_cli(fd, "%s\n", codec_buf);
03102 ast_cli(fd, "Neg Codecs: ");
03103 ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, d->capability);
03104 ast_cli(fd, "%s\n", codec_buf);
03105 ast_cli(fd, "Registered: %s\n", (d->registered ? "Yes" : "No"));
03106 ast_cli(fd, "Lines: %d\n", numlines);
03107 AST_LIST_TRAVERSE(&d->lines, l, list) {
03108 ast_cli(fd, " %s (%s)\n", l->name, l->label);
03109 }
03110 AST_LIST_TRAVERSE(&d->addons, sa, list) {
03111 numaddons++;
03112 }
03113 ast_cli(fd, "Addons: %d\n", numaddons);
03114 AST_LIST_TRAVERSE(&d->addons, sa, list) {
03115 ast_cli(fd, " %s\n", sa->type);
03116 }
03117 AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
03118 numspeeddials++;
03119 }
03120 ast_cli(fd, "Speeddials: %d\n", numspeeddials);
03121 AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
03122 ast_cli(fd, " %s (%s) ishint: %d\n", sd->exten, sd->label, sd->isHint);
03123 }
03124 } else {
03125 astman_append(s, "Channeltype: SKINNY\r\n");
03126 astman_append(s, "ObjectName: %s\r\n", d->name);
03127 astman_append(s, "ChannelObjectType: device\r\n");
03128 astman_append(s, "Id: %s\r\n", d->id);
03129 astman_append(s, "version: %s\r\n", S_OR(d->version_id, "Unknown"));
03130 astman_append(s, "Ipaddress: %s\r\n", (d->session ? ast_inet_ntoa(d->session->sin.sin_addr) : "Unknown"));
03131 astman_append(s, "Port: %d\r\n", (d->session ? ntohs(d->session->sin.sin_port) : 0));
03132 astman_append(s, "DeviceType: %s\r\n", device2str(d->type));
03133 astman_append(s, "Codecs: ");
03134 ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, d->confcapability);
03135 astman_append(s, "%s\r\n", codec_buf);
03136 astman_append(s, "CodecOrder: ");
03137 ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, d->capability);
03138 astman_append(s, "%s\r\n", codec_buf);
03139 astman_append(s, "Devicestatus: %s\r\n", (d->registered?"registered":"unregistered"));
03140 astman_append(s, "NumberOfLines: %d\r\n", numlines);
03141 AST_LIST_TRAVERSE(&d->lines, l, list) {
03142 astman_append(s, "Line: %s (%s)\r\n", l->name, l->label);
03143 }
03144 astman_append(s, "NumberOfAddons: %d\r\n", numaddons);
03145 AST_LIST_TRAVERSE(&d->addons, sa, list) {
03146 astman_append(s, "Addon: %s\r\n", sa->type);
03147 }
03148 astman_append(s, "NumberOfSpeeddials: %d\r\n", numspeeddials);
03149 AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
03150 astman_append(s, "Speeddial: %s (%s) ishint: %d\r\n", sd->exten, sd->label, sd->isHint);
03151 }
03152 }
03153 }
03154 }
03155 AST_LIST_UNLOCK(&devices);
03156 return CLI_SUCCESS;
03157 }
03158
03159 static char mandescr_show_device[] =
03160 "Description: Show one SKINNY device with details on current status.\n"
03161 "Variables: \n"
03162 " Device: <name> The device name you want to check.\n"
03163 " ActionID: <id> Optional action ID for this AMI transaction.\n";
03164
03165 static int manager_skinny_show_device(struct mansession *s, const struct message *m)
03166 {
03167 const char *a[4];
03168 const char *device;
03169
03170 device = astman_get_header(m, "Device");
03171 if (ast_strlen_zero(device)) {
03172 astman_send_error(s, m, "Device: <name> missing.");
03173 return 0;
03174 }
03175 a[0] = "skinny";
03176 a[1] = "show";
03177 a[2] = "device";
03178 a[3] = device;
03179
03180 _skinny_show_device(1, -1, s, m, 4, a);
03181 astman_append(s, "\r\n\r\n" );
03182 return 0;
03183 }
03184
03185
03186 static char *handle_skinny_show_device(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03187 {
03188 switch (cmd) {
03189 case CLI_INIT:
03190 e->command = "skinny show device";
03191 e->usage =
03192 "Usage: skinny show device <DeviceId|DeviceName>\n"
03193 " Lists all deviceinformation of a specific device known to the Skinny subsystem.\n";
03194 return NULL;
03195 case CLI_GENERATE:
03196 return complete_skinny_show_device(a->line, a->word, a->pos, a->n);
03197 }
03198
03199 return _skinny_show_device(0, a->fd, NULL, NULL, a->argc, (const char **) a->argv);
03200 }
03201
03202 static char *_skinny_show_lines(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[])
03203 {
03204 struct skinny_line *l;
03205 struct skinny_subchannel *sub;
03206 int total_lines = 0;
03207 int verbose = 0;
03208 const char *id;
03209 char idtext[256] = "";
03210
03211 if (s) {
03212 id = astman_get_header(m, "ActionID");
03213 if (!ast_strlen_zero(id))
03214 snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
03215 }
03216
03217 switch (argc) {
03218 case 4:
03219 verbose = 1;
03220 break;
03221 case 3:
03222 verbose = 0;
03223 break;
03224 default:
03225 return CLI_SHOWUSAGE;
03226 }
03227
03228 if (!s) {
03229 ast_cli(fd, "Name Device Name Instance Label \n");
03230 ast_cli(fd, "-------------------- -------------------- -------- --------------------\n");
03231 }
03232 AST_LIST_LOCK(&lines);
03233 AST_LIST_TRAVERSE(&lines, l, all) {
03234 total_lines++;
03235 if (!s) {
03236 ast_cli(fd, "%-20s %-20s %8d %-20s\n",
03237 l->name,
03238 (l->device ? l->device->name : "Not connected"),
03239 l->instance,
03240 l->label);
03241 if (verbose) {
03242 AST_LIST_TRAVERSE(&l->sub, sub, list) {
03243 ast_cli(fd, " %s> %s to %s\n",
03244 (sub == l->activesub?"Active ":"Inactive"),
03245 sub->owner->name,
03246 (ast_bridged_channel(sub->owner)?ast_bridged_channel(sub->owner)->name:"")
03247 );
03248 }
03249 }
03250 } else {
03251 astman_append(s,
03252 "Event: LineEntry\r\n%s"
03253 "Channeltype: SKINNY\r\n"
03254 "ObjectName: %s\r\n"
03255 "ChannelObjectType: line\r\n"
03256 "Device: %s\r\n"
03257 "Instance: %d\r\n"
03258 "Label: %s\r\n",
03259 idtext,
03260 l->name,
03261 (l->device?l->device->name:"None"),
03262 l->instance,
03263 l->label);
03264 }
03265 AST_LIST_UNLOCK(&lines);
03266 }
03267
03268 if (total) {
03269 *total = total_lines;
03270 }
03271
03272 return CLI_SUCCESS;
03273 }
03274
03275 static char mandescr_show_lines[] =
03276 "Description: Lists Skinny lines in text format with details on current status.\n"
03277 "Linelist will follow as separate events, followed by a final event called\n"
03278 "LinelistComplete.\n"
03279 "Variables: \n"
03280 " ActionID: <id> Action ID for this transaction. Will be returned.\n";
03281
03282
03283
03284 static int manager_skinny_show_lines(struct mansession *s, const struct message *m)
03285 {
03286 const char *id = astman_get_header(m, "ActionID");
03287 const char *a[] = {"skinny", "show", "lines"};
03288 char idtext[256] = "";
03289 int total = 0;
03290
03291 if (!ast_strlen_zero(id))
03292 snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
03293
03294 astman_send_listack(s, m, "Line status list will follow", "start");
03295
03296 _skinny_show_lines(-1, &total, s, m, 3, a);
03297
03298 astman_append(s,
03299 "Event: LinelistComplete\r\n"
03300 "EventList: Complete\r\n"
03301 "ListItems: %d\r\n"
03302 "%s"
03303 "\r\n", total, idtext);
03304 return 0;
03305 }
03306
03307 static char *handle_skinny_show_lines(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03308 {
03309 int verbose = 0;
03310
03311 switch (cmd) {
03312 case CLI_INIT:
03313 e->command = "skinny show lines [verbose]";
03314 e->usage =
03315 "Usage: skinny show lines\n"
03316 " Lists all lines known to the Skinny subsystem.\n"
03317 " If 'verbose' is specified, the output includes\n"
03318 " information about subs for each line.\n";
03319 return NULL;
03320 case CLI_GENERATE:
03321 return NULL;
03322 }
03323
03324 if (a->argc == e->args) {
03325 if (!strcasecmp(a->argv[e->args-1], "verbose")) {
03326 verbose = 1;
03327 } else {
03328 return CLI_SHOWUSAGE;
03329 }
03330 } else if (a->argc != e->args - 1) {
03331 return CLI_SHOWUSAGE;
03332 }
03333
03334 return _skinny_show_lines(a->fd, NULL, NULL, NULL, a->argc, (const char **) a->argv);
03335 }
03336
03337 static char *_skinny_show_line(int type, int fd, struct mansession *s, const struct message *m, int argc, const char *argv[])
03338 {
03339 struct skinny_device *d;
03340 struct skinny_line *l;
03341 struct ast_codec_pref *pref;
03342 int x = 0, codec = 0;
03343 char codec_buf[512];
03344 char group_buf[256];
03345 char cbuf[256];
03346
03347 switch (argc) {
03348 case 4:
03349 break;
03350 case 6:
03351 break;
03352 default:
03353 return CLI_SHOWUSAGE;
03354 }
03355
03356 AST_LIST_LOCK(&devices);
03357
03358
03359 AST_LIST_TRAVERSE(&devices, d, list) {
03360 if (argc == 6 && (strcasecmp(argv[5], d->id) && strcasecmp(argv[5], d->name))) {
03361 continue;
03362 }
03363 AST_LIST_TRAVERSE(&d->lines, l, list) {
03364 if (strcasecmp(argv[3], l->name)) {
03365 continue;
03366 }
03367 if (type == 0) {
03368 ast_cli(fd, "Line: %s\n", l->name);
03369 ast_cli(fd, "On Device: %s\n", d->name);
03370 ast_cli(fd, "Line Label: %s\n", l->label);
03371 ast_cli(fd, "Extension: %s\n", S_OR(l->exten, "<not set>"));
03372 ast_cli(fd, "Context: %s\n", l->context);
03373 ast_cli(fd, "CallGroup: %s\n", ast_print_group(group_buf, sizeof(group_buf), l->callgroup));
03374 ast_cli(fd, "PickupGroup: %s\n", ast_print_group(group_buf, sizeof(group_buf), l->pickupgroup));
03375 ast_cli(fd, "Language: %s\n", S_OR(l->language, "<not set>"));
03376 ast_cli(fd, "Accountcode: %s\n", S_OR(l->accountcode, "<not set>"));
03377 ast_cli(fd, "AmaFlag: %s\n", ast_cdr_flags2str(l->amaflags));
03378 ast_cli(fd, "CallerId Number: %s\n", S_OR(l->cid_num, "<not set>"));
03379 ast_cli(fd, "CallerId Name: %s\n", S_OR(l->cid_name, "<not set>"));
03380 ast_cli(fd, "Hide CallerId: %s\n", (l->hidecallerid ? "Yes" : "No"));
03381 ast_cli(fd, "CFwdAll: %s\n", S_COR((l->cfwdtype & SKINNY_CFWD_ALL), l->call_forward_all, "<not set>"));
03382 ast_cli(fd, "CFwdBusy: %s\n", S_COR((l->cfwdtype & SKINNY_CFWD_BUSY), l->call_forward_busy, "<not set>"));
03383 ast_cli(fd, "CFwdNoAnswer: %s\n", S_COR((l->cfwdtype & SKINNY_CFWD_NOANSWER), l->call_forward_noanswer, "<not set>"));
03384 ast_cli(fd, "VoicemailBox: %s\n", S_OR(l->mailbox, "<not set>"));
03385 ast_cli(fd, "VoicemailNumber: %s\n", S_OR(l->vmexten, "<not set>"));
03386 ast_cli(fd, "MWIblink: %d\n", l->mwiblink);
03387 ast_cli(fd, "Regextension: %s\n", S_OR(l->regexten, "<not set>"));
03388 ast_cli(fd, "Regcontext: %s\n", S_OR(l->regcontext, "<not set>"));
03389 ast_cli(fd, "MoHInterpret: %s\n", S_OR(l->mohinterpret, "<not set>"));
03390 ast_cli(fd, "MoHSuggest: %s\n", S_OR(l->mohsuggest, "<not set>"));
03391 ast_cli(fd, "Last dialed nr: %s\n", S_OR(l->lastnumberdialed, "<no calls made yet>"));
03392 ast_cli(fd, "Last CallerID: %s\n", S_OR(l->lastcallerid, "<not set>"));
03393 ast_cli(fd, "Transfer enabled: %s\n", (l->transfer ? "Yes" : "No"));
03394 ast_cli(fd, "Callwaiting: %s\n", (l->callwaiting ? "Yes" : "No"));
03395 ast_cli(fd, "3Way Calling: %s\n", (l->threewaycalling ? "Yes" : "No"));
03396 ast_cli(fd, "Can forward: %s\n", (l->cancallforward ? "Yes" : "No"));
03397 ast_cli(fd, "Do Not Disturb: %s\n", (l->dnd ? "Yes" : "No"));
03398 ast_cli(fd, "NAT: %s\n", (l->nat ? "Yes" : "No"));
03399 ast_cli(fd, "immediate: %s\n", (l->immediate ? "Yes" : "No"));
03400 ast_cli(fd, "Group: %d\n", l->group);
03401 ast_cli(fd, "Parkinglot: %s\n", S_OR(l->parkinglot, "<not set>"));
03402 ast_cli(fd, "Conf Codecs: ");
03403 ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, l->confcapability);
03404 ast_cli(fd, "%s\n", codec_buf);
03405 ast_cli(fd, "Neg Codecs: ");
03406 ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, l->capability);
03407 ast_cli(fd, "%s\n", codec_buf);
03408 ast_cli(fd, "Codec Order: (");
03409 print_codec_to_cli(fd, &l->prefs);
03410 ast_cli(fd, ")\n");
03411 ast_cli(fd, "\n");
03412 } else {
03413 astman_append(s, "Channeltype: SKINNY\r\n");
03414 astman_append(s, "ObjectName: %s\r\n", l->name);
03415 astman_append(s, "ChannelObjectType: line\r\n");
03416 astman_append(s, "Device: %s\r\n", d->name);
03417 astman_append(s, "LineLabel: %s\r\n", l->label);
03418 astman_append(s, "Extension: %s\r\n", S_OR(l->exten, "<not set>"));
03419 astman_append(s, "Context: %s\r\n", l->context);
03420 astman_append(s, "CallGroup: %s\r\n", ast_print_group(group_buf, sizeof(group_buf), l->callgroup));
03421 astman_append(s, "PickupGroup: %s\r\n", ast_print_group(group_buf, sizeof(group_buf), l->pickupgroup));
03422 astman_append(s, "Language: %s\r\n", S_OR(l->language, "<not set>"));
03423 astman_append(s, "Accountcode: %s\r\n", S_OR(l->accountcode, "<not set>"));
03424 astman_append(s, "AMAflags: %s\r\n", ast_cdr_flags2str(l->amaflags));
03425 astman_append(s, "Callerid: %s\r\n", ast_callerid_merge(cbuf, sizeof(cbuf), l->cid_name, l->cid_num, ""));
03426 astman_append(s, "HideCallerId: %s\r\n", (l->hidecallerid ? "Yes" : "No"));
03427 astman_append(s, "CFwdAll: %s\r\n", S_COR((l->cfwdtype & SKINNY_CFWD_ALL), l->call_forward_all, "<not set>"));
03428 astman_append(s, "CFwdBusy: %s\r\n", S_COR((l->cfwdtype & SKINNY_CFWD_BUSY), l->call_forward_busy, "<not set>"));
03429 astman_append(s, "CFwdNoAnswer: %s\r\n", S_COR((l->cfwdtype & SKINNY_CFWD_NOANSWER), l->call_forward_noanswer, "<not set>"));
03430 astman_append(s, "VoicemailBox: %s\r\n", S_OR(l->mailbox, "<not set>"));
03431 astman_append(s, "VoicemailNumber: %s\r\n", S_OR(l->vmexten, "<not set>"));
03432 astman_append(s, "MWIblink: %d\r\n", l->mwiblink);
03433 astman_append(s, "RegExtension: %s\r\n", S_OR(l->regexten, "<not set>"));
03434 astman_append(s, "Regcontext: %s\r\n", S_OR(l->regcontext, "<not set>"));
03435 astman_append(s, "MoHInterpret: %s\r\n", S_OR(l->mohinterpret, "<not set>"));
03436 astman_append(s, "MoHSuggest: %s\r\n", S_OR(l->mohsuggest, "<not set>"));
03437 astman_append(s, "LastDialedNr: %s\r\n", S_OR(l->lastnumberdialed, "<no calls made yet>"));
03438 astman_append(s, "LastCallerID: %s\r\n", S_OR(l->lastcallerid, "<not set>"));
03439 astman_append(s, "Transfer: %s\r\n", (l->transfer ? "Yes" : "No"));
03440 astman_append(s, "Callwaiting: %s\r\n", (l->callwaiting ? "Yes" : "No"));
03441 astman_append(s, "3WayCalling: %s\r\n", (l->threewaycalling ? "Yes" : "No"));
03442 astman_append(s, "CanForward: %s\r\n", (l->cancallforward ? "Yes" : "No"));
03443 astman_append(s, "DoNotDisturb: %s\r\n", (l->dnd ? "Yes" : "No"));
03444 astman_append(s, "NAT: %s\r\n", (l->nat ? "Yes" : "No"));
03445 astman_append(s, "immediate: %s\r\n", (l->immediate ? "Yes" : "No"));
03446 astman_append(s, "Group: %d\r\n", l->group);
03447 astman_append(s, "Parkinglot: %s\r\n", S_OR(l->parkinglot, "<not set>"));
03448 ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, l->confcapability);
03449 astman_append(s, "Codecs: %s\r\n", codec_buf);
03450 astman_append(s, "CodecOrder: ");
03451 pref = &l->prefs;
03452 for(x = 0; x < 32 ; x++) {
03453 codec = ast_codec_pref_index(pref, x);
03454 if (!codec)
03455 break;
03456 astman_append(s, "%s", ast_getformatname(codec));
03457 if (x < 31 && ast_codec_pref_index(pref, x+1))
03458 astman_append(s, ",");
03459 }
03460 astman_append(s, "\r\n");
03461 }
03462 }
03463 }
03464
03465 AST_LIST_UNLOCK(&devices);
03466 return CLI_SUCCESS;
03467 }
03468
03469 static char mandescr_show_line[] =
03470 "Description: Show one SKINNY line with details on current status.\n"
03471 "Variables: \n"
03472 " Line: <name> The line name you want to check.\n"
03473 " ActionID: <id> Optional action ID for this AMI transaction.\n";
03474
03475 static int manager_skinny_show_line(struct mansession *s, const struct message *m)
03476 {
03477 const char *a[4];
03478 const char *line;
03479
03480 line = astman_get_header(m, "Line");
03481 if (ast_strlen_zero(line)) {
03482 astman_send_error(s, m, "Line: <name> missing.");
03483 return 0;
03484 }
03485 a[0] = "skinny";
03486 a[1] = "show";
03487 a[2] = "line";
03488 a[3] = line;
03489
03490 _skinny_show_line(1, -1, s, m, 4, a);
03491 astman_append(s, "\r\n\r\n" );
03492 return 0;
03493 }
03494
03495
03496 static char *handle_skinny_show_line(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03497 {
03498 switch (cmd) {
03499 case CLI_INIT:
03500 e->command = "skinny show line";
03501 e->usage =
03502 "Usage: skinny show line <Line> [ on <DeviceID|DeviceName> ]\n"
03503 " List all lineinformation of a specific line known to the Skinny subsystem.\n";
03504 return NULL;
03505 case CLI_GENERATE:
03506 return complete_skinny_show_line(a->line, a->word, a->pos, a->n);
03507 }
03508
03509 return _skinny_show_line(0, a->fd, NULL, NULL, a->argc, (const char **) a->argv);
03510 }
03511
03512
03513 static char *handle_skinny_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03514 {
03515 switch (cmd) {
03516 case CLI_INIT:
03517 e->command = "skinny show settings";
03518 e->usage =
03519 "Usage: skinny show settings\n"
03520 " Lists all global configuration settings of the Skinny subsystem.\n";
03521 return NULL;
03522 case CLI_GENERATE:
03523 return NULL;
03524 }
03525
03526 if (a->argc != 3)
03527 return CLI_SHOWUSAGE;
03528
03529 ast_cli(a->fd, "\nGlobal Settings:\n");
03530 ast_cli(a->fd, " Skinny Port: %d\n", ntohs(bindaddr.sin_port));
03531 ast_cli(a->fd, " Bindaddress: %s\n", ast_inet_ntoa(bindaddr.sin_addr));
03532 ast_cli(a->fd, " KeepAlive: %d\n", keep_alive);
03533 ast_cli(a->fd, " Date Format: %s\n", date_format);
03534 ast_cli(a->fd, " Voice Mail Extension: %s\n", S_OR(global_vmexten, "(not set)"));
03535 ast_cli(a->fd, " Reg. context: %s\n", S_OR(regcontext, "(not set)"));
03536 ast_cli(a->fd, " Jitterbuffer enabled: %s\n", (ast_test_flag(&global_jbconf, AST_JB_ENABLED) ? "Yes" : "No"));
03537 ast_cli(a->fd, " Jitterbuffer forced: %s\n", (ast_test_flag(&global_jbconf, AST_JB_FORCED) ? "Yes" : "No"));
03538 ast_cli(a->fd, " Jitterbuffer max size: %ld\n", global_jbconf.max_size);
03539 ast_cli(a->fd, " Jitterbuffer resync: %ld\n", global_jbconf.resync_threshold);
03540 ast_cli(a->fd, " Jitterbuffer impl: %s\n", global_jbconf.impl);
03541 ast_cli(a->fd, " Jitterbuffer log: %s\n", (ast_test_flag(&global_jbconf, AST_JB_LOG) ? "Yes" : "No"));
03542
03543 return CLI_SUCCESS;
03544 }
03545
03546 static struct ast_cli_entry cli_skinny[] = {
03547 AST_CLI_DEFINE(handle_skinny_show_devices, "List defined Skinny devices"),
03548 AST_CLI_DEFINE(handle_skinny_show_device, "List Skinny device information"),
03549 AST_CLI_DEFINE(handle_skinny_show_lines, "List defined Skinny lines per device"),
03550 AST_CLI_DEFINE(handle_skinny_show_line, "List Skinny line information"),
03551 AST_CLI_DEFINE(handle_skinny_show_settings, "List global Skinny settings"),
03552 AST_CLI_DEFINE(handle_skinny_set_debug, "Enable/Disable Skinny debugging"),
03553 AST_CLI_DEFINE(handle_skinny_reset, "Reset Skinny device(s)"),
03554 AST_CLI_DEFINE(handle_skinny_reload, "Reload Skinny config"),
03555 };
03556
03557 static void start_rtp(struct skinny_subchannel *sub)
03558 {
03559 struct skinny_line *l = sub->parent;
03560 struct skinny_device *d = l->device;
03561 int hasvideo = 0;
03562
03563 ast_mutex_lock(&sub->lock);
03564
03565 sub->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr);
03566 if (hasvideo)
03567 sub->vrtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr);
03568
03569 if (sub->rtp && sub->owner) {
03570 ast_channel_set_fd(sub->owner, 0, ast_rtp_fd(sub->rtp));
03571 ast_channel_set_fd(sub->owner, 1, ast_rtcp_fd(sub->rtp));
03572 }
03573 if (hasvideo && sub->vrtp && sub->owner) {
03574 ast_channel_set_fd(sub->owner, 2, ast_rtp_fd(sub->vrtp));
03575 ast_channel_set_fd(sub->owner, 3, ast_rtcp_fd(sub->vrtp));
03576 }
03577 if (sub->rtp) {
03578 ast_rtp_setqos(sub->rtp, qos.tos_audio, qos.cos_audio, "Skinny RTP");
03579 ast_rtp_setnat(sub->rtp, l->nat);
03580 }
03581 if (sub->vrtp) {
03582 ast_rtp_setqos(sub->vrtp, qos.tos_video, qos.cos_video, "Skinny VRTP");
03583 ast_rtp_setnat(sub->vrtp, l->nat);
03584 }
03585
03586 if (sub->rtp)
03587 ast_rtp_codec_setpref(sub->rtp, &l->prefs);
03588
03589
03590 transmit_connect(d, sub);
03591 ast_mutex_unlock(&sub->lock);
03592 }
03593
03594 static void *skinny_newcall(void *data)
03595 {
03596 struct ast_channel *c = data;
03597 struct skinny_subchannel *sub = c->tech_pvt;
03598 struct skinny_line *l = sub->parent;
03599 struct skinny_device *d = l->device;
03600 int res = 0;
03601
03602 ast_copy_string(l->lastnumberdialed, c->exten, sizeof(l->lastnumberdialed));
03603 ast_set_callerid(c,
03604 l->hidecallerid ? "" : l->cid_num,
03605 l->hidecallerid ? "" : l->cid_name,
03606 c->cid.cid_ani ? NULL : l->cid_num);
03607 ast_setstate(c, AST_STATE_RING);
03608 if (!sub->rtp) {
03609 start_rtp(sub);
03610 }
03611 res = ast_pbx_run(c);
03612 if (res) {
03613 ast_log(LOG_WARNING, "PBX exited non-zero\n");
03614 transmit_tone(d, SKINNY_REORDER, l->instance, sub->callid);
03615 }
03616 return NULL;
03617 }
03618
03619 static void *skinny_ss(void *data)
03620 {
03621 struct ast_channel *c = data;
03622 struct skinny_subchannel *sub = c->tech_pvt;
03623 struct skinny_line *l = sub->parent;
03624 struct skinny_device *d = l->device;
03625 int len = 0;
03626 int timeout = firstdigittimeout;
03627 int res = 0;
03628 int loop_pause = 100;
03629
03630 ast_verb(3, "Starting simple switch on '%s@%s'\n", l->name, d->name);
03631
03632 len = strlen(d->exten);
03633
03634 while (len < AST_MAX_EXTENSION-1) {
03635 res = 1;
03636 while (strlen(d->exten) == len){
03637 ast_safe_sleep(c, loop_pause);
03638 timeout -= loop_pause;
03639 if ( (timeout -= loop_pause) <= 0){
03640 res = 0;
03641 break;
03642 }
03643 res = 1;
03644 }
03645
03646 timeout = 0;
03647 len = strlen(d->exten);
03648
03649 if (!ast_ignore_pattern(c->context, d->exten)) {
03650 transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
03651 }
03652 if (ast_exists_extension(c, c->context, d->exten, 1, l->cid_num)) {
03653 if (!res || !ast_matchmore_extension(c, c->context, d->exten, 1, l->cid_num)) {
03654 if (l->getforward) {
03655
03656 set_callforwards(l, d->exten, l->getforward);
03657 ast_verb(3, "Setting call forward (%d) to '%s' on channel %s\n",
03658 l->cfwdtype, d->exten, c->name);
03659 transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
03660 transmit_lamp_indication(d, STIMULUS_FORWARDALL, 1, SKINNY_LAMP_ON);
03661 transmit_displaynotify(d, "CFwd enabled", 10);
03662 transmit_cfwdstate(d, l);
03663 ast_safe_sleep(c, 500);
03664 ast_indicate(c, -1);
03665 ast_safe_sleep(c, 1000);
03666 memset(d->exten, 0, sizeof(d->exten));
03667 len = 0;
03668 l->getforward = 0;
03669 if (sub->owner && sub->owner->_state != AST_STATE_UP) {
03670 ast_indicate(c, -1);
03671 ast_hangup(c);
03672 }
03673 return NULL;
03674 } else {
03675 ast_copy_string(c->exten, d->exten, sizeof(c->exten));
03676 ast_copy_string(l->lastnumberdialed, d->exten, sizeof(l->lastnumberdialed));
03677 memset(d->exten, 0, sizeof(d->exten));
03678 skinny_newcall(c);
03679 return NULL;
03680 }
03681 } else {
03682
03683
03684 timeout = matchdigittimeout;
03685 }
03686 } else if (res == 0) {
03687 ast_debug(1, "Not enough digits (%s) (and no ambiguous match)...\n", d->exten);
03688 memset(d->exten, 0, sizeof(d->exten));
03689 if (l->hookstate == SKINNY_OFFHOOK) {
03690 transmit_tone(d, SKINNY_REORDER, l->instance, sub->callid);
03691 }
03692 if (sub->owner && sub->owner->_state != AST_STATE_UP) {
03693 ast_indicate(c, -1);
03694 ast_hangup(c);
03695 }
03696 return NULL;
03697 } else if (!ast_canmatch_extension(c, c->context, d->exten, 1, c->cid.cid_num) &&
03698 ((d->exten[0] != '*') || (!ast_strlen_zero(d->exten) > 2))) {
03699 ast_log(LOG_WARNING, "Can't match [%s] from '%s' in context %s\n", d->exten, c->cid.cid_num ? c->cid.cid_num : "<Unknown Caller>", c->context);
03700 memset(d->exten, 0, sizeof(d->exten));
03701 if (l->hookstate == SKINNY_OFFHOOK) {
03702 transmit_tone(d, SKINNY_REORDER, l->instance, sub->callid);
03703
03704 ast_safe_sleep(c, 3000);
03705 }
03706 break;
03707 }
03708 if (!timeout) {
03709 timeout = gendigittimeout;
03710 }
03711 if (len && !ast_ignore_pattern(c->context, d->exten)) {
03712 ast_indicate(c, -1);
03713 }
03714 }
03715 if (c)
03716 ast_hangup(c);
03717 memset(d->exten, 0, sizeof(d->exten));
03718 return NULL;
03719 }
03720
03721
03722
03723 static int skinny_call(struct ast_channel *ast, char *dest, int timeout)
03724 {
03725 int res = 0;
03726 int tone = 0;
03727 struct skinny_subchannel *sub = ast->tech_pvt;
03728 struct skinny_line *l = sub->parent;
03729 struct skinny_device *d = l->device;
03730
03731 if (!d->registered) {
03732 ast_log(LOG_ERROR, "Device not registered, cannot call %s\n", dest);
03733 return -1;
03734 }
03735
03736 if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
03737 ast_log(LOG_WARNING, "skinny_call called on %s, neither down nor reserved\n", ast->name);
03738 return -1;
03739 }
03740
03741 if (skinnydebug)
03742 ast_verb(3, "skinny_call(%s)\n", ast->name);
03743
03744 if (l->dnd) {
03745 ast_queue_control(ast, AST_CONTROL_BUSY);
03746 return -1;
03747 }
03748
03749 if (AST_LIST_NEXT(sub,list) && !l->callwaiting) {
03750 ast_queue_control(ast, AST_CONTROL_BUSY);
03751 return -1;
03752 }
03753
03754 switch (l->hookstate) {
03755 case SKINNY_OFFHOOK:
03756 tone = SKINNY_CALLWAITTONE;
03757 break;
03758 case SKINNY_ONHOOK:
03759 tone = SKINNY_ALERT;
03760 l->activesub = sub;
03761 break;
03762 default:
03763 ast_log(LOG_ERROR, "Don't know how to deal with hookstate %d\n", l->hookstate);
03764 break;
03765 }
03766
03767 transmit_callstateonly(d, sub, SKINNY_RINGIN);
03768 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGIN);
03769 transmit_displaypromptstatus(d, "Ring-In", 0, l->instance, sub->callid);
03770 transmit_callinfo(d, ast->cid.cid_name, ast->cid.cid_num, l->cid_name, l->cid_num, l->instance, sub->callid, 1);
03771 transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK);
03772 transmit_ringer_mode(d, SKINNY_RING_INSIDE);
03773
03774 ast_setstate(ast, AST_STATE_RINGING);
03775 ast_queue_control(ast, AST_CONTROL_RINGING);
03776 sub->outgoing = 1;
03777 return res;
03778 }
03779
03780 static int skinny_hangup(struct ast_channel *ast)
03781 {
03782 struct skinny_subchannel *sub = ast->tech_pvt;
03783 struct skinny_line *l;
03784 struct skinny_device *d;
03785 struct skinnysession *s;
03786
03787 if (!sub) {
03788 ast_debug(1, "Asked to hangup channel not connected\n");
03789 return 0;
03790 }
03791
03792 l = sub->parent;
03793 d = l->device;
03794 s = d->session;
03795
03796 if (skinnydebug)
03797 ast_verb(3,"Hanging up %s/%d\n",d->name,sub->callid);
03798
03799 AST_LIST_REMOVE(&l->sub, sub, list);
03800
03801 if (d->registered) {
03802
03803
03804 if (!AST_LIST_EMPTY(&l->sub)) {
03805 if (sub->related) {
03806 sub->related->related = NULL;
03807
03808 }
03809 if (sub == l->activesub) {
03810 ast_verb(4,"Killing active sub %d\n", sub->callid);
03811 if (sub->related) {
03812 l->activesub = sub->related;
03813 } else {
03814 if (AST_LIST_NEXT(sub, list)) {
03815 l->activesub = AST_LIST_NEXT(sub, list);
03816 } else {
03817 l->activesub = AST_LIST_FIRST(&l->sub);
03818 }
03819 }
03820
03821 transmit_activatecallplane(d, l);
03822 transmit_closereceivechannel(d, sub);
03823 transmit_stopmediatransmission(d, sub);
03824 transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK);
03825 transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
03826 } else {
03827 ast_verb(4,"Killing inactive sub %d\n", sub->callid);
03828 if (AST_LIST_NEXT(sub, list)) {
03829 transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK);
03830 } else {
03831 transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
03832 }
03833 }
03834 } else {
03835 ast_verb(4,"Killing only sub %d\n", sub->callid);
03836 l->hookstate = SKINNY_ONHOOK;
03837 transmit_callstate(d, l->instance, SKINNY_ONHOOK, sub->callid);
03838 l->activesub = NULL;
03839 transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF);
03840 if (sub->parent == d->activeline) {
03841 transmit_activatecallplane(d, l);
03842 transmit_closereceivechannel(d, sub);
03843 transmit_stopmediatransmission(d, sub);
03844 transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
03845 transmit_ringer_mode(d, SKINNY_RING_OFF);
03846 transmit_displaymessage(d, NULL, l->instance, sub->callid);
03847 transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
03848
03849 }
03850 }
03851 }
03852 ast_mutex_lock(&sub->lock);
03853 sub->owner = NULL;
03854 ast->tech_pvt = NULL;
03855 sub->alreadygone = 0;
03856 sub->outgoing = 0;
03857 if (sub->rtp) {
03858 ast_rtp_destroy(sub->rtp);
03859 sub->rtp = NULL;
03860 }
03861 ast_mutex_unlock(&sub->lock);
03862 ast_free(sub);
03863 ast_module_unref(ast_module_info->self);
03864 return 0;
03865 }
03866
03867 static int skinny_answer(struct ast_channel *ast)
03868 {
03869 int res = 0;
03870 struct skinny_subchannel *sub = ast->tech_pvt;
03871 struct skinny_line *l = sub->parent;
03872 struct skinny_device *d = l->device;
03873
03874 if (sub->blindxfer) {
03875 if (skinnydebug)
03876 ast_debug(1, "skinny_answer(%s) on %s@%s-%d with BlindXFER, transferring\n",
03877 ast->name, l->name, d->name, sub->callid);
03878 ast_setstate(ast, AST_STATE_UP);
03879 skinny_transfer(sub);
03880 return 0;
03881 }
03882
03883 sub->cxmode = SKINNY_CX_SENDRECV;
03884 if (!sub->rtp) {
03885 start_rtp(sub);
03886 }
03887 if (skinnydebug)
03888 ast_verb(1, "skinny_answer(%s) on %s@%s-%d\n", ast->name, l->name, d->name, sub->callid);
03889 if (ast->_state != AST_STATE_UP) {
03890 ast_setstate(ast, AST_STATE_UP);
03891 }
03892
03893 transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
03894
03895
03896
03897 transmit_callinfo(d, ast->cid.cid_name, ast->cid.cid_num, l->lastnumberdialed, l->lastnumberdialed, l->instance, sub->callid, 2);
03898 transmit_callstateonly(d, sub, SKINNY_CONNECTED);
03899 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
03900 transmit_dialednumber(d, l->lastnumberdialed, l->instance, sub->callid);
03901 transmit_displaypromptstatus(d, "Connected", 0, l->instance, sub->callid);
03902 l->activesub = sub;
03903 return res;
03904 }
03905
03906
03907 static struct ast_frame *skinny_rtp_read(struct skinny_subchannel *sub)
03908 {
03909 struct ast_channel *ast = sub->owner;
03910 struct ast_frame *f;
03911
03912 if (!sub->rtp) {
03913
03914 return &ast_null_frame;
03915 }
03916
03917 switch(ast->fdno) {
03918 case 0:
03919 f = ast_rtp_read(sub->rtp);
03920 break;
03921 case 1:
03922 f = ast_rtcp_read(sub->rtp);
03923 break;
03924 case 2:
03925 f = ast_rtp_read(sub->vrtp);
03926 break;
03927 case 3:
03928 f = ast_rtcp_read(sub->vrtp);
03929 break;
03930 #if 0
03931 case 5:
03932
03933 f = ast_udptl_read(sub->udptl);
03934 break;
03935 #endif
03936 default:
03937 f = &ast_null_frame;
03938 }
03939
03940 if (ast) {
03941
03942 if (f->frametype == AST_FRAME_VOICE) {
03943 if (f->subclass != ast->nativeformats) {
03944 ast_debug(1, "Oooh, format changed to %d\n", f->subclass);
03945 ast->nativeformats = f->subclass;
03946 ast_set_read_format(ast, ast->readformat);
03947 ast_set_write_format(ast, ast->writeformat);
03948 }
03949 }
03950 }
03951 return f;
03952 }
03953
03954 static struct ast_frame *skinny_read(struct ast_channel *ast)
03955 {
03956 struct ast_frame *fr;
03957 struct skinny_subchannel *sub = ast->tech_pvt;
03958 ast_mutex_lock(&sub->lock);
03959 fr = skinny_rtp_read(sub);
03960 ast_mutex_unlock(&sub->lock);
03961 return fr;
03962 }
03963
03964 static int skinny_write(struct ast_channel *ast, struct ast_frame *frame)
03965 {
03966 struct skinny_subchannel *sub = ast->tech_pvt;
03967 int res = 0;
03968 if (frame->frametype != AST_FRAME_VOICE) {
03969 if (frame->frametype == AST_FRAME_IMAGE) {
03970 return 0;
03971 } else {
03972 ast_log(LOG_WARNING, "Can't send %d type frames with skinny_write\n", frame->frametype);
03973 return 0;
03974 }
03975 } else {
03976 if (!(frame->subclass & ast->nativeformats)) {
03977 ast_log(LOG_WARNING, "Asked to transmit frame type %d, while native formats is %d (read/write = %d/%d)\n",
03978 frame->subclass, ast->nativeformats, ast->readformat, ast->writeformat);
03979 return -1;
03980 }
03981 }
03982 if (sub) {
03983 ast_mutex_lock(&sub->lock);
03984 if (sub->rtp) {
03985 res = ast_rtp_write(sub->rtp, frame);
03986 }
03987 ast_mutex_unlock(&sub->lock);
03988 }
03989 return res;
03990 }
03991
03992 static int skinny_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
03993 {
03994 struct skinny_subchannel *sub = newchan->tech_pvt;
03995 ast_log(LOG_NOTICE, "skinny_fixup(%s, %s)\n", oldchan->name, newchan->name);
03996 if (sub->owner != oldchan) {
03997 ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, sub->owner);
03998 return -1;
03999 }
04000 sub->owner = newchan;
04001 return 0;
04002 }
04003
04004 static int skinny_senddigit_begin(struct ast_channel *ast, char digit)
04005 {
04006 return -1;
04007 }
04008
04009 static int skinny_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration)
04010 {
04011 #if 0
04012 struct skinny_subchannel *sub = ast->tech_pvt;
04013 struct skinny_line *l = sub->parent;
04014 struct skinny_device *d = l->device;
04015 int tmp;
04016
04017 sprintf(tmp, "%d", digit);
04018 transmit_tone(d, digit, l->instance, sub->callid);
04019 #endif
04020 return -1;
04021 }
04022
04023 static int get_devicestate(struct skinny_line *l)
04024 {
04025 struct skinny_subchannel *sub;
04026 int res = AST_DEVICE_UNKNOWN;
04027
04028 if (!l)
04029 res = AST_DEVICE_INVALID;
04030 else if (!l->device)
04031 res = AST_DEVICE_UNAVAILABLE;
04032 else if (l->dnd)
04033 res = AST_DEVICE_BUSY;
04034 else {
04035 if (l->hookstate == SKINNY_ONHOOK) {
04036 res = AST_DEVICE_NOT_INUSE;
04037 } else {
04038 res = AST_DEVICE_INUSE;
04039 }
04040
04041 AST_LIST_TRAVERSE(&l->sub, sub, list) {
04042 if (sub->onhold) {
04043 res = AST_DEVICE_ONHOLD;
04044 break;
04045 }
04046 }
04047 }
04048
04049 return res;
04050 }
04051
04052 static char *control2str(int ind) {
04053 char *tmp;
04054
04055 switch (ind) {
04056 case AST_CONTROL_HANGUP:
04057 return "Other end has hungup";
04058 case AST_CONTROL_RING:
04059 return "Local ring";
04060 case AST_CONTROL_RINGING:
04061 return "Remote end is ringing";
04062 case AST_CONTROL_ANSWER:
04063 return "Remote end has answered";
04064 case AST_CONTROL_BUSY:
04065 return "Remote end is busy";
04066 case AST_CONTROL_TAKEOFFHOOK:
04067 return "Make it go off hook";
04068 case AST_CONTROL_OFFHOOK:
04069 return "Line is off hook";
04070 case AST_CONTROL_CONGESTION:
04071 return "Congestion (circuits busy)";
04072 case AST_CONTROL_FLASH:
04073 return "Flash hook";
04074 case AST_CONTROL_WINK:
04075 return "Wink";
04076 case AST_CONTROL_OPTION:
04077 return "Set a low-level option";
04078 case AST_CONTROL_RADIO_KEY:
04079 return "Key Radio";
04080 case AST_CONTROL_RADIO_UNKEY:
04081 return "Un-Key Radio";
04082 case AST_CONTROL_PROGRESS:
04083 return "Remote end is making Progress";
04084 case AST_CONTROL_PROCEEDING:
04085 return "Remote end is proceeding";
04086 case AST_CONTROL_HOLD:
04087 return "Hold";
04088 case AST_CONTROL_UNHOLD:
04089 return "Unhold";
04090 case AST_CONTROL_SRCUPDATE:
04091 return "Media Source Update";
04092 case -1:
04093 return "Stop tone";
04094 default:
04095 if (!(tmp = ast_threadstorage_get(&control2str_threadbuf, CONTROL2STR_BUFSIZE)))
04096 return "Unknown";
04097 snprintf(tmp, CONTROL2STR_BUFSIZE, "UNKNOWN-%d", ind);
04098 return tmp;
04099 }
04100 }
04101
04102 static int skinny_transfer(struct skinny_subchannel *sub)
04103 {
04104 struct skinny_subchannel *xferor;
04105 struct skinny_subchannel *xferee;
04106 struct ast_tone_zone_sound *ts = NULL;
04107
04108 if (ast_bridged_channel(sub->owner) || ast_bridged_channel(sub->related->owner)) {
04109 if (sub->xferor) {
04110 xferor = sub;
04111 xferee = sub->related;
04112 } else {
04113 xferor = sub;
04114 xferee = sub->related;
04115 }
04116
04117 if (skinnydebug) {
04118 ast_debug(1, "Transferee channels (local/remote): %s and %s\n",
04119 xferee->owner->name, ast_bridged_channel(xferee->owner)?ast_bridged_channel(xferee->owner)->name:"");
04120 ast_debug(1, "Transferor channels (local/remote): %s and %s\n",
04121 xferor->owner->name, ast_bridged_channel(xferor->owner)?ast_bridged_channel(xferor->owner)->name:"");
04122 }
04123 if (ast_bridged_channel(xferor->owner)) {
04124 if (ast_bridged_channel(xferee->owner)) {
04125 ast_queue_control(xferee->owner, AST_CONTROL_UNHOLD);
04126 }
04127 if (xferor->owner->_state == AST_STATE_RING) {
04128
04129 if ((ts = ast_get_indication_tone(xferor->owner->zone, "ring"))) {
04130 ast_playtones_start(xferor->owner, 0, ts->data, 1);
04131 ts = ast_tone_zone_sound_unref(ts);
04132 }
04133 }
04134 if (skinnydebug)
04135 ast_debug(1, "Transfer Masquerading %s to %s\n",
04136 xferee->owner->name, ast_bridged_channel(xferor->owner)?ast_bridged_channel(xferor->owner)->name:"");
04137 if (ast_channel_masquerade(xferee->owner, ast_bridged_channel(xferor->owner))) {
04138 ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
04139 ast_bridged_channel(xferor->owner)->name, xferee->owner->name);
04140 return -1;
04141 }
04142 } else if (ast_bridged_channel(xferee->owner)) {
04143 ast_queue_control(xferee->owner, AST_CONTROL_UNHOLD);
04144 if (xferor->owner->_state == AST_STATE_RING) {
04145
04146 if ((ts = ast_get_indication_tone(xferor->owner->zone, "ring"))) {
04147 ast_playtones_start(xferor->owner, 0, ts->data, 1);
04148 ts = ast_tone_zone_sound_unref(ts);
04149 }
04150 }
04151 if (skinnydebug)
04152 ast_debug(1, "Transfer Masquerading %s to %s\n",
04153 xferor->owner->name, ast_bridged_channel(xferee->owner)?ast_bridged_channel(xferee->owner)->name:"");
04154 if (ast_channel_masquerade(xferor->owner, ast_bridged_channel(xferee->owner))) {
04155 ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
04156 ast_bridged_channel(xferee->owner)->name, xferor->owner->name);
04157 return -1;
04158 }
04159 return 0;
04160 } else {
04161 if (option_debug)
04162 ast_log(LOG_DEBUG, "Neither %s nor %s are in a bridge, nothing to transfer\n",
04163 xferor->owner->name, xferee->owner->name);
04164 }
04165 }
04166 return 0;
04167 }
04168
04169 static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, size_t datalen)
04170 {
04171 struct skinny_subchannel *sub = ast->tech_pvt;
04172 struct skinny_line *l = sub->parent;
04173 struct skinny_device *d = l->device;
04174 struct skinnysession *s = d->session;
04175
04176 if (!s) {
04177 ast_log(LOG_NOTICE, "Asked to indicate '%s' condition on channel %s, but session does not exist.\n", control2str(ind), ast->name);
04178 return -1;
04179 }
04180
04181 if (skinnydebug)
04182 ast_verb(3, "Asked to indicate '%s' condition on channel %s\n", control2str(ind), ast->name);
04183 switch(ind) {
04184 case AST_CONTROL_RINGING:
04185 if (sub->blindxfer) {
04186 if (skinnydebug)
04187 ast_debug(1, "Channel %s set up for Blind Xfer, so Xfer rather than ring device\n", ast->name);
04188 skinny_transfer(sub);
04189 break;
04190 }
04191 if (ast->_state != AST_STATE_UP) {
04192 if (!sub->progress) {
04193 if (!d->earlyrtp) {
04194 transmit_tone(d, SKINNY_ALERT, l->instance, sub->callid);
04195 }
04196 transmit_callstateonly(d, sub, SKINNY_RINGOUT);
04197 transmit_dialednumber(d, l->lastnumberdialed, l->instance, sub->callid);
04198 transmit_displaypromptstatus(d, "Ring Out", 0, l->instance, sub->callid);
04199 transmit_callinfo(d, ast->cid.cid_name, ast->cid.cid_num, l->lastnumberdialed, l->lastnumberdialed, l->instance, sub->callid, 2);
04200 sub->ringing = 1;
04201 if (!d->earlyrtp) {
04202 break;
04203 }
04204 }
04205 }
04206 return -1;
04207 case AST_CONTROL_BUSY:
04208 if (ast->_state != AST_STATE_UP) {
04209 if (!d->earlyrtp) {
04210 transmit_tone(d, SKINNY_BUSYTONE, l->instance, sub->callid);
04211 }
04212 transmit_callstateonly(d, sub, SKINNY_BUSY);
04213 sub->alreadygone = 1;
04214 ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
04215 if (!d->earlyrtp) {
04216 break;
04217 }
04218 }
04219 return -1;
04220 case AST_CONTROL_CONGESTION:
04221 if (ast->_state != AST_STATE_UP) {
04222 if (!d->earlyrtp) {
04223 transmit_tone(d, SKINNY_REORDER, l->instance, sub->callid);
04224 }
04225 transmit_callstateonly(d, sub, SKINNY_CONGESTION);
04226 sub->alreadygone = 1;
04227 ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
04228 if (!d->earlyrtp) {
04229 break;
04230 }
04231 }
04232 return -1;
04233 case AST_CONTROL_PROGRESS:
04234 if ((ast->_state != AST_STATE_UP) && !sub->progress && !sub->outgoing) {
04235 if (!d->earlyrtp) {
04236 transmit_tone(d, SKINNY_ALERT, l->instance, sub->callid);
04237 }
04238 transmit_callstateonly(d, sub, SKINNY_PROGRESS);
04239 transmit_displaypromptstatus(d, "Call Progress", 0, l->instance, sub->callid);
04240 transmit_callinfo(d, ast->cid.cid_name, ast->cid.cid_num, l->lastnumberdialed, l->lastnumberdialed, l->instance, sub->callid, 2);
04241 sub->progress = 1;
04242 if (!d->earlyrtp) {
04243 break;
04244 }
04245 }
04246 return -1;
04247 case -1:
04248 transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
04249 break;
04250 case AST_CONTROL_HOLD:
04251 ast_moh_start(ast, data, l->mohinterpret);
04252 break;
04253 case AST_CONTROL_UNHOLD:
04254 ast_moh_stop(ast);
04255 break;
04256 case AST_CONTROL_PROCEEDING:
04257 break;
04258 case AST_CONTROL_SRCUPDATE:
04259 ast_rtp_new_source(sub->rtp);
04260 break;
04261 case AST_CONTROL_SRCCHANGE:
04262 ast_rtp_change_source(sub->rtp);
04263 break;
04264 default:
04265 ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", ind);
04266 return -1;
04267 }
04268 return 0;
04269 }
04270
04271 static struct ast_channel *skinny_new(struct skinny_line *l, int state)
04272 {
04273 struct ast_channel *tmp;
04274 struct skinny_subchannel *sub;
04275 struct skinny_device *d = l->device;
04276 struct ast_variable *v = NULL;
04277 int fmt;
04278
04279 if (!l->device) {
04280 ast_log(LOG_WARNING, "Device for line %s is not registered.\n", l->name);
04281 return NULL;
04282 }
04283
04284 tmp = ast_channel_alloc(1, state, l->cid_num, l->cid_name, l->accountcode, l->exten, l->context, l->amaflags, "Skinny/%s@%s-%d", l->name, d->name, callnums);
04285 if (!tmp) {
04286 ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
04287 return NULL;
04288 } else {
04289 sub = ast_calloc(1, sizeof(*sub));
04290 if (!sub) {
04291 ast_log(LOG_WARNING, "Unable to allocate Skinny subchannel\n");
04292 return NULL;
04293 } else {
04294 ast_mutex_init(&sub->lock);
04295
04296 sub->owner = tmp;
04297 sub->callid = callnums++;
04298 d->lastlineinstance = l->instance;
04299 d->lastcallreference = sub->callid;
04300 sub->cxmode = SKINNY_CX_INACTIVE;
04301 sub->nat = l->nat;
04302 sub->parent = l;
04303 sub->onhold = 0;
04304 sub->blindxfer = 0;
04305 sub->xferor = 0;
04306 sub->related = NULL;
04307
04308 AST_LIST_INSERT_HEAD(&l->sub, sub, list);
04309
04310 }
04311 tmp->tech = &skinny_tech;
04312 tmp->tech_pvt = sub;
04313 tmp->nativeformats = l->capability;
04314 if (!tmp->nativeformats)
04315
04316 tmp->nativeformats = default_capability;
04317 fmt = ast_best_codec(tmp->nativeformats);
04318 if (skinnydebug)
04319 ast_verb(1, "skinny_new: tmp->nativeformats=%d fmt=%d\n", tmp->nativeformats, fmt);
04320 if (sub->rtp) {
04321 ast_channel_set_fd(tmp, 0, ast_rtp_fd(sub->rtp));
04322 }
04323 if (state == AST_STATE_RING) {
04324 tmp->rings = 1;
04325 }
04326 tmp->writeformat = fmt;
04327 tmp->rawwriteformat = fmt;
04328 tmp->readformat = fmt;
04329 tmp->rawreadformat = fmt;
04330 if (!ast_strlen_zero(l->language))
04331 ast_string_field_set(tmp, language, l->language);
04332 if (!ast_strlen_zero(l->accountcode))
04333 ast_string_field_set(tmp, accountcode, l->accountcode);
04334 if (!ast_strlen_zero(l->parkinglot))
04335 ast_string_field_set(tmp, parkinglot, l->parkinglot);
04336 if (l->amaflags)
04337 tmp->amaflags = l->amaflags;
04338
04339 ast_module_ref(ast_module_info->self);
04340 tmp->callgroup = l->callgroup;
04341 tmp->pickupgroup = l->pickupgroup;
04342
04343
04344 if (l->cfwdtype & SKINNY_CFWD_ALL) {
04345 ast_string_field_set(tmp, call_forward, l->call_forward_all);
04346 } else if (l->cfwdtype & SKINNY_CFWD_BUSY) {
04347 if (get_devicestate(l) != AST_DEVICE_NOT_INUSE) {
04348 ast_string_field_set(tmp, call_forward, l->call_forward_busy);
04349 }
04350 }
04351
04352 ast_copy_string(tmp->context, l->context, sizeof(tmp->context));
04353 ast_copy_string(tmp->exten, l->exten, sizeof(tmp->exten));
04354
04355
04356
04357 tmp->cid.cid_ani = ast_strdup(l->cid_num);
04358
04359 tmp->priority = 1;
04360 tmp->adsicpe = AST_ADSI_UNAVAILABLE;
04361
04362 if (sub->rtp)
04363 ast_jb_configure(tmp, &global_jbconf);
04364
04365
04366 for (v = l->chanvars ; v ; v = v->next)
04367 pbx_builtin_setvar_helper(tmp, v->name, v->value);
04368
04369 if (state != AST_STATE_DOWN) {
04370 if (ast_pbx_start(tmp)) {
04371 ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
04372 ast_hangup(tmp);
04373 tmp = NULL;
04374 }
04375 }
04376 }
04377 return tmp;
04378 }
04379
04380 static int skinny_hold(struct skinny_subchannel *sub)
04381 {
04382 struct skinny_line *l = sub->parent;
04383 struct skinny_device *d = l->device;
04384
04385
04386 if (!sub || !sub->owner)
04387 return 0;
04388
04389
04390 if (skinnydebug)
04391 ast_verb(1, "Putting on Hold(%d)\n", l->instance);
04392
04393 ast_queue_control_data(sub->owner, AST_CONTROL_HOLD,
04394 S_OR(l->mohsuggest, NULL),
04395 !ast_strlen_zero(l->mohsuggest) ? strlen(l->mohsuggest) + 1 : 0);
04396
04397 transmit_activatecallplane(d, l);
04398 transmit_closereceivechannel(d, sub);
04399 transmit_stopmediatransmission(d, sub);
04400
04401 transmit_callstateonly(d, sub, SKINNY_HOLD);
04402 transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_WINK);
04403 sub->onhold = 1;
04404 return 1;
04405 }
04406
04407 static int skinny_unhold(struct skinny_subchannel *sub)
04408 {
04409 struct skinny_line *l = sub->parent;
04410 struct skinny_device *d = l->device;
04411
04412
04413 if (!sub || !sub->owner)
04414 return 0;
04415
04416
04417 if (skinnydebug)
04418 ast_verb(1, "Taking off Hold(%d)\n", l->instance);
04419
04420 ast_queue_control(sub->owner, AST_CONTROL_UNHOLD);
04421
04422 transmit_activatecallplane(d, l);
04423
04424 transmit_connect(d, sub);
04425 transmit_callstateonly(d, sub, SKINNY_CONNECTED);
04426 transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
04427 l->hookstate = SKINNY_OFFHOOK;
04428 sub->onhold = 0;
04429 return 1;
04430 }
04431
04432 static int handle_hold_button(struct skinny_subchannel *sub)
04433 {
04434 if (!sub)
04435 return -1;
04436 if (sub->related) {
04437 skinny_hold(sub);
04438 skinny_unhold(sub->related);
04439 sub->parent->activesub = sub->related;
04440 } else {
04441 if (sub->onhold) {
04442 skinny_unhold(sub);
04443 transmit_selectsoftkeys(sub->parent->device, sub->parent->instance, sub->callid, KEYDEF_CONNECTED);
04444 } else {
04445 skinny_hold(sub);
04446 transmit_selectsoftkeys(sub->parent->device, sub->parent->instance, sub->callid, KEYDEF_ONHOLD);
04447 }
04448 }
04449 return 1;
04450 }
04451
04452 static int handle_transfer_button(struct skinny_subchannel *sub)
04453 {
04454 struct skinny_line *l = sub->parent;
04455 struct skinny_device *d = l->device;
04456 struct skinny_subchannel *newsub;
04457 struct ast_channel *c;
04458 pthread_t t;
04459
04460 if (!sub) {
04461 ast_verbose("Transfer: No subchannel to transfer\n");
04462 return -1;
04463 }
04464 if (!sub->related) {
04465
04466 if (!sub->onhold) {
04467 skinny_hold(sub);
04468 }
04469 c = skinny_new(l, AST_STATE_DOWN);
04470 if (c) {
04471 newsub = c->tech_pvt;
04472
04473 newsub->related = sub;
04474 sub->related = newsub;
04475 newsub->xferor = 1;
04476 l->activesub = newsub;
04477 transmit_callstate(d, l->instance, SKINNY_OFFHOOK, newsub->callid);
04478 if (skinnydebug)
04479 ast_debug(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
04480 transmit_displaymessage(d, NULL, l->instance, newsub->callid);
04481 transmit_tone(d, SKINNY_DIALTONE, l->instance, newsub->callid);
04482 transmit_selectsoftkeys(d, l->instance, newsub->callid, KEYDEF_OFFHOOKWITHFEAT);
04483
04484 if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
04485 ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
04486 ast_hangup(c);
04487 }
04488 } else {
04489 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
04490 }
04491 } else {
04492
04493 if (sub->blindxfer) {
04494
04495 sub->blindxfer = 0;
04496 sub->related->blindxfer = 0;
04497
04498 } else {
04499
04500 if (sub->owner->_state == AST_STATE_DOWN || sub->related->owner->_state == AST_STATE_DOWN) {
04501
04502 sub->blindxfer = 1;
04503 sub->related->blindxfer = 1;
04504 } else {
04505
04506 skinny_transfer(sub);
04507 }
04508 }
04509 }
04510 return 0;
04511 }
04512
04513 static int handle_keep_alive_message(struct skinny_req *req, struct skinnysession *s)
04514 {
04515 if (!(req = req_alloc(0, KEEP_ALIVE_ACK_MESSAGE)))
04516 return -1;
04517
04518 transmit_response(s->device, req);
04519 return 1;
04520 }
04521
04522 static int handle_register_message(struct skinny_req *req, struct skinnysession *s)
04523 {
04524 struct skinny_device *d = NULL;
04525 char name[16];
04526 int res;
04527
04528 memcpy(&name, req->data.reg.name, sizeof(name));
04529
04530 res = skinny_register(req, s);
04531 if (!res) {
04532 ast_log(LOG_ERROR, "Rejecting Device %s: Device not found\n", name);
04533 if (!(req = req_alloc(sizeof(struct register_rej_message), REGISTER_REJ_MESSAGE)))
04534 return -1;
04535
04536 snprintf(req->data.regrej.errMsg, sizeof(req->data.regrej.errMsg), "No Authority: %s", name);
04537
04538
04539 ast_mutex_lock(&s->lock);
04540
04541 if (letohl(req->len > SKINNY_MAX_PACKET) || letohl(req->len < 0)) {
04542 ast_log(LOG_WARNING, "transmit_response: the length of the request is out of bounds\n");
04543 ast_mutex_unlock(&s->lock);
04544 return -1;
04545 }
04546
04547 memset(s->outbuf, 0, sizeof(s->outbuf));
04548 memcpy(s->outbuf, req, skinny_header_size);
04549 memcpy(s->outbuf+skinny_header_size, &req->data, letohl(req->len));
04550
04551 res = write(s->fd, s->outbuf, letohl(req->len)+8);
04552
04553 if (res != letohl(req->len)+8) {
04554 ast_log(LOG_WARNING, "Transmit: write only sent %d out of %d bytes: %s\n", res, letohl(req->len)+8, strerror(errno));
04555 }
04556
04557 ast_mutex_unlock(&s->lock);
04558
04559 return 0;
04560 }
04561 ast_verb(3, "Device '%s' successfully registered\n", name);
04562
04563 d = s->device;
04564
04565 if (!(req = req_alloc(sizeof(struct register_ack_message), REGISTER_ACK_MESSAGE)))
04566 return -1;
04567
04568 req->data.regack.res[0] = '0';
04569 req->data.regack.res[1] = '\0';
04570 req->data.regack.keepAlive = htolel(keep_alive);
04571 memcpy(req->data.regack.dateTemplate, date_format, sizeof(req->data.regack.dateTemplate));
04572 req->data.regack.res2[0] = '0';
04573 req->data.regack.res2[1] = '\0';
04574 req->data.regack.secondaryKeepAlive = htolel(keep_alive);
04575 transmit_response(d, req);
04576 if (skinnydebug)
04577 ast_verb(1, "Requesting capabilities\n");
04578
04579 if (!(req = req_alloc(0, CAPABILITIES_REQ_MESSAGE)))
04580 return -1;
04581
04582 transmit_response(d, req);
04583
04584 return res;
04585 }
04586
04587 static int handle_callforward_button(struct skinny_subchannel *sub, int cfwdtype)
04588 {
04589 struct skinny_line *l = sub->parent;
04590 struct skinny_device *d = l->device;
04591 struct ast_channel *c = sub->owner;
04592 pthread_t t;
04593
04594 if (l->hookstate == SKINNY_ONHOOK) {
04595 l->hookstate = SKINNY_OFFHOOK;
04596 transmit_speaker_mode(d, SKINNY_SPEAKERON);
04597 transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
04598 }
04599 if (skinnydebug)
04600 ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
04601 transmit_displaymessage(d, NULL, l->instance, sub->callid);
04602
04603 if (l->cfwdtype & cfwdtype) {
04604 set_callforwards(l, NULL, cfwdtype);
04605 ast_safe_sleep(c, 500);
04606 transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
04607 transmit_callstate(d, l->instance, SKINNY_ONHOOK, sub->callid);
04608 transmit_displaynotify(d, "CFwd disabled", 10);
04609 if (sub->owner && sub->owner->_state != AST_STATE_UP) {
04610 ast_indicate(c, -1);
04611 ast_hangup(c);
04612 }
04613 transmit_cfwdstate(d, l);
04614 } else {
04615 l->getforward = cfwdtype;
04616 transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
04617 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
04618 if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
04619 ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
04620 ast_hangup(c);
04621 }
04622 }
04623 return 0;
04624 }
04625 static int handle_ip_port_message(struct skinny_req *req, struct skinnysession *s)
04626 {
04627
04628 return 1;
04629 }
04630
04631 static int handle_keypad_button_message(struct skinny_req *req, struct skinnysession *s)
04632 {
04633 struct skinny_subchannel *sub = NULL;
04634 struct skinny_line *l;
04635 struct skinny_device *d = s->device;
04636 struct ast_frame f = { 0, };
04637 char dgt;
04638 int digit;
04639 int lineInstance;
04640 int callReference;
04641
04642 digit = letohl(req->data.keypad.button);
04643 lineInstance = letohl(req->data.keypad.lineInstance);
04644 callReference = letohl(req->data.keypad.callReference);
04645
04646 if (digit == 14) {
04647 dgt = '*';
04648 } else if (digit == 15) {
04649 dgt = '#';
04650 } else if (digit >= 0 && digit <= 9) {
04651 dgt = '0' + digit;
04652 } else {
04653
04654
04655
04656
04657
04658
04659
04660 dgt = '0' + digit;
04661 ast_log(LOG_WARNING, "Unsupported digit %d\n", digit);
04662 }
04663
04664 f.subclass = dgt;
04665
04666 f.src = "skinny";
04667
04668 if (lineInstance && callReference)
04669 sub = find_subchannel_by_instance_reference(d, lineInstance, callReference);
04670 else
04671 sub = d->activeline->activesub;
04672
04673
04674 if (!sub)
04675 return 0;
04676
04677 l = sub->parent;
04678 if (sub->owner) {
04679 if (sub->owner->_state == 0) {
04680 f.frametype = AST_FRAME_DTMF_BEGIN;
04681 ast_queue_frame(sub->owner, &f);
04682 }
04683
04684 f.frametype = AST_FRAME_DTMF_END;
04685 ast_queue_frame(sub->owner, &f);
04686
04687 if (AST_LIST_NEXT(sub, list) && AST_LIST_NEXT(sub, list)->owner) {
04688 if (sub->owner->_state == 0) {
04689 f.frametype = AST_FRAME_DTMF_BEGIN;
04690 ast_queue_frame(AST_LIST_NEXT(sub, list)->owner, &f);
04691 }
04692 f.frametype = AST_FRAME_DTMF_END;
04693 ast_queue_frame(AST_LIST_NEXT(sub, list)->owner, &f);
04694 }
04695 } else {
04696 if (skinnydebug)
04697 ast_verb(1, "No owner: %s\n", l->name);
04698 }
04699 return 1;
04700 }
04701
04702 static int handle_stimulus_message(struct skinny_req *req, struct skinnysession *s)
04703 {
04704 struct skinny_device *d = s->device;
04705 struct skinny_line *l;
04706 struct skinny_subchannel *sub;
04707
04708 struct ast_channel *c;
04709 pthread_t t;
04710 int event;
04711 int instance;
04712 int callreference;
04713
04714
04715 event = letohl(req->data.stimulus.stimulus);
04716 instance = letohl(req->data.stimulus.stimulusInstance);
04717 callreference = letohl(req->data.stimulus.callreference);
04718 if (skinnydebug)
04719 ast_verb(1, "callreference in handle_stimulus_message is '%d'\n", callreference);
04720
04721
04722 sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
04723
04724 if (!sub) {
04725 l = find_line_by_instance(d, d->lastlineinstance);
04726 if (!l) {
04727 return 0;
04728 }
04729 sub = l->activesub;
04730 } else {
04731 l = sub->parent;
04732 }
04733
04734 switch(event) {
04735 case STIMULUS_REDIAL:
04736 if (skinnydebug)
04737 ast_verb(1, "Received Stimulus: Redial(%d/%d)\n", instance, callreference);
04738
04739 if (ast_strlen_zero(l->lastnumberdialed)) {
04740 ast_log(LOG_WARNING, "Attempted redial, but no previously dialed number found.\n");
04741 l->hookstate = SKINNY_ONHOOK;
04742 transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
04743 transmit_callstate(d, l->instance, SKINNY_ONHOOK, instance);
04744 break;
04745 }
04746
04747 c = skinny_new(l, AST_STATE_DOWN);
04748 if (!c) {
04749 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
04750 } else {
04751 sub = c->tech_pvt;
04752 l = sub->parent;
04753 l->activesub = sub;
04754 if (l->hookstate == SKINNY_ONHOOK) {
04755 l->hookstate = SKINNY_OFFHOOK;
04756 transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
04757 }
04758 if (skinnydebug)
04759 ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
04760 transmit_displaymessage(d, NULL, l->instance, sub->callid);
04761 transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
04762 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
04763
04764 if (!ast_ignore_pattern(c->context, l->lastnumberdialed)) {
04765 transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
04766 }
04767 ast_copy_string(c->exten, l->lastnumberdialed, sizeof(c->exten));
04768 if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
04769 ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
04770 ast_hangup(c);
04771 }
04772 }
04773 break;
04774 case STIMULUS_SPEEDDIAL:
04775 {
04776 struct skinny_speeddial *sd;
04777
04778 if (skinnydebug)
04779 ast_verb(1, "Received Stimulus: SpeedDial(%d/%d)\n", instance, callreference);
04780 if (!(sd = find_speeddial_by_instance(d, instance, 0))) {
04781 return 0;
04782 }
04783
04784 if (!sub || !sub->owner)
04785 c = skinny_new(l, AST_STATE_DOWN);
04786 else
04787 c = sub->owner;
04788
04789 if (!c) {
04790 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
04791 } else {
04792 sub = c->tech_pvt;
04793 l = sub->parent;
04794 l->activesub = sub;
04795 if (l->hookstate == SKINNY_ONHOOK) {
04796 l->hookstate = SKINNY_OFFHOOK;
04797 transmit_speaker_mode(d, SKINNY_SPEAKERON);
04798 transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
04799 }
04800 if (skinnydebug)
04801 ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
04802 transmit_displaymessage(d, NULL, l->instance, sub->callid);
04803 transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
04804 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
04805
04806 if (!ast_ignore_pattern(c->context, sd->exten)) {
04807 transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
04808 }
04809 if (ast_exists_extension(c, c->context, sd->exten, 1, l->cid_num)) {
04810 ast_copy_string(c->exten, sd->exten, sizeof(c->exten));
04811 ast_copy_string(l->lastnumberdialed, sd->exten, sizeof(l->lastnumberdialed));
04812
04813 if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
04814 ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
04815 ast_hangup(c);
04816 }
04817 break;
04818 }
04819 }
04820 }
04821 break;
04822 case STIMULUS_HOLD:
04823 if (skinnydebug)
04824 ast_verb(1, "Received Stimulus: Hold(%d/%d)\n", instance, callreference);
04825 handle_hold_button(sub);
04826 break;
04827 case STIMULUS_TRANSFER:
04828 if (skinnydebug)
04829 ast_verb(1, "Received Stimulus: Transfer(%d/%d)\n", instance, callreference);
04830 if (l->transfer)
04831 handle_transfer_button(sub);
04832 else
04833 transmit_displaynotify(d, "Transfer disabled", 10);
04834 break;
04835 case STIMULUS_CONFERENCE:
04836 if (skinnydebug)
04837 ast_verb(1, "Received Stimulus: Conference(%d/%d)\n", instance, callreference);
04838
04839 break;
04840 case STIMULUS_VOICEMAIL:
04841 if (skinnydebug)
04842 ast_verb(1, "Received Stimulus: Voicemail(%d/%d)\n", instance, callreference);
04843
04844 if (!sub || !sub->owner) {
04845 c = skinny_new(l, AST_STATE_DOWN);
04846 } else {
04847 c = sub->owner;
04848 }
04849 if (!c) {
04850 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
04851 } else {
04852 sub = c->tech_pvt;
04853 l = sub->parent;
04854 l->activesub = sub;
04855
04856 if (ast_strlen_zero(l->vmexten))
04857 break;
04858
04859 if (l->hookstate == SKINNY_ONHOOK){
04860 l->hookstate = SKINNY_OFFHOOK;
04861 transmit_speaker_mode(d, SKINNY_SPEAKERON);
04862 transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
04863 }
04864
04865 if (skinnydebug)
04866 ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
04867
04868 transmit_displaymessage(d, NULL, l->instance, sub->callid);
04869 transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
04870 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
04871
04872 if (!ast_ignore_pattern(c->context, l->vmexten)) {
04873 transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
04874 }
04875
04876 if (ast_exists_extension(c, c->context, l->vmexten, 1, l->cid_num)) {
04877 ast_copy_string(c->exten, l->vmexten, sizeof(c->exten));
04878 ast_copy_string(l->lastnumberdialed, l->vmexten, sizeof(l->lastnumberdialed));
04879 if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
04880 ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
04881 ast_hangup(c);
04882 }
04883 break;
04884 }
04885 }
04886 break;
04887 case STIMULUS_CALLPARK:
04888 {
04889 int extout;
04890 char message[32];
04891
04892 if (skinnydebug)
04893 ast_verb(1, "Received Stimulus: Park Call(%d/%d)\n", instance, callreference);
04894
04895 if ((sub && sub->owner) && (sub->owner->_state == AST_STATE_UP)){
04896 c = sub->owner;
04897 if (!ast_masq_park_call(ast_bridged_channel(c), c, 0, &extout)) {
04898 snprintf(message, sizeof(message), "Call Parked at: %d", extout);
04899 transmit_displaynotify(d, message, 10);
04900 } else {
04901 transmit_displaynotify(d, "Call Park failed", 10);
04902 }
04903 } else {
04904 transmit_displaynotify(d, "Call Park not available", 10);
04905 }
04906 }
04907 break;
04908 case STIMULUS_DND:
04909 if (skinnydebug)
04910 ast_verb(1, "Received Stimulus: DND (%d/%d)\n", instance, callreference);
04911
04912
04913 if (l->dnd != 0){
04914 ast_verb(3, "Disabling DND on %s@%s\n", l->name, d->name);
04915 l->dnd = 0;
04916 transmit_lamp_indication(d, STIMULUS_DND, 1, SKINNY_LAMP_ON);
04917 transmit_displaynotify(d, "DnD disabled", 10);
04918 } else {
04919 ast_verb(3, "Enabling DND on %s@%s\n", l->name, d->name);
04920 l->dnd = 1;
04921 transmit_lamp_indication(d, STIMULUS_DND, 1, SKINNY_LAMP_OFF);
04922 transmit_displaynotify(d, "DnD enabled", 10);
04923 }
04924 break;
04925 case STIMULUS_FORWARDALL:
04926 if (skinnydebug)
04927 ast_verb(1, "Received Stimulus: Forward All(%d/%d)\n", instance, callreference);
04928
04929 if (!sub || !sub->owner) {
04930 c = skinny_new(l, AST_STATE_DOWN);
04931 } else {
04932 c = sub->owner;
04933 }
04934
04935 if (!c) {
04936 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
04937 } else {
04938 sub = c->tech_pvt;
04939 handle_callforward_button(sub, SKINNY_CFWD_ALL);
04940 }
04941 break;
04942 case STIMULUS_FORWARDBUSY:
04943 if (skinnydebug)
04944 ast_verb(1, "Received Stimulus: Forward Busy (%d/%d)\n", instance, callreference);
04945
04946 if (!sub || !sub->owner) {
04947 c = skinny_new(l, AST_STATE_DOWN);
04948 } else {
04949 c = sub->owner;
04950 }
04951
04952 if (!c) {
04953 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
04954 } else {
04955 sub = c->tech_pvt;
04956 handle_callforward_button(sub, SKINNY_CFWD_BUSY);
04957 }
04958 break;
04959 case STIMULUS_FORWARDNOANSWER:
04960 if (skinnydebug)
04961 ast_verb(1, "Received Stimulus: Forward No Answer (%d/%d)\n", instance, callreference);
04962
04963 #if 0
04964 if (!sub || !sub->owner) {
04965 c = skinny_new(l, AST_STATE_DOWN);
04966 } else {
04967 c = sub->owner;
04968 }
04969
04970 if (!c) {
04971 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
04972 } else {
04973 sub = c->tech_pvt;
04974 handle_callforward_button(sub, SKINNY_CFWD_NOANSWER);
04975 }
04976 #endif
04977 break;
04978 case STIMULUS_DISPLAY:
04979
04980 if (skinnydebug)
04981 ast_verb(1, "Received Stimulus: Display(%d/%d)\n", instance, callreference);
04982 break;
04983 case STIMULUS_LINE:
04984 if (skinnydebug)
04985 ast_verb(1, "Received Stimulus: Line(%d/%d)\n", instance, callreference);
04986
04987 l = find_line_by_instance(d, instance);
04988
04989 if (!l) {
04990 return 0;
04991 }
04992
04993 d->activeline = l;
04994
04995
04996 transmit_speaker_mode(d, SKINNY_SPEAKERON);
04997 transmit_ringer_mode(d, SKINNY_RING_OFF);
04998 transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
04999
05000 l->hookstate = SKINNY_OFFHOOK;
05001
05002 if (sub && sub->outgoing) {
05003
05004 ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
05005 transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
05006 transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
05007 transmit_callstateonly(d, sub, SKINNY_CONNECTED);
05008 transmit_displaypromptstatus(d, "Connected", 0, l->instance, sub->callid);
05009 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
05010 start_rtp(sub);
05011 ast_setstate(sub->owner, AST_STATE_UP);
05012 } else {
05013 if (sub && sub->owner) {
05014 ast_debug(1, "Current subchannel [%s] already has owner\n", sub->owner->name);
05015 } else {
05016 c = skinny_new(l, AST_STATE_DOWN);
05017 if (c) {
05018 sub = c->tech_pvt;
05019 l->activesub = sub;
05020 transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
05021 if (skinnydebug)
05022 ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
05023 transmit_displaymessage(d, NULL, l->instance, sub->callid);
05024 transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05025 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_OFFHOOK);
05026
05027
05028 if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
05029 ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
05030 ast_hangup(c);
05031 }
05032 } else {
05033 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05034 }
05035 }
05036 }
05037 break;
05038 default:
05039 if (skinnydebug)
05040 ast_verb(1, "RECEIVED UNKNOWN STIMULUS: %d(%d/%d)\n", event, instance, callreference);
05041 break;
05042 }
05043 ast_devstate_changed(AST_DEVICE_UNKNOWN, "Skinny/%s@%s", l->name, d->name);
05044
05045 return 1;
05046 }
05047
05048 static int handle_offhook_message(struct skinny_req *req, struct skinnysession *s)
05049 {
05050 struct skinny_device *d = s->device;
05051 struct skinny_line *l;
05052 struct skinny_subchannel *sub;
05053 struct ast_channel *c;
05054 struct skinny_line *tmp;
05055 pthread_t t;
05056 int instance;
05057 int reference;
05058
05059
05060
05061
05062
05063
05064
05065 AST_LIST_TRAVERSE(&d->lines, tmp, list) {
05066 if (tmp->hookstate == SKINNY_OFFHOOK) {
05067 ast_verbose(VERBOSE_PREFIX_3 "Got offhook message when device (%s@%s) already offhook\n", tmp->name, d->name);
05068 return 0;
05069 }
05070 }
05071
05072 instance = letohl(req->data.offhook.instance);
05073 reference = letohl(req->data.offhook.reference);
05074
05075 if (instance) {
05076 sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
05077 if (!sub) {
05078 l = find_line_by_instance(d, d->lastlineinstance);
05079 if (!l) {
05080 return 0;
05081 }
05082 } else {
05083 l = sub->parent;
05084 }
05085 } else {
05086 l = d->activeline;
05087 sub = l->activesub;
05088 }
05089
05090 transmit_ringer_mode(d, SKINNY_RING_OFF);
05091 l->hookstate = SKINNY_OFFHOOK;
05092
05093 ast_devstate_changed(AST_DEVICE_INUSE, "Skinny/%s@%s", l->name, d->name);
05094
05095 if (sub && sub->onhold) {
05096 return 1;
05097 }
05098
05099 transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
05100
05101 if (sub && sub->outgoing) {
05102
05103 ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
05104 transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
05105 transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
05106 transmit_callstateonly(d, sub, SKINNY_CONNECTED);
05107 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
05108 start_rtp(sub);
05109 ast_setstate(sub->owner, AST_STATE_UP);
05110 } else {
05111 if (sub && sub->owner) {
05112 ast_debug(1, "Current sub [%s] already has owner\n", sub->owner->name);
05113 } else {
05114 c = skinny_new(l, AST_STATE_DOWN);
05115 if (c) {
05116 sub = c->tech_pvt;
05117 l->activesub = sub;
05118 transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
05119 if (skinnydebug)
05120 ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
05121 transmit_displaymessage(d, NULL, l->instance, sub->callid);
05122 transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05123 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_OFFHOOK);
05124
05125
05126 if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
05127 ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
05128 ast_hangup(c);
05129 }
05130 } else {
05131 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05132 }
05133 }
05134 }
05135 return 1;
05136 }
05137
05138 static int handle_onhook_message(struct skinny_req *req, struct skinnysession *s)
05139 {
05140 struct skinny_device *d = s->device;
05141 struct skinny_line *l;
05142 struct skinny_subchannel *sub;
05143 int instance;
05144 int reference;
05145 int onlysub = 0;
05146
05147 instance = letohl(req->data.onhook.instance);
05148 reference = letohl(req->data.onhook.reference);
05149
05150 if (instance && reference) {
05151 sub = find_subchannel_by_instance_reference(d, instance, reference);
05152 if (!sub) {
05153 return 0;
05154 }
05155 l = sub->parent;
05156 } else {
05157 l = d->activeline;
05158 sub = l->activesub;
05159 if (!sub) {
05160 return 0;
05161 }
05162 }
05163
05164 if (l->hookstate == SKINNY_ONHOOK) {
05165
05166 return 0;
05167 }
05168
05169 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
05170
05171 if (sub->onhold) {
05172 return 0;
05173 }
05174
05175 if (!AST_LIST_NEXT(sub, list)) {
05176 onlysub = 1;
05177 } else {
05178 AST_LIST_REMOVE(&l->sub, sub, list);
05179 }
05180
05181 sub->cxmode = SKINNY_CX_RECVONLY;
05182 if (onlysub || sub->xferor){
05183 l->hookstate = SKINNY_ONHOOK;
05184 if (skinnydebug)
05185 ast_debug(1, "Skinny %s@%s-%d went on hook\n", l->name, d->name, reference);
05186 }
05187
05188 transmit_callstate(d, l->instance, l->hookstate, sub->callid);
05189 if (l->transfer && sub->xferor && sub->owner->_state >= AST_STATE_RING) {
05190
05191
05192 handle_transfer_button(sub);
05193 } else {
05194
05195
05196 if (sub->xferor && sub->related){
05197 sub->related->related = NULL;
05198 sub->related->blindxfer = 0;
05199 }
05200
05201 if (sub->owner) {
05202 sub->alreadygone = 1;
05203 ast_queue_hangup(sub->owner);
05204 } else {
05205 ast_log(LOG_WARNING, "Skinny(%s@%s-%d) channel already destroyed\n",
05206 l->name, d->name, sub->callid);
05207 }
05208 }
05209 return 1;
05210 }
05211
05212 static int handle_capabilities_res_message(struct skinny_req *req, struct skinnysession *s)
05213 {
05214 struct skinny_device *d = s->device;
05215 struct skinny_line *l;
05216 uint32_t count = 0;
05217 int codecs = 0;
05218 int i;
05219
05220 count = letohl(req->data.caps.count);
05221 if (count > SKINNY_MAX_CAPABILITIES) {
05222 count = SKINNY_MAX_CAPABILITIES;
05223 ast_log(LOG_WARNING, "Received more capabilities than we can handle (%d). Ignoring the rest.\n", SKINNY_MAX_CAPABILITIES);
05224 }
05225
05226 for (i = 0; i < count; i++) {
05227 int acodec = 0;
05228 int scodec = 0;
05229 scodec = letohl(req->data.caps.caps[i].codec);
05230 acodec = codec_skinny2ast(scodec);
05231 if (skinnydebug)
05232 ast_verb(1, "Adding codec capability '%d (%d)'\n", acodec, scodec);
05233 codecs |= acodec;
05234 }
05235
05236 d->capability = d->confcapability & codecs;
05237 ast_verb(0, "Device capability set to '%d'\n", d->capability);
05238 AST_LIST_TRAVERSE(&d->lines, l, list) {
05239 ast_mutex_lock(&l->lock);
05240 l->capability = l->confcapability & d->capability;
05241 ast_mutex_unlock(&l->lock);
05242 }
05243
05244 return 1;
05245 }
05246
05247 static int handle_speed_dial_stat_req_message(struct skinny_req *req, struct skinnysession *s)
05248 {
05249 struct skinny_device *d = s->device;
05250 struct skinny_speeddial *sd;
05251 int instance;
05252
05253 instance = letohl(req->data.speeddialreq.speedDialNumber);
05254
05255 sd = find_speeddial_by_instance(d, instance, 0);
05256
05257 if (!sd) {
05258 return 0;
05259 }
05260
05261 if (!(req = req_alloc(sizeof(struct speed_dial_stat_res_message), SPEED_DIAL_STAT_RES_MESSAGE)))
05262 return -1;
05263
05264 req->data.speeddialreq.speedDialNumber = htolel(instance);
05265 ast_copy_string(req->data.speeddial.speedDialDirNumber, sd->exten, sizeof(req->data.speeddial.speedDialDirNumber));
05266 ast_copy_string(req->data.speeddial.speedDialDisplayName, sd->label, sizeof(req->data.speeddial.speedDialDisplayName));
05267
05268 transmit_response(d, req);
05269 return 1;
05270 }
05271
05272 static int handle_line_state_req_message(struct skinny_req *req, struct skinnysession *s)
05273 {
05274 struct skinny_device *d = s->device;
05275 struct skinny_line *l;
05276 struct skinny_speeddial *sd = NULL;
05277 int instance;
05278
05279 instance = letohl(req->data.line.lineNumber);
05280
05281 AST_LIST_LOCK(&devices);
05282
05283 l = find_line_by_instance(d, instance);
05284
05285 if (!l) {
05286 sd = find_speeddial_by_instance(d, instance, 1);
05287 }
05288
05289 if (!l && !sd) {
05290 return 0;
05291 }
05292
05293 AST_LIST_UNLOCK(&devices);
05294
05295 if (!(req = req_alloc(sizeof(struct line_stat_res_message), LINE_STAT_RES_MESSAGE)))
05296 return -1;
05297
05298 req->data.linestat.lineNumber = letohl(instance);
05299 if (!l) {
05300 memcpy(req->data.linestat.lineDirNumber, sd->label, sizeof(req->data.linestat.lineDirNumber));
05301 memcpy(req->data.linestat.lineDisplayName, sd->label, sizeof(req->data.linestat.lineDisplayName));
05302 } else {
05303 memcpy(req->data.linestat.lineDirNumber, l->name, sizeof(req->data.linestat.lineDirNumber));
05304 memcpy(req->data.linestat.lineDisplayName, l->label, sizeof(req->data.linestat.lineDisplayName));
05305 }
05306 transmit_response(d, req);
05307 return 1;
05308 }
05309
05310 static int handle_time_date_req_message(struct skinny_req *req, struct skinnysession *s)
05311 {
05312 struct timeval now = ast_tvnow();
05313 struct ast_tm cmtime;
05314
05315 if (!(req = req_alloc(sizeof(struct definetimedate_message), DEFINETIMEDATE_MESSAGE)))
05316 return -1;
05317
05318 ast_localtime(&now, &cmtime, NULL);
05319 req->data.definetimedate.year = htolel(cmtime.tm_year+1900);
05320 req->data.definetimedate.month = htolel(cmtime.tm_mon+1);
05321 req->data.definetimedate.dayofweek = htolel(cmtime.tm_wday);
05322 req->data.definetimedate.day = htolel(cmtime.tm_mday);
05323 req->data.definetimedate.hour = htolel(cmtime.tm_hour);
05324 req->data.definetimedate.minute = htolel(cmtime.tm_min);
05325 req->data.definetimedate.seconds = htolel(cmtime.tm_sec);
05326 req->data.definetimedate.milliseconds = htolel(cmtime.tm_usec / 1000);
05327 req->data.definetimedate.timestamp = htolel(now.tv_sec);
05328 transmit_response(s->device, req);
05329 return 1;
05330 }
05331
05332 static int handle_button_template_req_message(struct skinny_req *req, struct skinnysession *s)
05333 {
05334 struct skinny_device *d = s->device;
05335 struct skinny_line *l;
05336 int i;
05337
05338 struct skinny_speeddial *sd;
05339 struct button_definition_template btn[42];
05340 int lineInstance = 1;
05341 int speeddialInstance = 1;
05342 int buttonCount = 0;
05343
05344 if (!(req = req_alloc(sizeof(struct button_template_res_message), BUTTON_TEMPLATE_RES_MESSAGE)))
05345 return -1;
05346
05347 memset(&btn, 0, sizeof(btn));
05348
05349 get_button_template(s, btn);
05350
05351 for (i=0; i<42; i++) {
05352 int btnSet = 0;
05353 switch (btn[i].buttonDefinition) {
05354 case BT_CUST_LINE:
05355
05356 req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE;
05357 req->data.buttontemplate.definition[i].instanceNumber = htolel(0);
05358
05359 AST_LIST_TRAVERSE(&d->lines, l, list) {
05360 if (l->instance == lineInstance) {
05361 ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
05362 req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
05363 req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance);
05364 lineInstance++;
05365 buttonCount++;
05366 btnSet = 1;
05367 break;
05368 }
05369 }
05370
05371 if (!btnSet) {
05372 AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
05373 if (sd->isHint && sd->instance == lineInstance) {
05374 ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
05375 req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
05376 req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance);
05377 lineInstance++;
05378 buttonCount++;
05379 btnSet = 1;
05380 break;
05381 }
05382 }
05383 }
05384 break;
05385 case BT_CUST_LINESPEEDDIAL:
05386
05387 req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE;
05388 req->data.buttontemplate.definition[i].instanceNumber = htolel(0);
05389
05390 AST_LIST_TRAVERSE(&d->lines, l, list) {
05391 if (l->instance == lineInstance) {
05392 ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
05393 req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
05394 req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance);
05395 lineInstance++;
05396 buttonCount++;
05397 btnSet = 1;
05398 break;
05399 }
05400 }
05401
05402 if (!btnSet) {
05403 AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
05404 if (sd->isHint && sd->instance == lineInstance) {
05405 ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
05406 req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
05407 req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance);
05408 lineInstance++;
05409 buttonCount++;
05410 btnSet = 1;
05411 break;
05412 } else if (!sd->isHint && sd->instance == speeddialInstance) {
05413 ast_verb(0, "Adding button: %d, %d\n", BT_SPEEDDIAL, speeddialInstance);
05414 req->data.buttontemplate.definition[i].buttonDefinition = BT_SPEEDDIAL;
05415 req->data.buttontemplate.definition[i].instanceNumber = htolel(speeddialInstance);
05416 speeddialInstance++;
05417 buttonCount++;
05418 btnSet = 1;
05419 break;
05420 }
05421 }
05422 }
05423 break;
05424 case BT_LINE:
05425 req->data.buttontemplate.definition[i].buttonDefinition = htolel(BT_NONE);
05426 req->data.buttontemplate.definition[i].instanceNumber = htolel(0);
05427
05428 AST_LIST_TRAVERSE(&d->lines, l, list) {
05429 if (l->instance == lineInstance) {
05430 ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
05431 req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
05432 req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance);
05433 lineInstance++;
05434 buttonCount++;
05435 btnSet = 1;
05436 break;
05437 }
05438 }
05439 break;
05440 case BT_SPEEDDIAL:
05441 req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE;
05442 req->data.buttontemplate.definition[i].instanceNumber = 0;
05443
05444 AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
05445 if (!sd->isHint && sd->instance == speeddialInstance) {
05446 ast_verb(0, "Adding button: %d, %d\n", BT_SPEEDDIAL, speeddialInstance);
05447 req->data.buttontemplate.definition[i].buttonDefinition = BT_SPEEDDIAL;
05448 req->data.buttontemplate.definition[i].instanceNumber = htolel(speeddialInstance - 1);
05449 speeddialInstance++;
05450 buttonCount++;
05451 btnSet = 1;
05452 break;
05453 }
05454 }
05455 break;
05456 case BT_NONE:
05457 break;
05458 default:
05459 ast_verb(0, "Adding button: %d, %d\n", btn[i].buttonDefinition, 0);
05460 req->data.buttontemplate.definition[i].buttonDefinition = htolel(btn[i].buttonDefinition);
05461 req->data.buttontemplate.definition[i].instanceNumber = htolel(0);
05462 buttonCount++;
05463 btnSet = 1;
05464 break;
05465 }
05466 }
05467
05468 req->data.buttontemplate.buttonOffset = htolel(0);
05469 req->data.buttontemplate.buttonCount = htolel(buttonCount);
05470 req->data.buttontemplate.totalButtonCount = htolel(buttonCount);
05471
05472 if (skinnydebug)
05473 ast_verb(1, "Sending %d template to %s\n",
05474 d->type,
05475 d->name);
05476 transmit_response(d, req);
05477 return 1;
05478 }
05479
05480 static int handle_version_req_message(struct skinny_req *req, struct skinnysession *s)
05481 {
05482 struct skinny_device *d = s->device;
05483 if (!(req = req_alloc(sizeof(struct version_res_message), VERSION_RES_MESSAGE)))
05484 return -1;
05485
05486 ast_copy_string(req->data.version.version, d->version_id, sizeof(req->data.version.version));
05487 transmit_response(d, req);
05488 return 1;
05489 }
05490
05491 static int handle_server_request_message(struct skinny_req *req, struct skinnysession *s)
05492 {
05493 struct skinny_device *d = s->device;
05494 if (!(req = req_alloc(sizeof(struct server_res_message), SERVER_RES_MESSAGE)))
05495 return -1;
05496
05497 memcpy(req->data.serverres.server[0].serverName, ourhost,
05498 sizeof(req->data.serverres.server[0].serverName));
05499 req->data.serverres.serverListenPort[0] = htolel(ourport);
05500 req->data.serverres.serverIpAddr[0] = htolel(d->ourip.s_addr);
05501 transmit_response(d, req);
05502 return 1;
05503 }
05504
05505 static int handle_alarm_message(struct skinny_req *req, struct skinnysession *s)
05506 {
05507
05508 if (skinnydebug)
05509 ast_verb(1, "Received Alarm Message: %s\n", req->data.alarm.displayMessage);
05510
05511 return 1;
05512 }
05513
05514 static int handle_open_receive_channel_ack_message(struct skinny_req *req, struct skinnysession *s)
05515 {
05516 struct skinny_device *d = s->device;
05517 struct skinny_line *l;
05518 struct skinny_subchannel *sub;
05519 struct ast_format_list fmt;
05520 struct sockaddr_in sin;
05521 struct sockaddr_in us;
05522 uint32_t addr;
05523 int port;
05524 int status;
05525 int passthruid;
05526
05527 status = letohl(req->data.openreceivechannelack.status);
05528 if (status) {
05529 ast_log(LOG_ERROR, "Open Receive Channel Failure\n");
05530 return 0;
05531 }
05532 addr = letohl(req->data.openreceivechannelack.ipAddr);
05533 port = letohl(req->data.openreceivechannelack.port);
05534 passthruid = letohl(req->data.openreceivechannelack.passThruId);
05535
05536 sin.sin_family = AF_INET;
05537 sin.sin_addr.s_addr = addr;
05538 sin.sin_port = htons(port);
05539
05540 sub = find_subchannel_by_reference(d, passthruid);
05541
05542 if (!sub)
05543 return 0;
05544
05545 l = sub->parent;
05546
05547 if (sub->rtp) {
05548 ast_rtp_set_peer(sub->rtp, &sin);
05549 ast_rtp_get_us(sub->rtp, &us);
05550 } else {
05551 ast_log(LOG_ERROR, "No RTP structure, this is very bad\n");
05552 return 0;
05553 }
05554
05555 if (skinnydebug)
05556 ast_verb(1, "ipaddr = %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
05557
05558 if (!(req = req_alloc(sizeof(struct start_media_transmission_message), START_MEDIA_TRANSMISSION_MESSAGE)))
05559 return -1;
05560
05561 fmt = ast_codec_pref_getsize(&l->prefs, ast_best_codec(l->capability));
05562
05563 if (skinnydebug)
05564 ast_verb(1, "Setting payloadType to '%d' (%d ms)\n", fmt.bits, fmt.cur_ms);
05565
05566 req->data.startmedia.conferenceId = htolel(sub->callid);
05567 req->data.startmedia.passThruPartyId = htolel(sub->callid);
05568 req->data.startmedia.remoteIp = htolel(d->ourip.s_addr);
05569 req->data.startmedia.remotePort = htolel(ntohs(us.sin_port));
05570 req->data.startmedia.packetSize = htolel(fmt.cur_ms);
05571 req->data.startmedia.payloadType = htolel(codec_ast2skinny(fmt.bits));
05572 req->data.startmedia.qualifier.precedence = htolel(127);
05573 req->data.startmedia.qualifier.vad = htolel(0);
05574 req->data.startmedia.qualifier.packets = htolel(0);
05575 req->data.startmedia.qualifier.bitRate = htolel(0);
05576 transmit_response(d, req);
05577
05578 return 1;
05579 }
05580
05581 static int handle_enbloc_call_message(struct skinny_req *req, struct skinnysession *s)
05582 {
05583 struct skinny_device *d = s->device;
05584 struct skinny_line *l;
05585 struct skinny_subchannel *sub = NULL;
05586 struct ast_channel *c;
05587 pthread_t t;
05588
05589 if (skinnydebug)
05590 ast_verb(1, "Received Enbloc Call: %s\n", req->data.enbloccallmessage.calledParty);
05591
05592 sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
05593
05594 if (!sub) {
05595 l = find_line_by_instance(d, d->lastlineinstance);
05596 if (!l) {
05597 return 0;
05598 }
05599 } else {
05600 l = sub->parent;
05601 }
05602
05603 c = skinny_new(l, AST_STATE_DOWN);
05604
05605 if(!c) {
05606 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05607 } else {
05608 l->hookstate = SKINNY_OFFHOOK;
05609
05610 sub = c->tech_pvt;
05611 l->activesub = sub;
05612 transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
05613 if (skinnydebug)
05614 ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
05615 transmit_displaymessage(d, NULL, l->instance, sub->callid);
05616 transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05617
05618 if (!ast_ignore_pattern(c->context, req->data.enbloccallmessage.calledParty)) {
05619 transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
05620 }
05621 ast_copy_string(c->exten, req->data.enbloccallmessage.calledParty, sizeof(c->exten));
05622 if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
05623 ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
05624 ast_hangup(c);
05625 }
05626 }
05627
05628 return 1;
05629 }
05630
05631
05632 static int handle_soft_key_set_req_message(struct skinny_req *req, struct skinnysession *s)
05633 {
05634 int i;
05635 int x;
05636 int y;
05637 const struct soft_key_definitions *softkeymode = soft_key_default_definitions;
05638 struct skinny_device *d = s->device;
05639
05640 if (!(req = req_alloc(sizeof(struct soft_key_set_res_message), SOFT_KEY_SET_RES_MESSAGE)))
05641 return -1;
05642
05643 req->data.softkeysets.softKeySetOffset = htolel(0);
05644 req->data.softkeysets.softKeySetCount = htolel(11);
05645 req->data.softkeysets.totalSoftKeySetCount = htolel(11);
05646 for (x = 0; x < sizeof(soft_key_default_definitions) / sizeof(struct soft_key_definitions); x++) {
05647 const uint8_t *defaults = softkeymode->defaults;
05648
05649
05650 for (y = 0; y < softkeymode->count; y++) {
05651 for (i = 0; i < (sizeof(soft_key_template_default) / sizeof(struct soft_key_template_definition)); i++) {
05652 if (defaults[y] == i+1) {
05653 req->data.softkeysets.softKeySetDefinition[softkeymode->mode].softKeyTemplateIndex[y] = htolel(i+1);
05654 req->data.softkeysets.softKeySetDefinition[softkeymode->mode].softKeyInfoIndex[y] = htolel(i+301);
05655 }
05656 }
05657 }
05658 softkeymode++;
05659 }
05660 transmit_response(d, req);
05661 transmit_selectsoftkeys(d, 0, 0, KEYDEF_ONHOOK);
05662 return 1;
05663 }
05664
05665 static int handle_soft_key_event_message(struct skinny_req *req, struct skinnysession *s)
05666 {
05667 struct skinny_device *d = s->device;
05668 struct skinny_line *l;
05669 struct skinny_subchannel *sub = NULL;
05670 struct ast_channel *c;
05671 pthread_t t;
05672 int event;
05673 int instance;
05674 int callreference;
05675
05676 event = letohl(req->data.softkeyeventmessage.softKeyEvent);
05677 instance = letohl(req->data.softkeyeventmessage.instance);
05678 callreference = letohl(req->data.softkeyeventmessage.callreference);
05679
05680 if (instance) {
05681 l = find_line_by_instance(d, instance);
05682 if (callreference) {
05683 sub = find_subchannel_by_instance_reference(d, instance, callreference);
05684 } else {
05685 sub = find_subchannel_by_instance_reference(d, instance, d->lastcallreference);
05686 }
05687 } else {
05688 l = find_line_by_instance(d, d->lastlineinstance);
05689 }
05690
05691 if (!l) {
05692 if (skinnydebug)
05693 ast_verb(1, "Received Softkey Event: %d(%d/%d)\n", event, instance, callreference);
05694 return 0;
05695 }
05696
05697 ast_devstate_changed(AST_DEVICE_INUSE, "Skinny/%s@%s", l->name, d->name);
05698
05699 switch(event) {
05700 case SOFTKEY_NONE:
05701 if (skinnydebug)
05702 ast_verb(1, "Received Softkey Event: None(%d/%d)\n", instance, callreference);
05703 break;
05704 case SOFTKEY_REDIAL:
05705 if (skinnydebug)
05706 ast_verb(1, "Received Softkey Event: Redial(%d/%d)\n", instance, callreference);
05707
05708 if (ast_strlen_zero(l->lastnumberdialed)) {
05709 ast_log(LOG_WARNING, "Attempted redial, but no previously dialed number found.\n");
05710 l->hookstate = SKINNY_ONHOOK;
05711 transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
05712 transmit_callstate(d, l->instance, SKINNY_ONHOOK, instance);
05713 break;
05714 }
05715
05716 if (!sub || !sub->owner) {
05717 c = skinny_new(l, AST_STATE_DOWN);
05718 } else {
05719 c = sub->owner;
05720 }
05721
05722 if (!c) {
05723 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05724 } else {
05725 sub = c->tech_pvt;
05726 l->activesub = sub;
05727 if (l->hookstate == SKINNY_ONHOOK) {
05728 l->hookstate = SKINNY_OFFHOOK;
05729 transmit_speaker_mode(d, SKINNY_SPEAKERON);
05730 transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
05731 }
05732 if (skinnydebug)
05733 ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
05734 transmit_displaymessage(d, NULL, l->instance, sub->callid);
05735 transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05736 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
05737
05738 if (!ast_ignore_pattern(c->context, l->lastnumberdialed)) {
05739 transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
05740 }
05741 ast_copy_string(c->exten, l->lastnumberdialed, sizeof(c->exten));
05742 if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
05743 ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
05744 ast_hangup(c);
05745 }
05746 }
05747 break;
05748 case SOFTKEY_NEWCALL:
05749 if (skinnydebug)
05750 ast_verb(1, "Received Softkey Event: New Call(%d/%d)\n", instance, callreference);
05751
05752
05753 c = skinny_new(l, AST_STATE_DOWN);
05754 sub = c->tech_pvt;
05755
05756
05757
05758
05759
05760
05761 if (!c) {
05762 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05763 } else {
05764 sub = c->tech_pvt;
05765 l->activesub = sub;
05766 if (l->hookstate == SKINNY_ONHOOK) {
05767 l->hookstate = SKINNY_OFFHOOK;
05768 transmit_speaker_mode(d, SKINNY_SPEAKERON);
05769 }
05770 ast_verb(1, "Call-id: %d\n", sub->callid);
05771
05772 transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
05773
05774 if (skinnydebug)
05775 ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
05776 transmit_displaymessage(d, NULL, l->instance, sub->callid);
05777 transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05778 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_OFFHOOK);
05779
05780
05781 if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
05782 ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
05783 ast_hangup(c);
05784 }
05785 }
05786 break;
05787 case SOFTKEY_HOLD:
05788 if (skinnydebug)
05789 ast_verb(1, "Received Softkey Event: Hold(%d/%d)\n", instance, callreference);
05790 handle_hold_button(sub);
05791 break;
05792 case SOFTKEY_TRNSFER:
05793 if (skinnydebug)
05794 ast_verb(1, "Received Softkey Event: Transfer(%d/%d)\n", instance, callreference);
05795 if (l->transfer)
05796 handle_transfer_button(sub);
05797 else
05798 transmit_displaynotify(d, "Transfer disabled", 10);
05799
05800 break;
05801 case SOFTKEY_DND:
05802 if (skinnydebug)
05803 ast_verb(1, "Received Softkey Event: DND(%d/%d)\n", instance, callreference);
05804
05805
05806 if (l->dnd != 0){
05807 ast_verb(3, "Disabling DND on %s@%s\n", l->name, d->name);
05808 l->dnd = 0;
05809 transmit_lamp_indication(d, STIMULUS_DND, 1, SKINNY_LAMP_ON);
05810 transmit_displaynotify(d, "DnD disabled", 10);
05811 } else {
05812 ast_verb(3, "Enabling DND on %s@%s\n", l->name, d->name);
05813 l->dnd = 1;
05814 transmit_lamp_indication(d, STIMULUS_DND, 1, SKINNY_LAMP_OFF);
05815 transmit_displaynotify(d, "DnD enabled", 10);
05816 }
05817 break;
05818 case SOFTKEY_CFWDALL:
05819 if (skinnydebug)
05820 ast_verb(1, "Received Softkey Event: Forward All(%d/%d)\n", instance, callreference);
05821
05822 if (!sub || !sub->owner) {
05823 c = skinny_new(l, AST_STATE_DOWN);
05824 } else {
05825 c = sub->owner;
05826 }
05827
05828 if (!c) {
05829 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05830 } else {
05831 sub = c->tech_pvt;
05832 l->activesub = sub;
05833 handle_callforward_button(sub, SKINNY_CFWD_ALL);
05834 }
05835 break;
05836 case SOFTKEY_CFWDBUSY:
05837 if (skinnydebug)
05838 ast_verb(1, "Received Softkey Event: Forward Busy (%d/%d)\n", instance, callreference);
05839
05840 if (!sub || !sub->owner) {
05841 c = skinny_new(l, AST_STATE_DOWN);
05842 } else {
05843 c = sub->owner;
05844 }
05845
05846 if (!c) {
05847 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05848 } else {
05849 sub = c->tech_pvt;
05850 l->activesub = sub;
05851 handle_callforward_button(sub, SKINNY_CFWD_BUSY);
05852 }
05853 break;
05854 case SOFTKEY_CFWDNOANSWER:
05855 if (skinnydebug)
05856 ast_verb(1, "Received Softkey Event: Forward No Answer (%d/%d)\n", instance, callreference);
05857
05858 #if 0
05859 if (!sub || !sub->owner) {
05860 c = skinny_new(l, AST_STATE_DOWN);
05861 } else {
05862 c = sub->owner;
05863 }
05864
05865 if (!c) {
05866 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05867 } else {
05868 sub = c->tech_pvt;
05869 l->activesub = sub;
05870 handle_callforward_button(sub, SKINNY_CFWD_NOANSWER);
05871 }
05872 #endif
05873 break;
05874 case SOFTKEY_BKSPC:
05875 if (skinnydebug)
05876 ast_verb(1, "Received Softkey Event: Backspace(%d/%d)\n", instance, callreference);
05877 break;
05878 case SOFTKEY_ENDCALL:
05879 if (skinnydebug)
05880 ast_verb(1, "Received Softkey Event: End Call(%d/%d)\n", instance, callreference);
05881
05882 if (l->hookstate == SKINNY_ONHOOK) {
05883
05884 break;
05885 }
05886 if (sub) {
05887 int onlysub = 0;
05888
05889 if (!AST_LIST_NEXT(sub, list)) {
05890 onlysub = 1;
05891 } else {
05892 AST_LIST_REMOVE(&l->sub, sub, list);
05893 }
05894
05895 sub->cxmode = SKINNY_CX_RECVONLY;
05896 if (onlysub || sub->xferor){
05897 l->hookstate = SKINNY_ONHOOK;
05898 if (skinnydebug)
05899 ast_debug(1, "Skinny %s@%s-%d went on hook\n", l->name, d->name, callreference);
05900 }
05901
05902 transmit_callstate(d, l->instance, l->hookstate, sub->callid);
05903 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
05904 if (skinnydebug)
05905 ast_verb(1, "Skinny %s@%s went on hook\n", l->name, d->name);
05906 if (l->transfer && sub->xferor && sub->owner->_state >= AST_STATE_RING) {
05907
05908
05909 handle_transfer_button(sub);
05910 } else {
05911
05912
05913 if (sub->xferor && sub->related){
05914 sub->related->related = NULL;
05915 sub->related->blindxfer = 0;
05916 }
05917
05918 if (sub->owner) {
05919 sub->alreadygone = 1;
05920 ast_queue_hangup(sub->owner);
05921 } else {
05922 ast_log(LOG_WARNING, "Skinny(%s@%s-%d) channel already destroyed\n",
05923 l->name, d->name, sub->callid);
05924 }
05925 }
05926 if ((l->hookstate == SKINNY_ONHOOK) && (AST_LIST_NEXT(sub, list) && !AST_LIST_NEXT(sub, list)->rtp)) {
05927 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
05928 }
05929 }
05930 break;
05931 case SOFTKEY_RESUME:
05932 if (skinnydebug)
05933 ast_verb(1, "Received Softkey Event: Resume(%d/%d)\n", instance, callreference);
05934
05935 if (sub) {
05936 if (sub->onhold) {
05937 skinny_unhold(sub);
05938 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
05939 } else {
05940 skinny_hold(sub);
05941 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_ONHOLD);
05942 }
05943 }
05944
05945 break;
05946 case SOFTKEY_ANSWER:
05947 if (skinnydebug)
05948 ast_verb(1, "Received Softkey Event: Answer(%d/%d)\n", instance, callreference);
05949
05950 transmit_ringer_mode(d, SKINNY_RING_OFF);
05951 transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
05952 if (l->hookstate == SKINNY_ONHOOK) {
05953 transmit_speaker_mode(d, SKINNY_SPEAKERON);
05954 l->hookstate = SKINNY_OFFHOOK;
05955 }
05956
05957 if (sub && sub->outgoing) {
05958
05959 ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
05960 transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
05961 transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
05962 transmit_callstateonly(d, sub, SKINNY_CONNECTED);
05963 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
05964 start_rtp(sub);
05965 ast_setstate(sub->owner, AST_STATE_UP);
05966 }
05967 break;
05968 case SOFTKEY_INFO:
05969 if (skinnydebug)
05970 ast_verb(1, "Received Softkey Event: Info(%d/%d)\n", instance, callreference);
05971 break;
05972 case SOFTKEY_CONFRN:
05973 if (skinnydebug)
05974 ast_verb(1, "Received Softkey Event: Conference(%d/%d)\n", instance, callreference);
05975
05976 break;
05977 case SOFTKEY_PARK:
05978 {
05979 int extout;
05980 char message[32];
05981
05982 if (skinnydebug)
05983 ast_verb(1, "Received Softkey Event: Park Call(%d/%d)\n", instance, callreference);
05984
05985 if ((sub && sub->owner) && (sub->owner->_state == AST_STATE_UP)){
05986 c = sub->owner;
05987 if (!ast_masq_park_call(ast_bridged_channel(c), c, 0, &extout)) {
05988 snprintf(message, sizeof(message), "Call Parked at: %d", extout);
05989 transmit_displaynotify(d, message, 10);
05990 } else {
05991 transmit_displaynotify(d, "Call Park failed", 10);
05992 }
05993 } else {
05994 transmit_displaynotify(d, "Call Park not available", 10);
05995 }
05996 }
05997 break;
05998 case SOFTKEY_JOIN:
05999 if (skinnydebug)
06000 ast_verb(1, "Received Softkey Event: Join(%d/%d)\n", instance, callreference);
06001 break;
06002 case SOFTKEY_MEETME:
06003
06004 if (skinnydebug)
06005 ast_verb(1, "Received Softkey Event: Meetme(%d/%d)\n", instance, callreference);
06006 break;
06007 case SOFTKEY_PICKUP:
06008 if (skinnydebug)
06009 ast_verb(1, "Received Softkey Event: Pickup(%d/%d)\n", instance, callreference);
06010 break;
06011 case SOFTKEY_GPICKUP:
06012 if (skinnydebug)
06013 ast_verb(1, "Received Softkey Event: Group Pickup(%d/%d)\n", instance, callreference);
06014 break;
06015 default:
06016 if (skinnydebug)
06017 ast_verb(1, "Received unknown Softkey Event: %d(%d/%d)\n", event, instance, callreference);
06018 break;
06019 }
06020
06021 return 1;
06022 }
06023
06024 static int handle_unregister_message(struct skinny_req *req, struct skinnysession *s)
06025 {
06026 return skinny_unregister(req, s);
06027 }
06028
06029 static int handle_soft_key_template_req_message(struct skinny_req *req, struct skinnysession *s)
06030 {
06031 if (!(req = req_alloc(sizeof(struct soft_key_template_res_message), SOFT_KEY_TEMPLATE_RES_MESSAGE)))
06032 return -1;
06033
06034 req->data.softkeytemplate.softKeyOffset = htolel(0);
06035 req->data.softkeytemplate.softKeyCount = htolel(sizeof(soft_key_template_default) / sizeof(struct soft_key_template_definition));
06036 req->data.softkeytemplate.totalSoftKeyCount = htolel(sizeof(soft_key_template_default) / sizeof(struct soft_key_template_definition));
06037 memcpy(req->data.softkeytemplate.softKeyTemplateDefinition,
06038 soft_key_template_default,
06039 sizeof(soft_key_template_default));
06040 transmit_response(s->device, req);
06041 return 1;
06042 }
06043
06044 static int handle_headset_status_message(struct skinny_req *req, struct skinnysession *s)
06045 {
06046
06047 return 1;
06048 }
06049
06050 static int handle_register_available_lines_message(struct skinny_req *req, struct skinnysession *s)
06051 {
06052
06053 return 1;
06054 }
06055
06056 static int handle_message(struct skinny_req *req, struct skinnysession *s)
06057 {
06058 int res = 0;
06059
06060 if ((!s->device) && (letohl(req->e) != REGISTER_MESSAGE && letohl(req->e) != ALARM_MESSAGE)) {
06061 ast_log(LOG_WARNING, "Client sent message #%d without first registering.\n", req->e);
06062 ast_free(req);
06063 return 0;
06064 }
06065
06066 SKINNY_DEVONLY(if (skinnydebug > 1) {
06067 ast_verb(4, "Received %s from %s\n", message2str(req->e), s->device->name);
06068 })
06069
06070 switch(letohl(req->e)) {
06071 case KEEP_ALIVE_MESSAGE:
06072 res = handle_keep_alive_message(req, s);
06073 break;
06074 case REGISTER_MESSAGE:
06075 if (skinnydebug)
06076 ast_verb(1, "Device %s is attempting to register\n", req->data.reg.name);
06077
06078 res = handle_register_message(req, s);
06079 break;
06080 case IP_PORT_MESSAGE:
06081 res = handle_ip_port_message(req, s);
06082 break;
06083 case KEYPAD_BUTTON_MESSAGE:
06084 {
06085 struct skinny_device *d = s->device;
06086 struct skinny_subchannel *sub;
06087 int lineInstance;
06088 int callReference;
06089
06090 if (skinnydebug)
06091 ast_verb(1, "Collected digit: [%d]\n", letohl(req->data.keypad.button));
06092
06093 lineInstance = letohl(req->data.keypad.lineInstance);
06094 callReference = letohl(req->data.keypad.callReference);
06095
06096 if (lineInstance) {
06097 sub = find_subchannel_by_instance_reference(d, lineInstance, callReference);
06098 } else {
06099 sub = d->activeline->activesub;
06100 }
06101
06102 if (sub && ((sub->owner && sub->owner->_state < AST_STATE_UP) || sub->onhold)) {
06103 char dgt;
06104 int digit = letohl(req->data.keypad.button);
06105
06106 if (digit == 14) {
06107 dgt = '*';
06108 } else if (digit == 15) {
06109 dgt = '#';
06110 } else if (digit >= 0 && digit <= 9) {
06111 dgt = '0' + digit;
06112 } else {
06113
06114
06115
06116
06117
06118
06119
06120 dgt = '0' + digit;
06121 ast_log(LOG_WARNING, "Unsupported digit %d\n", digit);
06122 }
06123
06124 d->exten[strlen(d->exten)] = dgt;
06125 d->exten[strlen(d->exten)+1] = '\0';
06126 } else
06127 res = handle_keypad_button_message(req, s);
06128 }
06129 break;
06130 case ENBLOC_CALL_MESSAGE:
06131 res = handle_enbloc_call_message(req, s);
06132 break;
06133 case STIMULUS_MESSAGE:
06134 res = handle_stimulus_message(req, s);
06135 break;
06136 case OFFHOOK_MESSAGE:
06137 res = handle_offhook_message(req, s);
06138 break;
06139 case ONHOOK_MESSAGE:
06140 res = handle_onhook_message(req, s);
06141 break;
06142 case CAPABILITIES_RES_MESSAGE:
06143 if (skinnydebug)
06144 ast_verb(1, "Received CapabilitiesRes\n");
06145
06146 res = handle_capabilities_res_message(req, s);
06147 break;
06148 case SPEED_DIAL_STAT_REQ_MESSAGE:
06149 if (skinnydebug)
06150 ast_verb(1, "Received SpeedDialStatRequest\n");
06151
06152 res = handle_speed_dial_stat_req_message(req, s);
06153 break;
06154 case LINE_STATE_REQ_MESSAGE:
06155 if (skinnydebug)
06156 ast_verb(1, "Received LineStatRequest\n");
06157 res = handle_line_state_req_message(req, s);
06158 break;
06159 case TIME_DATE_REQ_MESSAGE:
06160 if (skinnydebug)
06161 ast_verb(1, "Received Time/Date Request\n");
06162
06163 res = handle_time_date_req_message(req, s);
06164 break;
06165 case BUTTON_TEMPLATE_REQ_MESSAGE:
06166 if (skinnydebug)
06167 ast_verb(1, "Buttontemplate requested\n");
06168
06169 res = handle_button_template_req_message(req, s);
06170 break;
06171 case VERSION_REQ_MESSAGE:
06172 if (skinnydebug)
06173 ast_verb(1, "Version Request\n");
06174
06175 res = handle_version_req_message(req, s);
06176 break;
06177 case SERVER_REQUEST_MESSAGE:
06178 if (skinnydebug)
06179 ast_verb(1, "Received Server Request\n");
06180
06181 res = handle_server_request_message(req, s);
06182 break;
06183 case ALARM_MESSAGE:
06184 res = handle_alarm_message(req, s);
06185 break;
06186 case OPEN_RECEIVE_CHANNEL_ACK_MESSAGE:
06187 if (skinnydebug)
06188 ast_verb(1, "Received Open Receive Channel Ack\n");
06189
06190 res = handle_open_receive_channel_ack_message(req, s);
06191 break;
06192 case SOFT_KEY_SET_REQ_MESSAGE:
06193 if (skinnydebug)
06194 ast_verb(1, "Received SoftKeySetReq\n");
06195
06196 res = handle_soft_key_set_req_message(req, s);
06197 break;
06198 case SOFT_KEY_EVENT_MESSAGE:
06199 res = handle_soft_key_event_message(req, s);
06200 break;
06201 case UNREGISTER_MESSAGE:
06202 if (skinnydebug)
06203 ast_verb(1, "Received Unregister Request\n");
06204
06205 res = handle_unregister_message(req, s);
06206 break;
06207 case SOFT_KEY_TEMPLATE_REQ_MESSAGE:
06208 if (skinnydebug)
06209 ast_verb(1, "Received SoftKey Template Request\n");
06210
06211 res = handle_soft_key_template_req_message(req, s);
06212 break;
06213 case HEADSET_STATUS_MESSAGE:
06214 res = handle_headset_status_message(req, s);
06215 break;
06216 case REGISTER_AVAILABLE_LINES_MESSAGE:
06217 res = handle_register_available_lines_message(req, s);
06218 break;
06219 default:
06220 if (skinnydebug)
06221 ast_verb(1, "RECEIVED UNKNOWN MESSAGE TYPE: %x\n", letohl(req->e));
06222 break;
06223 }
06224 if (res >= 0 && req)
06225 ast_free(req);
06226 return res;
06227 }
06228
06229 static void destroy_session(struct skinnysession *s)
06230 {
06231 struct skinnysession *cur;
06232 AST_LIST_LOCK(&sessions);
06233 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, cur, list) {
06234 if (cur == s) {
06235 AST_LIST_REMOVE_CURRENT(list);
06236 if (s->fd > -1)
06237 close(s->fd);
06238
06239 ast_mutex_destroy(&s->lock);
06240
06241 ast_free(s);
06242 } else {
06243 ast_log(LOG_WARNING, "Trying to delete nonexistent session %p?\n", s);
06244 }
06245 }
06246 AST_LIST_TRAVERSE_SAFE_END
06247 AST_LIST_UNLOCK(&sessions);
06248 }
06249
06250 static int get_input(struct skinnysession *s)
06251 {
06252 int res;
06253 int dlen = 0;
06254 int *bufaddr;
06255 struct pollfd fds[1];
06256
06257 fds[0].fd = s->fd;
06258 fds[0].events = POLLIN;
06259 fds[0].revents = 0;
06260 res = ast_poll(fds, 1, (keep_alive * 1100));
06261
06262
06263 if (res < 0) {
06264 if (errno != EINTR) {
06265 ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
06266 return res;
06267 }
06268 } else if (res == 0) {
06269 if (skinnydebug)
06270 ast_verb(1, "Skinny Client was lost, unregistering\n");
06271 skinny_unregister(NULL, s);
06272 return -1;
06273 }
06274
06275 if (fds[0].revents) {
06276 ast_mutex_lock(&s->lock);
06277 memset(s->inbuf, 0, sizeof(s->inbuf));
06278 res = read(s->fd, s->inbuf, 4);
06279 if (res < 0) {
06280 ast_log(LOG_WARNING, "read() returned error: %s\n", strerror(errno));
06281
06282 if (skinnydebug)
06283 ast_verb(1, "Skinny Client was lost, unregistering\n");
06284
06285 skinny_unregister(NULL, s);
06286 ast_mutex_unlock(&s->lock);
06287 return res;
06288 } else if (res != 4) {
06289 ast_log(LOG_WARNING, "Skinny Client sent less data than expected. Expected 4 but got %d.\n", res);
06290 ast_mutex_unlock(&s->lock);
06291
06292 if (res == 0) {
06293 if (skinnydebug)
06294 ast_verb(1, "Skinny Client was lost, unregistering\n");
06295 skinny_unregister(NULL, s);
06296 }
06297
06298 return -1;
06299 }
06300
06301 bufaddr = (int *)s->inbuf;
06302 dlen = letohl(*bufaddr);
06303 if (dlen < 4) {
06304 ast_debug(1, "Skinny Client sent invalid data.\n");
06305 ast_mutex_unlock(&s->lock);
06306 return -1;
06307 }
06308 if (dlen+8 > sizeof(s->inbuf)) {
06309 dlen = sizeof(s->inbuf) - 8;
06310 }
06311 *bufaddr = htolel(dlen);
06312
06313 res = read(s->fd, s->inbuf+4, dlen+4);
06314 ast_mutex_unlock(&s->lock);
06315 if (res < 0) {
06316 ast_log(LOG_WARNING, "read() returned error: %s\n", strerror(errno));
06317 return res;
06318 } else if (res != (dlen+4)) {
06319 ast_log(LOG_WARNING, "Skinny Client sent less data than expected.\n");
06320 return -1;
06321 }
06322 return res;
06323 }
06324 return 0;
06325 }
06326
06327 static struct skinny_req *skinny_req_parse(struct skinnysession *s)
06328 {
06329 struct skinny_req *req;
06330 int *bufaddr;
06331
06332 if (!(req = ast_calloc(1, SKINNY_MAX_PACKET)))
06333 return NULL;
06334
06335 ast_mutex_lock(&s->lock);
06336 memcpy(req, s->inbuf, skinny_header_size);
06337 bufaddr = (int *)(s->inbuf);
06338 memcpy(&req->data, s->inbuf+skinny_header_size, letohl(*bufaddr)-4);
06339
06340 ast_mutex_unlock(&s->lock);
06341
06342 if (letohl(req->e) < 0) {
06343 ast_log(LOG_ERROR, "Event Message is NULL from socket %d, This is bad\n", s->fd);
06344 ast_free(req);
06345 return NULL;
06346 }
06347
06348 return req;
06349 }
06350
06351 static void *skinny_session(void *data)
06352 {
06353 int res;
06354 struct skinny_req *req;
06355 struct skinnysession *s = data;
06356
06357 ast_verb(3, "Starting Skinny session from %s\n", ast_inet_ntoa(s->sin.sin_addr));
06358
06359 for (;;) {
06360 res = get_input(s);
06361 if (res < 0) {
06362 break;
06363 }
06364
06365 if (res > 0)
06366 {
06367 if (!(req = skinny_req_parse(s))) {
06368 destroy_session(s);
06369 return NULL;
06370 }
06371
06372 res = handle_message(req, s);
06373 if (res < 0) {
06374 destroy_session(s);
06375 return NULL;
06376 }
06377 }
06378 }
06379 ast_debug(3, "Skinny Session returned: %s\n", strerror(errno));
06380
06381 if (s)
06382 destroy_session(s);
06383
06384 return 0;
06385 }
06386
06387 static void *accept_thread(void *ignore)
06388 {
06389 int as;
06390 struct sockaddr_in sin;
06391 socklen_t sinlen;
06392 struct skinnysession *s;
06393 struct protoent *p;
06394 int arg = 1;
06395
06396 for (;;) {
06397 sinlen = sizeof(sin);
06398 as = accept(skinnysock, (struct sockaddr *)&sin, &sinlen);
06399 if (as < 0) {
06400 ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
06401 continue;
06402 }
06403 p = getprotobyname("tcp");
06404 if(p) {
06405 if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
06406 ast_log(LOG_WARNING, "Failed to set Skinny tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
06407 }
06408 }
06409 if (!(s = ast_calloc(1, sizeof(struct skinnysession))))
06410 continue;
06411
06412 memcpy(&s->sin, &sin, sizeof(sin));
06413 ast_mutex_init(&s->lock);
06414 s->fd = as;
06415 AST_LIST_LOCK(&sessions);
06416 AST_LIST_INSERT_HEAD(&sessions, s, list);
06417 AST_LIST_UNLOCK(&sessions);
06418
06419 if (ast_pthread_create(&s->t, NULL, skinny_session, s)) {
06420 destroy_session(s);
06421 }
06422 }
06423 if (skinnydebug)
06424 ast_verb(1, "killing accept thread\n");
06425 close(as);
06426 return 0;
06427 }
06428
06429 static void *do_monitor(void *data)
06430 {
06431 int res;
06432
06433
06434
06435
06436 for(;;) {
06437 pthread_testcancel();
06438
06439 res = ast_sched_wait(sched);
06440 if ((res < 0) || (res > 1000)) {
06441 res = 1000;
06442 }
06443 res = ast_io_wait(io, res);
06444 ast_mutex_lock(&monlock);
06445 if (res >= 0) {
06446 ast_sched_runq(sched);
06447 }
06448 ast_mutex_unlock(&monlock);
06449 }
06450
06451 return NULL;
06452
06453 }
06454
06455 static int restart_monitor(void)
06456 {
06457
06458 if (monitor_thread == AST_PTHREADT_STOP)
06459 return 0;
06460
06461 ast_mutex_lock(&monlock);
06462 if (monitor_thread == pthread_self()) {
06463 ast_mutex_unlock(&monlock);
06464 ast_log(LOG_WARNING, "Cannot kill myself\n");
06465 return -1;
06466 }
06467 if (monitor_thread != AST_PTHREADT_NULL) {
06468
06469 pthread_kill(monitor_thread, SIGURG);
06470 } else {
06471
06472 if (ast_pthread_create_background(&monitor_thread, NULL, do_monitor, NULL) < 0) {
06473 ast_mutex_unlock(&monlock);
06474 ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
06475 return -1;
06476 }
06477 }
06478 ast_mutex_unlock(&monlock);
06479 return 0;
06480 }
06481
06482 static int skinny_devicestate(void *data)
06483 {
06484 struct skinny_line *l;
06485 char *tmp;
06486
06487 tmp = ast_strdupa(data);
06488
06489 l = find_line_by_name(tmp);
06490
06491 return get_devicestate(l);
06492 }
06493
06494 static struct ast_channel *skinny_request(const char *type, int format, void *data, int *cause)
06495 {
06496 int oldformat;
06497
06498 struct skinny_line *l;
06499 struct ast_channel *tmpc = NULL;
06500 char tmp[256];
06501 char *dest = data;
06502
06503 oldformat = format;
06504
06505 if (!(format &= AST_FORMAT_AUDIO_MASK)) {
06506 ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", format);
06507 return NULL;
06508 }
06509
06510 ast_copy_string(tmp, dest, sizeof(tmp));
06511 if (ast_strlen_zero(tmp)) {
06512 ast_log(LOG_NOTICE, "Skinny channels require a device\n");
06513 return NULL;
06514 }
06515 l = find_line_by_name(tmp);
06516 if (!l) {
06517 ast_log(LOG_NOTICE, "No available lines on: %s\n", dest);
06518 return NULL;
06519 }
06520 ast_verb(3, "skinny_request(%s)\n", tmp);
06521 tmpc = skinny_new(l, AST_STATE_DOWN);
06522 if (!tmpc) {
06523 ast_log(LOG_WARNING, "Unable to make channel for '%s'\n", tmp);
06524 }
06525 restart_monitor();
06526 return tmpc;
06527 }
06528
06529 #define TYPE_GENERAL 1
06530 #define TYPE_DEF_DEVICE 2
06531 #define TYPE_DEF_LINE 4
06532 #define TYPE_DEVICE 8
06533 #define TYPE_LINE 16
06534
06535 #define CLINE_OPTS ((struct skinny_line_options *)item)
06536 #define CLINE ((struct skinny_line *)item)
06537 #define CDEV_OPTS ((struct skinny_device_options *)item)
06538 #define CDEV ((struct skinny_device *)item)
06539
06540 static void config_parse_variables(int type, void *item, struct ast_variable *vptr)
06541 {
06542 struct ast_variable *v;
06543 int lineInstance = 1;
06544 int speeddialInstance = 1;
06545
06546 while(vptr) {
06547 v = vptr;
06548 vptr = vptr->next;
06549
06550 if (type & (TYPE_GENERAL)) {
06551 char newcontexts[AST_MAX_CONTEXT];
06552 char oldcontexts[AST_MAX_CONTEXT];
06553 char *stringp, *context, *oldregcontext;
06554 if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) {
06555 v = v->next;
06556 continue;
06557 }
06558 if (!strcasecmp(v->name, "bindaddr")) {
06559 if (!(hp = ast_gethostbyname(v->value, &ahp))) {
06560 ast_log(LOG_WARNING, "Invalid address: %s\n", v->value);
06561 } else {
06562 memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr));
06563 }
06564 continue;
06565 } else if (!strcasecmp(v->name, "keepalive")) {
06566 keep_alive = atoi(v->value);
06567 continue;
06568 } else if (!strcasecmp(v->name, "regcontext")) {
06569 ast_copy_string(newcontexts, v->value, sizeof(newcontexts));
06570 stringp = newcontexts;
06571
06572 ast_copy_string(oldcontexts, regcontext, sizeof(oldcontexts));
06573 oldregcontext = oldcontexts;
06574
06575 cleanup_stale_contexts(stringp, oldregcontext);
06576
06577 while ((context = strsep(&stringp, "&"))) {
06578 ast_copy_string(used_context, context, sizeof(used_context));
06579 ast_context_find_or_create(NULL, NULL, context, "Skinny");
06580 }
06581 ast_copy_string(regcontext, v->value, sizeof(regcontext));
06582 continue;
06583 } else if (!strcasecmp(v->name, "dateformat")) {
06584 memcpy(date_format, v->value, sizeof(date_format));
06585 continue;
06586 } else if (!strcasecmp(v->name, "tos")) {
06587 if (ast_str2tos(v->value, &qos.tos))
06588 ast_log(LOG_WARNING, "Invalid tos value at line %d, refer to QoS documentation\n", v->lineno);
06589 continue;
06590 } else if (!strcasecmp(v->name, "tos_audio")) {
06591 if (ast_str2tos(v->value, &qos.tos_audio))
06592 ast_log(LOG_WARNING, "Invalid tos_audio value at line %d, refer to QoS documentation\n", v->lineno);
06593 continue;
06594 } else if (!strcasecmp(v->name, "tos_video")) {
06595 if (ast_str2tos(v->value, &qos.tos_video))
06596 ast_log(LOG_WARNING, "Invalid tos_video value at line %d, refer to QoS documentation\n", v->lineno);
06597 continue;
06598 } else if (!strcasecmp(v->name, "cos")) {
06599 if (ast_str2cos(v->value, &qos.cos))
06600 ast_log(LOG_WARNING, "Invalid cos value at line %d, refer to QoS documentation\n", v->lineno);
06601 continue;
06602 } else if (!strcasecmp(v->name, "cos_audio")) {
06603 if (ast_str2cos(v->value, &qos.cos_audio))
06604 ast_log(LOG_WARNING, "Invalid cos_audio value at line %d, refer to QoS documentation\n", v->lineno);
06605 continue;
06606 } else if (!strcasecmp(v->name, "cos_video")) {
06607 if (ast_str2cos(v->value, &qos.cos_video))
06608 ast_log(LOG_WARNING, "Invalid cos_video value at line %d, refer to QoS documentation\n", v->lineno);
06609 continue;
06610 } else if (!strcasecmp(v->name, "bindport")) {
06611 if (sscanf(v->value, "%5d", &ourport) == 1) {
06612 bindaddr.sin_port = htons(ourport);
06613 } else {
06614 ast_log(LOG_WARNING, "Invalid bindport '%s' at line %d of %s\n", v->value, v->lineno, config);
06615 }
06616 continue;
06617 } else if (!strcasecmp(v->name, "allow")) {
06618 ast_parse_allow_disallow(&default_prefs, &default_capability, v->value, 1);
06619 continue;
06620 } else if (!strcasecmp(v->name, "disallow")) {
06621 ast_parse_allow_disallow(&default_prefs, &default_capability, v->value, 0);
06622 continue;
06623 }
06624 }
06625
06626 if (!strcasecmp(v->name, "transfer")) {
06627 if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06628 CDEV_OPTS->transfer = ast_true(v->value);
06629 continue;
06630 } else if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06631 CLINE_OPTS->transfer = ast_true(v->value);
06632 continue;
06633 }
06634 } else if (!strcasecmp(v->name, "callwaiting")) {
06635 if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06636 CDEV_OPTS->callwaiting = ast_true(v->value);
06637 continue;
06638 } else if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06639 CLINE_OPTS->callwaiting = ast_true(v->value);
06640 continue;
06641 }
06642 } else if (!strcasecmp(v->name, "directmedia") || !strcasecmp(v->name, "canreinvite")) {
06643 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06644 CLINE_OPTS->directmedia = ast_true(v->value);
06645 continue;
06646 }
06647 } else if (!strcasecmp(v->name, "nat")) {
06648 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06649 CLINE_OPTS->nat = ast_true(v->value);
06650 continue;
06651 }
06652 } else if (!strcasecmp(v->name, "context")) {
06653 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06654 ast_copy_string(CLINE_OPTS->context, v->value, sizeof(CLINE_OPTS->context));
06655 continue;
06656 }
06657 }else if (!strcasecmp(v->name, "vmexten")) {
06658 if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06659 ast_copy_string(CDEV_OPTS->vmexten, v->value, sizeof(CDEV_OPTS->vmexten));
06660 continue;
06661 } else if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06662 ast_copy_string(CLINE_OPTS->vmexten, v->value, sizeof(CLINE_OPTS->vmexten));
06663 continue;
06664 }
06665 } else if (!strcasecmp(v->name, "mwiblink")) {
06666 if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06667 CDEV_OPTS->mwiblink = ast_true(v->value);
06668 continue;
06669 } else if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06670 CLINE_OPTS->mwiblink = ast_true(v->value);
06671 continue;
06672 }
06673 } else if (!strcasecmp(v->name, "linelabel")) {
06674 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06675 ast_copy_string(CLINE_OPTS->label, v->value, sizeof(CLINE_OPTS->label));
06676 continue;
06677 }
06678 } else if (!strcasecmp(v->name, "callerid")) {
06679 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06680 if (!strcasecmp(v->value, "asreceived")) {
06681 CLINE_OPTS->cid_num[0] = '\0';
06682 CLINE_OPTS->cid_name[0] = '\0';
06683 } else {
06684 ast_callerid_split(v->value, CLINE_OPTS->cid_name, sizeof(CLINE_OPTS->cid_name), CLINE_OPTS->cid_num, sizeof(CLINE_OPTS->cid_num));
06685 }
06686 continue;
06687 }
06688 } else if (!strcasecmp(v->name, "amaflags")) {
06689 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06690 int tempamaflags = ast_cdr_amaflags2int(v->value);
06691 if (tempamaflags < 0) {
06692 ast_log(LOG_WARNING, "Invalid AMA flags: %s at line %d\n", v->value, v->lineno);
06693 } else {
06694 CLINE_OPTS->amaflags = tempamaflags;
06695 }
06696 continue;
06697 }
06698 } else if (!strcasecmp(v->name, "regexten")) {
06699 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06700 ast_copy_string(CLINE_OPTS->regexten, v->value, sizeof(CLINE_OPTS->regexten));
06701 continue;
06702 }
06703 } else if (!strcasecmp(v->name, "language")) {
06704 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06705 ast_copy_string(CLINE_OPTS->language, v->value, sizeof(CLINE_OPTS->language));
06706 continue;
06707 }
06708 } else if (!strcasecmp(v->name, "accountcode")) {
06709 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06710 ast_copy_string(CLINE_OPTS->accountcode, v->value, sizeof(CLINE_OPTS->accountcode));
06711 continue;
06712 }
06713 } else if (!strcasecmp(v->name, "mohinterpret") || !strcasecmp(v->name, "musiconhold")) {
06714 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06715 ast_copy_string(CLINE_OPTS->mohinterpret, v->value, sizeof(CLINE_OPTS->mohinterpret));
06716 continue;
06717 }
06718 } else if (!strcasecmp(v->name, "mohsuggest")) {
06719 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06720 ast_copy_string(CLINE_OPTS->mohsuggest, v->value, sizeof(CLINE_OPTS->mohsuggest));
06721 continue;
06722 }
06723 } else if (!strcasecmp(v->name, "callgroup")) {
06724 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06725 CLINE_OPTS->callgroup = ast_get_group(v->value);
06726 continue;
06727 }
06728 } else if (!strcasecmp(v->name, "pickupgroup")) {
06729 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06730 CLINE_OPTS->pickupgroup = ast_get_group(v->value);
06731 continue;
06732 }
06733 } else if (!strcasecmp(v->name, "immediate")) {
06734 if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE | TYPE_DEF_LINE | TYPE_LINE)) {
06735 CLINE_OPTS->immediate = ast_true(v->value);
06736 continue;
06737 }
06738 } else if (!strcasecmp(v->name, "cancallforward")) {
06739 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06740 CLINE_OPTS->cancallforward = ast_true(v->value);
06741 continue;
06742 }
06743 } else if (!strcasecmp(v->name, "mailbox")) {
06744 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06745 ast_copy_string(CLINE_OPTS->mailbox, v->value, sizeof(CLINE_OPTS->mailbox));
06746 continue;
06747 }
06748 } else if ( !strcasecmp(v->name, "parkinglot")) {
06749 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06750 ast_copy_string(CLINE_OPTS->parkinglot, v->value, sizeof(CLINE_OPTS->parkinglot));
06751 continue;
06752 }
06753 } else if (!strcasecmp(v->name, "hasvoicemail")) {
06754 if (type & (TYPE_LINE)) {
06755 if (ast_true(v->value) && ast_strlen_zero(CLINE->mailbox)) {
06756 ast_copy_string(CLINE->mailbox, CLINE->name, sizeof(CLINE->mailbox));
06757 }
06758 continue;
06759 }
06760 } else if (!strcasecmp(v->name, "callreturn")) {
06761 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06762 CLINE_OPTS->callreturn = ast_true(v->value);
06763 continue;
06764 }
06765 } else if (!strcasecmp(v->name, "threewaycalling")) {
06766 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06767 CLINE_OPTS->threewaycalling = ast_true(v->value);
06768 continue;
06769 }
06770 } else if (!strcasecmp(v->name, "setvar")) {
06771 if (type & (TYPE_LINE)) {
06772 CLINE->chanvars = add_var(v->value, CLINE->chanvars);
06773 continue;
06774 }
06775 } else if (!strcasecmp(v->name, "earlyrtp")) {
06776 if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06777 CDEV_OPTS->earlyrtp = ast_true(v->value);
06778 continue;
06779 }
06780 } else if (!strcasecmp(v->name, "host")) {
06781 if (type & (TYPE_DEVICE)) {
06782 if (ast_get_ip(&CDEV->addr, v->value)) {
06783 ast_log(LOG_WARNING, "Bad IP '%s' at line %d.\n", v->value, v->lineno);
06784 }
06785 continue;
06786 }
06787 } else if (!strcasecmp(v->name, "port")) {
06788 if (type & (TYPE_DEF_DEVICE)) {
06789 CDEV->addr.sin_port = htons(atoi(v->value));
06790 continue;
06791 }
06792 } else if (!strcasecmp(v->name, "device")) {
06793 if (type & (TYPE_DEVICE)) {
06794 ast_copy_string(CDEV_OPTS->id, v->value, sizeof(CDEV_OPTS->id));
06795 continue;
06796 }
06797 } else if (!strcasecmp(v->name, "permit") || !strcasecmp(v->name, "deny")) {
06798 if (type & (TYPE_DEVICE)) {
06799 CDEV->ha = ast_append_ha(v->name, v->value, CDEV->ha, NULL);
06800 continue;
06801 }
06802 } else if (!strcasecmp(v->name, "allow")) {
06803 if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06804 ast_parse_allow_disallow(&CDEV_OPTS->confprefs, &CDEV_OPTS->confcapability, v->value, 1);
06805 continue;
06806 }
06807 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06808 ast_parse_allow_disallow(&CLINE_OPTS->confprefs, &CLINE_OPTS->confcapability, v->value, 1);
06809 continue;
06810 }
06811 } else if (!strcasecmp(v->name, "disallow")) {
06812 if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06813 ast_parse_allow_disallow(&CDEV_OPTS->confprefs, &CDEV_OPTS->confcapability, v->value, 0);
06814 continue;
06815 }
06816 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06817 ast_parse_allow_disallow(&CLINE_OPTS->confprefs, &CLINE_OPTS->confcapability, v->value, 0);
06818 continue;
06819 }
06820 } else if (!strcasecmp(v->name, "version")) {
06821 if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06822 ast_copy_string(CDEV_OPTS->version_id, v->value, sizeof(CDEV_OPTS->version_id));
06823 continue;
06824 }
06825 } else if (!strcasecmp(v->name, "line")) {
06826 if (type & (TYPE_DEVICE)) {
06827 struct skinny_line *l;
06828 AST_LIST_TRAVERSE(&lines, l, all) {
06829 if (!strcasecmp(v->value, l->name) && !l->prune) {
06830
06831
06832 struct skinny_device *d;
06833 struct skinny_line *l2;
06834 int lineinuse = 0;
06835 AST_LIST_TRAVERSE(&devices, d, list) {
06836 AST_LIST_TRAVERSE(&d->lines, l2, list) {
06837 if (l2 == l && strcasecmp(d->id, CDEV->id)) {
06838 ast_log(LOG_WARNING, "Line %s already used by %s. Not connecting to %s.\n", l->name, d->name, CDEV->name);
06839 lineinuse++;
06840 }
06841 }
06842 }
06843 if (!lineinuse) {
06844 if (!AST_LIST_FIRST(&CDEV->lines)) {
06845 CDEV->activeline = l;
06846 }
06847 lineInstance++;
06848 AST_LIST_INSERT_HEAD(&CDEV->lines, l, list);
06849 }
06850 break;
06851 }
06852 }
06853 continue;
06854 }
06855 } else if (!strcasecmp(v->name, "speeddial")) {
06856 if (type & (TYPE_DEVICE)) {
06857 struct skinny_speeddial *sd;
06858 if (!(sd = ast_calloc(1, sizeof(*sd)))) {
06859 ast_log(LOG_WARNING, "Unable to allocate memory for speeddial %s. Ignoring speeddial.\n", v->name);
06860 continue;
06861 } else {
06862 char buf[256];
06863 char *stringp = buf, *exten, *context, *label;
06864 ast_copy_string(buf, v->value, sizeof(buf));
06865 exten = strsep(&stringp, ",");
06866 if ((context = strchr(exten, '@'))) {
06867 *context++ = '\0';
06868 }
06869 label = stringp;
06870 ast_mutex_init(&sd->lock);
06871 ast_copy_string(sd->exten, exten, sizeof(sd->exten));
06872 if (!ast_strlen_zero(context)) {
06873 sd->isHint = 1;
06874 sd->instance = lineInstance++;
06875 ast_copy_string(sd->context, context, sizeof(sd->context));
06876 } else {
06877 sd->isHint = 0;
06878 sd->instance = speeddialInstance++;
06879 sd->context[0] = '\0';
06880 }
06881 ast_copy_string(sd->label, S_OR(label, exten), sizeof(sd->label));
06882 sd->parent = CDEV;
06883 AST_LIST_INSERT_HEAD(&CDEV->speeddials, sd, list);
06884 }
06885 continue;
06886 }
06887 } else if (!strcasecmp(v->name, "addon")) {
06888 if (type & (TYPE_DEVICE)) {
06889 struct skinny_addon *a;
06890 if (!(a = ast_calloc(1, sizeof(*a)))) {
06891 ast_log(LOG_WARNING, "Unable to allocate memory for addon %s. Ignoring addon.\n", v->name);
06892 continue;
06893 } else {
06894 ast_mutex_init(&a->lock);
06895 ast_copy_string(a->type, v->value, sizeof(a->type));
06896 AST_LIST_INSERT_HEAD(&CDEV->addons, a, list);
06897 }
06898 continue;
06899 }
06900
06901 } else {
06902 ast_log(LOG_WARNING, "Don't know keyword '%s' at line %d\n", v->name, v->lineno);
06903 continue;
06904 }
06905 ast_log(LOG_WARNING, "Invalid category used: %s at line %d\n", v->name, v->lineno);
06906 }
06907 }
06908
06909 static struct skinny_line *config_line(const char *lname, struct ast_variable *v)
06910 {
06911 struct skinny_line *l, *temp;
06912 int update = 0;
06913
06914 ast_log(LOG_NOTICE, "Configuring skinny line %s.\n", lname);
06915
06916
06917
06918 AST_LIST_LOCK(&lines);
06919 AST_LIST_TRAVERSE(&lines, temp, all) {
06920 if (!strcasecmp(lname, temp->name) && temp->prune) {
06921 update = 1;
06922 break;
06923 }
06924 }
06925
06926 if (!(l=ast_calloc(1, sizeof(*l)))) {
06927 ast_verb(1, "Unable to allocate memory for line %s.\n", lname);
06928 AST_LIST_UNLOCK(&lines);
06929 return NULL;
06930 }
06931
06932 memcpy(l, default_line, sizeof(*default_line));
06933 ast_mutex_init(&l->lock);
06934 ast_copy_string(l->name, lname, sizeof(l->name));
06935 AST_LIST_INSERT_TAIL(&lines, l, all);
06936
06937 ast_mutex_lock(&l->lock);
06938 AST_LIST_UNLOCK(&lines);
06939
06940 config_parse_variables(TYPE_LINE, l, v);
06941
06942 if (!ast_strlen_zero(l->mailbox)) {
06943 char *cfg_mailbox, *cfg_context;
06944 cfg_context = cfg_mailbox = ast_strdupa(l->mailbox);
06945 ast_verb(3, "Setting mailbox '%s' on line %s\n", cfg_mailbox, l->name);
06946 strsep(&cfg_context, "@");
06947 if (ast_strlen_zero(cfg_context))
06948 cfg_context = "default";
06949 l->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, l,
06950 AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, cfg_mailbox,
06951 AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, cfg_context,
06952 AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
06953 AST_EVENT_IE_END);
06954 }
06955
06956 ast_mutex_unlock(&l->lock);
06957
06958
06959
06960
06961
06962 ast_verb(3, "%s config for line '%s'\n", update ? "Updated" : (skinnyreload ? "Reloaded" : "Created"), l->name);
06963
06964 return l;
06965 }
06966
06967 static struct skinny_device *config_device(const char *dname, struct ast_variable *v)
06968 {
06969 struct skinny_device *d, *temp;
06970 struct skinny_line *l, *ltemp;
06971 struct skinny_subchannel *sub;
06972 int update = 0;
06973
06974 ast_log(LOG_NOTICE, "Configuring skinny device %s.\n", dname);
06975
06976 AST_LIST_LOCK(&devices);
06977 AST_LIST_TRAVERSE(&devices, temp, list) {
06978 if (!strcasecmp(dname, temp->name) && temp->prune) {
06979 update = 1;
06980 break;
06981 }
06982 }
06983
06984 if (!(d = ast_calloc(1, sizeof(*d)))) {
06985 ast_verb(1, "Unable to allocate memory for device %s.\n", dname);
06986 AST_LIST_UNLOCK(&devices);
06987 return NULL;
06988 }
06989 memcpy(d, default_device, sizeof(*default_device));
06990 ast_mutex_init(&d->lock);
06991 ast_copy_string(d->name, dname, sizeof(d->name));
06992 AST_LIST_INSERT_TAIL(&devices, d, list);
06993
06994 ast_mutex_lock(&d->lock);
06995 AST_LIST_UNLOCK(&devices);
06996
06997 config_parse_variables(TYPE_DEVICE, d, v);
06998
06999 if (!AST_LIST_FIRST(&d->lines)) {
07000 ast_log(LOG_ERROR, "A Skinny device must have at least one line!\n");
07001 ast_mutex_unlock(&d->lock);
07002 return NULL;
07003 }
07004 if (!ntohs(d->addr.sin_port)) {
07005 d->addr.sin_port = htons(DEFAULT_SKINNY_PORT);
07006 }
07007
07008 if (skinnyreload){
07009 AST_LIST_LOCK(&devices);
07010 AST_LIST_TRAVERSE(&devices, temp, list) {
07011 if (strcasecmp(d->id, temp->id) || !temp->prune || !temp->session) {
07012 continue;
07013 }
07014 ast_mutex_lock(&d->lock);
07015 d->session = temp->session;
07016 d->session->device = d;
07017
07018 AST_LIST_LOCK(&d->lines);
07019 AST_LIST_TRAVERSE(&d->lines, l, list){
07020 l->device = d;
07021
07022 AST_LIST_LOCK(&temp->lines);
07023 AST_LIST_TRAVERSE(&temp->lines, ltemp, list) {
07024 if (strcasecmp(l->name, ltemp->name)) {
07025 continue;
07026 }
07027 ast_mutex_lock(<emp->lock);
07028 l->instance = ltemp->instance;
07029 l->hookstate = ltemp->hookstate;
07030 if (!AST_LIST_EMPTY(<emp->sub)) {
07031 ast_mutex_lock(&l->lock);
07032 l->sub = ltemp->sub;
07033 AST_LIST_TRAVERSE(&l->sub, sub, list) {
07034 sub->parent = l;
07035 }
07036 ast_mutex_unlock(&l->lock);
07037 }
07038 ast_mutex_unlock(<emp->lock);
07039 }
07040 AST_LIST_UNLOCK(&temp->lines);
07041 }
07042 AST_LIST_UNLOCK(&d->lines);
07043 ast_mutex_unlock(&d->lock);
07044 }
07045 AST_LIST_UNLOCK(&devices);
07046 }
07047
07048 ast_mutex_unlock(&d->lock);
07049
07050 ast_verb(3, "%s config for device '%s'\n", update ? "Updated" : (skinnyreload ? "Reloaded" : "Created"), d->name);
07051
07052 return d;
07053
07054 }
07055
07056 static int config_load(void)
07057 {
07058 int on = 1;
07059 struct ast_config *cfg;
07060 char *cat;
07061 struct skinny_device *d;
07062 struct skinny_line *l;
07063 int oldport = ntohs(bindaddr.sin_port);
07064 struct ast_flags config_flags = { 0 };
07065
07066 ast_log(LOG_NOTICE, "Configuring skinny from %s\n", config);
07067
07068 if (gethostname(ourhost, sizeof(ourhost))) {
07069 ast_log(LOG_WARNING, "Unable to get hostname, Skinny disabled.\n");
07070 return 0;
07071 }
07072 cfg = ast_config_load(config, config_flags);
07073
07074
07075 if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
07076 ast_log(LOG_NOTICE, "Unable to load config %s, Skinny disabled.\n", config);
07077 return -1;
07078 }
07079 memset(&bindaddr, 0, sizeof(bindaddr));
07080 memset(&default_prefs, 0, sizeof(default_prefs));
07081
07082
07083 memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
07084
07085
07086 cat = ast_category_browse(cfg, "general");
07087 config_parse_variables(TYPE_GENERAL, NULL, ast_variable_browse(cfg, "general"));
07088
07089 if (ntohl(bindaddr.sin_addr.s_addr)) {
07090 __ourip = bindaddr.sin_addr;
07091 } else {
07092 hp = ast_gethostbyname(ourhost, &ahp);
07093 if (!hp) {
07094 ast_log(LOG_WARNING, "Unable to get our IP address, Skinny disabled\n");
07095 ast_config_destroy(cfg);
07096 return 0;
07097 }
07098 memcpy(&__ourip, hp->h_addr, sizeof(__ourip));
07099 }
07100 if (!ntohs(bindaddr.sin_port)) {
07101 bindaddr.sin_port = ntohs(DEFAULT_SKINNY_PORT);
07102 }
07103 bindaddr.sin_family = AF_INET;
07104
07105
07106 default_line->confcapability = default_capability;
07107 default_line->confprefs = default_prefs;
07108 config_parse_variables(TYPE_DEF_LINE, default_line, ast_variable_browse(cfg, "lines"));
07109 cat = ast_category_browse(cfg, "lines");
07110 while (cat && strcasecmp(cat, "general") && strcasecmp(cat, "devices")) {
07111 l = config_line(cat, ast_variable_browse(cfg, cat));
07112 cat = ast_category_browse(cfg, cat);
07113 }
07114
07115
07116 default_device->confcapability = default_capability;
07117 default_device->confprefs = default_prefs;
07118 config_parse_variables(TYPE_DEF_DEVICE, default_device, ast_variable_browse(cfg, "devices"));
07119 cat = ast_category_browse(cfg, "devices");
07120 while (cat && strcasecmp(cat, "general") && strcasecmp(cat, "lines")) {
07121 d = config_device(cat, ast_variable_browse(cfg, cat));
07122 cat = ast_category_browse(cfg, cat);
07123 }
07124
07125 ast_mutex_lock(&netlock);
07126 if ((skinnysock > -1) && (ntohs(bindaddr.sin_port) != oldport)) {
07127 close(skinnysock);
07128 skinnysock = -1;
07129 }
07130 if (skinnysock < 0) {
07131 skinnysock = socket(AF_INET, SOCK_STREAM, 0);
07132 if(setsockopt(skinnysock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
07133 ast_log(LOG_ERROR, "Set Socket Options failed: errno %d, %s\n", errno, strerror(errno));
07134 ast_config_destroy(cfg);
07135 ast_mutex_unlock(&netlock);
07136 return 0;
07137 }
07138 if (skinnysock < 0) {
07139 ast_log(LOG_WARNING, "Unable to create Skinny socket: %s\n", strerror(errno));
07140 } else {
07141 if (bind(skinnysock, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) {
07142 ast_log(LOG_WARNING, "Failed to bind to %s:%d: %s\n",
07143 ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port),
07144 strerror(errno));
07145 close(skinnysock);
07146 skinnysock = -1;
07147 ast_config_destroy(cfg);
07148 ast_mutex_unlock(&netlock);
07149 return 0;
07150 }
07151 if (listen(skinnysock, DEFAULT_SKINNY_BACKLOG)) {
07152 ast_log(LOG_WARNING, "Failed to start listening to %s:%d: %s\n",
07153 ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port),
07154 strerror(errno));
07155 close(skinnysock);
07156 skinnysock = -1;
07157 ast_config_destroy(cfg);
07158 ast_mutex_unlock(&netlock);
07159 return 0;
07160 }
07161 ast_verb(2, "Skinny listening on %s:%d\n",
07162 ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port));
07163 ast_netsock_set_qos(skinnysock, qos.tos, qos.cos, "Skinny");
07164 ast_pthread_create_background(&accept_t, NULL, accept_thread, NULL);
07165 }
07166 }
07167 ast_mutex_unlock(&netlock);
07168 ast_config_destroy(cfg);
07169 return 1;
07170 }
07171
07172 static void delete_devices(void)
07173 {
07174 struct skinny_device *d;
07175 struct skinny_line *l;
07176 struct skinny_speeddial *sd;
07177 struct skinny_addon *a;
07178
07179 AST_LIST_LOCK(&devices);
07180 AST_LIST_LOCK(&lines);
07181
07182
07183 while ((d = AST_LIST_REMOVE_HEAD(&devices, list))) {
07184
07185 while ((l = AST_LIST_REMOVE_HEAD(&d->lines, list))) {
07186 AST_LIST_REMOVE(&lines, l, all);
07187 free(l);
07188 }
07189
07190 while ((sd = AST_LIST_REMOVE_HEAD(&d->speeddials, list))) {
07191 free(sd);
07192 }
07193
07194 while ((a = AST_LIST_REMOVE_HEAD(&d->addons, list))) {
07195 free(a);
07196 }
07197 free(d);
07198 }
07199 AST_LIST_UNLOCK(&lines);
07200 AST_LIST_UNLOCK(&devices);
07201 }
07202
07203 int skinny_reload(void)
07204 {
07205 struct skinny_device *d;
07206 struct skinny_line *l;
07207 struct skinny_speeddial *sd;
07208 struct skinny_addon *a;
07209 struct skinny_req *req;
07210
07211 if (skinnyreload) {
07212 ast_verb(3, "Chan_skinny is already reloading.\n");
07213 return 0;
07214 }
07215
07216 skinnyreload = 1;
07217
07218
07219 AST_LIST_LOCK(&devices);
07220 AST_LIST_TRAVERSE(&devices, d, list) {
07221 d->prune = 1;
07222 }
07223 AST_LIST_UNLOCK(&devices);
07224
07225 AST_LIST_LOCK(&lines);
07226 AST_LIST_TRAVERSE(&lines, l, all) {
07227 l->prune = 1;
07228 }
07229 AST_LIST_UNLOCK(&lines);
07230
07231 config_load();
07232
07233
07234 AST_LIST_LOCK(&devices);
07235 AST_LIST_TRAVERSE_SAFE_BEGIN(&devices, d, list) {
07236 if (!d->prune) {
07237 continue;
07238 }
07239 ast_verb(3, "Removing device '%s'\n", d->name);
07240
07241
07242
07243 while ((l = AST_LIST_REMOVE_HEAD(&d->lines, list))) {
07244 }
07245
07246 while ((sd = AST_LIST_REMOVE_HEAD(&d->speeddials, list))) {
07247 free(sd);
07248 }
07249
07250 while ((a = AST_LIST_REMOVE_HEAD(&d->addons, list))) {
07251 free(a);
07252 }
07253 AST_LIST_REMOVE_CURRENT(list);
07254 free(d);
07255 }
07256 AST_LIST_TRAVERSE_SAFE_END;
07257 AST_LIST_UNLOCK(&devices);
07258
07259 AST_LIST_LOCK(&lines);
07260 AST_LIST_TRAVERSE_SAFE_BEGIN(&lines, l, all) {
07261 if (l->prune) {
07262 AST_LIST_REMOVE_CURRENT(all);
07263 free(l);
07264 }
07265 }
07266 AST_LIST_TRAVERSE_SAFE_END;
07267 AST_LIST_UNLOCK(&lines);
07268
07269 AST_LIST_TRAVERSE(&devices, d, list) {
07270
07271
07272 if (d->session) {
07273 ast_verb(3, "Restarting device '%s'\n", d->name);
07274 if ((req = req_alloc(sizeof(struct reset_message), RESET_MESSAGE))) {
07275 req->data.reset.resetType = 2;
07276 transmit_response(d, req);
07277 }
07278 }
07279 }
07280
07281 skinnyreload = 0;
07282 return 0;
07283 }
07284
07285 static int load_module(void)
07286 {
07287 int res = 0;
07288
07289 for (; res < ARRAY_LEN(soft_key_template_default); res++) {
07290 soft_key_template_default[res].softKeyEvent = htolel(soft_key_template_default[res].softKeyEvent);
07291 }
07292
07293 res = config_load();
07294 if (res == -1) {
07295 return AST_MODULE_LOAD_DECLINE;
07296 }
07297
07298
07299 if (ast_channel_register(&skinny_tech)) {
07300 ast_log(LOG_ERROR, "Unable to register channel class 'Skinny'\n");
07301 return -1;
07302 }
07303
07304 ast_rtp_proto_register(&skinny_rtp);
07305 ast_cli_register_multiple(cli_skinny, ARRAY_LEN(cli_skinny));
07306
07307 ast_manager_register2("SKINNYdevices", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_devices,
07308 "List SKINNY devices (text format)", mandescr_show_devices);
07309 ast_manager_register2("SKINNYshowdevice", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_device,
07310 "Show SKINNY device (text format)", mandescr_show_device);
07311 ast_manager_register2("SKINNYlines", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_lines,
07312 "List SKINNY lines (text format)", mandescr_show_lines);
07313 ast_manager_register2("SKINNYshowline", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_line,
07314 "Show SKINNY line (text format)", mandescr_show_line);
07315
07316 sched = sched_context_create();
07317 if (!sched) {
07318 ast_log(LOG_WARNING, "Unable to create schedule context\n");
07319 }
07320 io = io_context_create();
07321 if (!io) {
07322 ast_log(LOG_WARNING, "Unable to create I/O context\n");
07323 }
07324
07325 restart_monitor();
07326
07327 return AST_MODULE_LOAD_SUCCESS;
07328 }
07329
07330 static int unload_module(void)
07331 {
07332 struct skinnysession *s;
07333 struct skinny_device *d;
07334 struct skinny_line *l;
07335 struct skinny_subchannel *sub;
07336 struct ast_context *con;
07337
07338 ast_rtp_proto_unregister(&skinny_rtp);
07339 ast_channel_unregister(&skinny_tech);
07340 ast_cli_unregister_multiple(cli_skinny, ARRAY_LEN(cli_skinny));
07341
07342 ast_manager_unregister("SKINNYdevices");
07343 ast_manager_unregister("SKINNYshowdevice");
07344 ast_manager_unregister("SKINNYlines");
07345 ast_manager_unregister("SKINNYshowline");
07346
07347 AST_LIST_LOCK(&sessions);
07348
07349 while((s = AST_LIST_REMOVE_HEAD(&sessions, list))) {
07350 d = s->device;
07351 AST_LIST_TRAVERSE(&d->lines, l, list){
07352 ast_mutex_lock(&l->lock);
07353 AST_LIST_TRAVERSE(&l->sub, sub, list) {
07354 ast_mutex_lock(&sub->lock);
07355 if (sub->owner) {
07356 sub->alreadygone = 1;
07357 ast_softhangup(sub->owner, AST_SOFTHANGUP_APPUNLOAD);
07358 }
07359 ast_mutex_unlock(&sub->lock);
07360 }
07361 if (l->mwi_event_sub)
07362 ast_event_unsubscribe(l->mwi_event_sub);
07363 ast_mutex_unlock(&l->lock);
07364 unregister_exten(l);
07365 }
07366 if (s->fd > -1)
07367 close(s->fd);
07368 pthread_cancel(s->t);
07369 pthread_kill(s->t, SIGURG);
07370 pthread_join(s->t, NULL);
07371 free(s);
07372 }
07373 AST_LIST_UNLOCK(&sessions);
07374
07375 delete_devices();
07376
07377 ast_mutex_lock(&monlock);
07378 if ((monitor_thread != AST_PTHREADT_NULL) && (monitor_thread != AST_PTHREADT_STOP)) {
07379 pthread_cancel(monitor_thread);
07380 pthread_kill(monitor_thread, SIGURG);
07381 pthread_join(monitor_thread, NULL);
07382 }
07383 monitor_thread = AST_PTHREADT_STOP;
07384 ast_mutex_unlock(&monlock);
07385
07386 ast_mutex_lock(&netlock);
07387 if (accept_t && (accept_t != AST_PTHREADT_STOP)) {
07388 pthread_cancel(accept_t);
07389 pthread_kill(accept_t, SIGURG);
07390 pthread_join(accept_t, NULL);
07391 }
07392 accept_t = AST_PTHREADT_STOP;
07393 ast_mutex_unlock(&netlock);
07394
07395 close(skinnysock);
07396 if (sched)
07397 sched_context_destroy(sched);
07398
07399 con = ast_context_find(used_context);
07400 if (con)
07401 ast_context_destroy(con, "Skinny");
07402
07403 return 0;
07404 }
07405
07406 static int reload(void)
07407 {
07408 skinny_reload();
07409 return 0;
07410 }
07411
07412 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Skinny Client Control Protocol (Skinny)",
07413 .load = load_module,
07414 .unload = unload_module,
07415 .reload = reload,
07416 );