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: 249895 $")
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 default:
04262 ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", ind);
04263 return -1;
04264 }
04265 return 0;
04266 }
04267
04268 static struct ast_channel *skinny_new(struct skinny_line *l, int state)
04269 {
04270 struct ast_channel *tmp;
04271 struct skinny_subchannel *sub;
04272 struct skinny_device *d = l->device;
04273 struct ast_variable *v = NULL;
04274 int fmt;
04275
04276 if (!l->device) {
04277 ast_log(LOG_WARNING, "Device for line %s is not registered.\n", l->name);
04278 return NULL;
04279 }
04280
04281 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);
04282 if (!tmp) {
04283 ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
04284 return NULL;
04285 } else {
04286 sub = ast_calloc(1, sizeof(*sub));
04287 if (!sub) {
04288 ast_log(LOG_WARNING, "Unable to allocate Skinny subchannel\n");
04289 return NULL;
04290 } else {
04291 ast_mutex_init(&sub->lock);
04292
04293 sub->owner = tmp;
04294 sub->callid = callnums++;
04295 d->lastlineinstance = l->instance;
04296 d->lastcallreference = sub->callid;
04297 sub->cxmode = SKINNY_CX_INACTIVE;
04298 sub->nat = l->nat;
04299 sub->parent = l;
04300 sub->onhold = 0;
04301 sub->blindxfer = 0;
04302 sub->xferor = 0;
04303 sub->related = NULL;
04304
04305 AST_LIST_INSERT_HEAD(&l->sub, sub, list);
04306
04307 }
04308 tmp->tech = &skinny_tech;
04309 tmp->tech_pvt = sub;
04310 tmp->nativeformats = l->capability;
04311 if (!tmp->nativeformats)
04312
04313 tmp->nativeformats = default_capability;
04314 fmt = ast_best_codec(tmp->nativeformats);
04315 if (skinnydebug)
04316 ast_verb(1, "skinny_new: tmp->nativeformats=%d fmt=%d\n", tmp->nativeformats, fmt);
04317 if (sub->rtp) {
04318 ast_channel_set_fd(tmp, 0, ast_rtp_fd(sub->rtp));
04319 }
04320 if (state == AST_STATE_RING) {
04321 tmp->rings = 1;
04322 }
04323 tmp->writeformat = fmt;
04324 tmp->rawwriteformat = fmt;
04325 tmp->readformat = fmt;
04326 tmp->rawreadformat = fmt;
04327 if (!ast_strlen_zero(l->language))
04328 ast_string_field_set(tmp, language, l->language);
04329 if (!ast_strlen_zero(l->accountcode))
04330 ast_string_field_set(tmp, accountcode, l->accountcode);
04331 if (!ast_strlen_zero(l->parkinglot))
04332 ast_string_field_set(tmp, parkinglot, l->parkinglot);
04333 if (l->amaflags)
04334 tmp->amaflags = l->amaflags;
04335
04336 ast_module_ref(ast_module_info->self);
04337 tmp->callgroup = l->callgroup;
04338 tmp->pickupgroup = l->pickupgroup;
04339
04340
04341 if (l->cfwdtype & SKINNY_CFWD_ALL) {
04342 ast_string_field_set(tmp, call_forward, l->call_forward_all);
04343 } else if (l->cfwdtype & SKINNY_CFWD_BUSY) {
04344 if (get_devicestate(l) != AST_DEVICE_NOT_INUSE) {
04345 ast_string_field_set(tmp, call_forward, l->call_forward_busy);
04346 }
04347 }
04348
04349 ast_copy_string(tmp->context, l->context, sizeof(tmp->context));
04350 ast_copy_string(tmp->exten, l->exten, sizeof(tmp->exten));
04351
04352
04353
04354 tmp->cid.cid_ani = ast_strdup(l->cid_num);
04355
04356 tmp->priority = 1;
04357 tmp->adsicpe = AST_ADSI_UNAVAILABLE;
04358
04359 if (sub->rtp)
04360 ast_jb_configure(tmp, &global_jbconf);
04361
04362
04363 for (v = l->chanvars ; v ; v = v->next)
04364 pbx_builtin_setvar_helper(tmp, v->name, v->value);
04365
04366 if (state != AST_STATE_DOWN) {
04367 if (ast_pbx_start(tmp)) {
04368 ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
04369 ast_hangup(tmp);
04370 tmp = NULL;
04371 }
04372 }
04373 }
04374 return tmp;
04375 }
04376
04377 static int skinny_hold(struct skinny_subchannel *sub)
04378 {
04379 struct skinny_line *l = sub->parent;
04380 struct skinny_device *d = l->device;
04381
04382
04383 if (!sub || !sub->owner)
04384 return 0;
04385
04386
04387 if (skinnydebug)
04388 ast_verb(1, "Putting on Hold(%d)\n", l->instance);
04389
04390 ast_queue_control_data(sub->owner, AST_CONTROL_HOLD,
04391 S_OR(l->mohsuggest, NULL),
04392 !ast_strlen_zero(l->mohsuggest) ? strlen(l->mohsuggest) + 1 : 0);
04393
04394 transmit_activatecallplane(d, l);
04395 transmit_closereceivechannel(d, sub);
04396 transmit_stopmediatransmission(d, sub);
04397
04398 transmit_callstateonly(d, sub, SKINNY_HOLD);
04399 transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_WINK);
04400 sub->onhold = 1;
04401 return 1;
04402 }
04403
04404 static int skinny_unhold(struct skinny_subchannel *sub)
04405 {
04406 struct skinny_line *l = sub->parent;
04407 struct skinny_device *d = l->device;
04408
04409
04410 if (!sub || !sub->owner)
04411 return 0;
04412
04413
04414 if (skinnydebug)
04415 ast_verb(1, "Taking off Hold(%d)\n", l->instance);
04416
04417 ast_queue_control(sub->owner, AST_CONTROL_UNHOLD);
04418
04419 transmit_activatecallplane(d, l);
04420
04421 transmit_connect(d, sub);
04422 transmit_callstateonly(d, sub, SKINNY_CONNECTED);
04423 transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
04424 l->hookstate = SKINNY_OFFHOOK;
04425 sub->onhold = 0;
04426 return 1;
04427 }
04428
04429 static int handle_hold_button(struct skinny_subchannel *sub)
04430 {
04431 if (!sub)
04432 return -1;
04433 if (sub->related) {
04434 skinny_hold(sub);
04435 skinny_unhold(sub->related);
04436 sub->parent->activesub = sub->related;
04437 } else {
04438 if (sub->onhold) {
04439 skinny_unhold(sub);
04440 transmit_selectsoftkeys(sub->parent->device, sub->parent->instance, sub->callid, KEYDEF_CONNECTED);
04441 } else {
04442 skinny_hold(sub);
04443 transmit_selectsoftkeys(sub->parent->device, sub->parent->instance, sub->callid, KEYDEF_ONHOLD);
04444 }
04445 }
04446 return 1;
04447 }
04448
04449 static int handle_transfer_button(struct skinny_subchannel *sub)
04450 {
04451 struct skinny_line *l = sub->parent;
04452 struct skinny_device *d = l->device;
04453 struct skinny_subchannel *newsub;
04454 struct ast_channel *c;
04455 pthread_t t;
04456
04457 if (!sub) {
04458 ast_verbose("Transfer: No subchannel to transfer\n");
04459 return -1;
04460 }
04461 if (!sub->related) {
04462
04463 if (!sub->onhold) {
04464 skinny_hold(sub);
04465 }
04466 c = skinny_new(l, AST_STATE_DOWN);
04467 if (c) {
04468 newsub = c->tech_pvt;
04469
04470 newsub->related = sub;
04471 sub->related = newsub;
04472 newsub->xferor = 1;
04473 l->activesub = newsub;
04474 transmit_callstate(d, l->instance, SKINNY_OFFHOOK, newsub->callid);
04475 if (skinnydebug)
04476 ast_debug(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
04477 transmit_displaymessage(d, NULL, l->instance, newsub->callid);
04478 transmit_tone(d, SKINNY_DIALTONE, l->instance, newsub->callid);
04479 transmit_selectsoftkeys(d, l->instance, newsub->callid, KEYDEF_OFFHOOKWITHFEAT);
04480
04481 if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
04482 ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
04483 ast_hangup(c);
04484 }
04485 } else {
04486 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
04487 }
04488 } else {
04489
04490 if (sub->blindxfer) {
04491
04492 sub->blindxfer = 0;
04493 sub->related->blindxfer = 0;
04494
04495 } else {
04496
04497 if (sub->owner->_state == AST_STATE_DOWN || sub->related->owner->_state == AST_STATE_DOWN) {
04498
04499 sub->blindxfer = 1;
04500 sub->related->blindxfer = 1;
04501 } else {
04502
04503 skinny_transfer(sub);
04504 }
04505 }
04506 }
04507 return 0;
04508 }
04509
04510 static int handle_keep_alive_message(struct skinny_req *req, struct skinnysession *s)
04511 {
04512 if (!(req = req_alloc(0, KEEP_ALIVE_ACK_MESSAGE)))
04513 return -1;
04514
04515 transmit_response(s->device, req);
04516 return 1;
04517 }
04518
04519 static int handle_register_message(struct skinny_req *req, struct skinnysession *s)
04520 {
04521 struct skinny_device *d = NULL;
04522 char name[16];
04523 int res;
04524
04525 memcpy(&name, req->data.reg.name, sizeof(name));
04526
04527 res = skinny_register(req, s);
04528 if (!res) {
04529 ast_log(LOG_ERROR, "Rejecting Device %s: Device not found\n", name);
04530 if (!(req = req_alloc(sizeof(struct register_rej_message), REGISTER_REJ_MESSAGE)))
04531 return -1;
04532
04533 snprintf(req->data.regrej.errMsg, sizeof(req->data.regrej.errMsg), "No Authority: %s", name);
04534
04535
04536 ast_mutex_lock(&s->lock);
04537
04538 if (letohl(req->len > SKINNY_MAX_PACKET) || letohl(req->len < 0)) {
04539 ast_log(LOG_WARNING, "transmit_response: the length of the request is out of bounds\n");
04540 ast_mutex_unlock(&s->lock);
04541 return -1;
04542 }
04543
04544 memset(s->outbuf, 0, sizeof(s->outbuf));
04545 memcpy(s->outbuf, req, skinny_header_size);
04546 memcpy(s->outbuf+skinny_header_size, &req->data, letohl(req->len));
04547
04548 res = write(s->fd, s->outbuf, letohl(req->len)+8);
04549
04550 if (res != letohl(req->len)+8) {
04551 ast_log(LOG_WARNING, "Transmit: write only sent %d out of %d bytes: %s\n", res, letohl(req->len)+8, strerror(errno));
04552 }
04553
04554 ast_mutex_unlock(&s->lock);
04555
04556 return 0;
04557 }
04558 ast_verb(3, "Device '%s' successfully registered\n", name);
04559
04560 d = s->device;
04561
04562 if (!(req = req_alloc(sizeof(struct register_ack_message), REGISTER_ACK_MESSAGE)))
04563 return -1;
04564
04565 req->data.regack.res[0] = '0';
04566 req->data.regack.res[1] = '\0';
04567 req->data.regack.keepAlive = htolel(keep_alive);
04568 memcpy(req->data.regack.dateTemplate, date_format, sizeof(req->data.regack.dateTemplate));
04569 req->data.regack.res2[0] = '0';
04570 req->data.regack.res2[1] = '\0';
04571 req->data.regack.secondaryKeepAlive = htolel(keep_alive);
04572 transmit_response(d, req);
04573 if (skinnydebug)
04574 ast_verb(1, "Requesting capabilities\n");
04575
04576 if (!(req = req_alloc(0, CAPABILITIES_REQ_MESSAGE)))
04577 return -1;
04578
04579 transmit_response(d, req);
04580
04581 return res;
04582 }
04583
04584 static int handle_callforward_button(struct skinny_subchannel *sub, int cfwdtype)
04585 {
04586 struct skinny_line *l = sub->parent;
04587 struct skinny_device *d = l->device;
04588 struct ast_channel *c = sub->owner;
04589 pthread_t t;
04590
04591 if (l->hookstate == SKINNY_ONHOOK) {
04592 l->hookstate = SKINNY_OFFHOOK;
04593 transmit_speaker_mode(d, SKINNY_SPEAKERON);
04594 transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
04595 }
04596 if (skinnydebug)
04597 ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
04598 transmit_displaymessage(d, NULL, l->instance, sub->callid);
04599
04600 if (l->cfwdtype & cfwdtype) {
04601 set_callforwards(l, NULL, cfwdtype);
04602 ast_safe_sleep(c, 500);
04603 transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
04604 transmit_callstate(d, l->instance, SKINNY_ONHOOK, sub->callid);
04605 transmit_displaynotify(d, "CFwd disabled", 10);
04606 if (sub->owner && sub->owner->_state != AST_STATE_UP) {
04607 ast_indicate(c, -1);
04608 ast_hangup(c);
04609 }
04610 transmit_cfwdstate(d, l);
04611 } else {
04612 l->getforward = cfwdtype;
04613 transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
04614 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
04615 if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
04616 ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
04617 ast_hangup(c);
04618 }
04619 }
04620 return 0;
04621 }
04622 static int handle_ip_port_message(struct skinny_req *req, struct skinnysession *s)
04623 {
04624
04625 return 1;
04626 }
04627
04628 static int handle_keypad_button_message(struct skinny_req *req, struct skinnysession *s)
04629 {
04630 struct skinny_subchannel *sub = NULL;
04631 struct skinny_line *l;
04632 struct skinny_device *d = s->device;
04633 struct ast_frame f = { 0, };
04634 char dgt;
04635 int digit;
04636 int lineInstance;
04637 int callReference;
04638
04639 digit = letohl(req->data.keypad.button);
04640 lineInstance = letohl(req->data.keypad.lineInstance);
04641 callReference = letohl(req->data.keypad.callReference);
04642
04643 if (digit == 14) {
04644 dgt = '*';
04645 } else if (digit == 15) {
04646 dgt = '#';
04647 } else if (digit >= 0 && digit <= 9) {
04648 dgt = '0' + digit;
04649 } else {
04650
04651
04652
04653
04654
04655
04656
04657 dgt = '0' + digit;
04658 ast_log(LOG_WARNING, "Unsupported digit %d\n", digit);
04659 }
04660
04661 f.subclass = dgt;
04662
04663 f.src = "skinny";
04664
04665 if (lineInstance && callReference)
04666 sub = find_subchannel_by_instance_reference(d, lineInstance, callReference);
04667 else
04668 sub = d->activeline->activesub;
04669
04670
04671 if (!sub)
04672 return 0;
04673
04674 l = sub->parent;
04675 if (sub->owner) {
04676 if (sub->owner->_state == 0) {
04677 f.frametype = AST_FRAME_DTMF_BEGIN;
04678 ast_queue_frame(sub->owner, &f);
04679 }
04680
04681 f.frametype = AST_FRAME_DTMF_END;
04682 ast_queue_frame(sub->owner, &f);
04683
04684 if (AST_LIST_NEXT(sub, list) && AST_LIST_NEXT(sub, list)->owner) {
04685 if (sub->owner->_state == 0) {
04686 f.frametype = AST_FRAME_DTMF_BEGIN;
04687 ast_queue_frame(AST_LIST_NEXT(sub, list)->owner, &f);
04688 }
04689 f.frametype = AST_FRAME_DTMF_END;
04690 ast_queue_frame(AST_LIST_NEXT(sub, list)->owner, &f);
04691 }
04692 } else {
04693 if (skinnydebug)
04694 ast_verb(1, "No owner: %s\n", l->name);
04695 }
04696 return 1;
04697 }
04698
04699 static int handle_stimulus_message(struct skinny_req *req, struct skinnysession *s)
04700 {
04701 struct skinny_device *d = s->device;
04702 struct skinny_line *l;
04703 struct skinny_subchannel *sub;
04704
04705 struct ast_channel *c;
04706 pthread_t t;
04707 int event;
04708 int instance;
04709 int callreference;
04710
04711
04712 event = letohl(req->data.stimulus.stimulus);
04713 instance = letohl(req->data.stimulus.stimulusInstance);
04714 callreference = letohl(req->data.stimulus.callreference);
04715 if (skinnydebug)
04716 ast_verb(1, "callreference in handle_stimulus_message is '%d'\n", callreference);
04717
04718
04719 sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
04720
04721 if (!sub) {
04722 l = find_line_by_instance(d, d->lastlineinstance);
04723 if (!l) {
04724 return 0;
04725 }
04726 sub = l->activesub;
04727 } else {
04728 l = sub->parent;
04729 }
04730
04731 switch(event) {
04732 case STIMULUS_REDIAL:
04733 if (skinnydebug)
04734 ast_verb(1, "Received Stimulus: Redial(%d/%d)\n", instance, callreference);
04735
04736 if (ast_strlen_zero(l->lastnumberdialed)) {
04737 ast_log(LOG_WARNING, "Attempted redial, but no previously dialed number found.\n");
04738 l->hookstate = SKINNY_ONHOOK;
04739 transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
04740 transmit_callstate(d, l->instance, SKINNY_ONHOOK, instance);
04741 break;
04742 }
04743
04744 c = skinny_new(l, AST_STATE_DOWN);
04745 if (!c) {
04746 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
04747 } else {
04748 sub = c->tech_pvt;
04749 l = sub->parent;
04750 l->activesub = sub;
04751 if (l->hookstate == SKINNY_ONHOOK) {
04752 l->hookstate = SKINNY_OFFHOOK;
04753 transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
04754 }
04755 if (skinnydebug)
04756 ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
04757 transmit_displaymessage(d, NULL, l->instance, sub->callid);
04758 transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
04759 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
04760
04761 if (!ast_ignore_pattern(c->context, l->lastnumberdialed)) {
04762 transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
04763 }
04764 ast_copy_string(c->exten, l->lastnumberdialed, sizeof(c->exten));
04765 if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
04766 ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
04767 ast_hangup(c);
04768 }
04769 }
04770 break;
04771 case STIMULUS_SPEEDDIAL:
04772 {
04773 struct skinny_speeddial *sd;
04774
04775 if (skinnydebug)
04776 ast_verb(1, "Received Stimulus: SpeedDial(%d/%d)\n", instance, callreference);
04777 if (!(sd = find_speeddial_by_instance(d, instance, 0))) {
04778 return 0;
04779 }
04780
04781 if (!sub || !sub->owner)
04782 c = skinny_new(l, AST_STATE_DOWN);
04783 else
04784 c = sub->owner;
04785
04786 if (!c) {
04787 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
04788 } else {
04789 sub = c->tech_pvt;
04790 l = sub->parent;
04791 l->activesub = sub;
04792 if (l->hookstate == SKINNY_ONHOOK) {
04793 l->hookstate = SKINNY_OFFHOOK;
04794 transmit_speaker_mode(d, SKINNY_SPEAKERON);
04795 transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
04796 }
04797 if (skinnydebug)
04798 ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
04799 transmit_displaymessage(d, NULL, l->instance, sub->callid);
04800 transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
04801 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
04802
04803 if (!ast_ignore_pattern(c->context, sd->exten)) {
04804 transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
04805 }
04806 if (ast_exists_extension(c, c->context, sd->exten, 1, l->cid_num)) {
04807 ast_copy_string(c->exten, sd->exten, sizeof(c->exten));
04808 ast_copy_string(l->lastnumberdialed, sd->exten, sizeof(l->lastnumberdialed));
04809
04810 if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
04811 ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
04812 ast_hangup(c);
04813 }
04814 break;
04815 }
04816 }
04817 }
04818 break;
04819 case STIMULUS_HOLD:
04820 if (skinnydebug)
04821 ast_verb(1, "Received Stimulus: Hold(%d/%d)\n", instance, callreference);
04822 handle_hold_button(sub);
04823 break;
04824 case STIMULUS_TRANSFER:
04825 if (skinnydebug)
04826 ast_verb(1, "Received Stimulus: Transfer(%d/%d)\n", instance, callreference);
04827 if (l->transfer)
04828 handle_transfer_button(sub);
04829 else
04830 transmit_displaynotify(d, "Transfer disabled", 10);
04831 break;
04832 case STIMULUS_CONFERENCE:
04833 if (skinnydebug)
04834 ast_verb(1, "Received Stimulus: Conference(%d/%d)\n", instance, callreference);
04835
04836 break;
04837 case STIMULUS_VOICEMAIL:
04838 if (skinnydebug)
04839 ast_verb(1, "Received Stimulus: Voicemail(%d/%d)\n", instance, callreference);
04840
04841 if (!sub || !sub->owner) {
04842 c = skinny_new(l, AST_STATE_DOWN);
04843 } else {
04844 c = sub->owner;
04845 }
04846 if (!c) {
04847 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
04848 } else {
04849 sub = c->tech_pvt;
04850 l = sub->parent;
04851 l->activesub = sub;
04852
04853 if (ast_strlen_zero(l->vmexten))
04854 break;
04855
04856 if (l->hookstate == SKINNY_ONHOOK){
04857 l->hookstate = SKINNY_OFFHOOK;
04858 transmit_speaker_mode(d, SKINNY_SPEAKERON);
04859 transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
04860 }
04861
04862 if (skinnydebug)
04863 ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
04864
04865 transmit_displaymessage(d, NULL, l->instance, sub->callid);
04866 transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
04867 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
04868
04869 if (!ast_ignore_pattern(c->context, l->vmexten)) {
04870 transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
04871 }
04872
04873 if (ast_exists_extension(c, c->context, l->vmexten, 1, l->cid_num)) {
04874 ast_copy_string(c->exten, l->vmexten, sizeof(c->exten));
04875 ast_copy_string(l->lastnumberdialed, l->vmexten, sizeof(l->lastnumberdialed));
04876 if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
04877 ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
04878 ast_hangup(c);
04879 }
04880 break;
04881 }
04882 }
04883 break;
04884 case STIMULUS_CALLPARK:
04885 {
04886 int extout;
04887 char message[32];
04888
04889 if (skinnydebug)
04890 ast_verb(1, "Received Stimulus: Park Call(%d/%d)\n", instance, callreference);
04891
04892 if ((sub && sub->owner) && (sub->owner->_state == AST_STATE_UP)){
04893 c = sub->owner;
04894 if (!ast_masq_park_call(ast_bridged_channel(c), c, 0, &extout)) {
04895 snprintf(message, sizeof(message), "Call Parked at: %d", extout);
04896 transmit_displaynotify(d, message, 10);
04897 } else {
04898 transmit_displaynotify(d, "Call Park failed", 10);
04899 }
04900 } else {
04901 transmit_displaynotify(d, "Call Park not available", 10);
04902 }
04903 }
04904 break;
04905 case STIMULUS_DND:
04906 if (skinnydebug)
04907 ast_verb(1, "Received Stimulus: DND (%d/%d)\n", instance, callreference);
04908
04909
04910 if (l->dnd != 0){
04911 ast_verb(3, "Disabling DND on %s@%s\n", l->name, d->name);
04912 l->dnd = 0;
04913 transmit_lamp_indication(d, STIMULUS_DND, 1, SKINNY_LAMP_ON);
04914 transmit_displaynotify(d, "DnD disabled", 10);
04915 } else {
04916 ast_verb(3, "Enabling DND on %s@%s\n", l->name, d->name);
04917 l->dnd = 1;
04918 transmit_lamp_indication(d, STIMULUS_DND, 1, SKINNY_LAMP_OFF);
04919 transmit_displaynotify(d, "DnD enabled", 10);
04920 }
04921 break;
04922 case STIMULUS_FORWARDALL:
04923 if (skinnydebug)
04924 ast_verb(1, "Received Stimulus: Forward All(%d/%d)\n", instance, callreference);
04925
04926 if (!sub || !sub->owner) {
04927 c = skinny_new(l, AST_STATE_DOWN);
04928 } else {
04929 c = sub->owner;
04930 }
04931
04932 if (!c) {
04933 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
04934 } else {
04935 sub = c->tech_pvt;
04936 handle_callforward_button(sub, SKINNY_CFWD_ALL);
04937 }
04938 break;
04939 case STIMULUS_FORWARDBUSY:
04940 if (skinnydebug)
04941 ast_verb(1, "Received Stimulus: Forward Busy (%d/%d)\n", instance, callreference);
04942
04943 if (!sub || !sub->owner) {
04944 c = skinny_new(l, AST_STATE_DOWN);
04945 } else {
04946 c = sub->owner;
04947 }
04948
04949 if (!c) {
04950 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
04951 } else {
04952 sub = c->tech_pvt;
04953 handle_callforward_button(sub, SKINNY_CFWD_BUSY);
04954 }
04955 break;
04956 case STIMULUS_FORWARDNOANSWER:
04957 if (skinnydebug)
04958 ast_verb(1, "Received Stimulus: Forward No Answer (%d/%d)\n", instance, callreference);
04959
04960 #if 0
04961 if (!sub || !sub->owner) {
04962 c = skinny_new(l, AST_STATE_DOWN);
04963 } else {
04964 c = sub->owner;
04965 }
04966
04967 if (!c) {
04968 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
04969 } else {
04970 sub = c->tech_pvt;
04971 handle_callforward_button(sub, SKINNY_CFWD_NOANSWER);
04972 }
04973 #endif
04974 break;
04975 case STIMULUS_DISPLAY:
04976
04977 if (skinnydebug)
04978 ast_verb(1, "Received Stimulus: Display(%d/%d)\n", instance, callreference);
04979 break;
04980 case STIMULUS_LINE:
04981 if (skinnydebug)
04982 ast_verb(1, "Received Stimulus: Line(%d/%d)\n", instance, callreference);
04983
04984 l = find_line_by_instance(d, instance);
04985
04986 if (!l) {
04987 return 0;
04988 }
04989
04990 d->activeline = l;
04991
04992
04993 transmit_speaker_mode(d, SKINNY_SPEAKERON);
04994 transmit_ringer_mode(d, SKINNY_RING_OFF);
04995 transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
04996
04997 l->hookstate = SKINNY_OFFHOOK;
04998
04999 if (sub && sub->outgoing) {
05000
05001 ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
05002 transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
05003 transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
05004 transmit_callstateonly(d, sub, SKINNY_CONNECTED);
05005 transmit_displaypromptstatus(d, "Connected", 0, l->instance, sub->callid);
05006 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
05007 start_rtp(sub);
05008 ast_setstate(sub->owner, AST_STATE_UP);
05009 } else {
05010 if (sub && sub->owner) {
05011 ast_debug(1, "Current subchannel [%s] already has owner\n", sub->owner->name);
05012 } else {
05013 c = skinny_new(l, AST_STATE_DOWN);
05014 if (c) {
05015 sub = c->tech_pvt;
05016 l->activesub = sub;
05017 transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
05018 if (skinnydebug)
05019 ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
05020 transmit_displaymessage(d, NULL, l->instance, sub->callid);
05021 transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05022 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_OFFHOOK);
05023
05024
05025 if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
05026 ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
05027 ast_hangup(c);
05028 }
05029 } else {
05030 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05031 }
05032 }
05033 }
05034 break;
05035 default:
05036 if (skinnydebug)
05037 ast_verb(1, "RECEIVED UNKNOWN STIMULUS: %d(%d/%d)\n", event, instance, callreference);
05038 break;
05039 }
05040 ast_devstate_changed(AST_DEVICE_UNKNOWN, "Skinny/%s@%s", l->name, d->name);
05041
05042 return 1;
05043 }
05044
05045 static int handle_offhook_message(struct skinny_req *req, struct skinnysession *s)
05046 {
05047 struct skinny_device *d = s->device;
05048 struct skinny_line *l;
05049 struct skinny_subchannel *sub;
05050 struct ast_channel *c;
05051 struct skinny_line *tmp;
05052 pthread_t t;
05053 int instance;
05054 int reference;
05055
05056
05057
05058
05059
05060
05061
05062 AST_LIST_TRAVERSE(&d->lines, tmp, list) {
05063 if (tmp->hookstate == SKINNY_OFFHOOK) {
05064 ast_verbose(VERBOSE_PREFIX_3 "Got offhook message when device (%s@%s) already offhook\n", tmp->name, d->name);
05065 return 0;
05066 }
05067 }
05068
05069 instance = letohl(req->data.offhook.instance);
05070 reference = letohl(req->data.offhook.reference);
05071
05072 if (instance) {
05073 sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
05074 if (!sub) {
05075 l = find_line_by_instance(d, d->lastlineinstance);
05076 if (!l) {
05077 return 0;
05078 }
05079 } else {
05080 l = sub->parent;
05081 }
05082 } else {
05083 l = d->activeline;
05084 sub = l->activesub;
05085 }
05086
05087 transmit_ringer_mode(d, SKINNY_RING_OFF);
05088 l->hookstate = SKINNY_OFFHOOK;
05089
05090 ast_devstate_changed(AST_DEVICE_INUSE, "Skinny/%s@%s", l->name, d->name);
05091
05092 if (sub && sub->onhold) {
05093 return 1;
05094 }
05095
05096 transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
05097
05098 if (sub && sub->outgoing) {
05099
05100 ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
05101 transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
05102 transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
05103 transmit_callstateonly(d, sub, SKINNY_CONNECTED);
05104 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
05105 start_rtp(sub);
05106 ast_setstate(sub->owner, AST_STATE_UP);
05107 } else {
05108 if (sub && sub->owner) {
05109 ast_debug(1, "Current sub [%s] already has owner\n", sub->owner->name);
05110 } else {
05111 c = skinny_new(l, AST_STATE_DOWN);
05112 if (c) {
05113 sub = c->tech_pvt;
05114 l->activesub = sub;
05115 transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
05116 if (skinnydebug)
05117 ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
05118 transmit_displaymessage(d, NULL, l->instance, sub->callid);
05119 transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05120 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_OFFHOOK);
05121
05122
05123 if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
05124 ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
05125 ast_hangup(c);
05126 }
05127 } else {
05128 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05129 }
05130 }
05131 }
05132 return 1;
05133 }
05134
05135 static int handle_onhook_message(struct skinny_req *req, struct skinnysession *s)
05136 {
05137 struct skinny_device *d = s->device;
05138 struct skinny_line *l;
05139 struct skinny_subchannel *sub;
05140 int instance;
05141 int reference;
05142 int onlysub = 0;
05143
05144 instance = letohl(req->data.onhook.instance);
05145 reference = letohl(req->data.onhook.reference);
05146
05147 if (instance && reference) {
05148 sub = find_subchannel_by_instance_reference(d, instance, reference);
05149 if (!sub) {
05150 return 0;
05151 }
05152 l = sub->parent;
05153 } else {
05154 l = d->activeline;
05155 sub = l->activesub;
05156 if (!sub) {
05157 return 0;
05158 }
05159 }
05160
05161 if (l->hookstate == SKINNY_ONHOOK) {
05162
05163 return 0;
05164 }
05165
05166 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
05167
05168 if (sub->onhold) {
05169 return 0;
05170 }
05171
05172 if (!AST_LIST_NEXT(sub, list)) {
05173 onlysub = 1;
05174 } else {
05175 AST_LIST_REMOVE(&l->sub, sub, list);
05176 }
05177
05178 sub->cxmode = SKINNY_CX_RECVONLY;
05179 if (onlysub || sub->xferor){
05180 l->hookstate = SKINNY_ONHOOK;
05181 if (skinnydebug)
05182 ast_debug(1, "Skinny %s@%s-%d went on hook\n", l->name, d->name, reference);
05183 }
05184
05185 transmit_callstate(d, l->instance, l->hookstate, sub->callid);
05186 if (l->transfer && sub->xferor && sub->owner->_state >= AST_STATE_RING) {
05187
05188
05189 handle_transfer_button(sub);
05190 } else {
05191
05192
05193 if (sub->xferor && sub->related){
05194 sub->related->related = NULL;
05195 sub->related->blindxfer = 0;
05196 }
05197
05198 if (sub->owner) {
05199 sub->alreadygone = 1;
05200 ast_queue_hangup(sub->owner);
05201 } else {
05202 ast_log(LOG_WARNING, "Skinny(%s@%s-%d) channel already destroyed\n",
05203 l->name, d->name, sub->callid);
05204 }
05205 }
05206 return 1;
05207 }
05208
05209 static int handle_capabilities_res_message(struct skinny_req *req, struct skinnysession *s)
05210 {
05211 struct skinny_device *d = s->device;
05212 struct skinny_line *l;
05213 uint32_t count = 0;
05214 int codecs = 0;
05215 int i;
05216
05217 count = letohl(req->data.caps.count);
05218 if (count > SKINNY_MAX_CAPABILITIES) {
05219 count = SKINNY_MAX_CAPABILITIES;
05220 ast_log(LOG_WARNING, "Received more capabilities than we can handle (%d). Ignoring the rest.\n", SKINNY_MAX_CAPABILITIES);
05221 }
05222
05223 for (i = 0; i < count; i++) {
05224 int acodec = 0;
05225 int scodec = 0;
05226 scodec = letohl(req->data.caps.caps[i].codec);
05227 acodec = codec_skinny2ast(scodec);
05228 if (skinnydebug)
05229 ast_verb(1, "Adding codec capability '%d (%d)'\n", acodec, scodec);
05230 codecs |= acodec;
05231 }
05232
05233 d->capability = d->confcapability & codecs;
05234 ast_verb(0, "Device capability set to '%d'\n", d->capability);
05235 AST_LIST_TRAVERSE(&d->lines, l, list) {
05236 ast_mutex_lock(&l->lock);
05237 l->capability = l->confcapability & d->capability;
05238 ast_mutex_unlock(&l->lock);
05239 }
05240
05241 return 1;
05242 }
05243
05244 static int handle_speed_dial_stat_req_message(struct skinny_req *req, struct skinnysession *s)
05245 {
05246 struct skinny_device *d = s->device;
05247 struct skinny_speeddial *sd;
05248 int instance;
05249
05250 instance = letohl(req->data.speeddialreq.speedDialNumber);
05251
05252 sd = find_speeddial_by_instance(d, instance, 0);
05253
05254 if (!sd) {
05255 return 0;
05256 }
05257
05258 if (!(req = req_alloc(sizeof(struct speed_dial_stat_res_message), SPEED_DIAL_STAT_RES_MESSAGE)))
05259 return -1;
05260
05261 req->data.speeddialreq.speedDialNumber = htolel(instance);
05262 ast_copy_string(req->data.speeddial.speedDialDirNumber, sd->exten, sizeof(req->data.speeddial.speedDialDirNumber));
05263 ast_copy_string(req->data.speeddial.speedDialDisplayName, sd->label, sizeof(req->data.speeddial.speedDialDisplayName));
05264
05265 transmit_response(d, req);
05266 return 1;
05267 }
05268
05269 static int handle_line_state_req_message(struct skinny_req *req, struct skinnysession *s)
05270 {
05271 struct skinny_device *d = s->device;
05272 struct skinny_line *l;
05273 struct skinny_speeddial *sd = NULL;
05274 int instance;
05275
05276 instance = letohl(req->data.line.lineNumber);
05277
05278 AST_LIST_LOCK(&devices);
05279
05280 l = find_line_by_instance(d, instance);
05281
05282 if (!l) {
05283 sd = find_speeddial_by_instance(d, instance, 1);
05284 }
05285
05286 if (!l && !sd) {
05287 return 0;
05288 }
05289
05290 AST_LIST_UNLOCK(&devices);
05291
05292 if (!(req = req_alloc(sizeof(struct line_stat_res_message), LINE_STAT_RES_MESSAGE)))
05293 return -1;
05294
05295 req->data.linestat.lineNumber = letohl(instance);
05296 if (!l) {
05297 memcpy(req->data.linestat.lineDirNumber, sd->label, sizeof(req->data.linestat.lineDirNumber));
05298 memcpy(req->data.linestat.lineDisplayName, sd->label, sizeof(req->data.linestat.lineDisplayName));
05299 } else {
05300 memcpy(req->data.linestat.lineDirNumber, l->name, sizeof(req->data.linestat.lineDirNumber));
05301 memcpy(req->data.linestat.lineDisplayName, l->label, sizeof(req->data.linestat.lineDisplayName));
05302 }
05303 transmit_response(d, req);
05304 return 1;
05305 }
05306
05307 static int handle_time_date_req_message(struct skinny_req *req, struct skinnysession *s)
05308 {
05309 struct timeval now = ast_tvnow();
05310 struct ast_tm cmtime;
05311
05312 if (!(req = req_alloc(sizeof(struct definetimedate_message), DEFINETIMEDATE_MESSAGE)))
05313 return -1;
05314
05315 ast_localtime(&now, &cmtime, NULL);
05316 req->data.definetimedate.year = htolel(cmtime.tm_year+1900);
05317 req->data.definetimedate.month = htolel(cmtime.tm_mon+1);
05318 req->data.definetimedate.dayofweek = htolel(cmtime.tm_wday);
05319 req->data.definetimedate.day = htolel(cmtime.tm_mday);
05320 req->data.definetimedate.hour = htolel(cmtime.tm_hour);
05321 req->data.definetimedate.minute = htolel(cmtime.tm_min);
05322 req->data.definetimedate.seconds = htolel(cmtime.tm_sec);
05323 req->data.definetimedate.milliseconds = htolel(cmtime.tm_usec / 1000);
05324 req->data.definetimedate.timestamp = htolel(now.tv_sec);
05325 transmit_response(s->device, req);
05326 return 1;
05327 }
05328
05329 static int handle_button_template_req_message(struct skinny_req *req, struct skinnysession *s)
05330 {
05331 struct skinny_device *d = s->device;
05332 struct skinny_line *l;
05333 int i;
05334
05335 struct skinny_speeddial *sd;
05336 struct button_definition_template btn[42];
05337 int lineInstance = 1;
05338 int speeddialInstance = 1;
05339 int buttonCount = 0;
05340
05341 if (!(req = req_alloc(sizeof(struct button_template_res_message), BUTTON_TEMPLATE_RES_MESSAGE)))
05342 return -1;
05343
05344 memset(&btn, 0, sizeof(btn));
05345
05346 get_button_template(s, btn);
05347
05348 for (i=0; i<42; i++) {
05349 int btnSet = 0;
05350 switch (btn[i].buttonDefinition) {
05351 case BT_CUST_LINE:
05352
05353 req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE;
05354 req->data.buttontemplate.definition[i].instanceNumber = htolel(0);
05355
05356 AST_LIST_TRAVERSE(&d->lines, l, list) {
05357 if (l->instance == lineInstance) {
05358 ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
05359 req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
05360 req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance);
05361 lineInstance++;
05362 buttonCount++;
05363 btnSet = 1;
05364 break;
05365 }
05366 }
05367
05368 if (!btnSet) {
05369 AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
05370 if (sd->isHint && sd->instance == lineInstance) {
05371 ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
05372 req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
05373 req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance);
05374 lineInstance++;
05375 buttonCount++;
05376 btnSet = 1;
05377 break;
05378 }
05379 }
05380 }
05381 break;
05382 case BT_CUST_LINESPEEDDIAL:
05383
05384 req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE;
05385 req->data.buttontemplate.definition[i].instanceNumber = htolel(0);
05386
05387 AST_LIST_TRAVERSE(&d->lines, l, list) {
05388 if (l->instance == lineInstance) {
05389 ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
05390 req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
05391 req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance);
05392 lineInstance++;
05393 buttonCount++;
05394 btnSet = 1;
05395 break;
05396 }
05397 }
05398
05399 if (!btnSet) {
05400 AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
05401 if (sd->isHint && sd->instance == lineInstance) {
05402 ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
05403 req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
05404 req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance);
05405 lineInstance++;
05406 buttonCount++;
05407 btnSet = 1;
05408 break;
05409 } else if (!sd->isHint && sd->instance == speeddialInstance) {
05410 ast_verb(0, "Adding button: %d, %d\n", BT_SPEEDDIAL, speeddialInstance);
05411 req->data.buttontemplate.definition[i].buttonDefinition = BT_SPEEDDIAL;
05412 req->data.buttontemplate.definition[i].instanceNumber = htolel(speeddialInstance);
05413 speeddialInstance++;
05414 buttonCount++;
05415 btnSet = 1;
05416 break;
05417 }
05418 }
05419 }
05420 break;
05421 case BT_LINE:
05422 req->data.buttontemplate.definition[i].buttonDefinition = htolel(BT_NONE);
05423 req->data.buttontemplate.definition[i].instanceNumber = htolel(0);
05424
05425 AST_LIST_TRAVERSE(&d->lines, l, list) {
05426 if (l->instance == lineInstance) {
05427 ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
05428 req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
05429 req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance);
05430 lineInstance++;
05431 buttonCount++;
05432 btnSet = 1;
05433 break;
05434 }
05435 }
05436 break;
05437 case BT_SPEEDDIAL:
05438 req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE;
05439 req->data.buttontemplate.definition[i].instanceNumber = 0;
05440
05441 AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
05442 if (!sd->isHint && sd->instance == speeddialInstance) {
05443 ast_verb(0, "Adding button: %d, %d\n", BT_SPEEDDIAL, speeddialInstance);
05444 req->data.buttontemplate.definition[i].buttonDefinition = BT_SPEEDDIAL;
05445 req->data.buttontemplate.definition[i].instanceNumber = htolel(speeddialInstance - 1);
05446 speeddialInstance++;
05447 buttonCount++;
05448 btnSet = 1;
05449 break;
05450 }
05451 }
05452 break;
05453 case BT_NONE:
05454 break;
05455 default:
05456 ast_verb(0, "Adding button: %d, %d\n", btn[i].buttonDefinition, 0);
05457 req->data.buttontemplate.definition[i].buttonDefinition = htolel(btn[i].buttonDefinition);
05458 req->data.buttontemplate.definition[i].instanceNumber = htolel(0);
05459 buttonCount++;
05460 btnSet = 1;
05461 break;
05462 }
05463 }
05464
05465 req->data.buttontemplate.buttonOffset = htolel(0);
05466 req->data.buttontemplate.buttonCount = htolel(buttonCount);
05467 req->data.buttontemplate.totalButtonCount = htolel(buttonCount);
05468
05469 if (skinnydebug)
05470 ast_verb(1, "Sending %d template to %s\n",
05471 d->type,
05472 d->name);
05473 transmit_response(d, req);
05474 return 1;
05475 }
05476
05477 static int handle_version_req_message(struct skinny_req *req, struct skinnysession *s)
05478 {
05479 struct skinny_device *d = s->device;
05480 if (!(req = req_alloc(sizeof(struct version_res_message), VERSION_RES_MESSAGE)))
05481 return -1;
05482
05483 ast_copy_string(req->data.version.version, d->version_id, sizeof(req->data.version.version));
05484 transmit_response(d, req);
05485 return 1;
05486 }
05487
05488 static int handle_server_request_message(struct skinny_req *req, struct skinnysession *s)
05489 {
05490 struct skinny_device *d = s->device;
05491 if (!(req = req_alloc(sizeof(struct server_res_message), SERVER_RES_MESSAGE)))
05492 return -1;
05493
05494 memcpy(req->data.serverres.server[0].serverName, ourhost,
05495 sizeof(req->data.serverres.server[0].serverName));
05496 req->data.serverres.serverListenPort[0] = htolel(ourport);
05497 req->data.serverres.serverIpAddr[0] = htolel(d->ourip.s_addr);
05498 transmit_response(d, req);
05499 return 1;
05500 }
05501
05502 static int handle_alarm_message(struct skinny_req *req, struct skinnysession *s)
05503 {
05504
05505 if (skinnydebug)
05506 ast_verb(1, "Received Alarm Message: %s\n", req->data.alarm.displayMessage);
05507
05508 return 1;
05509 }
05510
05511 static int handle_open_receive_channel_ack_message(struct skinny_req *req, struct skinnysession *s)
05512 {
05513 struct skinny_device *d = s->device;
05514 struct skinny_line *l;
05515 struct skinny_subchannel *sub;
05516 struct ast_format_list fmt;
05517 struct sockaddr_in sin;
05518 struct sockaddr_in us;
05519 uint32_t addr;
05520 int port;
05521 int status;
05522 int passthruid;
05523
05524 status = letohl(req->data.openreceivechannelack.status);
05525 if (status) {
05526 ast_log(LOG_ERROR, "Open Receive Channel Failure\n");
05527 return 0;
05528 }
05529 addr = letohl(req->data.openreceivechannelack.ipAddr);
05530 port = letohl(req->data.openreceivechannelack.port);
05531 passthruid = letohl(req->data.openreceivechannelack.passThruId);
05532
05533 sin.sin_family = AF_INET;
05534 sin.sin_addr.s_addr = addr;
05535 sin.sin_port = htons(port);
05536
05537 sub = find_subchannel_by_reference(d, passthruid);
05538
05539 if (!sub)
05540 return 0;
05541
05542 l = sub->parent;
05543
05544 if (sub->rtp) {
05545 ast_rtp_set_peer(sub->rtp, &sin);
05546 ast_rtp_get_us(sub->rtp, &us);
05547 } else {
05548 ast_log(LOG_ERROR, "No RTP structure, this is very bad\n");
05549 return 0;
05550 }
05551
05552 if (skinnydebug)
05553 ast_verb(1, "ipaddr = %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
05554
05555 if (!(req = req_alloc(sizeof(struct start_media_transmission_message), START_MEDIA_TRANSMISSION_MESSAGE)))
05556 return -1;
05557
05558 fmt = ast_codec_pref_getsize(&l->prefs, ast_best_codec(l->capability));
05559
05560 if (skinnydebug)
05561 ast_verb(1, "Setting payloadType to '%d' (%d ms)\n", fmt.bits, fmt.cur_ms);
05562
05563 req->data.startmedia.conferenceId = htolel(sub->callid);
05564 req->data.startmedia.passThruPartyId = htolel(sub->callid);
05565 req->data.startmedia.remoteIp = htolel(d->ourip.s_addr);
05566 req->data.startmedia.remotePort = htolel(ntohs(us.sin_port));
05567 req->data.startmedia.packetSize = htolel(fmt.cur_ms);
05568 req->data.startmedia.payloadType = htolel(codec_ast2skinny(fmt.bits));
05569 req->data.startmedia.qualifier.precedence = htolel(127);
05570 req->data.startmedia.qualifier.vad = htolel(0);
05571 req->data.startmedia.qualifier.packets = htolel(0);
05572 req->data.startmedia.qualifier.bitRate = htolel(0);
05573 transmit_response(d, req);
05574
05575 return 1;
05576 }
05577
05578 static int handle_enbloc_call_message(struct skinny_req *req, struct skinnysession *s)
05579 {
05580 struct skinny_device *d = s->device;
05581 struct skinny_line *l;
05582 struct skinny_subchannel *sub = NULL;
05583 struct ast_channel *c;
05584 pthread_t t;
05585
05586 if (skinnydebug)
05587 ast_verb(1, "Received Enbloc Call: %s\n", req->data.enbloccallmessage.calledParty);
05588
05589 sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
05590
05591 if (!sub) {
05592 l = find_line_by_instance(d, d->lastlineinstance);
05593 if (!l) {
05594 return 0;
05595 }
05596 } else {
05597 l = sub->parent;
05598 }
05599
05600 c = skinny_new(l, AST_STATE_DOWN);
05601
05602 if(!c) {
05603 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05604 } else {
05605 l->hookstate = SKINNY_OFFHOOK;
05606
05607 sub = c->tech_pvt;
05608 l->activesub = sub;
05609 transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
05610 if (skinnydebug)
05611 ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
05612 transmit_displaymessage(d, NULL, l->instance, sub->callid);
05613 transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05614
05615 if (!ast_ignore_pattern(c->context, req->data.enbloccallmessage.calledParty)) {
05616 transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
05617 }
05618 ast_copy_string(c->exten, req->data.enbloccallmessage.calledParty, sizeof(c->exten));
05619 if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
05620 ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
05621 ast_hangup(c);
05622 }
05623 }
05624
05625 return 1;
05626 }
05627
05628
05629 static int handle_soft_key_set_req_message(struct skinny_req *req, struct skinnysession *s)
05630 {
05631 int i;
05632 int x;
05633 int y;
05634 const struct soft_key_definitions *softkeymode = soft_key_default_definitions;
05635 struct skinny_device *d = s->device;
05636
05637 if (!(req = req_alloc(sizeof(struct soft_key_set_res_message), SOFT_KEY_SET_RES_MESSAGE)))
05638 return -1;
05639
05640 req->data.softkeysets.softKeySetOffset = htolel(0);
05641 req->data.softkeysets.softKeySetCount = htolel(11);
05642 req->data.softkeysets.totalSoftKeySetCount = htolel(11);
05643 for (x = 0; x < sizeof(soft_key_default_definitions) / sizeof(struct soft_key_definitions); x++) {
05644 const uint8_t *defaults = softkeymode->defaults;
05645
05646
05647 for (y = 0; y < softkeymode->count; y++) {
05648 for (i = 0; i < (sizeof(soft_key_template_default) / sizeof(struct soft_key_template_definition)); i++) {
05649 if (defaults[y] == i+1) {
05650 req->data.softkeysets.softKeySetDefinition[softkeymode->mode].softKeyTemplateIndex[y] = htolel(i+1);
05651 req->data.softkeysets.softKeySetDefinition[softkeymode->mode].softKeyInfoIndex[y] = htolel(i+301);
05652 }
05653 }
05654 }
05655 softkeymode++;
05656 }
05657 transmit_response(d, req);
05658 transmit_selectsoftkeys(d, 0, 0, KEYDEF_ONHOOK);
05659 return 1;
05660 }
05661
05662 static int handle_soft_key_event_message(struct skinny_req *req, struct skinnysession *s)
05663 {
05664 struct skinny_device *d = s->device;
05665 struct skinny_line *l;
05666 struct skinny_subchannel *sub = NULL;
05667 struct ast_channel *c;
05668 pthread_t t;
05669 int event;
05670 int instance;
05671 int callreference;
05672
05673 event = letohl(req->data.softkeyeventmessage.softKeyEvent);
05674 instance = letohl(req->data.softkeyeventmessage.instance);
05675 callreference = letohl(req->data.softkeyeventmessage.callreference);
05676
05677 if (instance) {
05678 l = find_line_by_instance(d, instance);
05679 if (callreference) {
05680 sub = find_subchannel_by_instance_reference(d, instance, callreference);
05681 } else {
05682 sub = find_subchannel_by_instance_reference(d, instance, d->lastcallreference);
05683 }
05684 } else {
05685 l = find_line_by_instance(d, d->lastlineinstance);
05686 }
05687
05688 if (!l) {
05689 if (skinnydebug)
05690 ast_verb(1, "Received Softkey Event: %d(%d/%d)\n", event, instance, callreference);
05691 return 0;
05692 }
05693
05694 ast_devstate_changed(AST_DEVICE_INUSE, "Skinny/%s@%s", l->name, d->name);
05695
05696 switch(event) {
05697 case SOFTKEY_NONE:
05698 if (skinnydebug)
05699 ast_verb(1, "Received Softkey Event: None(%d/%d)\n", instance, callreference);
05700 break;
05701 case SOFTKEY_REDIAL:
05702 if (skinnydebug)
05703 ast_verb(1, "Received Softkey Event: Redial(%d/%d)\n", instance, callreference);
05704
05705 if (ast_strlen_zero(l->lastnumberdialed)) {
05706 ast_log(LOG_WARNING, "Attempted redial, but no previously dialed number found.\n");
05707 l->hookstate = SKINNY_ONHOOK;
05708 transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
05709 transmit_callstate(d, l->instance, SKINNY_ONHOOK, instance);
05710 break;
05711 }
05712
05713 if (!sub || !sub->owner) {
05714 c = skinny_new(l, AST_STATE_DOWN);
05715 } else {
05716 c = sub->owner;
05717 }
05718
05719 if (!c) {
05720 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05721 } else {
05722 sub = c->tech_pvt;
05723 l->activesub = sub;
05724 if (l->hookstate == SKINNY_ONHOOK) {
05725 l->hookstate = SKINNY_OFFHOOK;
05726 transmit_speaker_mode(d, SKINNY_SPEAKERON);
05727 transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
05728 }
05729 if (skinnydebug)
05730 ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
05731 transmit_displaymessage(d, NULL, l->instance, sub->callid);
05732 transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05733 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
05734
05735 if (!ast_ignore_pattern(c->context, l->lastnumberdialed)) {
05736 transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
05737 }
05738 ast_copy_string(c->exten, l->lastnumberdialed, sizeof(c->exten));
05739 if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
05740 ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
05741 ast_hangup(c);
05742 }
05743 }
05744 break;
05745 case SOFTKEY_NEWCALL:
05746 if (skinnydebug)
05747 ast_verb(1, "Received Softkey Event: New Call(%d/%d)\n", instance, callreference);
05748
05749
05750 c = skinny_new(l, AST_STATE_DOWN);
05751 sub = c->tech_pvt;
05752
05753
05754
05755
05756
05757
05758 if (!c) {
05759 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05760 } else {
05761 sub = c->tech_pvt;
05762 l->activesub = sub;
05763 if (l->hookstate == SKINNY_ONHOOK) {
05764 l->hookstate = SKINNY_OFFHOOK;
05765 transmit_speaker_mode(d, SKINNY_SPEAKERON);
05766 }
05767 ast_verb(1, "Call-id: %d\n", sub->callid);
05768
05769 transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
05770
05771 if (skinnydebug)
05772 ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
05773 transmit_displaymessage(d, NULL, l->instance, sub->callid);
05774 transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05775 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_OFFHOOK);
05776
05777
05778 if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
05779 ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
05780 ast_hangup(c);
05781 }
05782 }
05783 break;
05784 case SOFTKEY_HOLD:
05785 if (skinnydebug)
05786 ast_verb(1, "Received Softkey Event: Hold(%d/%d)\n", instance, callreference);
05787 handle_hold_button(sub);
05788 break;
05789 case SOFTKEY_TRNSFER:
05790 if (skinnydebug)
05791 ast_verb(1, "Received Softkey Event: Transfer(%d/%d)\n", instance, callreference);
05792 if (l->transfer)
05793 handle_transfer_button(sub);
05794 else
05795 transmit_displaynotify(d, "Transfer disabled", 10);
05796
05797 break;
05798 case SOFTKEY_DND:
05799 if (skinnydebug)
05800 ast_verb(1, "Received Softkey Event: DND(%d/%d)\n", instance, callreference);
05801
05802
05803 if (l->dnd != 0){
05804 ast_verb(3, "Disabling DND on %s@%s\n", l->name, d->name);
05805 l->dnd = 0;
05806 transmit_lamp_indication(d, STIMULUS_DND, 1, SKINNY_LAMP_ON);
05807 transmit_displaynotify(d, "DnD disabled", 10);
05808 } else {
05809 ast_verb(3, "Enabling DND on %s@%s\n", l->name, d->name);
05810 l->dnd = 1;
05811 transmit_lamp_indication(d, STIMULUS_DND, 1, SKINNY_LAMP_OFF);
05812 transmit_displaynotify(d, "DnD enabled", 10);
05813 }
05814 break;
05815 case SOFTKEY_CFWDALL:
05816 if (skinnydebug)
05817 ast_verb(1, "Received Softkey Event: Forward All(%d/%d)\n", instance, callreference);
05818
05819 if (!sub || !sub->owner) {
05820 c = skinny_new(l, AST_STATE_DOWN);
05821 } else {
05822 c = sub->owner;
05823 }
05824
05825 if (!c) {
05826 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05827 } else {
05828 sub = c->tech_pvt;
05829 l->activesub = sub;
05830 handle_callforward_button(sub, SKINNY_CFWD_ALL);
05831 }
05832 break;
05833 case SOFTKEY_CFWDBUSY:
05834 if (skinnydebug)
05835 ast_verb(1, "Received Softkey Event: Forward Busy (%d/%d)\n", instance, callreference);
05836
05837 if (!sub || !sub->owner) {
05838 c = skinny_new(l, AST_STATE_DOWN);
05839 } else {
05840 c = sub->owner;
05841 }
05842
05843 if (!c) {
05844 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05845 } else {
05846 sub = c->tech_pvt;
05847 l->activesub = sub;
05848 handle_callforward_button(sub, SKINNY_CFWD_BUSY);
05849 }
05850 break;
05851 case SOFTKEY_CFWDNOANSWER:
05852 if (skinnydebug)
05853 ast_verb(1, "Received Softkey Event: Forward No Answer (%d/%d)\n", instance, callreference);
05854
05855 #if 0
05856 if (!sub || !sub->owner) {
05857 c = skinny_new(l, AST_STATE_DOWN);
05858 } else {
05859 c = sub->owner;
05860 }
05861
05862 if (!c) {
05863 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05864 } else {
05865 sub = c->tech_pvt;
05866 l->activesub = sub;
05867 handle_callforward_button(sub, SKINNY_CFWD_NOANSWER);
05868 }
05869 #endif
05870 break;
05871 case SOFTKEY_BKSPC:
05872 if (skinnydebug)
05873 ast_verb(1, "Received Softkey Event: Backspace(%d/%d)\n", instance, callreference);
05874 break;
05875 case SOFTKEY_ENDCALL:
05876 if (skinnydebug)
05877 ast_verb(1, "Received Softkey Event: End Call(%d/%d)\n", instance, callreference);
05878
05879 if (l->hookstate == SKINNY_ONHOOK) {
05880
05881 break;
05882 }
05883 if (sub) {
05884 int onlysub = 0;
05885
05886 if (!AST_LIST_NEXT(sub, list)) {
05887 onlysub = 1;
05888 } else {
05889 AST_LIST_REMOVE(&l->sub, sub, list);
05890 }
05891
05892 sub->cxmode = SKINNY_CX_RECVONLY;
05893 if (onlysub || sub->xferor){
05894 l->hookstate = SKINNY_ONHOOK;
05895 if (skinnydebug)
05896 ast_debug(1, "Skinny %s@%s-%d went on hook\n", l->name, d->name, callreference);
05897 }
05898
05899 transmit_callstate(d, l->instance, l->hookstate, sub->callid);
05900 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
05901 if (skinnydebug)
05902 ast_verb(1, "Skinny %s@%s went on hook\n", l->name, d->name);
05903 if (l->transfer && sub->xferor && sub->owner->_state >= AST_STATE_RING) {
05904
05905
05906 handle_transfer_button(sub);
05907 } else {
05908
05909
05910 if (sub->xferor && sub->related){
05911 sub->related->related = NULL;
05912 sub->related->blindxfer = 0;
05913 }
05914
05915 if (sub->owner) {
05916 sub->alreadygone = 1;
05917 ast_queue_hangup(sub->owner);
05918 } else {
05919 ast_log(LOG_WARNING, "Skinny(%s@%s-%d) channel already destroyed\n",
05920 l->name, d->name, sub->callid);
05921 }
05922 }
05923 if ((l->hookstate == SKINNY_ONHOOK) && (AST_LIST_NEXT(sub, list) && !AST_LIST_NEXT(sub, list)->rtp)) {
05924 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
05925 }
05926 }
05927 break;
05928 case SOFTKEY_RESUME:
05929 if (skinnydebug)
05930 ast_verb(1, "Received Softkey Event: Resume(%d/%d)\n", instance, callreference);
05931
05932 if (sub) {
05933 if (sub->onhold) {
05934 skinny_unhold(sub);
05935 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
05936 } else {
05937 skinny_hold(sub);
05938 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_ONHOLD);
05939 }
05940 }
05941
05942 break;
05943 case SOFTKEY_ANSWER:
05944 if (skinnydebug)
05945 ast_verb(1, "Received Softkey Event: Answer(%d/%d)\n", instance, callreference);
05946
05947 transmit_ringer_mode(d, SKINNY_RING_OFF);
05948 transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
05949 if (l->hookstate == SKINNY_ONHOOK) {
05950 transmit_speaker_mode(d, SKINNY_SPEAKERON);
05951 l->hookstate = SKINNY_OFFHOOK;
05952 }
05953
05954 if (sub && sub->outgoing) {
05955
05956 ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
05957 transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
05958 transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
05959 transmit_callstateonly(d, sub, SKINNY_CONNECTED);
05960 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
05961 start_rtp(sub);
05962 ast_setstate(sub->owner, AST_STATE_UP);
05963 }
05964 break;
05965 case SOFTKEY_INFO:
05966 if (skinnydebug)
05967 ast_verb(1, "Received Softkey Event: Info(%d/%d)\n", instance, callreference);
05968 break;
05969 case SOFTKEY_CONFRN:
05970 if (skinnydebug)
05971 ast_verb(1, "Received Softkey Event: Conference(%d/%d)\n", instance, callreference);
05972
05973 break;
05974 case SOFTKEY_PARK:
05975 {
05976 int extout;
05977 char message[32];
05978
05979 if (skinnydebug)
05980 ast_verb(1, "Received Softkey Event: Park Call(%d/%d)\n", instance, callreference);
05981
05982 if ((sub && sub->owner) && (sub->owner->_state == AST_STATE_UP)){
05983 c = sub->owner;
05984 if (!ast_masq_park_call(ast_bridged_channel(c), c, 0, &extout)) {
05985 snprintf(message, sizeof(message), "Call Parked at: %d", extout);
05986 transmit_displaynotify(d, message, 10);
05987 } else {
05988 transmit_displaynotify(d, "Call Park failed", 10);
05989 }
05990 } else {
05991 transmit_displaynotify(d, "Call Park not available", 10);
05992 }
05993 }
05994 break;
05995 case SOFTKEY_JOIN:
05996 if (skinnydebug)
05997 ast_verb(1, "Received Softkey Event: Join(%d/%d)\n", instance, callreference);
05998 break;
05999 case SOFTKEY_MEETME:
06000
06001 if (skinnydebug)
06002 ast_verb(1, "Received Softkey Event: Meetme(%d/%d)\n", instance, callreference);
06003 break;
06004 case SOFTKEY_PICKUP:
06005 if (skinnydebug)
06006 ast_verb(1, "Received Softkey Event: Pickup(%d/%d)\n", instance, callreference);
06007 break;
06008 case SOFTKEY_GPICKUP:
06009 if (skinnydebug)
06010 ast_verb(1, "Received Softkey Event: Group Pickup(%d/%d)\n", instance, callreference);
06011 break;
06012 default:
06013 if (skinnydebug)
06014 ast_verb(1, "Received unknown Softkey Event: %d(%d/%d)\n", event, instance, callreference);
06015 break;
06016 }
06017
06018 return 1;
06019 }
06020
06021 static int handle_unregister_message(struct skinny_req *req, struct skinnysession *s)
06022 {
06023 return skinny_unregister(req, s);
06024 }
06025
06026 static int handle_soft_key_template_req_message(struct skinny_req *req, struct skinnysession *s)
06027 {
06028 if (!(req = req_alloc(sizeof(struct soft_key_template_res_message), SOFT_KEY_TEMPLATE_RES_MESSAGE)))
06029 return -1;
06030
06031 req->data.softkeytemplate.softKeyOffset = htolel(0);
06032 req->data.softkeytemplate.softKeyCount = htolel(sizeof(soft_key_template_default) / sizeof(struct soft_key_template_definition));
06033 req->data.softkeytemplate.totalSoftKeyCount = htolel(sizeof(soft_key_template_default) / sizeof(struct soft_key_template_definition));
06034 memcpy(req->data.softkeytemplate.softKeyTemplateDefinition,
06035 soft_key_template_default,
06036 sizeof(soft_key_template_default));
06037 transmit_response(s->device, req);
06038 return 1;
06039 }
06040
06041 static int handle_headset_status_message(struct skinny_req *req, struct skinnysession *s)
06042 {
06043
06044 return 1;
06045 }
06046
06047 static int handle_register_available_lines_message(struct skinny_req *req, struct skinnysession *s)
06048 {
06049
06050 return 1;
06051 }
06052
06053 static int handle_message(struct skinny_req *req, struct skinnysession *s)
06054 {
06055 int res = 0;
06056
06057 if ((!s->device) && (letohl(req->e) != REGISTER_MESSAGE && letohl(req->e) != ALARM_MESSAGE)) {
06058 ast_log(LOG_WARNING, "Client sent message #%d without first registering.\n", req->e);
06059 ast_free(req);
06060 return 0;
06061 }
06062
06063 SKINNY_DEVONLY(if (skinnydebug > 1) {
06064 ast_verb(4, "Received %s from %s\n", message2str(req->e), s->device->name);
06065 })
06066
06067 switch(letohl(req->e)) {
06068 case KEEP_ALIVE_MESSAGE:
06069 res = handle_keep_alive_message(req, s);
06070 break;
06071 case REGISTER_MESSAGE:
06072 if (skinnydebug)
06073 ast_verb(1, "Device %s is attempting to register\n", req->data.reg.name);
06074
06075 res = handle_register_message(req, s);
06076 break;
06077 case IP_PORT_MESSAGE:
06078 res = handle_ip_port_message(req, s);
06079 break;
06080 case KEYPAD_BUTTON_MESSAGE:
06081 {
06082 struct skinny_device *d = s->device;
06083 struct skinny_subchannel *sub;
06084 int lineInstance;
06085 int callReference;
06086
06087 if (skinnydebug)
06088 ast_verb(1, "Collected digit: [%d]\n", letohl(req->data.keypad.button));
06089
06090 lineInstance = letohl(req->data.keypad.lineInstance);
06091 callReference = letohl(req->data.keypad.callReference);
06092
06093 if (lineInstance) {
06094 sub = find_subchannel_by_instance_reference(d, lineInstance, callReference);
06095 } else {
06096 sub = d->activeline->activesub;
06097 }
06098
06099 if (sub && ((sub->owner && sub->owner->_state < AST_STATE_UP) || sub->onhold)) {
06100 char dgt;
06101 int digit = letohl(req->data.keypad.button);
06102
06103 if (digit == 14) {
06104 dgt = '*';
06105 } else if (digit == 15) {
06106 dgt = '#';
06107 } else if (digit >= 0 && digit <= 9) {
06108 dgt = '0' + digit;
06109 } else {
06110
06111
06112
06113
06114
06115
06116
06117 dgt = '0' + digit;
06118 ast_log(LOG_WARNING, "Unsupported digit %d\n", digit);
06119 }
06120
06121 d->exten[strlen(d->exten)] = dgt;
06122 d->exten[strlen(d->exten)+1] = '\0';
06123 } else
06124 res = handle_keypad_button_message(req, s);
06125 }
06126 break;
06127 case ENBLOC_CALL_MESSAGE:
06128 res = handle_enbloc_call_message(req, s);
06129 break;
06130 case STIMULUS_MESSAGE:
06131 res = handle_stimulus_message(req, s);
06132 break;
06133 case OFFHOOK_MESSAGE:
06134 res = handle_offhook_message(req, s);
06135 break;
06136 case ONHOOK_MESSAGE:
06137 res = handle_onhook_message(req, s);
06138 break;
06139 case CAPABILITIES_RES_MESSAGE:
06140 if (skinnydebug)
06141 ast_verb(1, "Received CapabilitiesRes\n");
06142
06143 res = handle_capabilities_res_message(req, s);
06144 break;
06145 case SPEED_DIAL_STAT_REQ_MESSAGE:
06146 if (skinnydebug)
06147 ast_verb(1, "Received SpeedDialStatRequest\n");
06148
06149 res = handle_speed_dial_stat_req_message(req, s);
06150 break;
06151 case LINE_STATE_REQ_MESSAGE:
06152 if (skinnydebug)
06153 ast_verb(1, "Received LineStatRequest\n");
06154 res = handle_line_state_req_message(req, s);
06155 break;
06156 case TIME_DATE_REQ_MESSAGE:
06157 if (skinnydebug)
06158 ast_verb(1, "Received Time/Date Request\n");
06159
06160 res = handle_time_date_req_message(req, s);
06161 break;
06162 case BUTTON_TEMPLATE_REQ_MESSAGE:
06163 if (skinnydebug)
06164 ast_verb(1, "Buttontemplate requested\n");
06165
06166 res = handle_button_template_req_message(req, s);
06167 break;
06168 case VERSION_REQ_MESSAGE:
06169 if (skinnydebug)
06170 ast_verb(1, "Version Request\n");
06171
06172 res = handle_version_req_message(req, s);
06173 break;
06174 case SERVER_REQUEST_MESSAGE:
06175 if (skinnydebug)
06176 ast_verb(1, "Received Server Request\n");
06177
06178 res = handle_server_request_message(req, s);
06179 break;
06180 case ALARM_MESSAGE:
06181 res = handle_alarm_message(req, s);
06182 break;
06183 case OPEN_RECEIVE_CHANNEL_ACK_MESSAGE:
06184 if (skinnydebug)
06185 ast_verb(1, "Received Open Receive Channel Ack\n");
06186
06187 res = handle_open_receive_channel_ack_message(req, s);
06188 break;
06189 case SOFT_KEY_SET_REQ_MESSAGE:
06190 if (skinnydebug)
06191 ast_verb(1, "Received SoftKeySetReq\n");
06192
06193 res = handle_soft_key_set_req_message(req, s);
06194 break;
06195 case SOFT_KEY_EVENT_MESSAGE:
06196 res = handle_soft_key_event_message(req, s);
06197 break;
06198 case UNREGISTER_MESSAGE:
06199 if (skinnydebug)
06200 ast_verb(1, "Received Unregister Request\n");
06201
06202 res = handle_unregister_message(req, s);
06203 break;
06204 case SOFT_KEY_TEMPLATE_REQ_MESSAGE:
06205 if (skinnydebug)
06206 ast_verb(1, "Received SoftKey Template Request\n");
06207
06208 res = handle_soft_key_template_req_message(req, s);
06209 break;
06210 case HEADSET_STATUS_MESSAGE:
06211 res = handle_headset_status_message(req, s);
06212 break;
06213 case REGISTER_AVAILABLE_LINES_MESSAGE:
06214 res = handle_register_available_lines_message(req, s);
06215 break;
06216 default:
06217 if (skinnydebug)
06218 ast_verb(1, "RECEIVED UNKNOWN MESSAGE TYPE: %x\n", letohl(req->e));
06219 break;
06220 }
06221 if (res >= 0 && req)
06222 ast_free(req);
06223 return res;
06224 }
06225
06226 static void destroy_session(struct skinnysession *s)
06227 {
06228 struct skinnysession *cur;
06229 AST_LIST_LOCK(&sessions);
06230 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, cur, list) {
06231 if (cur == s) {
06232 AST_LIST_REMOVE_CURRENT(list);
06233 if (s->fd > -1)
06234 close(s->fd);
06235
06236 ast_mutex_destroy(&s->lock);
06237
06238 ast_free(s);
06239 } else {
06240 ast_log(LOG_WARNING, "Trying to delete nonexistent session %p?\n", s);
06241 }
06242 }
06243 AST_LIST_TRAVERSE_SAFE_END
06244 AST_LIST_UNLOCK(&sessions);
06245 }
06246
06247 static int get_input(struct skinnysession *s)
06248 {
06249 int res;
06250 int dlen = 0;
06251 int *bufaddr;
06252 struct pollfd fds[1];
06253
06254 fds[0].fd = s->fd;
06255 fds[0].events = POLLIN;
06256 fds[0].revents = 0;
06257 res = ast_poll(fds, 1, (keep_alive * 1100));
06258
06259
06260 if (res < 0) {
06261 if (errno != EINTR) {
06262 ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
06263 return res;
06264 }
06265 } else if (res == 0) {
06266 if (skinnydebug)
06267 ast_verb(1, "Skinny Client was lost, unregistering\n");
06268 skinny_unregister(NULL, s);
06269 return -1;
06270 }
06271
06272 if (fds[0].revents) {
06273 ast_mutex_lock(&s->lock);
06274 memset(s->inbuf, 0, sizeof(s->inbuf));
06275 res = read(s->fd, s->inbuf, 4);
06276 if (res < 0) {
06277 ast_log(LOG_WARNING, "read() returned error: %s\n", strerror(errno));
06278
06279 if (skinnydebug)
06280 ast_verb(1, "Skinny Client was lost, unregistering\n");
06281
06282 skinny_unregister(NULL, s);
06283 ast_mutex_unlock(&s->lock);
06284 return res;
06285 } else if (res != 4) {
06286 ast_log(LOG_WARNING, "Skinny Client sent less data than expected. Expected 4 but got %d.\n", res);
06287 ast_mutex_unlock(&s->lock);
06288
06289 if (res == 0) {
06290 if (skinnydebug)
06291 ast_verb(1, "Skinny Client was lost, unregistering\n");
06292 skinny_unregister(NULL, s);
06293 }
06294
06295 return -1;
06296 }
06297
06298 bufaddr = (int *)s->inbuf;
06299 dlen = letohl(*bufaddr);
06300 if (dlen < 4) {
06301 ast_debug(1, "Skinny Client sent invalid data.\n");
06302 ast_mutex_unlock(&s->lock);
06303 return -1;
06304 }
06305 if (dlen+8 > sizeof(s->inbuf)) {
06306 dlen = sizeof(s->inbuf) - 8;
06307 }
06308 *bufaddr = htolel(dlen);
06309
06310 res = read(s->fd, s->inbuf+4, dlen+4);
06311 ast_mutex_unlock(&s->lock);
06312 if (res < 0) {
06313 ast_log(LOG_WARNING, "read() returned error: %s\n", strerror(errno));
06314 return res;
06315 } else if (res != (dlen+4)) {
06316 ast_log(LOG_WARNING, "Skinny Client sent less data than expected.\n");
06317 return -1;
06318 }
06319 return res;
06320 }
06321 return 0;
06322 }
06323
06324 static struct skinny_req *skinny_req_parse(struct skinnysession *s)
06325 {
06326 struct skinny_req *req;
06327 int *bufaddr;
06328
06329 if (!(req = ast_calloc(1, SKINNY_MAX_PACKET)))
06330 return NULL;
06331
06332 ast_mutex_lock(&s->lock);
06333 memcpy(req, s->inbuf, skinny_header_size);
06334 bufaddr = (int *)(s->inbuf);
06335 memcpy(&req->data, s->inbuf+skinny_header_size, letohl(*bufaddr)-4);
06336
06337 ast_mutex_unlock(&s->lock);
06338
06339 if (letohl(req->e) < 0) {
06340 ast_log(LOG_ERROR, "Event Message is NULL from socket %d, This is bad\n", s->fd);
06341 ast_free(req);
06342 return NULL;
06343 }
06344
06345 return req;
06346 }
06347
06348 static void *skinny_session(void *data)
06349 {
06350 int res;
06351 struct skinny_req *req;
06352 struct skinnysession *s = data;
06353
06354 ast_verb(3, "Starting Skinny session from %s\n", ast_inet_ntoa(s->sin.sin_addr));
06355
06356 for (;;) {
06357 res = get_input(s);
06358 if (res < 0) {
06359 break;
06360 }
06361
06362 if (res > 0)
06363 {
06364 if (!(req = skinny_req_parse(s))) {
06365 destroy_session(s);
06366 return NULL;
06367 }
06368
06369 res = handle_message(req, s);
06370 if (res < 0) {
06371 destroy_session(s);
06372 return NULL;
06373 }
06374 }
06375 }
06376 ast_debug(3, "Skinny Session returned: %s\n", strerror(errno));
06377
06378 if (s)
06379 destroy_session(s);
06380
06381 return 0;
06382 }
06383
06384 static void *accept_thread(void *ignore)
06385 {
06386 int as;
06387 struct sockaddr_in sin;
06388 socklen_t sinlen;
06389 struct skinnysession *s;
06390 struct protoent *p;
06391 int arg = 1;
06392
06393 for (;;) {
06394 sinlen = sizeof(sin);
06395 as = accept(skinnysock, (struct sockaddr *)&sin, &sinlen);
06396 if (as < 0) {
06397 ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
06398 continue;
06399 }
06400 p = getprotobyname("tcp");
06401 if(p) {
06402 if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
06403 ast_log(LOG_WARNING, "Failed to set Skinny tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
06404 }
06405 }
06406 if (!(s = ast_calloc(1, sizeof(struct skinnysession))))
06407 continue;
06408
06409 memcpy(&s->sin, &sin, sizeof(sin));
06410 ast_mutex_init(&s->lock);
06411 s->fd = as;
06412 AST_LIST_LOCK(&sessions);
06413 AST_LIST_INSERT_HEAD(&sessions, s, list);
06414 AST_LIST_UNLOCK(&sessions);
06415
06416 if (ast_pthread_create(&s->t, NULL, skinny_session, s)) {
06417 destroy_session(s);
06418 }
06419 }
06420 if (skinnydebug)
06421 ast_verb(1, "killing accept thread\n");
06422 close(as);
06423 return 0;
06424 }
06425
06426 static void *do_monitor(void *data)
06427 {
06428 int res;
06429
06430
06431
06432
06433 for(;;) {
06434 pthread_testcancel();
06435
06436 res = ast_sched_wait(sched);
06437 if ((res < 0) || (res > 1000)) {
06438 res = 1000;
06439 }
06440 res = ast_io_wait(io, res);
06441 ast_mutex_lock(&monlock);
06442 if (res >= 0) {
06443 ast_sched_runq(sched);
06444 }
06445 ast_mutex_unlock(&monlock);
06446 }
06447
06448 return NULL;
06449
06450 }
06451
06452 static int restart_monitor(void)
06453 {
06454
06455 if (monitor_thread == AST_PTHREADT_STOP)
06456 return 0;
06457
06458 ast_mutex_lock(&monlock);
06459 if (monitor_thread == pthread_self()) {
06460 ast_mutex_unlock(&monlock);
06461 ast_log(LOG_WARNING, "Cannot kill myself\n");
06462 return -1;
06463 }
06464 if (monitor_thread != AST_PTHREADT_NULL) {
06465
06466 pthread_kill(monitor_thread, SIGURG);
06467 } else {
06468
06469 if (ast_pthread_create_background(&monitor_thread, NULL, do_monitor, NULL) < 0) {
06470 ast_mutex_unlock(&monlock);
06471 ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
06472 return -1;
06473 }
06474 }
06475 ast_mutex_unlock(&monlock);
06476 return 0;
06477 }
06478
06479 static int skinny_devicestate(void *data)
06480 {
06481 struct skinny_line *l;
06482 char *tmp;
06483
06484 tmp = ast_strdupa(data);
06485
06486 l = find_line_by_name(tmp);
06487
06488 return get_devicestate(l);
06489 }
06490
06491 static struct ast_channel *skinny_request(const char *type, int format, void *data, int *cause)
06492 {
06493 int oldformat;
06494
06495 struct skinny_line *l;
06496 struct ast_channel *tmpc = NULL;
06497 char tmp[256];
06498 char *dest = data;
06499
06500 oldformat = format;
06501
06502 if (!(format &= AST_FORMAT_AUDIO_MASK)) {
06503 ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", format);
06504 return NULL;
06505 }
06506
06507 ast_copy_string(tmp, dest, sizeof(tmp));
06508 if (ast_strlen_zero(tmp)) {
06509 ast_log(LOG_NOTICE, "Skinny channels require a device\n");
06510 return NULL;
06511 }
06512 l = find_line_by_name(tmp);
06513 if (!l) {
06514 ast_log(LOG_NOTICE, "No available lines on: %s\n", dest);
06515 return NULL;
06516 }
06517 ast_verb(3, "skinny_request(%s)\n", tmp);
06518 tmpc = skinny_new(l, AST_STATE_DOWN);
06519 if (!tmpc) {
06520 ast_log(LOG_WARNING, "Unable to make channel for '%s'\n", tmp);
06521 }
06522 restart_monitor();
06523 return tmpc;
06524 }
06525
06526 #define TYPE_GENERAL 1
06527 #define TYPE_DEF_DEVICE 2
06528 #define TYPE_DEF_LINE 4
06529 #define TYPE_DEVICE 8
06530 #define TYPE_LINE 16
06531
06532 #define CLINE_OPTS ((struct skinny_line_options *)item)
06533 #define CLINE ((struct skinny_line *)item)
06534 #define CDEV_OPTS ((struct skinny_device_options *)item)
06535 #define CDEV ((struct skinny_device *)item)
06536
06537 static void config_parse_variables(int type, void *item, struct ast_variable *vptr)
06538 {
06539 struct ast_variable *v;
06540 int lineInstance = 1;
06541 int speeddialInstance = 1;
06542
06543 while(vptr) {
06544 v = vptr;
06545 vptr = vptr->next;
06546
06547 if (type & (TYPE_GENERAL)) {
06548 char newcontexts[AST_MAX_CONTEXT];
06549 char oldcontexts[AST_MAX_CONTEXT];
06550 char *stringp, *context, *oldregcontext;
06551 if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) {
06552 v = v->next;
06553 continue;
06554 }
06555 if (!strcasecmp(v->name, "bindaddr")) {
06556 if (!(hp = ast_gethostbyname(v->value, &ahp))) {
06557 ast_log(LOG_WARNING, "Invalid address: %s\n", v->value);
06558 } else {
06559 memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr));
06560 }
06561 continue;
06562 } else if (!strcasecmp(v->name, "keepalive")) {
06563 keep_alive = atoi(v->value);
06564 continue;
06565 } else if (!strcasecmp(v->name, "regcontext")) {
06566 ast_copy_string(newcontexts, v->value, sizeof(newcontexts));
06567 stringp = newcontexts;
06568
06569 ast_copy_string(oldcontexts, regcontext, sizeof(oldcontexts));
06570 oldregcontext = oldcontexts;
06571
06572 cleanup_stale_contexts(stringp, oldregcontext);
06573
06574 while ((context = strsep(&stringp, "&"))) {
06575 ast_copy_string(used_context, context, sizeof(used_context));
06576 ast_context_find_or_create(NULL, NULL, context, "Skinny");
06577 }
06578 ast_copy_string(regcontext, v->value, sizeof(regcontext));
06579 continue;
06580 } else if (!strcasecmp(v->name, "dateformat")) {
06581 memcpy(date_format, v->value, sizeof(date_format));
06582 continue;
06583 } else if (!strcasecmp(v->name, "tos")) {
06584 if (ast_str2tos(v->value, &qos.tos))
06585 ast_log(LOG_WARNING, "Invalid tos value at line %d, refer to QoS documentation\n", v->lineno);
06586 continue;
06587 } else if (!strcasecmp(v->name, "tos_audio")) {
06588 if (ast_str2tos(v->value, &qos.tos_audio))
06589 ast_log(LOG_WARNING, "Invalid tos_audio value at line %d, refer to QoS documentation\n", v->lineno);
06590 continue;
06591 } else if (!strcasecmp(v->name, "tos_video")) {
06592 if (ast_str2tos(v->value, &qos.tos_video))
06593 ast_log(LOG_WARNING, "Invalid tos_video value at line %d, refer to QoS documentation\n", v->lineno);
06594 continue;
06595 } else if (!strcasecmp(v->name, "cos")) {
06596 if (ast_str2cos(v->value, &qos.cos))
06597 ast_log(LOG_WARNING, "Invalid cos value at line %d, refer to QoS documentation\n", v->lineno);
06598 continue;
06599 } else if (!strcasecmp(v->name, "cos_audio")) {
06600 if (ast_str2cos(v->value, &qos.cos_audio))
06601 ast_log(LOG_WARNING, "Invalid cos_audio value at line %d, refer to QoS documentation\n", v->lineno);
06602 continue;
06603 } else if (!strcasecmp(v->name, "cos_video")) {
06604 if (ast_str2cos(v->value, &qos.cos_video))
06605 ast_log(LOG_WARNING, "Invalid cos_video value at line %d, refer to QoS documentation\n", v->lineno);
06606 continue;
06607 } else if (!strcasecmp(v->name, "bindport")) {
06608 if (sscanf(v->value, "%5d", &ourport) == 1) {
06609 bindaddr.sin_port = htons(ourport);
06610 } else {
06611 ast_log(LOG_WARNING, "Invalid bindport '%s' at line %d of %s\n", v->value, v->lineno, config);
06612 }
06613 continue;
06614 } else if (!strcasecmp(v->name, "allow")) {
06615 ast_parse_allow_disallow(&default_prefs, &default_capability, v->value, 1);
06616 continue;
06617 } else if (!strcasecmp(v->name, "disallow")) {
06618 ast_parse_allow_disallow(&default_prefs, &default_capability, v->value, 0);
06619 continue;
06620 }
06621 }
06622
06623 if (!strcasecmp(v->name, "transfer")) {
06624 if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06625 CDEV_OPTS->transfer = ast_true(v->value);
06626 continue;
06627 } else if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06628 CLINE_OPTS->transfer = ast_true(v->value);
06629 continue;
06630 }
06631 } else if (!strcasecmp(v->name, "callwaiting")) {
06632 if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06633 CDEV_OPTS->callwaiting = ast_true(v->value);
06634 continue;
06635 } else if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06636 CLINE_OPTS->callwaiting = ast_true(v->value);
06637 continue;
06638 }
06639 } else if (!strcasecmp(v->name, "directmedia") || !strcasecmp(v->name, "canreinvite")) {
06640 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06641 CLINE_OPTS->directmedia = ast_true(v->value);
06642 continue;
06643 }
06644 } else if (!strcasecmp(v->name, "nat")) {
06645 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06646 CLINE_OPTS->nat = ast_true(v->value);
06647 continue;
06648 }
06649 } else if (!strcasecmp(v->name, "context")) {
06650 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06651 ast_copy_string(CLINE_OPTS->context, v->value, sizeof(CLINE_OPTS->context));
06652 continue;
06653 }
06654 }else if (!strcasecmp(v->name, "vmexten")) {
06655 if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06656 ast_copy_string(CDEV_OPTS->vmexten, v->value, sizeof(CDEV_OPTS->vmexten));
06657 continue;
06658 } else if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06659 ast_copy_string(CLINE_OPTS->vmexten, v->value, sizeof(CLINE_OPTS->vmexten));
06660 continue;
06661 }
06662 } else if (!strcasecmp(v->name, "mwiblink")) {
06663 if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06664 CDEV_OPTS->mwiblink = ast_true(v->value);
06665 continue;
06666 } else if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06667 CLINE_OPTS->mwiblink = ast_true(v->value);
06668 continue;
06669 }
06670 } else if (!strcasecmp(v->name, "linelabel")) {
06671 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06672 ast_copy_string(CLINE_OPTS->label, v->value, sizeof(CLINE_OPTS->label));
06673 continue;
06674 }
06675 } else if (!strcasecmp(v->name, "callerid")) {
06676 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06677 if (!strcasecmp(v->value, "asreceived")) {
06678 CLINE_OPTS->cid_num[0] = '\0';
06679 CLINE_OPTS->cid_name[0] = '\0';
06680 } else {
06681 ast_callerid_split(v->value, CLINE_OPTS->cid_name, sizeof(CLINE_OPTS->cid_name), CLINE_OPTS->cid_num, sizeof(CLINE_OPTS->cid_num));
06682 }
06683 continue;
06684 }
06685 } else if (!strcasecmp(v->name, "amaflags")) {
06686 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06687 int tempamaflags = ast_cdr_amaflags2int(v->value);
06688 if (tempamaflags < 0) {
06689 ast_log(LOG_WARNING, "Invalid AMA flags: %s at line %d\n", v->value, v->lineno);
06690 } else {
06691 CLINE_OPTS->amaflags = tempamaflags;
06692 }
06693 continue;
06694 }
06695 } else if (!strcasecmp(v->name, "regexten")) {
06696 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06697 ast_copy_string(CLINE_OPTS->regexten, v->value, sizeof(CLINE_OPTS->regexten));
06698 continue;
06699 }
06700 } else if (!strcasecmp(v->name, "language")) {
06701 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06702 ast_copy_string(CLINE_OPTS->language, v->value, sizeof(CLINE_OPTS->language));
06703 continue;
06704 }
06705 } else if (!strcasecmp(v->name, "accountcode")) {
06706 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06707 ast_copy_string(CLINE_OPTS->accountcode, v->value, sizeof(CLINE_OPTS->accountcode));
06708 continue;
06709 }
06710 } else if (!strcasecmp(v->name, "mohinterpret") || !strcasecmp(v->name, "musiconhold")) {
06711 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06712 ast_copy_string(CLINE_OPTS->mohinterpret, v->value, sizeof(CLINE_OPTS->mohinterpret));
06713 continue;
06714 }
06715 } else if (!strcasecmp(v->name, "mohsuggest")) {
06716 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06717 ast_copy_string(CLINE_OPTS->mohsuggest, v->value, sizeof(CLINE_OPTS->mohsuggest));
06718 continue;
06719 }
06720 } else if (!strcasecmp(v->name, "callgroup")) {
06721 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06722 CLINE_OPTS->callgroup = ast_get_group(v->value);
06723 continue;
06724 }
06725 } else if (!strcasecmp(v->name, "pickupgroup")) {
06726 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06727 CLINE_OPTS->pickupgroup = ast_get_group(v->value);
06728 continue;
06729 }
06730 } else if (!strcasecmp(v->name, "immediate")) {
06731 if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE | TYPE_DEF_LINE | TYPE_LINE)) {
06732 CLINE_OPTS->immediate = ast_true(v->value);
06733 continue;
06734 }
06735 } else if (!strcasecmp(v->name, "cancallforward")) {
06736 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06737 CLINE_OPTS->cancallforward = ast_true(v->value);
06738 continue;
06739 }
06740 } else if (!strcasecmp(v->name, "mailbox")) {
06741 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06742 ast_copy_string(CLINE_OPTS->mailbox, v->value, sizeof(CLINE_OPTS->mailbox));
06743 continue;
06744 }
06745 } else if ( !strcasecmp(v->name, "parkinglot")) {
06746 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06747 ast_copy_string(CLINE_OPTS->parkinglot, v->value, sizeof(CLINE_OPTS->parkinglot));
06748 continue;
06749 }
06750 } else if (!strcasecmp(v->name, "hasvoicemail")) {
06751 if (type & (TYPE_LINE)) {
06752 if (ast_true(v->value) && ast_strlen_zero(CLINE->mailbox)) {
06753 ast_copy_string(CLINE->mailbox, CLINE->name, sizeof(CLINE->mailbox));
06754 }
06755 continue;
06756 }
06757 } else if (!strcasecmp(v->name, "callreturn")) {
06758 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06759 CLINE_OPTS->callreturn = ast_true(v->value);
06760 continue;
06761 }
06762 } else if (!strcasecmp(v->name, "threewaycalling")) {
06763 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06764 CLINE_OPTS->threewaycalling = ast_true(v->value);
06765 continue;
06766 }
06767 } else if (!strcasecmp(v->name, "setvar")) {
06768 if (type & (TYPE_LINE)) {
06769 CLINE->chanvars = add_var(v->value, CLINE->chanvars);
06770 continue;
06771 }
06772 } else if (!strcasecmp(v->name, "earlyrtp")) {
06773 if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06774 CDEV_OPTS->earlyrtp = ast_true(v->value);
06775 continue;
06776 }
06777 } else if (!strcasecmp(v->name, "host")) {
06778 if (type & (TYPE_DEVICE)) {
06779 if (ast_get_ip(&CDEV->addr, v->value)) {
06780 ast_log(LOG_WARNING, "Bad IP '%s' at line %d.\n", v->value, v->lineno);
06781 }
06782 continue;
06783 }
06784 } else if (!strcasecmp(v->name, "port")) {
06785 if (type & (TYPE_DEF_DEVICE)) {
06786 CDEV->addr.sin_port = htons(atoi(v->value));
06787 continue;
06788 }
06789 } else if (!strcasecmp(v->name, "device")) {
06790 if (type & (TYPE_DEVICE)) {
06791 ast_copy_string(CDEV_OPTS->id, v->value, sizeof(CDEV_OPTS->id));
06792 continue;
06793 }
06794 } else if (!strcasecmp(v->name, "permit") || !strcasecmp(v->name, "deny")) {
06795 if (type & (TYPE_DEVICE)) {
06796 CDEV->ha = ast_append_ha(v->name, v->value, CDEV->ha, NULL);
06797 continue;
06798 }
06799 } else if (!strcasecmp(v->name, "allow")) {
06800 if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06801 ast_parse_allow_disallow(&CDEV_OPTS->confprefs, &CDEV_OPTS->confcapability, v->value, 1);
06802 continue;
06803 }
06804 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06805 ast_parse_allow_disallow(&CLINE_OPTS->confprefs, &CLINE_OPTS->confcapability, v->value, 1);
06806 continue;
06807 }
06808 } else if (!strcasecmp(v->name, "disallow")) {
06809 if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06810 ast_parse_allow_disallow(&CDEV_OPTS->confprefs, &CDEV_OPTS->confcapability, v->value, 0);
06811 continue;
06812 }
06813 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06814 ast_parse_allow_disallow(&CLINE_OPTS->confprefs, &CLINE_OPTS->confcapability, v->value, 0);
06815 continue;
06816 }
06817 } else if (!strcasecmp(v->name, "version")) {
06818 if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06819 ast_copy_string(CDEV_OPTS->version_id, v->value, sizeof(CDEV_OPTS->version_id));
06820 continue;
06821 }
06822 } else if (!strcasecmp(v->name, "line")) {
06823 if (type & (TYPE_DEVICE)) {
06824 struct skinny_line *l;
06825 AST_LIST_TRAVERSE(&lines, l, all) {
06826 if (!strcasecmp(v->value, l->name) && !l->prune) {
06827
06828
06829 struct skinny_device *d;
06830 struct skinny_line *l2;
06831 int lineinuse = 0;
06832 AST_LIST_TRAVERSE(&devices, d, list) {
06833 AST_LIST_TRAVERSE(&d->lines, l2, list) {
06834 if (l2 == l && strcasecmp(d->id, CDEV->id)) {
06835 ast_log(LOG_WARNING, "Line %s already used by %s. Not connecting to %s.\n", l->name, d->name, CDEV->name);
06836 lineinuse++;
06837 }
06838 }
06839 }
06840 if (!lineinuse) {
06841 if (!AST_LIST_FIRST(&CDEV->lines)) {
06842 CDEV->activeline = l;
06843 }
06844 lineInstance++;
06845 AST_LIST_INSERT_HEAD(&CDEV->lines, l, list);
06846 }
06847 break;
06848 }
06849 }
06850 continue;
06851 }
06852 } else if (!strcasecmp(v->name, "speeddial")) {
06853 if (type & (TYPE_DEVICE)) {
06854 struct skinny_speeddial *sd;
06855 if (!(sd = ast_calloc(1, sizeof(*sd)))) {
06856 ast_log(LOG_WARNING, "Unable to allocate memory for speeddial %s. Ignoring speeddial.\n", v->name);
06857 continue;
06858 } else {
06859 char buf[256];
06860 char *stringp = buf, *exten, *context, *label;
06861 ast_copy_string(buf, v->value, sizeof(buf));
06862 exten = strsep(&stringp, ",");
06863 if ((context = strchr(exten, '@'))) {
06864 *context++ = '\0';
06865 }
06866 label = stringp;
06867 ast_mutex_init(&sd->lock);
06868 ast_copy_string(sd->exten, exten, sizeof(sd->exten));
06869 if (!ast_strlen_zero(context)) {
06870 sd->isHint = 1;
06871 sd->instance = lineInstance++;
06872 ast_copy_string(sd->context, context, sizeof(sd->context));
06873 } else {
06874 sd->isHint = 0;
06875 sd->instance = speeddialInstance++;
06876 sd->context[0] = '\0';
06877 }
06878 ast_copy_string(sd->label, S_OR(label, exten), sizeof(sd->label));
06879 sd->parent = CDEV;
06880 AST_LIST_INSERT_HEAD(&CDEV->speeddials, sd, list);
06881 }
06882 continue;
06883 }
06884 } else if (!strcasecmp(v->name, "addon")) {
06885 if (type & (TYPE_DEVICE)) {
06886 struct skinny_addon *a;
06887 if (!(a = ast_calloc(1, sizeof(*a)))) {
06888 ast_log(LOG_WARNING, "Unable to allocate memory for addon %s. Ignoring addon.\n", v->name);
06889 continue;
06890 } else {
06891 ast_mutex_init(&a->lock);
06892 ast_copy_string(a->type, v->value, sizeof(a->type));
06893 AST_LIST_INSERT_HEAD(&CDEV->addons, a, list);
06894 }
06895 continue;
06896 }
06897
06898 } else {
06899 ast_log(LOG_WARNING, "Don't know keyword '%s' at line %d\n", v->name, v->lineno);
06900 continue;
06901 }
06902 ast_log(LOG_WARNING, "Invalid category used: %s at line %d\n", v->name, v->lineno);
06903 }
06904 }
06905
06906 static struct skinny_line *config_line(const char *lname, struct ast_variable *v)
06907 {
06908 struct skinny_line *l, *temp;
06909 int update = 0;
06910
06911 ast_log(LOG_NOTICE, "Configuring skinny line %s.\n", lname);
06912
06913
06914
06915 AST_LIST_LOCK(&lines);
06916 AST_LIST_TRAVERSE(&lines, temp, all) {
06917 if (!strcasecmp(lname, temp->name) && temp->prune) {
06918 update = 1;
06919 break;
06920 }
06921 }
06922
06923 if (!(l=ast_calloc(1, sizeof(*l)))) {
06924 ast_verb(1, "Unable to allocate memory for line %s.\n", lname);
06925 AST_LIST_UNLOCK(&lines);
06926 return NULL;
06927 }
06928
06929 memcpy(l, default_line, sizeof(*default_line));
06930 ast_mutex_init(&l->lock);
06931 ast_copy_string(l->name, lname, sizeof(l->name));
06932 AST_LIST_INSERT_TAIL(&lines, l, all);
06933
06934 ast_mutex_lock(&l->lock);
06935 AST_LIST_UNLOCK(&lines);
06936
06937 config_parse_variables(TYPE_LINE, l, v);
06938
06939 if (!ast_strlen_zero(l->mailbox)) {
06940 char *cfg_mailbox, *cfg_context;
06941 cfg_context = cfg_mailbox = ast_strdupa(l->mailbox);
06942 ast_verb(3, "Setting mailbox '%s' on line %s\n", cfg_mailbox, l->name);
06943 strsep(&cfg_context, "@");
06944 if (ast_strlen_zero(cfg_context))
06945 cfg_context = "default";
06946 l->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, l,
06947 AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, cfg_mailbox,
06948 AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, cfg_context,
06949 AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
06950 AST_EVENT_IE_END);
06951 }
06952
06953 ast_mutex_unlock(&l->lock);
06954
06955
06956
06957
06958
06959 ast_verb(3, "%s config for line '%s'\n", update ? "Updated" : (skinnyreload ? "Reloaded" : "Created"), l->name);
06960
06961 return l;
06962 }
06963
06964 static struct skinny_device *config_device(const char *dname, struct ast_variable *v)
06965 {
06966 struct skinny_device *d, *temp;
06967 struct skinny_line *l, *ltemp;
06968 struct skinny_subchannel *sub;
06969 int update = 0;
06970
06971 ast_log(LOG_NOTICE, "Configuring skinny device %s.\n", dname);
06972
06973 AST_LIST_LOCK(&devices);
06974 AST_LIST_TRAVERSE(&devices, temp, list) {
06975 if (!strcasecmp(dname, temp->name) && temp->prune) {
06976 update = 1;
06977 break;
06978 }
06979 }
06980
06981 if (!(d = ast_calloc(1, sizeof(*d)))) {
06982 ast_verb(1, "Unable to allocate memory for device %s.\n", dname);
06983 AST_LIST_UNLOCK(&devices);
06984 return NULL;
06985 }
06986 memcpy(d, default_device, sizeof(*default_device));
06987 ast_mutex_init(&d->lock);
06988 ast_copy_string(d->name, dname, sizeof(d->name));
06989 AST_LIST_INSERT_TAIL(&devices, d, list);
06990
06991 ast_mutex_lock(&d->lock);
06992 AST_LIST_UNLOCK(&devices);
06993
06994 config_parse_variables(TYPE_DEVICE, d, v);
06995
06996 if (!AST_LIST_FIRST(&d->lines)) {
06997 ast_log(LOG_ERROR, "A Skinny device must have at least one line!\n");
06998 ast_mutex_unlock(&d->lock);
06999 return NULL;
07000 }
07001 if (!ntohs(d->addr.sin_port)) {
07002 d->addr.sin_port = htons(DEFAULT_SKINNY_PORT);
07003 }
07004
07005 if (skinnyreload){
07006 AST_LIST_LOCK(&devices);
07007 AST_LIST_TRAVERSE(&devices, temp, list) {
07008 if (strcasecmp(d->id, temp->id) || !temp->prune || !temp->session) {
07009 continue;
07010 }
07011 ast_mutex_lock(&d->lock);
07012 d->session = temp->session;
07013 d->session->device = d;
07014
07015 AST_LIST_LOCK(&d->lines);
07016 AST_LIST_TRAVERSE(&d->lines, l, list){
07017 l->device = d;
07018
07019 AST_LIST_LOCK(&temp->lines);
07020 AST_LIST_TRAVERSE(&temp->lines, ltemp, list) {
07021 if (strcasecmp(l->name, ltemp->name)) {
07022 continue;
07023 }
07024 ast_mutex_lock(<emp->lock);
07025 l->instance = ltemp->instance;
07026 l->hookstate = ltemp->hookstate;
07027 if (!AST_LIST_EMPTY(<emp->sub)) {
07028 ast_mutex_lock(&l->lock);
07029 l->sub = ltemp->sub;
07030 AST_LIST_TRAVERSE(&l->sub, sub, list) {
07031 sub->parent = l;
07032 }
07033 ast_mutex_unlock(&l->lock);
07034 }
07035 ast_mutex_unlock(<emp->lock);
07036 }
07037 AST_LIST_UNLOCK(&temp->lines);
07038 }
07039 AST_LIST_UNLOCK(&d->lines);
07040 ast_mutex_unlock(&d->lock);
07041 }
07042 AST_LIST_UNLOCK(&devices);
07043 }
07044
07045 ast_mutex_unlock(&d->lock);
07046
07047 ast_verb(3, "%s config for device '%s'\n", update ? "Updated" : (skinnyreload ? "Reloaded" : "Created"), d->name);
07048
07049 return d;
07050
07051 }
07052
07053 static int config_load(void)
07054 {
07055 int on = 1;
07056 struct ast_config *cfg;
07057 char *cat;
07058 struct skinny_device *d;
07059 struct skinny_line *l;
07060 int oldport = ntohs(bindaddr.sin_port);
07061 struct ast_flags config_flags = { 0 };
07062
07063 ast_log(LOG_NOTICE, "Configuring skinny from %s\n", config);
07064
07065 if (gethostname(ourhost, sizeof(ourhost))) {
07066 ast_log(LOG_WARNING, "Unable to get hostname, Skinny disabled.\n");
07067 return 0;
07068 }
07069 cfg = ast_config_load(config, config_flags);
07070
07071
07072 if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
07073 ast_log(LOG_NOTICE, "Unable to load config %s, Skinny disabled.\n", config);
07074 return -1;
07075 }
07076 memset(&bindaddr, 0, sizeof(bindaddr));
07077 memset(&default_prefs, 0, sizeof(default_prefs));
07078
07079
07080 memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
07081
07082
07083 cat = ast_category_browse(cfg, "general");
07084 config_parse_variables(TYPE_GENERAL, NULL, ast_variable_browse(cfg, "general"));
07085
07086 if (ntohl(bindaddr.sin_addr.s_addr)) {
07087 __ourip = bindaddr.sin_addr;
07088 } else {
07089 hp = ast_gethostbyname(ourhost, &ahp);
07090 if (!hp) {
07091 ast_log(LOG_WARNING, "Unable to get our IP address, Skinny disabled\n");
07092 ast_config_destroy(cfg);
07093 return 0;
07094 }
07095 memcpy(&__ourip, hp->h_addr, sizeof(__ourip));
07096 }
07097 if (!ntohs(bindaddr.sin_port)) {
07098 bindaddr.sin_port = ntohs(DEFAULT_SKINNY_PORT);
07099 }
07100 bindaddr.sin_family = AF_INET;
07101
07102
07103 default_line->confcapability = default_capability;
07104 default_line->confprefs = default_prefs;
07105 config_parse_variables(TYPE_DEF_LINE, default_line, ast_variable_browse(cfg, "lines"));
07106 cat = ast_category_browse(cfg, "lines");
07107 while (cat && strcasecmp(cat, "general") && strcasecmp(cat, "devices")) {
07108 l = config_line(cat, ast_variable_browse(cfg, cat));
07109 cat = ast_category_browse(cfg, cat);
07110 }
07111
07112
07113 default_device->confcapability = default_capability;
07114 default_device->confprefs = default_prefs;
07115 config_parse_variables(TYPE_DEF_DEVICE, default_device, ast_variable_browse(cfg, "devices"));
07116 cat = ast_category_browse(cfg, "devices");
07117 while (cat && strcasecmp(cat, "general") && strcasecmp(cat, "lines")) {
07118 d = config_device(cat, ast_variable_browse(cfg, cat));
07119 cat = ast_category_browse(cfg, cat);
07120 }
07121
07122 ast_mutex_lock(&netlock);
07123 if ((skinnysock > -1) && (ntohs(bindaddr.sin_port) != oldport)) {
07124 close(skinnysock);
07125 skinnysock = -1;
07126 }
07127 if (skinnysock < 0) {
07128 skinnysock = socket(AF_INET, SOCK_STREAM, 0);
07129 if(setsockopt(skinnysock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
07130 ast_log(LOG_ERROR, "Set Socket Options failed: errno %d, %s\n", errno, strerror(errno));
07131 ast_config_destroy(cfg);
07132 ast_mutex_unlock(&netlock);
07133 return 0;
07134 }
07135 if (skinnysock < 0) {
07136 ast_log(LOG_WARNING, "Unable to create Skinny socket: %s\n", strerror(errno));
07137 } else {
07138 if (bind(skinnysock, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) {
07139 ast_log(LOG_WARNING, "Failed to bind to %s:%d: %s\n",
07140 ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port),
07141 strerror(errno));
07142 close(skinnysock);
07143 skinnysock = -1;
07144 ast_config_destroy(cfg);
07145 ast_mutex_unlock(&netlock);
07146 return 0;
07147 }
07148 if (listen(skinnysock, DEFAULT_SKINNY_BACKLOG)) {
07149 ast_log(LOG_WARNING, "Failed to start listening to %s:%d: %s\n",
07150 ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port),
07151 strerror(errno));
07152 close(skinnysock);
07153 skinnysock = -1;
07154 ast_config_destroy(cfg);
07155 ast_mutex_unlock(&netlock);
07156 return 0;
07157 }
07158 ast_verb(2, "Skinny listening on %s:%d\n",
07159 ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port));
07160 ast_netsock_set_qos(skinnysock, qos.tos, qos.cos, "Skinny");
07161 ast_pthread_create_background(&accept_t, NULL, accept_thread, NULL);
07162 }
07163 }
07164 ast_mutex_unlock(&netlock);
07165 ast_config_destroy(cfg);
07166 return 1;
07167 }
07168
07169 static void delete_devices(void)
07170 {
07171 struct skinny_device *d;
07172 struct skinny_line *l;
07173 struct skinny_speeddial *sd;
07174 struct skinny_addon *a;
07175
07176 AST_LIST_LOCK(&devices);
07177 AST_LIST_LOCK(&lines);
07178
07179
07180 while ((d = AST_LIST_REMOVE_HEAD(&devices, list))) {
07181
07182 while ((l = AST_LIST_REMOVE_HEAD(&d->lines, list))) {
07183 AST_LIST_REMOVE(&lines, l, all);
07184 free(l);
07185 }
07186
07187 while ((sd = AST_LIST_REMOVE_HEAD(&d->speeddials, list))) {
07188 free(sd);
07189 }
07190
07191 while ((a = AST_LIST_REMOVE_HEAD(&d->addons, list))) {
07192 free(a);
07193 }
07194 free(d);
07195 }
07196 AST_LIST_UNLOCK(&lines);
07197 AST_LIST_UNLOCK(&devices);
07198 }
07199
07200 int skinny_reload(void)
07201 {
07202 struct skinny_device *d;
07203 struct skinny_line *l;
07204 struct skinny_speeddial *sd;
07205 struct skinny_addon *a;
07206 struct skinny_req *req;
07207
07208 if (skinnyreload) {
07209 ast_verb(3, "Chan_skinny is already reloading.\n");
07210 return 0;
07211 }
07212
07213 skinnyreload = 1;
07214
07215
07216 AST_LIST_LOCK(&devices);
07217 AST_LIST_TRAVERSE(&devices, d, list) {
07218 d->prune = 1;
07219 }
07220 AST_LIST_UNLOCK(&devices);
07221
07222 AST_LIST_LOCK(&lines);
07223 AST_LIST_TRAVERSE(&lines, l, all) {
07224 l->prune = 1;
07225 }
07226 AST_LIST_UNLOCK(&lines);
07227
07228 config_load();
07229
07230
07231 AST_LIST_LOCK(&devices);
07232 AST_LIST_TRAVERSE_SAFE_BEGIN(&devices, d, list) {
07233 if (!d->prune) {
07234 continue;
07235 }
07236 ast_verb(3, "Removing device '%s'\n", d->name);
07237
07238
07239
07240 while ((l = AST_LIST_REMOVE_HEAD(&d->lines, list))) {
07241 }
07242
07243 while ((sd = AST_LIST_REMOVE_HEAD(&d->speeddials, list))) {
07244 free(sd);
07245 }
07246
07247 while ((a = AST_LIST_REMOVE_HEAD(&d->addons, list))) {
07248 free(a);
07249 }
07250 AST_LIST_REMOVE_CURRENT(list);
07251 free(d);
07252 }
07253 AST_LIST_TRAVERSE_SAFE_END;
07254 AST_LIST_UNLOCK(&devices);
07255
07256 AST_LIST_LOCK(&lines);
07257 AST_LIST_TRAVERSE_SAFE_BEGIN(&lines, l, all) {
07258 if (l->prune) {
07259 AST_LIST_REMOVE_CURRENT(all);
07260 free(l);
07261 }
07262 }
07263 AST_LIST_TRAVERSE_SAFE_END;
07264 AST_LIST_UNLOCK(&lines);
07265
07266 AST_LIST_TRAVERSE(&devices, d, list) {
07267
07268
07269 if (d->session) {
07270 ast_verb(3, "Restarting device '%s'\n", d->name);
07271 if ((req = req_alloc(sizeof(struct reset_message), RESET_MESSAGE))) {
07272 req->data.reset.resetType = 2;
07273 transmit_response(d, req);
07274 }
07275 }
07276 }
07277
07278 skinnyreload = 0;
07279 return 0;
07280 }
07281
07282 static int load_module(void)
07283 {
07284 int res = 0;
07285
07286 for (; res < ARRAY_LEN(soft_key_template_default); res++) {
07287 soft_key_template_default[res].softKeyEvent = htolel(soft_key_template_default[res].softKeyEvent);
07288 }
07289
07290 res = config_load();
07291 if (res == -1) {
07292 return AST_MODULE_LOAD_DECLINE;
07293 }
07294
07295
07296 if (ast_channel_register(&skinny_tech)) {
07297 ast_log(LOG_ERROR, "Unable to register channel class 'Skinny'\n");
07298 return -1;
07299 }
07300
07301 ast_rtp_proto_register(&skinny_rtp);
07302 ast_cli_register_multiple(cli_skinny, ARRAY_LEN(cli_skinny));
07303
07304 ast_manager_register2("SKINNYdevices", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_devices,
07305 "List SKINNY devices (text format)", mandescr_show_devices);
07306 ast_manager_register2("SKINNYshowdevice", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_device,
07307 "Show SKINNY device (text format)", mandescr_show_device);
07308 ast_manager_register2("SKINNYlines", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_lines,
07309 "List SKINNY lines (text format)", mandescr_show_lines);
07310 ast_manager_register2("SKINNYshowline", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_line,
07311 "Show SKINNY line (text format)", mandescr_show_line);
07312
07313 sched = sched_context_create();
07314 if (!sched) {
07315 ast_log(LOG_WARNING, "Unable to create schedule context\n");
07316 }
07317 io = io_context_create();
07318 if (!io) {
07319 ast_log(LOG_WARNING, "Unable to create I/O context\n");
07320 }
07321
07322 restart_monitor();
07323
07324 return AST_MODULE_LOAD_SUCCESS;
07325 }
07326
07327 static int unload_module(void)
07328 {
07329 struct skinnysession *s;
07330 struct skinny_device *d;
07331 struct skinny_line *l;
07332 struct skinny_subchannel *sub;
07333 struct ast_context *con;
07334
07335 ast_rtp_proto_unregister(&skinny_rtp);
07336 ast_channel_unregister(&skinny_tech);
07337 ast_cli_unregister_multiple(cli_skinny, ARRAY_LEN(cli_skinny));
07338
07339 ast_manager_unregister("SKINNYdevices");
07340 ast_manager_unregister("SKINNYshowdevice");
07341 ast_manager_unregister("SKINNYlines");
07342 ast_manager_unregister("SKINNYshowline");
07343
07344 AST_LIST_LOCK(&sessions);
07345
07346 while((s = AST_LIST_REMOVE_HEAD(&sessions, list))) {
07347 d = s->device;
07348 AST_LIST_TRAVERSE(&d->lines, l, list){
07349 ast_mutex_lock(&l->lock);
07350 AST_LIST_TRAVERSE(&l->sub, sub, list) {
07351 ast_mutex_lock(&sub->lock);
07352 if (sub->owner) {
07353 sub->alreadygone = 1;
07354 ast_softhangup(sub->owner, AST_SOFTHANGUP_APPUNLOAD);
07355 }
07356 ast_mutex_unlock(&sub->lock);
07357 }
07358 if (l->mwi_event_sub)
07359 ast_event_unsubscribe(l->mwi_event_sub);
07360 ast_mutex_unlock(&l->lock);
07361 unregister_exten(l);
07362 }
07363 if (s->fd > -1)
07364 close(s->fd);
07365 pthread_cancel(s->t);
07366 pthread_kill(s->t, SIGURG);
07367 pthread_join(s->t, NULL);
07368 free(s);
07369 }
07370 AST_LIST_UNLOCK(&sessions);
07371
07372 delete_devices();
07373
07374 ast_mutex_lock(&monlock);
07375 if ((monitor_thread != AST_PTHREADT_NULL) && (monitor_thread != AST_PTHREADT_STOP)) {
07376 pthread_cancel(monitor_thread);
07377 pthread_kill(monitor_thread, SIGURG);
07378 pthread_join(monitor_thread, NULL);
07379 }
07380 monitor_thread = AST_PTHREADT_STOP;
07381 ast_mutex_unlock(&monlock);
07382
07383 ast_mutex_lock(&netlock);
07384 if (accept_t && (accept_t != AST_PTHREADT_STOP)) {
07385 pthread_cancel(accept_t);
07386 pthread_kill(accept_t, SIGURG);
07387 pthread_join(accept_t, NULL);
07388 }
07389 accept_t = AST_PTHREADT_STOP;
07390 ast_mutex_unlock(&netlock);
07391
07392 close(skinnysock);
07393 if (sched)
07394 sched_context_destroy(sched);
07395
07396 con = ast_context_find(used_context);
07397 if (con)
07398 ast_context_destroy(con, "Skinny");
07399
07400 return 0;
07401 }
07402
07403 static int reload(void)
07404 {
07405 skinny_reload();
07406 return 0;
07407 }
07408
07409 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Skinny Client Control Protocol (Skinny)",
07410 .load = load_module,
07411 .unload = unload_module,
07412 .reload = reload,
07413 );