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