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