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