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: 413586 $")
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, AST_DEVSTATE_CACHABLE, "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, AST_DEVSTATE_CACHABLE, "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 (!d) {
02681 return;
02682 }
02683
02684 if (!c->caller.id.number.valid
02685 || ast_strlen_zero(c->caller.id.number.str)
02686 || !c->connected.id.number.valid
02687 || ast_strlen_zero(c->connected.id.number.str))
02688 return;
02689
02690 if (sub->owner->_state == AST_STATE_UP) {
02691 transmit_callstate(d, l->instance, sub->callid, SKINNY_CONNECTED);
02692 transmit_displaypromptstatus(d, "Connected", 0, l->instance, sub->callid);
02693 if (sub->outgoing)
02694 transmit_callinfo(d,
02695 S_COR(c->connected.id.name.valid, c->connected.id.name.str, ""),
02696 c->connected.id.number.str,
02697 l->cid_name, l->cid_num, l->instance, sub->callid, 1);
02698 else
02699 transmit_callinfo(d, l->cid_name, l->cid_num,
02700 S_COR(c->connected.id.name.valid, c->connected.id.name.str, ""),
02701 c->connected.id.number.str,
02702 l->instance, sub->callid, 2);
02703 } else {
02704 if (sub->outgoing) {
02705 transmit_callstate(d, l->instance, sub->callid, SKINNY_RINGIN);
02706 transmit_displaypromptstatus(d, "Ring-In", 0, l->instance, sub->callid);
02707 transmit_callinfo(d,
02708 S_COR(c->connected.id.name.valid, c->connected.id.name.str, ""),
02709 c->connected.id.number.str,
02710 l->cid_name, l->cid_num, l->instance, sub->callid, 1);
02711 } else {
02712 if (!sub->ringing) {
02713 transmit_callstate(d, l->instance, sub->callid, SKINNY_RINGOUT);
02714 transmit_displaypromptstatus(d, "Ring-Out", 0, l->instance, sub->callid);
02715 sub->ringing = 1;
02716 } else {
02717 transmit_callstate(d, l->instance, sub->callid, SKINNY_PROGRESS);
02718 transmit_displaypromptstatus(d, "Call Progress", 0, l->instance, sub->callid);
02719 sub->progress = 1;
02720 }
02721
02722 transmit_callinfo(d, l->cid_name, l->cid_num,
02723 S_COR(c->connected.id.name.valid, c->connected.id.name.str, ""),
02724 c->connected.id.number.str,
02725 l->instance, sub->callid, 2);
02726 }
02727 }
02728 }
02729
02730 static void mwi_event_cb(const struct ast_event *event, void *userdata)
02731 {
02732 struct skinny_line *l = userdata;
02733 struct skinny_device *d = l->device;
02734 if (d) {
02735 struct skinnysession *s = d->session;
02736 struct skinny_line *l2;
02737 int new_msgs = 0;
02738 int dev_msgs = 0;
02739
02740 if (s) {
02741 if (event) {
02742 l->newmsgs = ast_event_get_ie_uint(event, AST_EVENT_IE_NEWMSGS);
02743 }
02744
02745 if (l->newmsgs) {
02746 transmit_lamp_indication(d, STIMULUS_VOICEMAIL, l->instance, l->mwiblink?SKINNY_LAMP_BLINK:SKINNY_LAMP_ON);
02747 } else {
02748 transmit_lamp_indication(d, STIMULUS_VOICEMAIL, l->instance, SKINNY_LAMP_OFF);
02749 }
02750
02751
02752 AST_LIST_TRAVERSE(&d->lines, l2, list) {
02753 if (l2->newmsgs) {
02754 dev_msgs++;
02755 }
02756 }
02757
02758 if (dev_msgs) {
02759 transmit_lamp_indication(d, STIMULUS_VOICEMAIL, 0, d->mwiblink?SKINNY_LAMP_BLINK:SKINNY_LAMP_ON);
02760 } else {
02761 transmit_lamp_indication(d, STIMULUS_VOICEMAIL, 0, SKINNY_LAMP_OFF);
02762 }
02763 ast_verb(3, "Skinny mwi_event_cb found %d new messages\n", new_msgs);
02764 }
02765 }
02766 }
02767
02768
02769
02770
02771 static enum ast_rtp_glue_result skinny_get_vrtp_peer(struct ast_channel *c, struct ast_rtp_instance **instance)
02772 {
02773 struct skinny_subchannel *sub = NULL;
02774
02775 if (!(sub = c->tech_pvt) || !(sub->vrtp))
02776 return AST_RTP_GLUE_RESULT_FORBID;
02777
02778 ao2_ref(sub->vrtp, +1);
02779 *instance = sub->vrtp;
02780
02781 return AST_RTP_GLUE_RESULT_REMOTE;
02782 }
02783
02784 static enum ast_rtp_glue_result skinny_get_rtp_peer(struct ast_channel *c, struct ast_rtp_instance **instance)
02785 {
02786 struct skinny_subchannel *sub = NULL;
02787 struct skinny_line *l;
02788 enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_REMOTE;
02789
02790 if (skinnydebug)
02791 ast_verb(1, "skinny_get_rtp_peer() Channel = %s\n", c->name);
02792
02793
02794 if (!(sub = c->tech_pvt))
02795 return AST_RTP_GLUE_RESULT_FORBID;
02796
02797 ast_mutex_lock(&sub->lock);
02798
02799 if (!(sub->rtp)){
02800 ast_mutex_unlock(&sub->lock);
02801 return AST_RTP_GLUE_RESULT_FORBID;
02802 }
02803
02804 ao2_ref(sub->rtp, +1);
02805 *instance = sub->rtp;
02806
02807 l = sub->parent;
02808
02809 if (!l->directmedia || l->nat){
02810 res = AST_RTP_GLUE_RESULT_LOCAL;
02811 if (skinnydebug)
02812 ast_verb(1, "skinny_get_rtp_peer() Using AST_RTP_GLUE_RESULT_LOCAL \n");
02813 }
02814
02815 ast_mutex_unlock(&sub->lock);
02816
02817 return res;
02818
02819 }
02820
02821 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)
02822 {
02823 struct skinny_subchannel *sub;
02824 struct skinny_line *l;
02825 struct skinny_device *d;
02826 struct ast_format_list fmt;
02827 struct sockaddr_in us = { 0, };
02828 struct sockaddr_in them = { 0, };
02829 struct ast_sockaddr them_tmp;
02830 struct ast_sockaddr us_tmp;
02831
02832 sub = c->tech_pvt;
02833
02834 if (c->_state != AST_STATE_UP)
02835 return 0;
02836
02837 if (!sub) {
02838 return -1;
02839 }
02840
02841 l = sub->parent;
02842 d = l->device;
02843
02844 if (rtp){
02845 ast_rtp_instance_get_remote_address(rtp, &them_tmp);
02846 ast_sockaddr_to_sin(&them_tmp, &them);
02847
02848
02849 transmit_stopmediatransmission(d, sub);
02850
02851 if (skinnydebug)
02852 ast_verb(1, "Peerip = %s:%d\n", ast_inet_ntoa(them.sin_addr), ntohs(them.sin_port));
02853
02854 fmt = ast_codec_pref_getsize(&l->prefs, ast_best_codec(l->capability));
02855
02856 if (skinnydebug)
02857 ast_verb(1, "Setting payloadType to '%s' (%d ms)\n", ast_getformatname(fmt.bits), fmt.cur_ms);
02858
02859 if (!(l->directmedia) || (l->nat)){
02860 ast_rtp_instance_get_local_address(rtp, &us_tmp);
02861 ast_sockaddr_to_sin(&us_tmp, &us);
02862 us.sin_addr.s_addr = us.sin_addr.s_addr ? us.sin_addr.s_addr : d->ourip.s_addr;
02863 transmit_startmediatransmission(d, sub, us, fmt);
02864 } else {
02865 transmit_startmediatransmission(d, sub, them, fmt);
02866 }
02867
02868 return 0;
02869 }
02870
02871 return 0;
02872 }
02873
02874 static struct ast_rtp_glue skinny_rtp_glue = {
02875 .type = "Skinny",
02876 .get_rtp_info = skinny_get_rtp_peer,
02877 .get_vrtp_info = skinny_get_vrtp_peer,
02878 .update_peer = skinny_set_rtp_peer,
02879 };
02880
02881 static char *handle_skinny_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02882 {
02883 switch (cmd) {
02884 case CLI_INIT:
02885 #ifdef SKINNY_DEVMODE
02886 e->command = "skinny set debug {off|on|packet}";
02887 e->usage =
02888 "Usage: skinny set debug {off|on|packet}\n"
02889 " Enables/Disables dumping of Skinny packets for debugging purposes\n";
02890 #else
02891 e->command = "skinny set debug {off|on}";
02892 e->usage =
02893 "Usage: skinny set debug {off|on}\n"
02894 " Enables/Disables dumping of Skinny packets for debugging purposes\n";
02895 #endif
02896 return NULL;
02897 case CLI_GENERATE:
02898 return NULL;
02899 }
02900
02901 if (a->argc != e->args)
02902 return CLI_SHOWUSAGE;
02903
02904 if (!strncasecmp(a->argv[e->args - 1], "on", 2)) {
02905 skinnydebug = 1;
02906 ast_cli(a->fd, "Skinny Debugging Enabled\n");
02907 return CLI_SUCCESS;
02908 } else if (!strncasecmp(a->argv[e->args - 1], "off", 3)) {
02909 skinnydebug = 0;
02910 ast_cli(a->fd, "Skinny Debugging Disabled\n");
02911 return CLI_SUCCESS;
02912 #ifdef SKINNY_DEVMODE
02913 } else if (!strncasecmp(a->argv[e->args - 1], "packet", 6)) {
02914 skinnydebug = 2;
02915 ast_cli(a->fd, "Skinny Debugging Enabled including Packets\n");
02916 return CLI_SUCCESS;
02917 #endif
02918 } else {
02919 return CLI_SHOWUSAGE;
02920 }
02921 }
02922
02923 static char *handle_skinny_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02924 {
02925 switch (cmd) {
02926 case CLI_INIT:
02927 e->command = "skinny reload";
02928 e->usage =
02929 "Usage: skinny reload\n"
02930 " Reloads the chan_skinny configuration\n";
02931 return NULL;
02932 case CLI_GENERATE:
02933 return NULL;
02934 }
02935
02936 if (a->argc != e->args)
02937 return CLI_SHOWUSAGE;
02938
02939 skinny_reload();
02940 return CLI_SUCCESS;
02941
02942 }
02943
02944 static char *complete_skinny_devices(const char *word, int state)
02945 {
02946 struct skinny_device *d;
02947 char *result = NULL;
02948 int wordlen = strlen(word), which = 0;
02949
02950 AST_LIST_TRAVERSE(&devices, d, list) {
02951 if (!strncasecmp(word, d->id, wordlen) && ++which > state)
02952 result = ast_strdup(d->id);
02953 }
02954
02955 return result;
02956 }
02957
02958 static char *complete_skinny_show_device(const char *line, const char *word, int pos, int state)
02959 {
02960 return (pos == 3 ? ast_strdup(complete_skinny_devices(word, state)) : NULL);
02961 }
02962
02963 static char *complete_skinny_reset(const char *line, const char *word, int pos, int state)
02964 {
02965 return (pos == 2 ? ast_strdup(complete_skinny_devices(word, state)) : NULL);
02966 }
02967
02968 static char *complete_skinny_show_line(const char *line, const char *word, int pos, int state)
02969 {
02970 struct skinny_device *d;
02971 struct skinny_line *l;
02972 char *result = NULL;
02973 int wordlen = strlen(word), which = 0;
02974
02975 if (pos != 3)
02976 return NULL;
02977
02978 AST_LIST_TRAVERSE(&devices, d, list) {
02979 AST_LIST_TRAVERSE(&d->lines, l, list) {
02980 if (!strncasecmp(word, l->name, wordlen) && ++which > state)
02981 result = ast_strdup(l->name);
02982 }
02983 }
02984
02985 return result;
02986 }
02987
02988 static char *handle_skinny_reset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02989 {
02990 struct skinny_device *d;
02991 struct skinny_req *req;
02992
02993 switch (cmd) {
02994 case CLI_INIT:
02995 e->command = "skinny reset";
02996 e->usage =
02997 "Usage: skinny reset <DeviceId|DeviceName|all> [restart]\n"
02998 " Causes a Skinny device to reset itself, optionally with a full restart\n";
02999 return NULL;
03000 case CLI_GENERATE:
03001 return complete_skinny_reset(a->line, a->word, a->pos, a->n);
03002 }
03003
03004 if (a->argc < 3 || a->argc > 4)
03005 return CLI_SHOWUSAGE;
03006
03007 AST_LIST_LOCK(&devices);
03008 AST_LIST_TRAVERSE(&devices, d, list) {
03009 int fullrestart = 0;
03010 if (!strcasecmp(a->argv[2], d->id) || !strcasecmp(a->argv[2], d->name) || !strcasecmp(a->argv[2], "all")) {
03011 if (!(d->session))
03012 continue;
03013
03014 if (!(req = req_alloc(sizeof(struct reset_message), RESET_MESSAGE)))
03015 continue;
03016
03017 if (a->argc == 4 && !strcasecmp(a->argv[3], "restart"))
03018 fullrestart = 1;
03019
03020 if (fullrestart)
03021 req->data.reset.resetType = 2;
03022 else
03023 req->data.reset.resetType = 1;
03024
03025 ast_verb(3, "%s device %s.\n", (fullrestart) ? "Restarting" : "Resetting", d->id);
03026 transmit_response(d, req);
03027 }
03028 }
03029 AST_LIST_UNLOCK(&devices);
03030 return CLI_SUCCESS;
03031 }
03032
03033 static char *device2str(int type)
03034 {
03035 char *tmp;
03036
03037 switch (type) {
03038 case SKINNY_DEVICE_NONE:
03039 return "No Device";
03040 case SKINNY_DEVICE_30SPPLUS:
03041 return "30SP Plus";
03042 case SKINNY_DEVICE_12SPPLUS:
03043 return "12SP Plus";
03044 case SKINNY_DEVICE_12SP:
03045 return "12SP";
03046 case SKINNY_DEVICE_12:
03047 return "12";
03048 case SKINNY_DEVICE_30VIP:
03049 return "30VIP";
03050 case SKINNY_DEVICE_7910:
03051 return "7910";
03052 case SKINNY_DEVICE_7960:
03053 return "7960";
03054 case SKINNY_DEVICE_7940:
03055 return "7940";
03056 case SKINNY_DEVICE_7935:
03057 return "7935";
03058 case SKINNY_DEVICE_ATA186:
03059 return "ATA186";
03060 case SKINNY_DEVICE_7941:
03061 return "7941";
03062 case SKINNY_DEVICE_7971:
03063 return "7971";
03064 case SKINNY_DEVICE_7914:
03065 return "7914";
03066 case SKINNY_DEVICE_7985:
03067 return "7985";
03068 case SKINNY_DEVICE_7911:
03069 return "7911";
03070 case SKINNY_DEVICE_7961GE:
03071 return "7961GE";
03072 case SKINNY_DEVICE_7941GE:
03073 return "7941GE";
03074 case SKINNY_DEVICE_7931:
03075 return "7931";
03076 case SKINNY_DEVICE_7921:
03077 return "7921";
03078 case SKINNY_DEVICE_7906:
03079 return "7906";
03080 case SKINNY_DEVICE_7962:
03081 return "7962";
03082 case SKINNY_DEVICE_7937:
03083 return "7937";
03084 case SKINNY_DEVICE_7942:
03085 return "7942";
03086 case SKINNY_DEVICE_7945:
03087 return "7945";
03088 case SKINNY_DEVICE_7965:
03089 return "7965";
03090 case SKINNY_DEVICE_7975:
03091 return "7975";
03092 case SKINNY_DEVICE_7905:
03093 return "7905";
03094 case SKINNY_DEVICE_7920:
03095 return "7920";
03096 case SKINNY_DEVICE_7970:
03097 return "7970";
03098 case SKINNY_DEVICE_7912:
03099 return "7912";
03100 case SKINNY_DEVICE_7902:
03101 return "7902";
03102 case SKINNY_DEVICE_CIPC:
03103 return "IP Communicator";
03104 case SKINNY_DEVICE_7961:
03105 return "7961";
03106 case SKINNY_DEVICE_7936:
03107 return "7936";
03108 case SKINNY_DEVICE_SCCPGATEWAY_AN:
03109 return "SCCPGATEWAY_AN";
03110 case SKINNY_DEVICE_SCCPGATEWAY_BRI:
03111 return "SCCPGATEWAY_BRI";
03112 case SKINNY_DEVICE_UNKNOWN:
03113 return "Unknown";
03114 default:
03115 if (!(tmp = ast_threadstorage_get(&device2str_threadbuf, DEVICE2STR_BUFSIZE)))
03116 return "Unknown";
03117 snprintf(tmp, DEVICE2STR_BUFSIZE, "UNKNOWN-%d", type);
03118 return tmp;
03119 }
03120 }
03121
03122
03123 static void print_codec_to_cli(int fd, struct ast_codec_pref *pref)
03124 {
03125 int x, codec;
03126
03127 for(x = 0; x < 32 ; x++) {
03128 codec = ast_codec_pref_index(pref, x);
03129 if (!codec)
03130 break;
03131 ast_cli(fd, "%s", ast_getformatname(codec));
03132 ast_cli(fd, ":%d", pref->framing[x]);
03133 if (x < 31 && ast_codec_pref_index(pref, x + 1))
03134 ast_cli(fd, ",");
03135 }
03136 if (!x)
03137 ast_cli(fd, "none");
03138 }
03139
03140 static char *_skinny_show_devices(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[])
03141 {
03142 struct skinny_device *d;
03143 struct skinny_line *l;
03144 const char *id;
03145 char idtext[256] = "";
03146 int total_devices = 0;
03147
03148 if (s) {
03149 id = astman_get_header(m, "ActionID");
03150 if (!ast_strlen_zero(id))
03151 snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
03152 }
03153
03154 switch (argc) {
03155 case 3:
03156 break;
03157 default:
03158 return CLI_SHOWUSAGE;
03159 }
03160
03161 if (!s) {
03162 ast_cli(fd, "Name DeviceId IP Type R NL\n");
03163 ast_cli(fd, "-------------------- ---------------- --------------- --------------- - --\n");
03164 }
03165
03166 AST_LIST_LOCK(&devices);
03167 AST_LIST_TRAVERSE(&devices, d, list) {
03168 int numlines = 0;
03169 total_devices++;
03170 AST_LIST_TRAVERSE(&d->lines, l, list) {
03171 numlines++;
03172 }
03173 if (!s) {
03174 ast_cli(fd, "%-20s %-16s %-15s %-15s %c %2d\n",
03175 d->name,
03176 d->id,
03177 d->session?ast_inet_ntoa(d->session->sin.sin_addr):"",
03178 device2str(d->type),
03179 d->registered?'Y':'N',
03180 numlines);
03181 } else {
03182 astman_append(s,
03183 "Event: DeviceEntry\r\n%s"
03184 "Channeltype: SKINNY\r\n"
03185 "ObjectName: %s\r\n"
03186 "ChannelObjectType: device\r\n"
03187 "DeviceId: %s\r\n"
03188 "IPaddress: %s\r\n"
03189 "Type: %s\r\n"
03190 "Devicestatus: %s\r\n"
03191 "NumberOfLines: %d\r\n",
03192 idtext,
03193 d->name,
03194 d->id,
03195 d->session?ast_inet_ntoa(d->session->sin.sin_addr):"-none-",
03196 device2str(d->type),
03197 d->registered?"registered":"unregistered",
03198 numlines);
03199 }
03200 }
03201 AST_LIST_UNLOCK(&devices);
03202
03203 if (total)
03204 *total = total_devices;
03205
03206 return CLI_SUCCESS;
03207 }
03208
03209
03210
03211 static int manager_skinny_show_devices(struct mansession *s, const struct message *m)
03212 {
03213 const char *id = astman_get_header(m, "ActionID");
03214 const char *a[] = {"skinny", "show", "devices"};
03215 char idtext[256] = "";
03216 int total = 0;
03217
03218 if (!ast_strlen_zero(id))
03219 snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
03220
03221 astman_send_listack(s, m, "Device status list will follow", "start");
03222
03223 _skinny_show_devices(-1, &total, s, m, 3, a);
03224
03225 astman_append(s,
03226 "Event: DevicelistComplete\r\n"
03227 "EventList: Complete\r\n"
03228 "ListItems: %d\r\n"
03229 "%s"
03230 "\r\n", total, idtext);
03231 return 0;
03232 }
03233
03234 static char *handle_skinny_show_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03235 {
03236
03237 switch (cmd) {
03238 case CLI_INIT:
03239 e->command = "skinny show devices";
03240 e->usage =
03241 "Usage: skinny show devices\n"
03242 " Lists all devices known to the Skinny subsystem.\n";
03243 return NULL;
03244 case CLI_GENERATE:
03245 return NULL;
03246 }
03247
03248 return _skinny_show_devices(a->fd, NULL, NULL, NULL, a->argc, (const char **) a->argv);
03249 }
03250
03251 static char *_skinny_show_device(int type, int fd, struct mansession *s, const struct message *m, int argc, const char *argv[])
03252 {
03253 struct skinny_device *d;
03254 struct skinny_line *l;
03255 struct skinny_speeddial *sd;
03256 struct skinny_addon *sa;
03257 char codec_buf[512];
03258
03259 if (argc < 4) {
03260 return CLI_SHOWUSAGE;
03261 }
03262
03263 AST_LIST_LOCK(&devices);
03264 AST_LIST_TRAVERSE(&devices, d, list) {
03265 if (!strcasecmp(argv[3], d->id) || !strcasecmp(argv[3], d->name)) {
03266 int numlines = 0, numaddons = 0, numspeeddials = 0;
03267
03268 AST_LIST_TRAVERSE(&d->lines, l, list){
03269 numlines++;
03270 }
03271
03272 AST_LIST_TRAVERSE(&d->addons, sa, list) {
03273 numaddons++;
03274 }
03275
03276 AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
03277 numspeeddials++;
03278 }
03279
03280 if (type == 0) {
03281 ast_cli(fd, "Name: %s\n", d->name);
03282 ast_cli(fd, "Id: %s\n", d->id);
03283 ast_cli(fd, "version: %s\n", S_OR(d->version_id, "Unknown"));
03284 ast_cli(fd, "Ip address: %s\n", (d->session ? ast_inet_ntoa(d->session->sin.sin_addr) : "Unknown"));
03285 ast_cli(fd, "Port: %d\n", (d->session ? ntohs(d->session->sin.sin_port) : 0));
03286 ast_cli(fd, "Device Type: %s\n", device2str(d->type));
03287 ast_cli(fd, "Conf Codecs:");
03288 ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, d->confcapability);
03289 ast_cli(fd, "%s\n", codec_buf);
03290 ast_cli(fd, "Neg Codecs: ");
03291 ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, d->capability);
03292 ast_cli(fd, "%s\n", codec_buf);
03293 ast_cli(fd, "Registered: %s\n", (d->registered ? "Yes" : "No"));
03294 ast_cli(fd, "Lines: %d\n", numlines);
03295 AST_LIST_TRAVERSE(&d->lines, l, list) {
03296 ast_cli(fd, " %s (%s)\n", l->name, l->label);
03297 }
03298 AST_LIST_TRAVERSE(&d->addons, sa, list) {
03299 numaddons++;
03300 }
03301 ast_cli(fd, "Addons: %d\n", numaddons);
03302 AST_LIST_TRAVERSE(&d->addons, sa, list) {
03303 ast_cli(fd, " %s\n", sa->type);
03304 }
03305 AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
03306 numspeeddials++;
03307 }
03308 ast_cli(fd, "Speeddials: %d\n", numspeeddials);
03309 AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
03310 ast_cli(fd, " %s (%s) ishint: %d\n", sd->exten, sd->label, sd->isHint);
03311 }
03312 } else {
03313 astman_append(s, "Channeltype: SKINNY\r\n");
03314 astman_append(s, "ObjectName: %s\r\n", d->name);
03315 astman_append(s, "ChannelObjectType: device\r\n");
03316 astman_append(s, "Id: %s\r\n", d->id);
03317 astman_append(s, "version: %s\r\n", S_OR(d->version_id, "Unknown"));
03318 astman_append(s, "Ipaddress: %s\r\n", (d->session ? ast_inet_ntoa(d->session->sin.sin_addr) : "Unknown"));
03319 astman_append(s, "Port: %d\r\n", (d->session ? ntohs(d->session->sin.sin_port) : 0));
03320 astman_append(s, "DeviceType: %s\r\n", device2str(d->type));
03321 astman_append(s, "Codecs: ");
03322 ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, d->confcapability);
03323 astman_append(s, "%s\r\n", codec_buf);
03324 astman_append(s, "CodecOrder: ");
03325 ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, d->capability);
03326 astman_append(s, "%s\r\n", codec_buf);
03327 astman_append(s, "Devicestatus: %s\r\n", (d->registered?"registered":"unregistered"));
03328 astman_append(s, "NumberOfLines: %d\r\n", numlines);
03329 AST_LIST_TRAVERSE(&d->lines, l, list) {
03330 astman_append(s, "Line: %s (%s)\r\n", l->name, l->label);
03331 }
03332 astman_append(s, "NumberOfAddons: %d\r\n", numaddons);
03333 AST_LIST_TRAVERSE(&d->addons, sa, list) {
03334 astman_append(s, "Addon: %s\r\n", sa->type);
03335 }
03336 astman_append(s, "NumberOfSpeeddials: %d\r\n", numspeeddials);
03337 AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
03338 astman_append(s, "Speeddial: %s (%s) ishint: %d\r\n", sd->exten, sd->label, sd->isHint);
03339 }
03340 }
03341 }
03342 }
03343 AST_LIST_UNLOCK(&devices);
03344 return CLI_SUCCESS;
03345 }
03346
03347 static int manager_skinny_show_device(struct mansession *s, const struct message *m)
03348 {
03349 const char *a[4];
03350 const char *device;
03351
03352 device = astman_get_header(m, "Device");
03353 if (ast_strlen_zero(device)) {
03354 astman_send_error(s, m, "Device: <name> missing.");
03355 return 0;
03356 }
03357 a[0] = "skinny";
03358 a[1] = "show";
03359 a[2] = "device";
03360 a[3] = device;
03361
03362 _skinny_show_device(1, -1, s, m, 4, a);
03363 astman_append(s, "\r\n\r\n" );
03364 return 0;
03365 }
03366
03367
03368 static char *handle_skinny_show_device(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03369 {
03370 switch (cmd) {
03371 case CLI_INIT:
03372 e->command = "skinny show device";
03373 e->usage =
03374 "Usage: skinny show device <DeviceId|DeviceName>\n"
03375 " Lists all deviceinformation of a specific device known to the Skinny subsystem.\n";
03376 return NULL;
03377 case CLI_GENERATE:
03378 return complete_skinny_show_device(a->line, a->word, a->pos, a->n);
03379 }
03380
03381 return _skinny_show_device(0, a->fd, NULL, NULL, a->argc, (const char **) a->argv);
03382 }
03383
03384 static char *_skinny_show_lines(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[])
03385 {
03386 struct skinny_line *l;
03387 struct skinny_subchannel *sub;
03388 int total_lines = 0;
03389 int verbose = 0;
03390 const char *id;
03391 char idtext[256] = "";
03392
03393 if (s) {
03394 id = astman_get_header(m, "ActionID");
03395 if (!ast_strlen_zero(id))
03396 snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
03397 }
03398
03399 switch (argc) {
03400 case 4:
03401 verbose = 1;
03402 break;
03403 case 3:
03404 verbose = 0;
03405 break;
03406 default:
03407 return CLI_SHOWUSAGE;
03408 }
03409
03410 if (!s) {
03411 ast_cli(fd, "Name Device Name Instance Label \n");
03412 ast_cli(fd, "-------------------- -------------------- -------- --------------------\n");
03413 }
03414 AST_LIST_LOCK(&lines);
03415 AST_LIST_TRAVERSE(&lines, l, all) {
03416 total_lines++;
03417 if (!s) {
03418 ast_cli(fd, "%-20s %-20s %8d %-20s\n",
03419 l->name,
03420 (l->device ? l->device->name : "Not connected"),
03421 l->instance,
03422 l->label);
03423 if (verbose) {
03424 AST_LIST_TRAVERSE(&l->sub, sub, list) {
03425 ast_cli(fd, " %s> %s to %s\n",
03426 (sub == l->activesub?"Active ":"Inactive"),
03427 sub->owner->name,
03428 (ast_bridged_channel(sub->owner)?ast_bridged_channel(sub->owner)->name:"")
03429 );
03430 }
03431 }
03432 } else {
03433 astman_append(s,
03434 "Event: LineEntry\r\n%s"
03435 "Channeltype: SKINNY\r\n"
03436 "ObjectName: %s\r\n"
03437 "ChannelObjectType: line\r\n"
03438 "Device: %s\r\n"
03439 "Instance: %d\r\n"
03440 "Label: %s\r\n",
03441 idtext,
03442 l->name,
03443 (l->device?l->device->name:"None"),
03444 l->instance,
03445 l->label);
03446 }
03447 }
03448 AST_LIST_UNLOCK(&lines);
03449
03450 if (total) {
03451 *total = total_lines;
03452 }
03453
03454 return CLI_SUCCESS;
03455 }
03456
03457
03458
03459 static int manager_skinny_show_lines(struct mansession *s, const struct message *m)
03460 {
03461 const char *id = astman_get_header(m, "ActionID");
03462 const char *a[] = {"skinny", "show", "lines"};
03463 char idtext[256] = "";
03464 int total = 0;
03465
03466 if (!ast_strlen_zero(id))
03467 snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
03468
03469 astman_send_listack(s, m, "Line status list will follow", "start");
03470
03471 _skinny_show_lines(-1, &total, s, m, 3, a);
03472
03473 astman_append(s,
03474 "Event: LinelistComplete\r\n"
03475 "EventList: Complete\r\n"
03476 "ListItems: %d\r\n"
03477 "%s"
03478 "\r\n", total, idtext);
03479 return 0;
03480 }
03481
03482 static char *handle_skinny_show_lines(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03483 {
03484 switch (cmd) {
03485 case CLI_INIT:
03486 e->command = "skinny show lines [verbose]";
03487 e->usage =
03488 "Usage: skinny show lines\n"
03489 " Lists all lines known to the Skinny subsystem.\n"
03490 " If 'verbose' is specified, the output includes\n"
03491 " information about subs for each line.\n";
03492 return NULL;
03493 case CLI_GENERATE:
03494 return NULL;
03495 }
03496
03497 if (a->argc == e->args) {
03498 if (strcasecmp(a->argv[e->args-1], "verbose")) {
03499 return CLI_SHOWUSAGE;
03500 }
03501 } else if (a->argc != e->args - 1) {
03502 return CLI_SHOWUSAGE;
03503 }
03504
03505 return _skinny_show_lines(a->fd, NULL, NULL, NULL, a->argc, (const char **) a->argv);
03506 }
03507
03508 static char *_skinny_show_line(int type, int fd, struct mansession *s, const struct message *m, int argc, const char *argv[])
03509 {
03510 struct skinny_device *d;
03511 struct skinny_line *l;
03512 struct ast_codec_pref *pref;
03513 int x = 0, codec = 0;
03514 char codec_buf[512];
03515 char group_buf[256];
03516 char cbuf[256];
03517
03518 switch (argc) {
03519 case 4:
03520 break;
03521 case 6:
03522 break;
03523 default:
03524 return CLI_SHOWUSAGE;
03525 }
03526
03527 AST_LIST_LOCK(&devices);
03528
03529
03530 AST_LIST_TRAVERSE(&devices, d, list) {
03531 if (argc == 6 && (strcasecmp(argv[5], d->id) && strcasecmp(argv[5], d->name))) {
03532 continue;
03533 }
03534 AST_LIST_TRAVERSE(&d->lines, l, list) {
03535 if (strcasecmp(argv[3], l->name)) {
03536 continue;
03537 }
03538 if (type == 0) {
03539 ast_cli(fd, "Line: %s\n", l->name);
03540 ast_cli(fd, "On Device: %s\n", d->name);
03541 ast_cli(fd, "Line Label: %s\n", l->label);
03542 ast_cli(fd, "Extension: %s\n", S_OR(l->exten, "<not set>"));
03543 ast_cli(fd, "Context: %s\n", l->context);
03544 ast_cli(fd, "CallGroup: %s\n", ast_print_group(group_buf, sizeof(group_buf), l->callgroup));
03545 ast_cli(fd, "PickupGroup: %s\n", ast_print_group(group_buf, sizeof(group_buf), l->pickupgroup));
03546 ast_cli(fd, "Language: %s\n", S_OR(l->language, "<not set>"));
03547 ast_cli(fd, "Accountcode: %s\n", S_OR(l->accountcode, "<not set>"));
03548 ast_cli(fd, "AmaFlag: %s\n", ast_cdr_flags2str(l->amaflags));
03549 ast_cli(fd, "CallerId Number: %s\n", S_OR(l->cid_num, "<not set>"));
03550 ast_cli(fd, "CallerId Name: %s\n", S_OR(l->cid_name, "<not set>"));
03551 ast_cli(fd, "Hide CallerId: %s\n", (l->hidecallerid ? "Yes" : "No"));
03552 ast_cli(fd, "CFwdAll: %s\n", S_COR((l->cfwdtype & SKINNY_CFWD_ALL), l->call_forward_all, "<not set>"));
03553 ast_cli(fd, "CFwdBusy: %s\n", S_COR((l->cfwdtype & SKINNY_CFWD_BUSY), l->call_forward_busy, "<not set>"));
03554 ast_cli(fd, "CFwdNoAnswer: %s\n", S_COR((l->cfwdtype & SKINNY_CFWD_NOANSWER), l->call_forward_noanswer, "<not set>"));
03555 ast_cli(fd, "VoicemailBox: %s\n", S_OR(l->mailbox, "<not set>"));
03556 ast_cli(fd, "VoicemailNumber: %s\n", S_OR(l->vmexten, "<not set>"));
03557 ast_cli(fd, "MWIblink: %d\n", l->mwiblink);
03558 ast_cli(fd, "Regextension: %s\n", S_OR(l->regexten, "<not set>"));
03559 ast_cli(fd, "Regcontext: %s\n", S_OR(l->regcontext, "<not set>"));
03560 ast_cli(fd, "MoHInterpret: %s\n", S_OR(l->mohinterpret, "<not set>"));
03561 ast_cli(fd, "MoHSuggest: %s\n", S_OR(l->mohsuggest, "<not set>"));
03562 ast_cli(fd, "Last dialed nr: %s\n", S_OR(l->lastnumberdialed, "<no calls made yet>"));
03563 ast_cli(fd, "Last CallerID: %s\n", S_OR(l->lastcallerid, "<not set>"));
03564 ast_cli(fd, "Transfer enabled: %s\n", (l->transfer ? "Yes" : "No"));
03565 ast_cli(fd, "Callwaiting: %s\n", (l->callwaiting ? "Yes" : "No"));
03566 ast_cli(fd, "3Way Calling: %s\n", (l->threewaycalling ? "Yes" : "No"));
03567 ast_cli(fd, "Can forward: %s\n", (l->cancallforward ? "Yes" : "No"));
03568 ast_cli(fd, "Do Not Disturb: %s\n", (l->dnd ? "Yes" : "No"));
03569 ast_cli(fd, "NAT: %s\n", (l->nat ? "Yes" : "No"));
03570 ast_cli(fd, "immediate: %s\n", (l->immediate ? "Yes" : "No"));
03571 ast_cli(fd, "Group: %d\n", l->group);
03572 ast_cli(fd, "Parkinglot: %s\n", S_OR(l->parkinglot, "<not set>"));
03573 ast_cli(fd, "Conf Codecs: ");
03574 ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, l->confcapability);
03575 ast_cli(fd, "%s\n", codec_buf);
03576 ast_cli(fd, "Neg Codecs: ");
03577 ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, l->capability);
03578 ast_cli(fd, "%s\n", codec_buf);
03579 ast_cli(fd, "Codec Order: (");
03580 print_codec_to_cli(fd, &l->prefs);
03581 ast_cli(fd, ")\n");
03582 ast_cli(fd, "\n");
03583 } else {
03584 astman_append(s, "Channeltype: SKINNY\r\n");
03585 astman_append(s, "ObjectName: %s\r\n", l->name);
03586 astman_append(s, "ChannelObjectType: line\r\n");
03587 astman_append(s, "Device: %s\r\n", d->name);
03588 astman_append(s, "LineLabel: %s\r\n", l->label);
03589 astman_append(s, "Extension: %s\r\n", S_OR(l->exten, "<not set>"));
03590 astman_append(s, "Context: %s\r\n", l->context);
03591 astman_append(s, "CallGroup: %s\r\n", ast_print_group(group_buf, sizeof(group_buf), l->callgroup));
03592 astman_append(s, "PickupGroup: %s\r\n", ast_print_group(group_buf, sizeof(group_buf), l->pickupgroup));
03593 astman_append(s, "Language: %s\r\n", S_OR(l->language, "<not set>"));
03594 astman_append(s, "Accountcode: %s\r\n", S_OR(l->accountcode, "<not set>"));
03595 astman_append(s, "AMAflags: %s\r\n", ast_cdr_flags2str(l->amaflags));
03596 astman_append(s, "Callerid: %s\r\n", ast_callerid_merge(cbuf, sizeof(cbuf), l->cid_name, l->cid_num, ""));
03597 astman_append(s, "HideCallerId: %s\r\n", (l->hidecallerid ? "Yes" : "No"));
03598 astman_append(s, "CFwdAll: %s\r\n", S_COR((l->cfwdtype & SKINNY_CFWD_ALL), l->call_forward_all, "<not set>"));
03599 astman_append(s, "CFwdBusy: %s\r\n", S_COR((l->cfwdtype & SKINNY_CFWD_BUSY), l->call_forward_busy, "<not set>"));
03600 astman_append(s, "CFwdNoAnswer: %s\r\n", S_COR((l->cfwdtype & SKINNY_CFWD_NOANSWER), l->call_forward_noanswer, "<not set>"));
03601 astman_append(s, "VoicemailBox: %s\r\n", S_OR(l->mailbox, "<not set>"));
03602 astman_append(s, "VoicemailNumber: %s\r\n", S_OR(l->vmexten, "<not set>"));
03603 astman_append(s, "MWIblink: %d\r\n", l->mwiblink);
03604 astman_append(s, "RegExtension: %s\r\n", S_OR(l->regexten, "<not set>"));
03605 astman_append(s, "Regcontext: %s\r\n", S_OR(l->regcontext, "<not set>"));
03606 astman_append(s, "MoHInterpret: %s\r\n", S_OR(l->mohinterpret, "<not set>"));
03607 astman_append(s, "MoHSuggest: %s\r\n", S_OR(l->mohsuggest, "<not set>"));
03608 astman_append(s, "LastDialedNr: %s\r\n", S_OR(l->lastnumberdialed, "<no calls made yet>"));
03609 astman_append(s, "LastCallerID: %s\r\n", S_OR(l->lastcallerid, "<not set>"));
03610 astman_append(s, "Transfer: %s\r\n", (l->transfer ? "Yes" : "No"));
03611 astman_append(s, "Callwaiting: %s\r\n", (l->callwaiting ? "Yes" : "No"));
03612 astman_append(s, "3WayCalling: %s\r\n", (l->threewaycalling ? "Yes" : "No"));
03613 astman_append(s, "CanForward: %s\r\n", (l->cancallforward ? "Yes" : "No"));
03614 astman_append(s, "DoNotDisturb: %s\r\n", (l->dnd ? "Yes" : "No"));
03615 astman_append(s, "NAT: %s\r\n", (l->nat ? "Yes" : "No"));
03616 astman_append(s, "immediate: %s\r\n", (l->immediate ? "Yes" : "No"));
03617 astman_append(s, "Group: %d\r\n", l->group);
03618 astman_append(s, "Parkinglot: %s\r\n", S_OR(l->parkinglot, "<not set>"));
03619 ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, l->confcapability);
03620 astman_append(s, "Codecs: %s\r\n", codec_buf);
03621 astman_append(s, "CodecOrder: ");
03622 pref = &l->prefs;
03623 for(x = 0; x < 32 ; x++) {
03624 codec = ast_codec_pref_index(pref, x);
03625 if (!codec)
03626 break;
03627 astman_append(s, "%s", ast_getformatname(codec));
03628 if (x < 31 && ast_codec_pref_index(pref, x+1))
03629 astman_append(s, ",");
03630 }
03631 astman_append(s, "\r\n");
03632 }
03633 }
03634 }
03635
03636 AST_LIST_UNLOCK(&devices);
03637 return CLI_SUCCESS;
03638 }
03639
03640 static int manager_skinny_show_line(struct mansession *s, const struct message *m)
03641 {
03642 const char *a[4];
03643 const char *line;
03644
03645 line = astman_get_header(m, "Line");
03646 if (ast_strlen_zero(line)) {
03647 astman_send_error(s, m, "Line: <name> missing.");
03648 return 0;
03649 }
03650 a[0] = "skinny";
03651 a[1] = "show";
03652 a[2] = "line";
03653 a[3] = line;
03654
03655 _skinny_show_line(1, -1, s, m, 4, a);
03656 astman_append(s, "\r\n\r\n" );
03657 return 0;
03658 }
03659
03660
03661 static char *handle_skinny_show_line(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03662 {
03663 switch (cmd) {
03664 case CLI_INIT:
03665 e->command = "skinny show line";
03666 e->usage =
03667 "Usage: skinny show line <Line> [ on <DeviceID|DeviceName> ]\n"
03668 " List all lineinformation of a specific line known to the Skinny subsystem.\n";
03669 return NULL;
03670 case CLI_GENERATE:
03671 return complete_skinny_show_line(a->line, a->word, a->pos, a->n);
03672 }
03673
03674 return _skinny_show_line(0, a->fd, NULL, NULL, a->argc, (const char **) a->argv);
03675 }
03676
03677
03678 static char *handle_skinny_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03679 {
03680 switch (cmd) {
03681 case CLI_INIT:
03682 e->command = "skinny show settings";
03683 e->usage =
03684 "Usage: skinny show settings\n"
03685 " Lists all global configuration settings of the Skinny subsystem.\n";
03686 return NULL;
03687 case CLI_GENERATE:
03688 return NULL;
03689 }
03690
03691 if (a->argc != 3)
03692 return CLI_SHOWUSAGE;
03693
03694 ast_cli(a->fd, "\nGlobal Settings:\n");
03695 ast_cli(a->fd, " Skinny Port: %d\n", ntohs(bindaddr.sin_port));
03696 ast_cli(a->fd, " Bindaddress: %s\n", ast_inet_ntoa(bindaddr.sin_addr));
03697 ast_cli(a->fd, " KeepAlive: %d\n", keep_alive);
03698 ast_cli(a->fd, " Date Format: %s\n", date_format);
03699 ast_cli(a->fd, " Voice Mail Extension: %s\n", S_OR(global_vmexten, "(not set)"));
03700 ast_cli(a->fd, " Reg. context: %s\n", S_OR(regcontext, "(not set)"));
03701 ast_cli(a->fd, " Jitterbuffer enabled: %s\n", AST_CLI_YESNO(ast_test_flag(&global_jbconf, AST_JB_ENABLED)));
03702 if (ast_test_flag(&global_jbconf, AST_JB_ENABLED)) {
03703 ast_cli(a->fd, " Jitterbuffer forced: %s\n", AST_CLI_YESNO(ast_test_flag(&global_jbconf, AST_JB_FORCED)));
03704 ast_cli(a->fd, " Jitterbuffer max size: %ld\n", global_jbconf.max_size);
03705 ast_cli(a->fd, " Jitterbuffer resync: %ld\n", global_jbconf.resync_threshold);
03706 ast_cli(a->fd, " Jitterbuffer impl: %s\n", global_jbconf.impl);
03707 if (!strcasecmp(global_jbconf.impl, "adaptive")) {
03708 ast_cli(a->fd, " Jitterbuffer tgt extra: %ld\n", global_jbconf.target_extra);
03709 }
03710 ast_cli(a->fd, " Jitterbuffer log: %s\n", AST_CLI_YESNO(ast_test_flag(&global_jbconf, AST_JB_LOG)));
03711 }
03712
03713 return CLI_SUCCESS;
03714 }
03715
03716 static struct ast_cli_entry cli_skinny[] = {
03717 AST_CLI_DEFINE(handle_skinny_show_devices, "List defined Skinny devices"),
03718 AST_CLI_DEFINE(handle_skinny_show_device, "List Skinny device information"),
03719 AST_CLI_DEFINE(handle_skinny_show_lines, "List defined Skinny lines per device"),
03720 AST_CLI_DEFINE(handle_skinny_show_line, "List Skinny line information"),
03721 AST_CLI_DEFINE(handle_skinny_show_settings, "List global Skinny settings"),
03722 AST_CLI_DEFINE(handle_skinny_set_debug, "Enable/Disable Skinny debugging"),
03723 AST_CLI_DEFINE(handle_skinny_reset, "Reset Skinny device(s)"),
03724 AST_CLI_DEFINE(handle_skinny_reload, "Reload Skinny config"),
03725 };
03726
03727 static void start_rtp(struct skinny_subchannel *sub)
03728 {
03729 struct skinny_line *l = sub->parent;
03730 struct skinny_device *d = l->device;
03731 int hasvideo = 0;
03732 struct ast_sockaddr bindaddr_tmp;
03733
03734 ast_mutex_lock(&sub->lock);
03735
03736 ast_sockaddr_from_sin(&bindaddr_tmp, &bindaddr);
03737 sub->rtp = ast_rtp_instance_new("asterisk", sched, &bindaddr_tmp, NULL);
03738 if (hasvideo)
03739 sub->vrtp = ast_rtp_instance_new("asterisk", sched, &bindaddr_tmp, NULL);
03740
03741 if (sub->rtp) {
03742 ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_RTCP, 1);
03743 }
03744 if (sub->vrtp) {
03745 ast_rtp_instance_set_prop(sub->vrtp, AST_RTP_PROPERTY_RTCP, 1);
03746 }
03747
03748 if (sub->rtp && sub->owner) {
03749 ast_channel_set_fd(sub->owner, 0, ast_rtp_instance_fd(sub->rtp, 0));
03750 ast_channel_set_fd(sub->owner, 1, ast_rtp_instance_fd(sub->rtp, 1));
03751 }
03752 if (hasvideo && sub->vrtp && sub->owner) {
03753 ast_channel_set_fd(sub->owner, 2, ast_rtp_instance_fd(sub->vrtp, 0));
03754 ast_channel_set_fd(sub->owner, 3, ast_rtp_instance_fd(sub->vrtp, 1));
03755 }
03756 if (sub->rtp) {
03757 ast_rtp_instance_set_qos(sub->rtp, qos.tos_audio, qos.cos_audio, "Skinny RTP");
03758 ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_NAT, l->nat);
03759 }
03760 if (sub->vrtp) {
03761 ast_rtp_instance_set_qos(sub->vrtp, qos.tos_video, qos.cos_video, "Skinny VRTP");
03762 ast_rtp_instance_set_prop(sub->vrtp, AST_RTP_PROPERTY_NAT, l->nat);
03763 }
03764
03765 if (sub->rtp)
03766 ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(sub->rtp), sub->rtp, &l->prefs);
03767
03768
03769 transmit_connect(d, sub);
03770 ast_mutex_unlock(&sub->lock);
03771 }
03772
03773 static void *skinny_newcall(void *data)
03774 {
03775 struct ast_channel *c = data;
03776 struct skinny_subchannel *sub = c->tech_pvt;
03777 struct skinny_line *l = sub->parent;
03778 struct skinny_device *d = l->device;
03779 int res = 0;
03780
03781 ast_copy_string(l->lastnumberdialed, c->exten, sizeof(l->lastnumberdialed));
03782 ast_set_callerid(c,
03783 l->hidecallerid ? "" : l->cid_num,
03784 l->hidecallerid ? "" : l->cid_name,
03785 c->caller.ani.number.valid ? NULL : l->cid_num);
03786 #if 1
03787 ast_party_number_free(&c->connected.id.number);
03788 ast_party_number_init(&c->connected.id.number);
03789 c->connected.id.number.valid = 1;
03790 c->connected.id.number.str = ast_strdup(c->exten);
03791 ast_party_name_free(&c->connected.id.name);
03792 ast_party_name_init(&c->connected.id.name);
03793 #endif
03794 ast_setstate(c, AST_STATE_RING);
03795 if (!sub->rtp) {
03796 start_rtp(sub);
03797 }
03798 res = ast_pbx_run(c);
03799 if (res) {
03800 ast_log(LOG_WARNING, "PBX exited non-zero\n");
03801 transmit_start_tone(d, SKINNY_REORDER, l->instance, sub->callid);
03802 }
03803 return NULL;
03804 }
03805
03806 static void *skinny_ss(void *data)
03807 {
03808 struct ast_channel *c = data;
03809 struct skinny_subchannel *sub = c->tech_pvt;
03810 struct skinny_line *l = sub->parent;
03811 struct skinny_device *d = l->device;
03812 int len = 0;
03813 int timeout = firstdigittimeout;
03814 int res = 0;
03815 int loop_pause = 100;
03816
03817 if (!d) {
03818 ast_log(LOG_WARNING, "Device for line %s is not registered.\n", l->name);
03819 return NULL;
03820 }
03821
03822 ast_verb(3, "Starting simple switch on '%s@%s'\n", l->name, d->name);
03823
03824 len = strlen(d->exten);
03825
03826 while (len < AST_MAX_EXTENSION-1) {
03827 res = 1;
03828 while (strlen(d->exten) == len){
03829 ast_safe_sleep(c, loop_pause);
03830 timeout -= loop_pause;
03831 if ( (timeout -= loop_pause) <= 0){
03832 res = 0;
03833 break;
03834 }
03835 res = 1;
03836 }
03837
03838 timeout = 0;
03839 len = strlen(d->exten);
03840
03841 if (!ast_ignore_pattern(c->context, d->exten)) {
03842 transmit_stop_tone(d, l->instance, sub->callid);
03843 }
03844 if (ast_exists_extension(c, c->context, d->exten, 1, l->cid_num)) {
03845 if (!res || !ast_matchmore_extension(c, c->context, d->exten, 1, l->cid_num)) {
03846 if (l->getforward) {
03847
03848 set_callforwards(l, d->exten, l->getforward);
03849 ast_verb(3, "Setting call forward (%d) to '%s' on channel %s\n",
03850 l->cfwdtype, d->exten, c->name);
03851 transmit_start_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
03852 transmit_lamp_indication(d, STIMULUS_FORWARDALL, 1, SKINNY_LAMP_ON);
03853 transmit_displaynotify(d, "CFwd enabled", 10);
03854 transmit_cfwdstate(d, l);
03855 ast_safe_sleep(c, 500);
03856 ast_indicate(c, -1);
03857 ast_safe_sleep(c, 1000);
03858 memset(d->exten, 0, sizeof(d->exten));
03859 len = 0;
03860 l->getforward = 0;
03861 if (sub->owner && sub->owner->_state != AST_STATE_UP) {
03862 ast_indicate(c, -1);
03863 ast_hangup(c);
03864 }
03865 return NULL;
03866 } else {
03867 ast_copy_string(c->exten, d->exten, sizeof(c->exten));
03868 ast_copy_string(l->lastnumberdialed, d->exten, sizeof(l->lastnumberdialed));
03869 memset(d->exten, 0, sizeof(d->exten));
03870 skinny_newcall(c);
03871 return NULL;
03872 }
03873 } else {
03874
03875
03876 timeout = matchdigittimeout;
03877 }
03878 } else if (res == 0) {
03879 ast_debug(1, "Not enough digits (%s) (and no ambiguous match)...\n", d->exten);
03880 memset(d->exten, 0, sizeof(d->exten));
03881 if (l->hookstate == SKINNY_OFFHOOK) {
03882 transmit_start_tone(d, SKINNY_REORDER, l->instance, sub->callid);
03883 }
03884 if (sub->owner && sub->owner->_state != AST_STATE_UP) {
03885 ast_indicate(c, -1);
03886 ast_hangup(c);
03887 }
03888 return NULL;
03889 } else if (!ast_canmatch_extension(c, c->context, d->exten, 1,
03890 S_COR(c->caller.id.number.valid, c->caller.id.number.str, NULL))
03891 && ((d->exten[0] != '*') || (!ast_strlen_zero(d->exten) > 2))) {
03892 ast_log(LOG_WARNING, "Can't match [%s] from '%s' in context %s\n", d->exten,
03893 S_COR(c->caller.id.number.valid, c->caller.id.number.str, "<Unknown Caller>"),
03894 c->context);
03895 memset(d->exten, 0, sizeof(d->exten));
03896 if (l->hookstate == SKINNY_OFFHOOK) {
03897 transmit_start_tone(d, SKINNY_REORDER, l->instance, sub->callid);
03898
03899 ast_safe_sleep(c, 3000);
03900 }
03901 break;
03902 }
03903 if (!timeout) {
03904 timeout = gendigittimeout;
03905 }
03906 if (len && !ast_ignore_pattern(c->context, d->exten)) {
03907 ast_indicate(c, -1);
03908 }
03909 }
03910 if (c)
03911 ast_hangup(c);
03912 memset(d->exten, 0, sizeof(d->exten));
03913 return NULL;
03914 }
03915
03916
03917
03918 static int skinny_call(struct ast_channel *ast, char *dest, int timeout)
03919 {
03920 int res = 0;
03921 struct skinny_subchannel *sub = ast->tech_pvt;
03922 struct skinny_line *l = sub->parent;
03923 struct skinny_device *d = l->device;
03924
03925 if (!d || !d->registered) {
03926 ast_log(LOG_ERROR, "Device not registered, cannot call %s\n", dest);
03927 return -1;
03928 }
03929
03930 if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
03931 ast_log(LOG_WARNING, "skinny_call called on %s, neither down nor reserved\n", ast->name);
03932 return -1;
03933 }
03934
03935 if (skinnydebug)
03936 ast_verb(3, "skinny_call(%s)\n", ast->name);
03937
03938 if (l->dnd) {
03939 ast_queue_control(ast, AST_CONTROL_BUSY);
03940 return -1;
03941 }
03942
03943 if (AST_LIST_NEXT(sub,list) && !l->callwaiting) {
03944 ast_queue_control(ast, AST_CONTROL_BUSY);
03945 return -1;
03946 }
03947
03948 switch (l->hookstate) {
03949 case SKINNY_OFFHOOK:
03950 break;
03951 case SKINNY_ONHOOK:
03952 l->activesub = sub;
03953 break;
03954 default:
03955 ast_log(LOG_ERROR, "Don't know how to deal with hookstate %d\n", l->hookstate);
03956 break;
03957 }
03958
03959 transmit_callstate(d, sub->parent->instance, sub->callid, SKINNY_RINGIN);
03960 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGIN);
03961 transmit_displaypromptstatus(d, "Ring-In", 0, l->instance, sub->callid);
03962 transmit_callinfo(d,
03963 S_COR(ast->connected.id.name.valid, ast->connected.id.name.str, ""),
03964 S_COR(ast->connected.id.number.valid, ast->connected.id.number.str, ""),
03965 l->cid_name, l->cid_num, l->instance, sub->callid, 1);
03966 transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK);
03967 transmit_ringer_mode(d, SKINNY_RING_INSIDE);
03968
03969 ast_setstate(ast, AST_STATE_RINGING);
03970 ast_queue_control(ast, AST_CONTROL_RINGING);
03971 sub->outgoing = 1;
03972 return res;
03973 }
03974
03975 static int skinny_hangup(struct ast_channel *ast)
03976 {
03977 struct skinny_subchannel *sub = ast->tech_pvt;
03978 struct skinny_line *l;
03979 struct skinny_device *d;
03980
03981 if (!sub) {
03982 ast_debug(1, "Asked to hangup channel not connected\n");
03983 return 0;
03984 }
03985
03986 l = sub->parent;
03987 d = l->device;
03988
03989 if (!d) {
03990 ast_log(LOG_WARNING, "Device for line %s is not registered.\n", l->name);
03991 return 0;
03992 }
03993
03994 if (skinnydebug)
03995 ast_verb(3,"Hanging up %s/%u\n", d->name, sub->callid);
03996
03997 AST_LIST_REMOVE(&l->sub, sub, list);
03998
03999 if (d->registered) {
04000
04001
04002 if (!AST_LIST_EMPTY(&l->sub)) {
04003 if (sub->related) {
04004 sub->related->related = NULL;
04005
04006 }
04007 if (sub == l->activesub) {
04008 ast_verb(4,"Killing active sub %u\n", sub->callid);
04009 if (sub->related) {
04010 l->activesub = sub->related;
04011 } else {
04012 if (AST_LIST_NEXT(sub, list)) {
04013 l->activesub = AST_LIST_NEXT(sub, list);
04014 } else {
04015 l->activesub = AST_LIST_FIRST(&l->sub);
04016 }
04017 }
04018
04019 transmit_activatecallplane(d, l);
04020 transmit_closereceivechannel(d, sub);
04021 transmit_stopmediatransmission(d, sub);
04022 transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK);
04023 transmit_stop_tone(d, l->instance, sub->callid);
04024 } else {
04025 ast_verb(4,"Killing inactive sub %u\n", sub->callid);
04026 if (AST_LIST_NEXT(sub, list)) {
04027 transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK);
04028 } else {
04029 transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
04030 }
04031 }
04032 } else {
04033 ast_verb(4,"Killing only sub %u\n", sub->callid);
04034 l->hookstate = SKINNY_ONHOOK;
04035 transmit_closereceivechannel(d, sub);
04036 transmit_stopmediatransmission(d, sub);
04037 transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
04038 transmit_clearpromptmessage(d, l->instance, sub->callid);
04039 transmit_callstate(d, l->instance, sub->callid, SKINNY_ONHOOK);
04040 transmit_selectsoftkeys(d, 0, 0, KEYDEF_ONHOOK);
04041 transmit_activatecallplane(d, l);
04042 l->activesub = NULL;
04043 transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF);
04044 if (sub->parent == d->activeline) {
04045 transmit_activatecallplane(d, l);
04046 transmit_closereceivechannel(d, sub);
04047 transmit_stopmediatransmission(d, sub);
04048 transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
04049 transmit_ringer_mode(d, SKINNY_RING_OFF);
04050 transmit_clear_display_message(d, l->instance, sub->callid);
04051 transmit_stop_tone(d, l->instance, sub->callid);
04052
04053 }
04054 }
04055 }
04056 ast_mutex_lock(&sub->lock);
04057 sub->owner = NULL;
04058 ast->tech_pvt = NULL;
04059 sub->alreadygone = 0;
04060 sub->outgoing = 0;
04061 if (sub->rtp) {
04062 ast_rtp_instance_destroy(sub->rtp);
04063 sub->rtp = NULL;
04064 }
04065 ast_mutex_unlock(&sub->lock);
04066 ast_free(sub);
04067 ast_module_unref(ast_module_info->self);
04068 return 0;
04069 }
04070
04071 static int skinny_answer(struct ast_channel *ast)
04072 {
04073 int res = 0;
04074 struct skinny_subchannel *sub = ast->tech_pvt;
04075 struct skinny_line *l = sub->parent;
04076 struct skinny_device *d = l->device;
04077
04078 if (sub->blindxfer) {
04079 if (skinnydebug)
04080 ast_debug(1, "skinny_answer(%s) on %s@%s-%u with BlindXFER, transferring\n",
04081 ast->name, l->name, d->name, sub->callid);
04082 ast_setstate(ast, AST_STATE_UP);
04083 skinny_transfer(sub);
04084 return 0;
04085 }
04086
04087 sub->cxmode = SKINNY_CX_SENDRECV;
04088 if (!sub->rtp) {
04089 start_rtp(sub);
04090 }
04091 if (skinnydebug)
04092 ast_verb(1, "skinny_answer(%s) on %s@%s-%u\n", ast->name, l->name, d->name, sub->callid);
04093 if (ast->_state != AST_STATE_UP) {
04094 ast_setstate(ast, AST_STATE_UP);
04095 }
04096
04097 transmit_stop_tone(d, l->instance, sub->callid);
04098
04099
04100
04101 transmit_callinfo(d,
04102 S_COR(ast->connected.id.name.valid, ast->connected.id.name.str, ""),
04103 S_COR(ast->connected.id.number.valid, ast->connected.id.number.str, ""),
04104 l->lastnumberdialed, l->lastnumberdialed, l->instance, sub->callid, 2);
04105 transmit_callstate(d, sub->parent->instance, sub->callid, SKINNY_CONNECTED);
04106 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
04107 transmit_dialednumber(d, l->lastnumberdialed, l->instance, sub->callid);
04108 transmit_displaypromptstatus(d, "Connected", 0, l->instance, sub->callid);
04109 l->activesub = sub;
04110 return res;
04111 }
04112
04113
04114 static struct ast_frame *skinny_rtp_read(struct skinny_subchannel *sub)
04115 {
04116 struct ast_channel *ast = sub->owner;
04117 struct ast_frame *f;
04118
04119 if (!sub->rtp) {
04120
04121 return &ast_null_frame;
04122 }
04123
04124 switch(ast->fdno) {
04125 case 0:
04126 f = ast_rtp_instance_read(sub->rtp, 0);
04127 break;
04128 case 1:
04129 f = ast_rtp_instance_read(sub->rtp, 1);
04130 break;
04131 case 2:
04132 f = ast_rtp_instance_read(sub->vrtp, 0);
04133 break;
04134 case 3:
04135 f = ast_rtp_instance_read(sub->vrtp, 1);
04136 break;
04137 #if 0
04138 case 5:
04139
04140 f = ast_udptl_read(sub->udptl);
04141 break;
04142 #endif
04143 default:
04144 f = &ast_null_frame;
04145 }
04146
04147 if (ast) {
04148
04149 if (f->frametype == AST_FRAME_VOICE) {
04150 if (f->subclass.codec != ast->nativeformats) {
04151 ast_debug(1, "Oooh, format changed to %s\n", ast_getformatname(f->subclass.codec));
04152 ast->nativeformats = f->subclass.codec;
04153 ast_set_read_format(ast, ast->readformat);
04154 ast_set_write_format(ast, ast->writeformat);
04155 }
04156 }
04157 }
04158 return f;
04159 }
04160
04161 static struct ast_frame *skinny_read(struct ast_channel *ast)
04162 {
04163 struct ast_frame *fr;
04164 struct skinny_subchannel *sub = ast->tech_pvt;
04165 ast_mutex_lock(&sub->lock);
04166 fr = skinny_rtp_read(sub);
04167 ast_mutex_unlock(&sub->lock);
04168 return fr;
04169 }
04170
04171 static int skinny_write(struct ast_channel *ast, struct ast_frame *frame)
04172 {
04173 struct skinny_subchannel *sub = ast->tech_pvt;
04174 int res = 0;
04175 if (frame->frametype != AST_FRAME_VOICE) {
04176 if (frame->frametype == AST_FRAME_IMAGE) {
04177 return 0;
04178 } else {
04179 ast_log(LOG_WARNING, "Can't send %u type frames with skinny_write\n", frame->frametype);
04180 return 0;
04181 }
04182 } else {
04183 if (!(frame->subclass.codec & ast->nativeformats)) {
04184 char buf[256];
04185 ast_log(LOG_WARNING, "Asked to transmit frame type %s, while native formats is %s (read/write = %s/%s)\n",
04186 ast_getformatname(frame->subclass.codec),
04187 ast_getformatname_multiple(buf, sizeof(buf), ast->nativeformats),
04188 ast_getformatname(ast->readformat),
04189 ast_getformatname(ast->writeformat));
04190 return -1;
04191 }
04192 }
04193 if (sub) {
04194 ast_mutex_lock(&sub->lock);
04195 if (sub->rtp) {
04196 res = ast_rtp_instance_write(sub->rtp, frame);
04197 }
04198 ast_mutex_unlock(&sub->lock);
04199 }
04200 return res;
04201 }
04202
04203 static int skinny_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
04204 {
04205 struct skinny_subchannel *sub = newchan->tech_pvt;
04206 ast_log(LOG_NOTICE, "skinny_fixup(%s, %s)\n", oldchan->name, newchan->name);
04207 if (sub->owner != oldchan) {
04208 ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, sub->owner);
04209 return -1;
04210 }
04211 sub->owner = newchan;
04212 return 0;
04213 }
04214
04215 static int skinny_senddigit_begin(struct ast_channel *ast, char digit)
04216 {
04217 return -1;
04218 }
04219
04220 static int skinny_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration)
04221 {
04222 #if 0
04223 struct skinny_subchannel *sub = ast->tech_pvt;
04224 struct skinny_line *l = sub->parent;
04225 struct skinny_device *d = l->device;
04226 int tmp;
04227
04228 sprintf(tmp, "%d", digit);
04229
04230 #endif
04231 return -1;
04232 }
04233
04234 static int get_devicestate(struct skinny_line *l)
04235 {
04236 struct skinny_subchannel *sub;
04237 int res = AST_DEVICE_UNKNOWN;
04238
04239 if (!l)
04240 res = AST_DEVICE_INVALID;
04241 else if (!l->device)
04242 res = AST_DEVICE_UNAVAILABLE;
04243 else if (l->dnd)
04244 res = AST_DEVICE_BUSY;
04245 else {
04246 if (l->hookstate == SKINNY_ONHOOK) {
04247 res = AST_DEVICE_NOT_INUSE;
04248 } else {
04249 res = AST_DEVICE_INUSE;
04250 }
04251
04252 AST_LIST_TRAVERSE(&l->sub, sub, list) {
04253 if (sub->onhold) {
04254 res = AST_DEVICE_ONHOLD;
04255 break;
04256 }
04257 }
04258 }
04259
04260 return res;
04261 }
04262
04263 static char *control2str(int ind) {
04264 char *tmp;
04265
04266 switch (ind) {
04267 case AST_CONTROL_HANGUP:
04268 return "Other end has hungup";
04269 case AST_CONTROL_RING:
04270 return "Local ring";
04271 case AST_CONTROL_RINGING:
04272 return "Remote end is ringing";
04273 case AST_CONTROL_ANSWER:
04274 return "Remote end has answered";
04275 case AST_CONTROL_BUSY:
04276 return "Remote end is busy";
04277 case AST_CONTROL_TAKEOFFHOOK:
04278 return "Make it go off hook";
04279 case AST_CONTROL_OFFHOOK:
04280 return "Line is off hook";
04281 case AST_CONTROL_CONGESTION:
04282 return "Congestion (circuits busy)";
04283 case AST_CONTROL_FLASH:
04284 return "Flash hook";
04285 case AST_CONTROL_WINK:
04286 return "Wink";
04287 case AST_CONTROL_OPTION:
04288 return "Set a low-level option";
04289 case AST_CONTROL_RADIO_KEY:
04290 return "Key Radio";
04291 case AST_CONTROL_RADIO_UNKEY:
04292 return "Un-Key Radio";
04293 case AST_CONTROL_PROGRESS:
04294 return "Remote end is making Progress";
04295 case AST_CONTROL_PROCEEDING:
04296 return "Remote end is proceeding";
04297 case AST_CONTROL_HOLD:
04298 return "Hold";
04299 case AST_CONTROL_UNHOLD:
04300 return "Unhold";
04301 case AST_CONTROL_SRCUPDATE:
04302 return "Media Source Update";
04303 case AST_CONTROL_CONNECTED_LINE:
04304 return "Connected Line";
04305 case AST_CONTROL_REDIRECTING:
04306 return "Redirecting";
04307 case AST_CONTROL_INCOMPLETE:
04308 return "Incomplete";
04309 case -1:
04310 return "Stop tone";
04311 default:
04312 if (!(tmp = ast_threadstorage_get(&control2str_threadbuf, CONTROL2STR_BUFSIZE)))
04313 return "Unknown";
04314 snprintf(tmp, CONTROL2STR_BUFSIZE, "UNKNOWN-%d", ind);
04315 return tmp;
04316 }
04317 }
04318
04319 static int skinny_transfer(struct skinny_subchannel *sub)
04320 {
04321 struct skinny_subchannel *xferor;
04322 struct skinny_subchannel *xferee;
04323 struct ast_tone_zone_sound *ts = NULL;
04324
04325 if (ast_bridged_channel(sub->owner) || ast_bridged_channel(sub->related->owner)) {
04326 if (sub->xferor) {
04327 xferor = sub;
04328 xferee = sub->related;
04329 } else {
04330 xferor = sub;
04331 xferee = sub->related;
04332 }
04333
04334 if (skinnydebug) {
04335 ast_debug(1, "Transferee channels (local/remote): %s and %s\n",
04336 xferee->owner->name, ast_bridged_channel(xferee->owner)?ast_bridged_channel(xferee->owner)->name:"");
04337 ast_debug(1, "Transferor channels (local/remote): %s and %s\n",
04338 xferor->owner->name, ast_bridged_channel(xferor->owner)?ast_bridged_channel(xferor->owner)->name:"");
04339 }
04340 if (ast_bridged_channel(xferor->owner)) {
04341 if (ast_bridged_channel(xferee->owner)) {
04342 ast_queue_control(xferee->owner, AST_CONTROL_UNHOLD);
04343 }
04344 if (xferor->owner->_state == AST_STATE_RING) {
04345
04346 if ((ts = ast_get_indication_tone(xferor->owner->zone, "ring"))) {
04347 ast_playtones_start(xferor->owner, 0, ts->data, 1);
04348 ts = ast_tone_zone_sound_unref(ts);
04349 }
04350 }
04351 if (skinnydebug)
04352 ast_debug(1, "Transfer Masquerading %s to %s\n",
04353 xferee->owner->name, ast_bridged_channel(xferor->owner)?ast_bridged_channel(xferor->owner)->name:"");
04354 if (ast_channel_masquerade(xferee->owner, ast_bridged_channel(xferor->owner))) {
04355 ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
04356 ast_bridged_channel(xferor->owner)->name, xferee->owner->name);
04357 return -1;
04358 }
04359 } else if (ast_bridged_channel(xferee->owner)) {
04360 ast_queue_control(xferee->owner, AST_CONTROL_UNHOLD);
04361 if (xferor->owner->_state == AST_STATE_RING) {
04362
04363 if ((ts = ast_get_indication_tone(xferor->owner->zone, "ring"))) {
04364 ast_playtones_start(xferor->owner, 0, ts->data, 1);
04365 ts = ast_tone_zone_sound_unref(ts);
04366 }
04367 }
04368 if (skinnydebug)
04369 ast_debug(1, "Transfer Masquerading %s to %s\n",
04370 xferor->owner->name, ast_bridged_channel(xferee->owner)?ast_bridged_channel(xferee->owner)->name:"");
04371 if (ast_channel_masquerade(xferor->owner, ast_bridged_channel(xferee->owner))) {
04372 ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
04373 ast_bridged_channel(xferee->owner)->name, xferor->owner->name);
04374 return -1;
04375 }
04376 return 0;
04377 } else {
04378 if (option_debug)
04379 ast_log(LOG_DEBUG, "Neither %s nor %s are in a bridge, nothing to transfer\n",
04380 xferor->owner->name, xferee->owner->name);
04381 }
04382 }
04383 return 0;
04384 }
04385
04386 static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, size_t datalen)
04387 {
04388 struct skinny_subchannel *sub = ast->tech_pvt;
04389 struct skinny_line *l = sub->parent;
04390 struct skinny_device *d = l->device;
04391 struct skinnysession *s;
04392
04393 if (!d) {
04394 ast_log(LOG_WARNING, "Device for line %s is not registered.\n", l->name);
04395 return -1;
04396 }
04397 s = d->session;
04398
04399 if (!s) {
04400 ast_log(LOG_NOTICE, "Asked to indicate '%s' condition on channel %s, but session does not exist.\n", control2str(ind), ast->name);
04401 return -1;
04402 }
04403
04404 if (skinnydebug)
04405 ast_verb(3, "Asked to indicate '%s' condition on channel %s\n", control2str(ind), ast->name);
04406 switch(ind) {
04407 case AST_CONTROL_RINGING:
04408 if (sub->blindxfer) {
04409 if (skinnydebug)
04410 ast_debug(1, "Channel %s set up for Blind Xfer, so Xfer rather than ring device\n", ast->name);
04411 skinny_transfer(sub);
04412 break;
04413 }
04414 if (ast->_state != AST_STATE_UP) {
04415 if (!sub->progress) {
04416 if (!d->earlyrtp) {
04417 transmit_start_tone(d, SKINNY_ALERT, l->instance, sub->callid);
04418 }
04419 transmit_callstate(d, sub->parent->instance, sub->callid, SKINNY_RINGOUT);
04420 transmit_dialednumber(d, l->lastnumberdialed, l->instance, sub->callid);
04421 transmit_displaypromptstatus(d, "Ring Out", 0, l->instance, sub->callid);
04422 transmit_callinfo(d,
04423 S_COR(ast->caller.id.name.valid, ast->caller.id.name.str, ""),
04424 S_COR(ast->caller.id.number.valid, ast->caller.id.number.str, ""),
04425 S_COR(ast->connected.id.name.valid, ast->connected.id.name.str, l->lastnumberdialed),
04426 S_COR(ast->connected.id.number.valid, ast->connected.id.number.str, l->lastnumberdialed),
04427 l->instance, sub->callid, 2);
04428 sub->ringing = 1;
04429 if (!d->earlyrtp) {
04430 break;
04431 }
04432 }
04433 }
04434 return -1;
04435 case AST_CONTROL_BUSY:
04436 if (ast->_state != AST_STATE_UP) {
04437 if (!d->earlyrtp) {
04438 transmit_start_tone(d, SKINNY_BUSYTONE, l->instance, sub->callid);
04439 }
04440 transmit_callstate(d, sub->parent->instance, sub->callid, SKINNY_BUSY);
04441 sub->alreadygone = 1;
04442 ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
04443 if (!d->earlyrtp) {
04444 break;
04445 }
04446 }
04447 return -1;
04448 case AST_CONTROL_INCOMPLETE:
04449
04450 case AST_CONTROL_CONGESTION:
04451 if (ast->_state != AST_STATE_UP) {
04452 if (!d->earlyrtp) {
04453 transmit_start_tone(d, SKINNY_REORDER, l->instance, sub->callid);
04454 }
04455 transmit_callstate(d, sub->parent->instance, sub->callid, SKINNY_CONGESTION);
04456 sub->alreadygone = 1;
04457 ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
04458 if (!d->earlyrtp) {
04459 break;
04460 }
04461 }
04462 return -1;
04463 case AST_CONTROL_PROGRESS:
04464 if ((ast->_state != AST_STATE_UP) && !sub->progress && !sub->outgoing) {
04465 if (!d->earlyrtp) {
04466 transmit_start_tone(d, SKINNY_ALERT, l->instance, sub->callid);
04467 }
04468 transmit_callstate(d, sub->parent->instance, sub->callid, SKINNY_PROGRESS);
04469 transmit_displaypromptstatus(d, "Call Progress", 0, l->instance, sub->callid);
04470 transmit_callinfo(d,
04471 S_COR(ast->caller.id.name.valid, ast->caller.id.name.str, ""),
04472 S_COR(ast->caller.id.number.valid, ast->caller.id.number.str, ""),
04473 S_COR(ast->connected.id.name.valid, ast->connected.id.name.str, l->lastnumberdialed),
04474 S_COR(ast->connected.id.number.valid, ast->connected.id.number.str, l->lastnumberdialed),
04475 l->instance, sub->callid, 2);
04476 sub->progress = 1;
04477 if (!d->earlyrtp) {
04478 break;
04479 }
04480 }
04481 return -1;
04482 case -1:
04483 transmit_stop_tone(d, l->instance, sub->callid);
04484 break;
04485 case AST_CONTROL_HOLD:
04486 ast_moh_start(ast, data, l->mohinterpret);
04487 break;
04488 case AST_CONTROL_UNHOLD:
04489 ast_moh_stop(ast);
04490 break;
04491 case AST_CONTROL_PROCEEDING:
04492 break;
04493 case AST_CONTROL_SRCUPDATE:
04494 ast_rtp_instance_update_source(sub->rtp);
04495 break;
04496 case AST_CONTROL_SRCCHANGE:
04497 ast_rtp_instance_change_source(sub->rtp);
04498 break;
04499 case AST_CONTROL_CONNECTED_LINE:
04500 update_connectedline(sub, data, datalen);
04501 break;
04502 default:
04503 ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", ind);
04504 return -1;
04505 }
04506 return 0;
04507 }
04508
04509 static struct ast_channel *skinny_new(struct skinny_line *l, int state, const char *linkedid)
04510 {
04511 struct ast_channel *tmp;
04512 struct skinny_subchannel *sub;
04513 struct skinny_device *d = l->device;
04514 struct ast_variable *v = NULL;
04515 int fmt;
04516
04517 if (!l->device) {
04518 ast_log(LOG_WARNING, "Device for line %s is not registered.\n", l->name);
04519 return NULL;
04520 }
04521
04522 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);
04523 if (!tmp) {
04524 ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
04525 return NULL;
04526 } else {
04527 sub = ast_calloc(1, sizeof(*sub));
04528 if (!sub) {
04529 ast_log(LOG_WARNING, "Unable to allocate Skinny subchannel\n");
04530 return NULL;
04531 } else {
04532 ast_mutex_init(&sub->lock);
04533
04534 sub->owner = tmp;
04535 sub->callid = callnums++;
04536 d->lastlineinstance = l->instance;
04537 d->lastcallreference = sub->callid;
04538 sub->cxmode = SKINNY_CX_INACTIVE;
04539 sub->nat = l->nat;
04540 sub->parent = l;
04541 sub->onhold = 0;
04542 sub->blindxfer = 0;
04543 sub->xferor = 0;
04544 sub->related = NULL;
04545
04546 AST_LIST_INSERT_HEAD(&l->sub, sub, list);
04547
04548 }
04549 tmp->tech = &skinny_tech;
04550 tmp->tech_pvt = sub;
04551 tmp->nativeformats = l->capability;
04552 if (!tmp->nativeformats)
04553
04554 tmp->nativeformats = default_capability;
04555 fmt = ast_best_codec(tmp->nativeformats);
04556 if (skinnydebug) {
04557 char buf[256];
04558 ast_verb(1, "skinny_new: tmp->nativeformats=%s fmt=%s\n",
04559 ast_getformatname_multiple(buf, sizeof(buf), tmp->nativeformats),
04560 ast_getformatname(fmt));
04561 }
04562 if (sub->rtp) {
04563 ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(sub->rtp, 0));
04564 }
04565 if (state == AST_STATE_RING) {
04566 tmp->rings = 1;
04567 }
04568 tmp->writeformat = fmt;
04569 tmp->rawwriteformat = fmt;
04570 tmp->readformat = fmt;
04571 tmp->rawreadformat = fmt;
04572 if (!ast_strlen_zero(l->language))
04573 ast_string_field_set(tmp, language, l->language);
04574 if (!ast_strlen_zero(l->accountcode))
04575 ast_string_field_set(tmp, accountcode, l->accountcode);
04576 if (!ast_strlen_zero(l->parkinglot))
04577 ast_string_field_set(tmp, parkinglot, l->parkinglot);
04578 if (l->amaflags)
04579 tmp->amaflags = l->amaflags;
04580
04581 ast_module_ref(ast_module_info->self);
04582 tmp->callgroup = l->callgroup;
04583 tmp->pickupgroup = l->pickupgroup;
04584
04585
04586 if (l->cfwdtype & SKINNY_CFWD_ALL) {
04587 ast_string_field_set(tmp, call_forward, l->call_forward_all);
04588 } else if (l->cfwdtype & SKINNY_CFWD_BUSY) {
04589 if (get_devicestate(l) != AST_DEVICE_NOT_INUSE) {
04590 ast_string_field_set(tmp, call_forward, l->call_forward_busy);
04591 }
04592 }
04593
04594 ast_copy_string(tmp->context, l->context, sizeof(tmp->context));
04595 ast_copy_string(tmp->exten, l->exten, sizeof(tmp->exten));
04596
04597
04598
04599 if (!ast_strlen_zero(l->cid_num)) {
04600 tmp->caller.ani.number.valid = 1;
04601 tmp->caller.ani.number.str = ast_strdup(l->cid_num);
04602 }
04603
04604 tmp->priority = 1;
04605 tmp->adsicpe = AST_ADSI_UNAVAILABLE;
04606
04607 if (sub->rtp)
04608 ast_jb_configure(tmp, &global_jbconf);
04609
04610
04611 for (v = l->chanvars ; v ; v = v->next)
04612 pbx_builtin_setvar_helper(tmp, v->name, v->value);
04613
04614 if (state != AST_STATE_DOWN) {
04615 if (ast_pbx_start(tmp)) {
04616 ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
04617 ast_hangup(tmp);
04618 tmp = NULL;
04619 }
04620 }
04621 }
04622 return tmp;
04623 }
04624
04625 static int skinny_hold(struct skinny_subchannel *sub)
04626 {
04627 struct skinny_line *l = sub->parent;
04628 struct skinny_device *d = l->device;
04629
04630
04631 if (!sub || !sub->owner) {
04632 return 0;
04633 }
04634 if (!d) {
04635 ast_log(LOG_WARNING, "Device for line %s is not registered.\n", l->name);
04636 return 0;
04637 }
04638
04639
04640 if (skinnydebug)
04641 ast_verb(1, "Putting on Hold(%d)\n", l->instance);
04642
04643 ast_queue_control_data(sub->owner, AST_CONTROL_HOLD,
04644 S_OR(l->mohsuggest, NULL),
04645 !ast_strlen_zero(l->mohsuggest) ? strlen(l->mohsuggest) + 1 : 0);
04646
04647 transmit_activatecallplane(d, l);
04648 transmit_closereceivechannel(d, sub);
04649 transmit_stopmediatransmission(d, sub);
04650
04651 transmit_callstate(d, sub->parent->instance, sub->callid, SKINNY_HOLD);
04652 transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_WINK);
04653 sub->onhold = 1;
04654 return 1;
04655 }
04656
04657 static int skinny_unhold(struct skinny_subchannel *sub)
04658 {
04659 struct skinny_line *l = sub->parent;
04660 struct skinny_device *d = l->device;
04661
04662
04663 if (!sub || !sub->owner) {
04664 return 0;
04665 }
04666 if (!d) {
04667 ast_log(LOG_WARNING, "Device for line %s is not registered.\n", l->name);
04668 return 0;
04669 }
04670
04671
04672 if (skinnydebug)
04673 ast_verb(1, "Taking off Hold(%d)\n", l->instance);
04674
04675 ast_queue_control(sub->owner, AST_CONTROL_UNHOLD);
04676
04677 transmit_activatecallplane(d, l);
04678
04679 transmit_connect(d, sub);
04680 transmit_callstate(d, sub->parent->instance, sub->callid, SKINNY_CONNECTED);
04681 transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
04682 l->hookstate = SKINNY_OFFHOOK;
04683 sub->onhold = 0;
04684 return 1;
04685 }
04686
04687 static int handle_hold_button(struct skinny_subchannel *sub)
04688 {
04689 if (!sub)
04690 return -1;
04691 if (sub->related) {
04692 skinny_hold(sub);
04693 skinny_unhold(sub->related);
04694 sub->parent->activesub = sub->related;
04695 } else {
04696 if (sub->onhold) {
04697 skinny_unhold(sub);
04698 transmit_selectsoftkeys(sub->parent->device, sub->parent->instance, sub->callid, KEYDEF_CONNECTED);
04699 } else {
04700 skinny_hold(sub);
04701 transmit_selectsoftkeys(sub->parent->device, sub->parent->instance, sub->callid, KEYDEF_ONHOLD);
04702 }
04703 }
04704 return 1;
04705 }
04706
04707 static int handle_transfer_button(struct skinny_subchannel *sub)
04708 {
04709 struct skinny_line *l;
04710 struct skinny_device *d;
04711 struct skinny_subchannel *newsub;
04712 struct ast_channel *c;
04713 pthread_t t;
04714
04715 if (!sub) {
04716 ast_verbose("Transfer: No subchannel to transfer\n");
04717 return -1;
04718 }
04719
04720 l = sub->parent;
04721 d = l->device;
04722
04723 if (!d) {
04724 ast_log(LOG_WARNING, "Device for line %s is not registered.\n", l->name);
04725 return -1;
04726 }
04727
04728 if (!sub->related) {
04729
04730 if (!sub->onhold) {
04731 skinny_hold(sub);
04732 }
04733 c = skinny_new(l, AST_STATE_DOWN, NULL);
04734 if (c) {
04735 newsub = c->tech_pvt;
04736
04737 newsub->related = sub;
04738 sub->related = newsub;
04739 newsub->xferor = 1;
04740 l->activesub = newsub;
04741 transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
04742 transmit_activatecallplane(d, l);
04743 transmit_clear_display_message(d, l->instance, newsub->callid);
04744 transmit_start_tone(d, SKINNY_DIALTONE, l->instance, newsub->callid);
04745 transmit_selectsoftkeys(d, l->instance, newsub->callid, KEYDEF_OFFHOOKWITHFEAT);
04746
04747 if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
04748 ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
04749 ast_hangup(c);
04750 }
04751 } else {
04752 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
04753 }
04754 } else {
04755
04756 if (sub->blindxfer) {
04757
04758 sub->blindxfer = 0;
04759 sub->related->blindxfer = 0;
04760
04761 } else {
04762
04763 if (sub->owner->_state == AST_STATE_DOWN || sub->related->owner->_state == AST_STATE_DOWN) {
04764
04765 sub->blindxfer = 1;
04766 sub->related->blindxfer = 1;
04767 } else {
04768
04769 skinny_transfer(sub);
04770 }
04771 }
04772 }
04773 return 0;
04774 }
04775
04776 static int handle_keep_alive_message(struct skinny_req *req, struct skinnysession *s)
04777 {
04778 if (!(req = req_alloc(0, KEEP_ALIVE_ACK_MESSAGE)))
04779 return -1;
04780
04781 transmit_response(s->device, req);
04782 return 1;
04783 }
04784
04785 static int handle_register_message(struct skinny_req *req, struct skinnysession *s)
04786 {
04787 struct skinny_device *d = NULL;
04788 char name[16];
04789 int res;
04790
04791 memcpy(&name, req->data.reg.name, sizeof(name));
04792
04793 res = skinny_register(req, s);
04794 if (!res) {
04795 ast_log(LOG_ERROR, "Rejecting Device %s: Device not found\n", name);
04796 if (!(req = req_alloc(sizeof(struct register_rej_message), REGISTER_REJ_MESSAGE)))
04797 return -1;
04798
04799 snprintf(req->data.regrej.errMsg, sizeof(req->data.regrej.errMsg), "No Authority: %s", name);
04800
04801
04802 ast_mutex_lock(&s->lock);
04803
04804 if (letohl(req->len) > SKINNY_MAX_PACKET || letohl(req->len) < 0) {
04805 ast_log(LOG_WARNING, "transmit_response: the length (%d) of the request is out of bounds (%d) \n", letohl(req->len), SKINNY_MAX_PACKET);
04806 ast_mutex_unlock(&s->lock);
04807 return -1;
04808 }
04809
04810 memset(s->outbuf, 0, sizeof(s->outbuf));
04811 memcpy(s->outbuf, req, skinny_header_size);
04812 memcpy(s->outbuf+skinny_header_size, &req->data, letohl(req->len));
04813
04814 res = write(s->fd, s->outbuf, letohl(req->len)+8);
04815
04816 if (res != letohl(req->len)+8) {
04817 ast_log(LOG_WARNING, "Transmit: write only sent %d out of %d bytes: %s\n", res, letohl(req->len)+8, strerror(errno));
04818 }
04819
04820 ast_mutex_unlock(&s->lock);
04821
04822 return 0;
04823 }
04824 ast_atomic_fetchadd_int(&unauth_sessions, -1);
04825
04826 ast_verb(3, "Device '%s' successfully registered\n", name);
04827
04828 d = s->device;
04829
04830 if (!(req = req_alloc(sizeof(struct register_ack_message), REGISTER_ACK_MESSAGE)))
04831 return -1;
04832
04833 req->data.regack.res[0] = '0';
04834 req->data.regack.res[1] = '\0';
04835 req->data.regack.keepAlive = htolel(keep_alive);
04836 memcpy(req->data.regack.dateTemplate, date_format, sizeof(req->data.regack.dateTemplate));
04837 req->data.regack.res2[0] = '0';
04838 req->data.regack.res2[1] = '\0';
04839 req->data.regack.secondaryKeepAlive = htolel(keep_alive);
04840 transmit_response(d, req);
04841 if (skinnydebug)
04842 ast_verb(1, "Requesting capabilities\n");
04843
04844 if (!(req = req_alloc(0, CAPABILITIES_REQ_MESSAGE)))
04845 return -1;
04846
04847 transmit_response(d, req);
04848
04849 return res;
04850 }
04851
04852 static int handle_callforward_button(struct skinny_subchannel *sub, int cfwdtype)
04853 {
04854 struct skinny_line *l = sub->parent;
04855 struct skinny_device *d = l->device;
04856 struct ast_channel *c = sub->owner;
04857 pthread_t t;
04858
04859 if (!d) {
04860 ast_log(LOG_WARNING, "Device for line %s is not registered.\n", l->name);
04861 return 0;
04862 }
04863
04864 if (l->hookstate == SKINNY_ONHOOK) {
04865 l->hookstate = SKINNY_OFFHOOK;
04866 transmit_speaker_mode(d, SKINNY_SPEAKERON);
04867 transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
04868 transmit_activatecallplane(d, l);
04869 }
04870 transmit_clear_display_message(d, l->instance, sub->callid);
04871
04872 if (l->cfwdtype & cfwdtype) {
04873 set_callforwards(l, NULL, cfwdtype);
04874 ast_safe_sleep(c, 500);
04875 transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
04876 transmit_closereceivechannel(d, sub);
04877 transmit_stopmediatransmission(d, sub);
04878 transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
04879 transmit_clearpromptmessage(d, l->instance, sub->callid);
04880 transmit_callstate(d, l->instance, sub->callid, SKINNY_ONHOOK);
04881 transmit_selectsoftkeys(d, 0, 0, KEYDEF_ONHOOK);
04882 transmit_activatecallplane(d, l);
04883 transmit_displaynotify(d, "CFwd disabled", 10);
04884 if (sub->owner && sub->owner->_state != AST_STATE_UP) {
04885 ast_indicate(c, -1);
04886 ast_hangup(c);
04887 }
04888 transmit_cfwdstate(d, l);
04889 } else {
04890 l->getforward = cfwdtype;
04891 transmit_start_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
04892 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
04893 if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
04894 ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
04895 ast_hangup(c);
04896 }
04897 }
04898 return 0;
04899 }
04900 static int handle_ip_port_message(struct skinny_req *req, struct skinnysession *s)
04901 {
04902
04903 return 1;
04904 }
04905
04906 static int handle_keypad_button_message(struct skinny_req *req, struct skinnysession *s)
04907 {
04908 struct skinny_subchannel *sub = NULL;
04909 struct skinny_line *l;
04910 struct skinny_device *d = s->device;
04911 struct ast_frame f = { 0, };
04912 char dgt;
04913 int digit;
04914 int lineInstance;
04915 int callReference;
04916
04917 digit = letohl(req->data.keypad.button);
04918 lineInstance = letohl(req->data.keypad.lineInstance);
04919 callReference = letohl(req->data.keypad.callReference);
04920
04921 if (digit == 14) {
04922 dgt = '*';
04923 } else if (digit == 15) {
04924 dgt = '#';
04925 } else if (digit >= 0 && digit <= 9) {
04926 dgt = '0' + digit;
04927 } else {
04928
04929
04930
04931
04932
04933
04934
04935 dgt = '0' + digit;
04936 ast_log(LOG_WARNING, "Unsupported digit %d\n", digit);
04937 }
04938
04939 f.subclass.integer = dgt;
04940
04941 f.src = "skinny";
04942
04943 if (lineInstance && callReference)
04944 sub = find_subchannel_by_instance_reference(d, lineInstance, callReference);
04945 else
04946 sub = d->activeline->activesub;
04947
04948
04949 if (!sub)
04950 return 0;
04951
04952 l = sub->parent;
04953 if (sub->owner) {
04954 if (sub->owner->_state == 0) {
04955 f.frametype = AST_FRAME_DTMF_BEGIN;
04956 ast_queue_frame(sub->owner, &f);
04957 }
04958
04959 f.frametype = AST_FRAME_DTMF_END;
04960 ast_queue_frame(sub->owner, &f);
04961
04962 if (AST_LIST_NEXT(sub, list) && AST_LIST_NEXT(sub, list)->owner) {
04963 if (sub->owner->_state == 0) {
04964 f.frametype = AST_FRAME_DTMF_BEGIN;
04965 ast_queue_frame(AST_LIST_NEXT(sub, list)->owner, &f);
04966 }
04967 f.frametype = AST_FRAME_DTMF_END;
04968 ast_queue_frame(AST_LIST_NEXT(sub, list)->owner, &f);
04969 }
04970 } else {
04971 if (skinnydebug)
04972 ast_verb(1, "No owner: %s\n", l->name);
04973 }
04974 return 1;
04975 }
04976
04977 static int handle_stimulus_message(struct skinny_req *req, struct skinnysession *s)
04978 {
04979 struct skinny_device *d = s->device;
04980 struct skinny_line *l;
04981 struct skinny_subchannel *sub;
04982
04983 struct ast_channel *c;
04984 pthread_t t;
04985 int event;
04986 int instance;
04987 int callreference;
04988
04989
04990 event = letohl(req->data.stimulus.stimulus);
04991 instance = letohl(req->data.stimulus.stimulusInstance);
04992 callreference = letohl(req->data.stimulus.callreference);
04993 if (skinnydebug)
04994 ast_verb(1, "callreference in handle_stimulus_message is '%d'\n", callreference);
04995
04996
04997 sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
04998
04999 if (!sub) {
05000 l = find_line_by_instance(d, d->lastlineinstance);
05001 if (!l) {
05002 return 0;
05003 }
05004 sub = l->activesub;
05005 } else {
05006 l = sub->parent;
05007 }
05008
05009 switch(event) {
05010 case STIMULUS_REDIAL:
05011 if (skinnydebug)
05012 ast_verb(1, "Received Stimulus: Redial(%d/%d)\n", instance, callreference);
05013
05014 if (ast_strlen_zero(l->lastnumberdialed)) {
05015 ast_log(LOG_WARNING, "Attempted redial, but no previously dialed number found.\n");
05016 l->hookstate = SKINNY_ONHOOK;
05017 transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
05018 transmit_closereceivechannel(d, sub);
05019 transmit_stopmediatransmission(d, sub);
05020 transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
05021 transmit_clearpromptmessage(d, l->instance, sub->callid);
05022 transmit_callstate(d, l->instance, sub->callid, SKINNY_ONHOOK);
05023 transmit_selectsoftkeys(d, 0, 0, KEYDEF_ONHOOK);
05024 transmit_activatecallplane(d, l);
05025 break;
05026 }
05027
05028 c = skinny_new(l, AST_STATE_DOWN, NULL);
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_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
05038 transmit_activatecallplane(d, l);
05039 }
05040 transmit_clear_display_message(d, l->instance, sub->callid);
05041 transmit_start_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05042 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
05043
05044 if (!ast_ignore_pattern(c->context, l->lastnumberdialed)) {
05045 transmit_stop_tone(d, l->instance, sub->callid);
05046 }
05047 ast_copy_string(c->exten, l->lastnumberdialed, sizeof(c->exten));
05048 if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
05049 ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
05050 ast_hangup(c);
05051 }
05052 }
05053 break;
05054 case STIMULUS_SPEEDDIAL:
05055 {
05056 struct skinny_speeddial *sd;
05057
05058 if (skinnydebug)
05059 ast_verb(1, "Received Stimulus: SpeedDial(%d/%d)\n", instance, callreference);
05060 if (!(sd = find_speeddial_by_instance(d, instance, 0))) {
05061 return 0;
05062 }
05063
05064 if (!sub || !sub->owner)
05065 c = skinny_new(l, AST_STATE_DOWN, NULL);
05066 else
05067 c = sub->owner;
05068
05069 if (!c) {
05070 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05071 } else {
05072 sub = c->tech_pvt;
05073 l = sub->parent;
05074 l->activesub = sub;
05075 if (l->hookstate == SKINNY_ONHOOK) {
05076 l->hookstate = SKINNY_OFFHOOK;
05077 transmit_speaker_mode(d, SKINNY_SPEAKERON);
05078 transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
05079 transmit_activatecallplane(d, l);
05080 }
05081 transmit_clear_display_message(d, l->instance, sub->callid);
05082 transmit_start_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05083 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
05084
05085 if (!ast_ignore_pattern(c->context, sd->exten)) {
05086 transmit_stop_tone(d, l->instance, sub->callid);
05087 }
05088 if (ast_exists_extension(c, c->context, sd->exten, 1, l->cid_num)) {
05089 ast_copy_string(c->exten, sd->exten, sizeof(c->exten));
05090 ast_copy_string(l->lastnumberdialed, sd->exten, sizeof(l->lastnumberdialed));
05091
05092 if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
05093 ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
05094 ast_hangup(c);
05095 }
05096 break;
05097 }
05098 }
05099 }
05100 break;
05101 case STIMULUS_HOLD:
05102 if (skinnydebug)
05103 ast_verb(1, "Received Stimulus: Hold(%d/%d)\n", instance, callreference);
05104 handle_hold_button(sub);
05105 break;
05106 case STIMULUS_TRANSFER:
05107 if (skinnydebug)
05108 ast_verb(1, "Received Stimulus: Transfer(%d/%d)\n", instance, callreference);
05109 if (l->transfer)
05110 handle_transfer_button(sub);
05111 else
05112 transmit_displaynotify(d, "Transfer disabled", 10);
05113 break;
05114 case STIMULUS_CONFERENCE:
05115 if (skinnydebug)
05116 ast_verb(1, "Received Stimulus: Conference(%d/%d)\n", instance, callreference);
05117
05118 break;
05119 case STIMULUS_VOICEMAIL:
05120 if (skinnydebug)
05121 ast_verb(1, "Received Stimulus: Voicemail(%d/%d)\n", instance, callreference);
05122
05123 if (!sub || !sub->owner) {
05124 c = skinny_new(l, AST_STATE_DOWN, NULL);
05125 } else {
05126 c = sub->owner;
05127 }
05128 if (!c) {
05129 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05130 } else {
05131 sub = c->tech_pvt;
05132 l = sub->parent;
05133 l->activesub = sub;
05134
05135 if (ast_strlen_zero(l->vmexten))
05136 break;
05137
05138 if (l->hookstate == SKINNY_ONHOOK){
05139 l->hookstate = SKINNY_OFFHOOK;
05140 transmit_speaker_mode(d, SKINNY_SPEAKERON);
05141 transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
05142 transmit_activatecallplane(d, l);
05143 }
05144
05145 transmit_clear_display_message(d, l->instance, sub->callid);
05146 transmit_start_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05147 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
05148
05149 if (!ast_ignore_pattern(c->context, l->vmexten)) {
05150 transmit_stop_tone(d, l->instance, sub->callid);
05151 }
05152
05153 if (ast_exists_extension(c, c->context, l->vmexten, 1, l->cid_num)) {
05154 ast_copy_string(c->exten, l->vmexten, sizeof(c->exten));
05155 ast_copy_string(l->lastnumberdialed, l->vmexten, sizeof(l->lastnumberdialed));
05156 if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
05157 ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
05158 ast_hangup(c);
05159 }
05160 break;
05161 }
05162 }
05163 break;
05164 case STIMULUS_CALLPARK:
05165 {
05166 int extout;
05167 char message[32];
05168
05169 if (skinnydebug)
05170 ast_verb(1, "Received Stimulus: Park Call(%d/%d)\n", instance, callreference);
05171
05172 if ((sub && sub->owner) && (sub->owner->_state == AST_STATE_UP)){
05173 c = sub->owner;
05174 if (ast_bridged_channel(c)) {
05175 if (!ast_masq_park_call(ast_bridged_channel(c), c, 0, &extout)) {
05176 snprintf(message, sizeof(message), "Call Parked at: %d", extout);
05177 transmit_displaynotify(d, message, 10);
05178 } else {
05179 transmit_displaynotify(d, "Call Park failed", 10);
05180 }
05181 } else {
05182 transmit_displaynotify(d, "Call Park not available", 10);
05183 }
05184 } else {
05185 transmit_displaynotify(d, "Call Park not available", 10);
05186 }
05187 break;
05188 }
05189 case STIMULUS_DND:
05190 if (skinnydebug)
05191 ast_verb(1, "Received Stimulus: DND (%d/%d)\n", instance, callreference);
05192
05193
05194 if (l->dnd != 0){
05195 ast_verb(3, "Disabling DND on %s@%s\n", l->name, d->name);
05196 l->dnd = 0;
05197 transmit_lamp_indication(d, STIMULUS_DND, 1, SKINNY_LAMP_ON);
05198 transmit_displaynotify(d, "DnD disabled", 10);
05199 } else {
05200 ast_verb(3, "Enabling DND on %s@%s\n", l->name, d->name);
05201 l->dnd = 1;
05202 transmit_lamp_indication(d, STIMULUS_DND, 1, SKINNY_LAMP_OFF);
05203 transmit_displaynotify(d, "DnD enabled", 10);
05204 }
05205 break;
05206 case STIMULUS_FORWARDALL:
05207 if (skinnydebug)
05208 ast_verb(1, "Received Stimulus: Forward All(%d/%d)\n", instance, callreference);
05209
05210 if (!sub || !sub->owner) {
05211 c = skinny_new(l, AST_STATE_DOWN, NULL);
05212 } else {
05213 c = sub->owner;
05214 }
05215
05216 if (!c) {
05217 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05218 } else {
05219 sub = c->tech_pvt;
05220 handle_callforward_button(sub, SKINNY_CFWD_ALL);
05221 }
05222 break;
05223 case STIMULUS_FORWARDBUSY:
05224 if (skinnydebug)
05225 ast_verb(1, "Received Stimulus: Forward Busy (%d/%d)\n", instance, callreference);
05226
05227 if (!sub || !sub->owner) {
05228 c = skinny_new(l, AST_STATE_DOWN, NULL);
05229 } else {
05230 c = sub->owner;
05231 }
05232
05233 if (!c) {
05234 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05235 } else {
05236 sub = c->tech_pvt;
05237 handle_callforward_button(sub, SKINNY_CFWD_BUSY);
05238 }
05239 break;
05240 case STIMULUS_FORWARDNOANSWER:
05241 if (skinnydebug)
05242 ast_verb(1, "Received Stimulus: Forward No Answer (%d/%d)\n", instance, callreference);
05243
05244 #if 0
05245 if (!sub || !sub->owner) {
05246 c = skinny_new(l, AST_STATE_DOWN, NULL);
05247 } else {
05248 c = sub->owner;
05249 }
05250
05251 if (!c) {
05252 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05253 } else {
05254 sub = c->tech_pvt;
05255 handle_callforward_button(sub, SKINNY_CFWD_NOANSWER);
05256 }
05257 #endif
05258 break;
05259 case STIMULUS_DISPLAY:
05260
05261 if (skinnydebug)
05262 ast_verb(1, "Received Stimulus: Display(%d/%d)\n", instance, callreference);
05263 break;
05264 case STIMULUS_LINE:
05265 if (skinnydebug)
05266 ast_verb(1, "Received Stimulus: Line(%d/%d)\n", instance, callreference);
05267
05268 l = find_line_by_instance(d, instance);
05269
05270 if (!l) {
05271 return 0;
05272 }
05273
05274 d->activeline = l;
05275
05276
05277 transmit_speaker_mode(d, SKINNY_SPEAKERON);
05278 transmit_ringer_mode(d, SKINNY_RING_OFF);
05279 transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
05280
05281 l->hookstate = SKINNY_OFFHOOK;
05282
05283 if (sub && sub->outgoing) {
05284
05285 ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
05286 transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
05287 transmit_activatecallplane(d, l);
05288 transmit_stop_tone(d, l->instance, sub->callid);
05289 transmit_callstate(d, sub->parent->instance, sub->callid, SKINNY_CONNECTED);
05290 transmit_displaypromptstatus(d, "Connected", 0, l->instance, sub->callid);
05291 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
05292 start_rtp(sub);
05293 ast_setstate(sub->owner, AST_STATE_UP);
05294 } else {
05295 if (sub && sub->owner) {
05296 ast_debug(1, "Current subchannel [%s] already has owner\n", sub->owner->name);
05297 } else {
05298 c = skinny_new(l, AST_STATE_DOWN, NULL);
05299 if (c) {
05300 sub = c->tech_pvt;
05301 l->activesub = sub;
05302 transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
05303 transmit_activatecallplane(d, l);
05304 transmit_clear_display_message(d, l->instance, sub->callid);
05305 transmit_start_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05306 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_OFFHOOK);
05307
05308
05309 if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
05310 ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
05311 ast_hangup(c);
05312 }
05313 } else {
05314 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05315 }
05316 }
05317 }
05318 break;
05319 default:
05320 if (skinnydebug)
05321 ast_verb(1, "RECEIVED UNKNOWN STIMULUS: %d(%d/%d)\n", event, instance, callreference);
05322 break;
05323 }
05324 ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "Skinny/%s@%s", l->name, d->name);
05325
05326 return 1;
05327 }
05328
05329 static int handle_offhook_message(struct skinny_req *req, struct skinnysession *s)
05330 {
05331 struct skinny_device *d = s->device;
05332 struct skinny_line *l;
05333 struct skinny_subchannel *sub;
05334 struct ast_channel *c;
05335 struct skinny_line *tmp;
05336 pthread_t t;
05337 int instance;
05338
05339
05340
05341
05342
05343
05344
05345 AST_LIST_TRAVERSE(&d->lines, tmp, list) {
05346 if (tmp->hookstate == SKINNY_OFFHOOK) {
05347 ast_verbose(VERBOSE_PREFIX_3 "Got offhook message when device (%s@%s) already offhook\n", tmp->name, d->name);
05348 return 0;
05349 }
05350 }
05351
05352 instance = letohl(req->data.offhook.instance);
05353
05354 if (instance) {
05355 sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
05356 if (!sub) {
05357 l = find_line_by_instance(d, d->lastlineinstance);
05358 if (!l) {
05359 return 0;
05360 }
05361 } else {
05362 l = sub->parent;
05363 }
05364 } else {
05365 l = d->activeline;
05366 sub = l->activesub;
05367 }
05368
05369
05370 transmit_definetimedate(d);
05371
05372 transmit_ringer_mode(d, SKINNY_RING_OFF);
05373 l->hookstate = SKINNY_OFFHOOK;
05374
05375 ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "Skinny/%s@%s", l->name, d->name);
05376
05377 if (sub && sub->onhold) {
05378 return 1;
05379 }
05380
05381 transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
05382
05383 if (sub && sub->outgoing) {
05384
05385 ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
05386 transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
05387 transmit_activatecallplane(d, l);
05388 transmit_stop_tone(d, l->instance, sub->callid);
05389 transmit_callstate(d, sub->parent->instance, sub->callid, SKINNY_CONNECTED);
05390 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
05391 start_rtp(sub);
05392 ast_setstate(sub->owner, AST_STATE_UP);
05393 } else {
05394 if (sub && sub->owner) {
05395 ast_debug(1, "Current sub [%s] already has owner\n", sub->owner->name);
05396 } else {
05397 c = skinny_new(l, AST_STATE_DOWN, NULL);
05398 if (c) {
05399 sub = c->tech_pvt;
05400 l->activesub = sub;
05401 transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
05402 transmit_activatecallplane(d, l);
05403 transmit_clear_display_message(d, l->instance, sub->callid);
05404 transmit_start_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05405 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_OFFHOOK);
05406
05407
05408 if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
05409 ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
05410 ast_hangup(c);
05411 }
05412 } else {
05413 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05414 }
05415 }
05416 }
05417 return 1;
05418 }
05419
05420 static int handle_onhook_message(struct skinny_req *req, struct skinnysession *s)
05421 {
05422 struct skinny_device *d = s->device;
05423 struct skinny_line *l;
05424 struct skinny_subchannel *sub;
05425 int instance;
05426 int reference;
05427 int onlysub = 0;
05428
05429 instance = letohl(req->data.onhook.instance);
05430 reference = letohl(req->data.onhook.reference);
05431
05432 if (instance && reference) {
05433 sub = find_subchannel_by_instance_reference(d, instance, reference);
05434 if (!sub) {
05435 return 0;
05436 }
05437 l = sub->parent;
05438 } else {
05439 l = d->activeline;
05440 sub = l->activesub;
05441 if (!sub) {
05442 return 0;
05443 }
05444 }
05445
05446 if (l->hookstate == SKINNY_ONHOOK) {
05447
05448 return 0;
05449 }
05450
05451 ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Skinny/%s@%s", l->name, d->name);
05452
05453 if (sub->onhold) {
05454 return 0;
05455 }
05456
05457 if (!AST_LIST_NEXT(sub, list)) {
05458 onlysub = 1;
05459 } else {
05460 AST_LIST_REMOVE(&l->sub, sub, list);
05461 }
05462
05463 sub->cxmode = SKINNY_CX_RECVONLY;
05464 if (onlysub || sub->xferor){
05465 l->hookstate = SKINNY_ONHOOK;
05466 if (skinnydebug)
05467 ast_debug(1, "Skinny %s@%s-%d went on hook\n", l->name, d->name, reference);
05468 }
05469
05470 if (l->hookstate == SKINNY_ONHOOK) {
05471 transmit_closereceivechannel(d, sub);
05472 transmit_stopmediatransmission(d, sub);
05473 transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
05474 transmit_clearpromptmessage(d, instance, sub->callid);
05475 transmit_callstate(d, l->instance, sub->callid, SKINNY_ONHOOK);
05476 transmit_selectsoftkeys(d, 0, 0, KEYDEF_ONHOOK);
05477 transmit_activatecallplane(d, l);
05478 } else if (l->hookstate == SKINNY_OFFHOOK) {
05479 transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
05480 transmit_activatecallplane(d, l);
05481 } else {
05482 transmit_callstate(d, l->instance, sub->callid, l->hookstate);
05483 }
05484
05485 if (l->transfer && sub->xferor && sub->owner->_state >= AST_STATE_RING) {
05486
05487
05488 handle_transfer_button(sub);
05489 } else {
05490
05491
05492 if (sub->xferor && sub->related){
05493 sub->related->related = NULL;
05494 sub->related->blindxfer = 0;
05495 }
05496
05497 if (sub->owner) {
05498 sub->alreadygone = 1;
05499 ast_queue_hangup(sub->owner);
05500 } else {
05501 ast_log(LOG_WARNING, "Skinny(%s@%s-%u) channel already destroyed\n",
05502 l->name, d->name, sub->callid);
05503 }
05504
05505 transmit_definetimedate(d);
05506 }
05507 return 1;
05508 }
05509
05510 static int handle_capabilities_res_message(struct skinny_req *req, struct skinnysession *s)
05511 {
05512 struct skinny_device *d = s->device;
05513 struct skinny_line *l;
05514 uint32_t count = 0;
05515 format_t codecs = 0;
05516 int i;
05517 char buf[256];
05518
05519 count = letohl(req->data.caps.count);
05520 if (count > SKINNY_MAX_CAPABILITIES) {
05521 count = SKINNY_MAX_CAPABILITIES;
05522 ast_log(LOG_WARNING, "Received more capabilities than we can handle (%d). Ignoring the rest.\n", SKINNY_MAX_CAPABILITIES);
05523 }
05524
05525 for (i = 0; i < count; i++) {
05526 format_t acodec = 0;
05527 int scodec = 0;
05528 scodec = letohl(req->data.caps.caps[i].codec);
05529 acodec = codec_skinny2ast(scodec);
05530 if (skinnydebug)
05531 ast_verb(1, "Adding codec capability '%" PRId64 " (%d)'\n", acodec, scodec);
05532 codecs |= acodec;
05533 }
05534
05535 d->capability = d->confcapability & codecs;
05536 ast_verb(0, "Device capability set to '%s'\n", ast_getformatname_multiple(buf, sizeof(buf), d->capability));
05537 AST_LIST_TRAVERSE(&d->lines, l, list) {
05538 ast_mutex_lock(&l->lock);
05539 l->capability = l->confcapability & d->capability;
05540 ast_mutex_unlock(&l->lock);
05541 }
05542
05543 return 1;
05544 }
05545
05546 static int handle_button_template_req_message(struct skinny_req *req, struct skinnysession *s)
05547 {
05548 struct skinny_device *d = s->device;
05549 struct skinny_line *l;
05550 int i;
05551
05552 struct skinny_speeddial *sd;
05553 struct button_definition_template btn[42];
05554 int lineInstance = 1;
05555 int speeddialInstance = 1;
05556 int buttonCount = 0;
05557
05558 if (!(req = req_alloc(sizeof(struct button_template_res_message), BUTTON_TEMPLATE_RES_MESSAGE)))
05559 return -1;
05560
05561 memset(&btn, 0, sizeof(btn));
05562
05563 get_button_template(s, btn);
05564
05565 for (i=0; i<42; i++) {
05566 int btnSet = 0;
05567 switch (btn[i].buttonDefinition) {
05568 case BT_CUST_LINE:
05569
05570 req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE;
05571 req->data.buttontemplate.definition[i].instanceNumber = 0;
05572
05573 AST_LIST_TRAVERSE(&d->lines, l, list) {
05574 if (l->instance == lineInstance) {
05575 ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
05576 req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
05577 req->data.buttontemplate.definition[i].instanceNumber = lineInstance;
05578 lineInstance++;
05579 buttonCount++;
05580 btnSet = 1;
05581 break;
05582 }
05583 }
05584
05585 if (!btnSet) {
05586 AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
05587 if (sd->isHint && sd->instance == lineInstance) {
05588 ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
05589 req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
05590 req->data.buttontemplate.definition[i].instanceNumber = lineInstance;
05591 lineInstance++;
05592 buttonCount++;
05593 btnSet = 1;
05594 break;
05595 }
05596 }
05597 }
05598 break;
05599 case BT_CUST_LINESPEEDDIAL:
05600
05601 req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE;
05602 req->data.buttontemplate.definition[i].instanceNumber = 0;
05603
05604 AST_LIST_TRAVERSE(&d->lines, l, list) {
05605 if (l->instance == lineInstance) {
05606 ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
05607 req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
05608 req->data.buttontemplate.definition[i].instanceNumber = lineInstance;
05609 lineInstance++;
05610 buttonCount++;
05611 btnSet = 1;
05612 break;
05613 }
05614 }
05615
05616 if (!btnSet) {
05617 AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
05618 if (sd->isHint && sd->instance == lineInstance) {
05619 ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
05620 req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
05621 req->data.buttontemplate.definition[i].instanceNumber = lineInstance;
05622 lineInstance++;
05623 buttonCount++;
05624 btnSet = 1;
05625 break;
05626 } else if (!sd->isHint && sd->instance == speeddialInstance) {
05627 ast_verb(0, "Adding button: %d, %d\n", BT_SPEEDDIAL, speeddialInstance);
05628 req->data.buttontemplate.definition[i].buttonDefinition = BT_SPEEDDIAL;
05629 req->data.buttontemplate.definition[i].instanceNumber = speeddialInstance;
05630 speeddialInstance++;
05631 buttonCount++;
05632 btnSet = 1;
05633 break;
05634 }
05635 }
05636 }
05637 break;
05638 case BT_LINE:
05639 req->data.buttontemplate.definition[i].buttonDefinition = htolel(BT_NONE);
05640 req->data.buttontemplate.definition[i].instanceNumber = htolel(0);
05641
05642 AST_LIST_TRAVERSE(&d->lines, l, list) {
05643 if (l->instance == lineInstance) {
05644 ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
05645 req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
05646 req->data.buttontemplate.definition[i].instanceNumber = lineInstance;
05647 lineInstance++;
05648 buttonCount++;
05649 btnSet = 1;
05650 break;
05651 }
05652 }
05653 break;
05654 case BT_SPEEDDIAL:
05655 req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE;
05656 req->data.buttontemplate.definition[i].instanceNumber = 0;
05657
05658 AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
05659 if (!sd->isHint && sd->instance == speeddialInstance) {
05660 ast_verb(0, "Adding button: %d, %d\n", BT_SPEEDDIAL, speeddialInstance);
05661 req->data.buttontemplate.definition[i].buttonDefinition = BT_SPEEDDIAL;
05662 req->data.buttontemplate.definition[i].instanceNumber = speeddialInstance - 1;
05663 speeddialInstance++;
05664 buttonCount++;
05665 btnSet = 1;
05666 break;
05667 }
05668 }
05669 break;
05670 case BT_NONE:
05671 break;
05672 default:
05673 ast_verb(0, "Adding button: %d, %d\n", btn[i].buttonDefinition, 0);
05674 req->data.buttontemplate.definition[i].buttonDefinition = htolel(btn[i].buttonDefinition);
05675 req->data.buttontemplate.definition[i].instanceNumber = 0;
05676 buttonCount++;
05677 btnSet = 1;
05678 break;
05679 }
05680 }
05681
05682 req->data.buttontemplate.buttonOffset = 0;
05683 req->data.buttontemplate.buttonCount = htolel(buttonCount);
05684 req->data.buttontemplate.totalButtonCount = htolel(buttonCount);
05685
05686 if (skinnydebug)
05687 ast_verb(1, "Sending %d template to %s\n",
05688 d->type,
05689 d->name);
05690 transmit_response(d, req);
05691 return 1;
05692 }
05693
05694 static int handle_open_receive_channel_ack_message(struct skinny_req *req, struct skinnysession *s)
05695 {
05696 struct skinny_device *d = s->device;
05697 struct skinny_line *l;
05698 struct skinny_subchannel *sub;
05699 struct ast_format_list fmt;
05700 struct sockaddr_in sin = { 0, };
05701 struct sockaddr_in us = { 0, };
05702 struct ast_sockaddr sin_tmp;
05703 struct ast_sockaddr us_tmp;
05704 uint32_t addr;
05705 int port;
05706 int status;
05707 int passthruid;
05708
05709 status = letohl(req->data.openreceivechannelack.status);
05710 if (status) {
05711 ast_log(LOG_ERROR, "Open Receive Channel Failure\n");
05712 return 0;
05713 }
05714 addr = req->data.openreceivechannelack.ipAddr;
05715 port = letohl(req->data.openreceivechannelack.port);
05716 passthruid = letohl(req->data.openreceivechannelack.passThruId);
05717
05718 sin.sin_family = AF_INET;
05719 sin.sin_addr.s_addr = addr;
05720 sin.sin_port = htons(port);
05721
05722 sub = find_subchannel_by_reference(d, passthruid);
05723
05724 if (!sub)
05725 return 0;
05726
05727 l = sub->parent;
05728
05729 if (sub->rtp) {
05730 ast_sockaddr_from_sin(&sin_tmp, &sin);
05731 ast_rtp_instance_set_remote_address(sub->rtp, &sin_tmp);
05732 ast_rtp_instance_get_local_address(sub->rtp, &us_tmp);
05733 ast_sockaddr_to_sin(&us_tmp, &us);
05734 us.sin_addr.s_addr = us.sin_addr.s_addr ? us.sin_addr.s_addr : d->ourip.s_addr;
05735 } else {
05736 ast_log(LOG_ERROR, "No RTP structure, this is very bad\n");
05737 return 0;
05738 }
05739
05740 if (skinnydebug) {
05741 ast_verb(1, "device ipaddr = %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
05742 ast_verb(1, "asterisk ipaddr = %s:%d\n", ast_inet_ntoa(us.sin_addr), ntohs(us.sin_port));
05743 }
05744
05745 fmt = ast_codec_pref_getsize(&l->prefs, ast_best_codec(l->capability));
05746
05747 if (skinnydebug)
05748 ast_verb(1, "Setting payloadType to '%s' (%d ms)\n", ast_getformatname(fmt.bits), fmt.cur_ms);
05749
05750 transmit_startmediatransmission(d, sub, us, fmt);
05751
05752 return 1;
05753 }
05754
05755 static int handle_enbloc_call_message(struct skinny_req *req, struct skinnysession *s)
05756 {
05757 struct skinny_device *d = s->device;
05758 struct skinny_line *l;
05759 struct skinny_subchannel *sub = NULL;
05760 struct ast_channel *c;
05761 pthread_t t;
05762
05763 if (skinnydebug)
05764 ast_verb(1, "Received Enbloc Call: %s\n", req->data.enbloccallmessage.calledParty);
05765
05766 sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
05767
05768 if (!sub) {
05769 l = find_line_by_instance(d, d->lastlineinstance);
05770 if (!l) {
05771 return 0;
05772 }
05773 } else {
05774 l = sub->parent;
05775 }
05776
05777 c = skinny_new(l, AST_STATE_DOWN, NULL);
05778
05779 if(!c) {
05780 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05781 } else {
05782 l->hookstate = SKINNY_OFFHOOK;
05783
05784 sub = c->tech_pvt;
05785 l->activesub = sub;
05786 transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
05787 transmit_activatecallplane(d, l);
05788 transmit_clear_display_message(d, l->instance, sub->callid);
05789 transmit_start_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05790
05791 if (!ast_ignore_pattern(c->context, req->data.enbloccallmessage.calledParty)) {
05792 transmit_stop_tone(d, l->instance, sub->callid);
05793 }
05794 ast_copy_string(c->exten, req->data.enbloccallmessage.calledParty, sizeof(c->exten));
05795 if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
05796 ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
05797 ast_hangup(c);
05798 }
05799 }
05800
05801 return 1;
05802 }
05803
05804
05805 static int handle_soft_key_event_message(struct skinny_req *req, struct skinnysession *s)
05806 {
05807 struct skinny_device *d = s->device;
05808 struct skinny_line *l;
05809 struct skinny_subchannel *sub = NULL;
05810 struct ast_channel *c;
05811 pthread_t t;
05812 int event;
05813 int instance;
05814 int callreference;
05815
05816 event = letohl(req->data.softkeyeventmessage.softKeyEvent);
05817 instance = letohl(req->data.softkeyeventmessage.instance);
05818 callreference = letohl(req->data.softkeyeventmessage.callreference);
05819
05820 if (instance) {
05821 l = find_line_by_instance(d, instance);
05822 if (callreference) {
05823 sub = find_subchannel_by_instance_reference(d, instance, callreference);
05824 } else {
05825 sub = find_subchannel_by_instance_reference(d, instance, d->lastcallreference);
05826 }
05827 } else {
05828 l = find_line_by_instance(d, d->lastlineinstance);
05829 }
05830
05831 if (!l) {
05832 if (skinnydebug)
05833 ast_verb(1, "Received Softkey Event: %d(%d/%d)\n", event, instance, callreference);
05834 return 0;
05835 }
05836
05837 ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "Skinny/%s@%s", l->name, d->name);
05838
05839 switch(event) {
05840 case SOFTKEY_NONE:
05841 if (skinnydebug)
05842 ast_verb(1, "Received Softkey Event: None(%d/%d)\n", instance, callreference);
05843 break;
05844 case SOFTKEY_REDIAL:
05845 if (skinnydebug)
05846 ast_verb(1, "Received Softkey Event: Redial(%d/%d)\n", instance, callreference);
05847
05848 if (ast_strlen_zero(l->lastnumberdialed)) {
05849 ast_log(LOG_WARNING, "Attempted redial, but no previously dialed number found. Ignoring button.\n");
05850 break;
05851 }
05852
05853 if (!sub || !sub->owner) {
05854 c = skinny_new(l, AST_STATE_DOWN, NULL);
05855 } else {
05856 c = sub->owner;
05857 }
05858
05859 if (!c) {
05860 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05861 } else {
05862 sub = c->tech_pvt;
05863 l->activesub = sub;
05864 if (l->hookstate == SKINNY_ONHOOK) {
05865 l->hookstate = SKINNY_OFFHOOK;
05866 transmit_speaker_mode(d, SKINNY_SPEAKERON);
05867 transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
05868 transmit_activatecallplane(d, l);
05869 }
05870 transmit_clear_display_message(d, l->instance, sub->callid);
05871 transmit_start_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05872 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
05873
05874 if (!ast_ignore_pattern(c->context, l->lastnumberdialed)) {
05875 transmit_stop_tone(d, l->instance, sub->callid);
05876 }
05877 ast_copy_string(c->exten, l->lastnumberdialed, sizeof(c->exten));
05878 if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
05879 ast_log(LOG_WARNING, "Unable to create new call thread: %s\n", strerror(errno));
05880 ast_hangup(c);
05881 }
05882 }
05883 break;
05884 case SOFTKEY_NEWCALL:
05885 if (skinnydebug)
05886 ast_verb(1, "Received Softkey Event: New Call(%d/%d)\n", instance, callreference);
05887
05888
05889 c = skinny_new(l, AST_STATE_DOWN, NULL);
05890 sub = c->tech_pvt;
05891
05892
05893
05894
05895
05896
05897 if (!c) {
05898 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05899 } else {
05900 sub = c->tech_pvt;
05901 l->activesub = sub;
05902 if (l->hookstate == SKINNY_ONHOOK) {
05903 l->hookstate = SKINNY_OFFHOOK;
05904 transmit_speaker_mode(d, SKINNY_SPEAKERON);
05905 }
05906 ast_verb(1, "Call-id: %u\n", sub->callid);
05907
05908 transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
05909 transmit_activatecallplane(d, l);
05910
05911 transmit_clear_display_message(d, l->instance, sub->callid);
05912 transmit_start_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
05913 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_OFFHOOK);
05914
05915
05916 if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
05917 ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
05918 ast_hangup(c);
05919 }
05920 }
05921 break;
05922 case SOFTKEY_HOLD:
05923 if (skinnydebug)
05924 ast_verb(1, "Received Softkey Event: Hold(%d/%d)\n", instance, callreference);
05925 handle_hold_button(sub);
05926 break;
05927 case SOFTKEY_TRNSFER:
05928 if (skinnydebug)
05929 ast_verb(1, "Received Softkey Event: Transfer(%d/%d)\n", instance, callreference);
05930 if (l->transfer)
05931 handle_transfer_button(sub);
05932 else
05933 transmit_displaynotify(d, "Transfer disabled", 10);
05934
05935 break;
05936 case SOFTKEY_DND:
05937 if (skinnydebug)
05938 ast_verb(1, "Received Softkey Event: DND(%d/%d)\n", instance, callreference);
05939
05940
05941 if (l->dnd != 0){
05942 ast_verb(3, "Disabling DND on %s@%s\n", l->name, d->name);
05943 l->dnd = 0;
05944 transmit_lamp_indication(d, STIMULUS_DND, 1, SKINNY_LAMP_ON);
05945 transmit_displaynotify(d, "DnD disabled", 10);
05946 } else {
05947 ast_verb(3, "Enabling DND on %s@%s\n", l->name, d->name);
05948 l->dnd = 1;
05949 transmit_lamp_indication(d, STIMULUS_DND, 1, SKINNY_LAMP_OFF);
05950 transmit_displaynotify(d, "DnD enabled", 10);
05951 }
05952 break;
05953 case SOFTKEY_CFWDALL:
05954 if (skinnydebug)
05955 ast_verb(1, "Received Softkey Event: Forward All(%d/%d)\n", instance, callreference);
05956
05957 if (!sub || !sub->owner) {
05958 c = skinny_new(l, AST_STATE_DOWN, NULL);
05959 } else {
05960 c = sub->owner;
05961 }
05962
05963 if (!c) {
05964 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05965 } else {
05966 sub = c->tech_pvt;
05967 l->activesub = sub;
05968 handle_callforward_button(sub, SKINNY_CFWD_ALL);
05969 }
05970 break;
05971 case SOFTKEY_CFWDBUSY:
05972 if (skinnydebug)
05973 ast_verb(1, "Received Softkey Event: Forward Busy (%d/%d)\n", instance, callreference);
05974
05975 if (!sub || !sub->owner) {
05976 c = skinny_new(l, AST_STATE_DOWN, NULL);
05977 } else {
05978 c = sub->owner;
05979 }
05980
05981 if (!c) {
05982 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
05983 } else {
05984 sub = c->tech_pvt;
05985 l->activesub = sub;
05986 handle_callforward_button(sub, SKINNY_CFWD_BUSY);
05987 }
05988 break;
05989 case SOFTKEY_CFWDNOANSWER:
05990 if (skinnydebug)
05991 ast_verb(1, "Received Softkey Event: Forward No Answer (%d/%d)\n", instance, callreference);
05992
05993 #if 0
05994 if (!sub || !sub->owner) {
05995 c = skinny_new(l, AST_STATE_DOWN, NULL);
05996 } else {
05997 c = sub->owner;
05998 }
05999
06000 if (!c) {
06001 ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
06002 } else {
06003 sub = c->tech_pvt;
06004 l->activesub = sub;
06005 handle_callforward_button(sub, SKINNY_CFWD_NOANSWER);
06006 }
06007 #endif
06008 break;
06009 case SOFTKEY_BKSPC:
06010 if (skinnydebug)
06011 ast_verb(1, "Received Softkey Event: Backspace(%d/%d)\n", instance, callreference);
06012 break;
06013 case SOFTKEY_ENDCALL:
06014 if (skinnydebug)
06015 ast_verb(1, "Received Softkey Event: End Call(%d/%d)\n", instance, callreference);
06016
06017 if (l->hookstate == SKINNY_ONHOOK) {
06018
06019 break;
06020 }
06021 if (sub) {
06022 int onlysub = 0;
06023
06024 if (!AST_LIST_NEXT(sub, list)) {
06025 onlysub = 1;
06026 } else {
06027 AST_LIST_REMOVE(&l->sub, sub, list);
06028 }
06029
06030 sub->cxmode = SKINNY_CX_RECVONLY;
06031 if (onlysub || sub->xferor){
06032 l->hookstate = SKINNY_ONHOOK;
06033 if (skinnydebug)
06034 ast_debug(1, "Skinny %s@%s-%d went on hook\n", l->name, d->name, callreference);
06035 }
06036
06037 if (l->hookstate == SKINNY_ONHOOK) {
06038 transmit_closereceivechannel(d, sub);
06039 transmit_stopmediatransmission(d, sub);
06040 transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
06041 transmit_clearpromptmessage(d, instance, sub->callid);
06042 transmit_callstate(d, l->instance, sub->callid, SKINNY_ONHOOK);
06043 transmit_selectsoftkeys(d, 0, 0, KEYDEF_ONHOOK);
06044 transmit_activatecallplane(d, l);
06045 } else if (l->hookstate == SKINNY_OFFHOOK) {
06046 transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
06047 transmit_activatecallplane(d, l);
06048 } else {
06049 transmit_callstate(d, l->instance, sub->callid, l->hookstate);
06050 }
06051
06052 ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Skinny/%s@%s", l->name, d->name);
06053 if (skinnydebug)
06054 ast_verb(1, "Skinny %s@%s went on hook\n", l->name, d->name);
06055 if (l->transfer && sub->xferor && sub->owner->_state >= AST_STATE_RING) {
06056
06057
06058 handle_transfer_button(sub);
06059 } else {
06060
06061
06062 if (sub->xferor && sub->related){
06063 sub->related->related = NULL;
06064 sub->related->blindxfer = 0;
06065 }
06066
06067 if (sub->owner) {
06068 sub->alreadygone = 1;
06069 ast_queue_hangup(sub->owner);
06070 } else {
06071 ast_log(LOG_WARNING, "Skinny(%s@%s-%u) channel already destroyed\n",
06072 l->name, d->name, sub->callid);
06073 }
06074 }
06075 if ((l->hookstate == SKINNY_ONHOOK) && (AST_LIST_NEXT(sub, list) && !AST_LIST_NEXT(sub, list)->rtp)) {
06076 ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Skinny/%s@%s", l->name, d->name);
06077 }
06078 }
06079 break;
06080 case SOFTKEY_RESUME:
06081 if (skinnydebug)
06082 ast_verb(1, "Received Softkey Event: Resume(%d/%d)\n", instance, callreference);
06083
06084 if (sub) {
06085 if (sub->onhold) {
06086 skinny_unhold(sub);
06087 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
06088 } else {
06089 skinny_hold(sub);
06090 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_ONHOLD);
06091 }
06092 }
06093
06094 break;
06095 case SOFTKEY_ANSWER:
06096 if (skinnydebug)
06097 ast_verb(1, "Received Softkey Event: Answer(%d/%d)\n", instance, callreference);
06098
06099 transmit_ringer_mode(d, SKINNY_RING_OFF);
06100 transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
06101 if (l->hookstate == SKINNY_ONHOOK) {
06102 transmit_speaker_mode(d, SKINNY_SPEAKERON);
06103 l->hookstate = SKINNY_OFFHOOK;
06104 }
06105
06106 if (sub && sub->outgoing) {
06107
06108 ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
06109 transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
06110 transmit_activatecallplane(d, l);
06111 transmit_stop_tone(d, l->instance, sub->callid);
06112 transmit_callstate(d, sub->parent->instance, sub->callid, SKINNY_CONNECTED);
06113 transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
06114 start_rtp(sub);
06115 ast_setstate(sub->owner, AST_STATE_UP);
06116 }
06117 break;
06118 case SOFTKEY_INFO:
06119 if (skinnydebug)
06120 ast_verb(1, "Received Softkey Event: Info(%d/%d)\n", instance, callreference);
06121 break;
06122 case SOFTKEY_CONFRN:
06123 if (skinnydebug)
06124 ast_verb(1, "Received Softkey Event: Conference(%d/%d)\n", instance, callreference);
06125
06126 break;
06127 case SOFTKEY_PARK:
06128 {
06129 int extout;
06130 char message[32];
06131
06132 if (skinnydebug)
06133 ast_verb(1, "Received Softkey Event: Park Call(%d/%d)\n", instance, callreference);
06134
06135 if ((sub && sub->owner) && (sub->owner->_state == AST_STATE_UP)){
06136 c = sub->owner;
06137 if (ast_bridged_channel(c)) {
06138 if (!ast_masq_park_call(ast_bridged_channel(c), c, 0, &extout)) {
06139 snprintf(message, sizeof(message), "Call Parked at: %d", extout);
06140 transmit_displaynotify(d, message, 10);
06141 } else {
06142 transmit_displaynotify(d, "Call Park failed", 10);
06143 }
06144 } else {
06145 transmit_displaynotify(d, "Call Park not available", 10);
06146 }
06147 } else {
06148 transmit_displaynotify(d, "Call Park not available", 10);
06149 }
06150 break;
06151 }
06152 case SOFTKEY_JOIN:
06153 if (skinnydebug)
06154 ast_verb(1, "Received Softkey Event: Join(%d/%d)\n", instance, callreference);
06155 break;
06156 case SOFTKEY_MEETME:
06157
06158 if (skinnydebug)
06159 ast_verb(1, "Received Softkey Event: Meetme(%d/%d)\n", instance, callreference);
06160 break;
06161 case SOFTKEY_PICKUP:
06162 if (skinnydebug)
06163 ast_verb(1, "Received Softkey Event: Pickup(%d/%d)\n", instance, callreference);
06164 break;
06165 case SOFTKEY_GPICKUP:
06166 if (skinnydebug)
06167 ast_verb(1, "Received Softkey Event: Group Pickup(%d/%d)\n", instance, callreference);
06168 break;
06169 default:
06170 if (skinnydebug)
06171 ast_verb(1, "Received unknown Softkey Event: %d(%d/%d)\n", event, instance, callreference);
06172 break;
06173 }
06174
06175 return 1;
06176 }
06177
06178 static int handle_message(struct skinny_req *req, struct skinnysession *s)
06179 {
06180 int res = 0;
06181 struct skinny_speeddial *sd;
06182 struct skinny_line *l;
06183 struct skinny_device *d = s->device;
06184 size_t len;
06185
06186 if ((!s->device) && (letohl(req->e) != REGISTER_MESSAGE && letohl(req->e) != ALARM_MESSAGE)) {
06187 ast_log(LOG_WARNING, "Client sent message #%d without first registering.\n", req->e);
06188 ast_free(req);
06189 return 0;
06190 }
06191
06192 SKINNY_DEVONLY(if (skinnydebug > 1) {
06193 ast_verb(4, "Received %s from %s\n", message2str(req->e), s->device->name);
06194 })
06195
06196 switch(letohl(req->e)) {
06197 case KEEP_ALIVE_MESSAGE:
06198 res = handle_keep_alive_message(req, s);
06199 break;
06200 case REGISTER_MESSAGE:
06201 if (skinnydebug)
06202 ast_verb(1, "Device %s is attempting to register\n", req->data.reg.name);
06203
06204 res = handle_register_message(req, s);
06205 break;
06206 case IP_PORT_MESSAGE:
06207 res = handle_ip_port_message(req, s);
06208 break;
06209 case KEYPAD_BUTTON_MESSAGE:
06210 {
06211 struct skinny_device *d = s->device;
06212 struct skinny_subchannel *sub;
06213 int lineInstance;
06214 int callReference;
06215
06216 if (skinnydebug)
06217 ast_verb(1, "Collected digit: [%u]\n", letohl(req->data.keypad.button));
06218
06219 lineInstance = letohl(req->data.keypad.lineInstance);
06220 callReference = letohl(req->data.keypad.callReference);
06221
06222 if (lineInstance) {
06223 sub = find_subchannel_by_instance_reference(d, lineInstance, callReference);
06224 } else {
06225 sub = d->activeline->activesub;
06226 }
06227
06228 if (sub && ((sub->owner && sub->owner->_state < AST_STATE_UP) || sub->onhold)) {
06229 char dgt;
06230 int digit = letohl(req->data.keypad.button);
06231
06232 if (digit == 14) {
06233 dgt = '*';
06234 } else if (digit == 15) {
06235 dgt = '#';
06236 } else if (digit >= 0 && digit <= 9) {
06237 dgt = '0' + digit;
06238 } else {
06239
06240
06241
06242
06243
06244
06245
06246 dgt = '0' + digit;
06247 ast_log(LOG_WARNING, "Unsupported digit %d\n", digit);
06248 }
06249
06250 len = strlen(d->exten);
06251 if (len < sizeof(d->exten) - 1) {
06252 d->exten[len] = dgt;
06253 d->exten[len + 1] = '\0';
06254 } else {
06255 ast_log(AST_LOG_WARNING, "Dropping digit with value %d because digit queue is full\n", dgt);
06256 }
06257 } else
06258 res = handle_keypad_button_message(req, s);
06259 }
06260 break;
06261 case ENBLOC_CALL_MESSAGE:
06262 res = handle_enbloc_call_message(req, s);
06263 break;
06264 case STIMULUS_MESSAGE:
06265 res = handle_stimulus_message(req, s);
06266 break;
06267 case OFFHOOK_MESSAGE:
06268 res = handle_offhook_message(req, s);
06269 break;
06270 case ONHOOK_MESSAGE:
06271 res = handle_onhook_message(req, s);
06272 break;
06273 case CAPABILITIES_RES_MESSAGE:
06274 if (skinnydebug)
06275 ast_verb(1, "Received CapabilitiesRes\n");
06276
06277 res = handle_capabilities_res_message(req, s);
06278 break;
06279 case SPEED_DIAL_STAT_REQ_MESSAGE:
06280 if (skinnydebug)
06281 ast_verb(1, "Received SpeedDialStatRequest\n");
06282 if ( (sd = find_speeddial_by_instance(s->device, letohl(req->data.speeddialreq.speedDialNumber), 0)) ) {
06283 transmit_speeddialstatres(d, sd);
06284 }
06285 break;
06286 case LINE_STATE_REQ_MESSAGE:
06287 if (skinnydebug)
06288 ast_verb(1, "Received LineStatRequest\n");
06289 if ((l = find_line_by_instance(d, letohl(req->data.line.lineNumber)))) {
06290 transmit_linestatres(d, l);
06291 }
06292 break;
06293 case TIME_DATE_REQ_MESSAGE:
06294 if (skinnydebug)
06295 ast_verb(1, "Received Time/Date Request\n");
06296
06297 transmit_definetimedate(d);
06298 break;
06299 case BUTTON_TEMPLATE_REQ_MESSAGE:
06300 if (skinnydebug)
06301 ast_verb(1, "Buttontemplate requested\n");
06302
06303 res = handle_button_template_req_message(req, s);
06304 break;
06305 case VERSION_REQ_MESSAGE:
06306 if (skinnydebug)
06307 ast_verb(1, "Version Request\n");
06308 transmit_versionres(d);
06309 break;
06310 case SERVER_REQUEST_MESSAGE:
06311 if (skinnydebug)
06312 ast_verb(1, "Received Server Request\n");
06313 transmit_serverres(d);
06314 break;
06315 case ALARM_MESSAGE:
06316
06317 if (skinnydebug)
06318 ast_verb(1, "Received Alarm Message: %s\n", req->data.alarm.displayMessage);
06319 break;
06320 case OPEN_RECEIVE_CHANNEL_ACK_MESSAGE:
06321 if (skinnydebug)
06322 ast_verb(1, "Received Open Receive Channel Ack\n");
06323
06324 res = handle_open_receive_channel_ack_message(req, s);
06325 break;
06326 case SOFT_KEY_SET_REQ_MESSAGE:
06327 if (skinnydebug)
06328 ast_verb(1, "Received SoftKeySetReq\n");
06329 transmit_softkeysetres(d);
06330 transmit_selectsoftkeys(d, 0, 0, KEYDEF_ONHOOK);
06331 break;
06332 case SOFT_KEY_EVENT_MESSAGE:
06333 res = handle_soft_key_event_message(req, s);
06334 break;
06335 case UNREGISTER_MESSAGE:
06336 if (skinnydebug)
06337 ast_verb(1, "Received Unregister Request\n");
06338
06339 res = skinny_unregister(req, s);
06340 break;
06341 case SOFT_KEY_TEMPLATE_REQ_MESSAGE:
06342 if (skinnydebug)
06343 ast_verb(1, "Received SoftKey Template Request\n");
06344 transmit_softkeytemplateres(d);
06345 break;
06346 case HEADSET_STATUS_MESSAGE:
06347
06348 break;
06349 case REGISTER_AVAILABLE_LINES_MESSAGE:
06350
06351 break;
06352 default:
06353 if (skinnydebug)
06354 ast_verb(1, "RECEIVED UNKNOWN MESSAGE TYPE: %x\n", (unsigned)letohl(req->e));
06355 break;
06356 }
06357 if (res >= 0 && req)
06358 ast_free(req);
06359 return res;
06360 }
06361
06362 static void destroy_session(struct skinnysession *s)
06363 {
06364 struct skinnysession *cur;
06365 AST_LIST_LOCK(&sessions);
06366 AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, cur, list) {
06367 if (cur == s) {
06368 AST_LIST_REMOVE_CURRENT(list);
06369 if (s->fd > -1)
06370 close(s->fd);
06371
06372 if (!s->device)
06373 ast_atomic_fetchadd_int(&unauth_sessions, -1);
06374
06375 ast_mutex_destroy(&s->lock);
06376
06377 ast_free(s);
06378 } else {
06379 ast_log(LOG_WARNING, "Trying to delete nonexistent session %p?\n", s);
06380 }
06381 }
06382 AST_LIST_TRAVERSE_SAFE_END
06383 AST_LIST_UNLOCK(&sessions);
06384 }
06385
06386 static int get_input(struct skinnysession *s)
06387 {
06388 int res;
06389 int dlen = 0;
06390 int timeout = keep_alive * 1100;
06391 time_t now;
06392 int *bufaddr;
06393 struct pollfd fds[1];
06394
06395 if (!s->device) {
06396 if(time(&now) == -1) {
06397 ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
06398 return -1;
06399 }
06400
06401 timeout = (auth_timeout - (now - s->start)) * 1000;
06402 if (timeout < 0) {
06403
06404 if (skinnydebug)
06405 ast_verb(1, "Skinny Client failed to authenticate in %d seconds\n", auth_timeout);
06406 return -1;
06407 }
06408 }
06409
06410 fds[0].fd = s->fd;
06411 fds[0].events = POLLIN;
06412 fds[0].revents = 0;
06413 res = ast_poll(fds, 1, timeout);
06414
06415
06416 if (res < 0) {
06417 if (errno != EINTR) {
06418 ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
06419 return res;
06420 }
06421 } else if (res == 0) {
06422 if (skinnydebug) {
06423 if (s->device) {
06424 ast_verb(1, "Skinny Client was lost, unregistering\n");
06425 } else {
06426 ast_verb(1, "Skinny Client failed to authenticate in %d seconds\n", auth_timeout);
06427 }
06428 }
06429 skinny_unregister(NULL, s);
06430 return -1;
06431 }
06432
06433 if (fds[0].revents) {
06434 ast_mutex_lock(&s->lock);
06435 memset(s->inbuf, 0, sizeof(s->inbuf));
06436 res = read(s->fd, s->inbuf, 4);
06437 if (res < 0) {
06438 ast_log(LOG_WARNING, "read() returned error: %s\n", strerror(errno));
06439
06440 if (skinnydebug)
06441 ast_verb(1, "Skinny Client was lost, unregistering\n");
06442
06443 skinny_unregister(NULL, s);
06444 ast_mutex_unlock(&s->lock);
06445 return res;
06446 } else if (res != 4) {
06447 ast_log(LOG_WARNING, "Skinny Client sent less data than expected. Expected 4 but got %d.\n", res);
06448 ast_mutex_unlock(&s->lock);
06449
06450 if (res == 0) {
06451 if (skinnydebug)
06452 ast_verb(1, "Skinny Client was lost, unregistering\n");
06453 skinny_unregister(NULL, s);
06454 }
06455
06456 return -1;
06457 }
06458
06459 bufaddr = (int *)s->inbuf;
06460 dlen = letohl(*bufaddr);
06461 if (dlen < 4) {
06462 ast_debug(1, "Skinny Client sent invalid data.\n");
06463 ast_mutex_unlock(&s->lock);
06464 return -1;
06465 }
06466 if (dlen+8 > sizeof(s->inbuf)) {
06467 dlen = sizeof(s->inbuf) - 8;
06468 }
06469 *bufaddr = htolel(dlen);
06470
06471 res = read(s->fd, s->inbuf+4, dlen+4);
06472 ast_mutex_unlock(&s->lock);
06473 if (res < 0) {
06474 ast_log(LOG_WARNING, "read() returned error: %s\n", strerror(errno));
06475 return res;
06476 } else if (res != (dlen+4)) {
06477 ast_log(LOG_WARNING, "Skinny Client sent less data than expected.\n");
06478 return -1;
06479 }
06480 return res;
06481 }
06482 return 0;
06483 }
06484
06485 static struct skinny_req *skinny_req_parse(struct skinnysession *s)
06486 {
06487 struct skinny_req *req;
06488 int *bufaddr;
06489
06490 if (!(req = ast_calloc(1, SKINNY_MAX_PACKET)))
06491 return NULL;
06492
06493 ast_mutex_lock(&s->lock);
06494 memcpy(req, s->inbuf, skinny_header_size);
06495 bufaddr = (int *)(s->inbuf);
06496 memcpy(&req->data, s->inbuf+skinny_header_size, letohl(*bufaddr)-4);
06497
06498 ast_mutex_unlock(&s->lock);
06499
06500 if (letohl(req->e) < 0) {
06501 ast_log(LOG_ERROR, "Event Message is NULL from socket %d, This is bad\n", s->fd);
06502 ast_free(req);
06503 return NULL;
06504 }
06505
06506 return req;
06507 }
06508
06509 static void *skinny_session(void *data)
06510 {
06511 int res;
06512 struct skinny_req *req;
06513 struct skinnysession *s = data;
06514
06515 ast_verb(3, "Starting Skinny session from %s\n", ast_inet_ntoa(s->sin.sin_addr));
06516
06517 for (;;) {
06518 res = get_input(s);
06519 if (res < 0) {
06520 break;
06521 }
06522
06523 if (res > 0)
06524 {
06525 if (!(req = skinny_req_parse(s))) {
06526 destroy_session(s);
06527 return NULL;
06528 }
06529
06530 res = handle_message(req, s);
06531 if (res < 0) {
06532 destroy_session(s);
06533 return NULL;
06534 }
06535 }
06536 }
06537 ast_debug(3, "Skinny Session returned: %s\n", strerror(errno));
06538
06539 if (s)
06540 destroy_session(s);
06541
06542 return 0;
06543 }
06544
06545 static void *accept_thread(void *ignore)
06546 {
06547 int as;
06548 struct sockaddr_in sin;
06549 socklen_t sinlen;
06550 struct skinnysession *s;
06551 struct protoent *p;
06552 int arg = 1;
06553
06554 for (;;) {
06555 sinlen = sizeof(sin);
06556 as = accept(skinnysock, (struct sockaddr *)&sin, &sinlen);
06557 if (as < 0) {
06558 ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
06559 continue;
06560 }
06561
06562 if (ast_atomic_fetchadd_int(&unauth_sessions, +1) >= auth_limit) {
06563 close(as);
06564 ast_atomic_fetchadd_int(&unauth_sessions, -1);
06565 continue;
06566 }
06567
06568 p = getprotobyname("tcp");
06569 if(p) {
06570 if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
06571 ast_log(LOG_WARNING, "Failed to set Skinny tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
06572 }
06573 }
06574 if (!(s = ast_calloc(1, sizeof(struct skinnysession)))) {
06575 close(as);
06576 ast_atomic_fetchadd_int(&unauth_sessions, -1);
06577 continue;
06578 }
06579
06580 memcpy(&s->sin, &sin, sizeof(sin));
06581 ast_mutex_init(&s->lock);
06582 s->fd = as;
06583
06584 if(time(&s->start) == -1) {
06585 ast_log(LOG_ERROR, "error executing time(): %s; disconnecting client\n", strerror(errno));
06586 destroy_session(s);
06587 continue;
06588 }
06589
06590 AST_LIST_LOCK(&sessions);
06591 AST_LIST_INSERT_HEAD(&sessions, s, list);
06592 AST_LIST_UNLOCK(&sessions);
06593
06594 if (ast_pthread_create(&s->t, NULL, skinny_session, s)) {
06595 destroy_session(s);
06596 }
06597 }
06598 if (skinnydebug)
06599 ast_verb(1, "killing accept thread\n");
06600 close(as);
06601 return 0;
06602 }
06603
06604 static void *do_monitor(void *data)
06605 {
06606 int res;
06607
06608
06609
06610
06611 for(;;) {
06612 pthread_testcancel();
06613
06614 res = ast_sched_wait(sched);
06615 if ((res < 0) || (res > 1000)) {
06616 res = 1000;
06617 }
06618 res = ast_io_wait(io, res);
06619 ast_mutex_lock(&monlock);
06620 if (res >= 0) {
06621 ast_sched_runq(sched);
06622 }
06623 ast_mutex_unlock(&monlock);
06624 }
06625
06626 return NULL;
06627
06628 }
06629
06630 static int restart_monitor(void)
06631 {
06632
06633 if (monitor_thread == AST_PTHREADT_STOP)
06634 return 0;
06635
06636 ast_mutex_lock(&monlock);
06637 if (monitor_thread == pthread_self()) {
06638 ast_mutex_unlock(&monlock);
06639 ast_log(LOG_WARNING, "Cannot kill myself\n");
06640 return -1;
06641 }
06642 if (monitor_thread != AST_PTHREADT_NULL) {
06643
06644 pthread_kill(monitor_thread, SIGURG);
06645 } else {
06646
06647 if (ast_pthread_create_background(&monitor_thread, NULL, do_monitor, NULL) < 0) {
06648 ast_mutex_unlock(&monlock);
06649 ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
06650 return -1;
06651 }
06652 }
06653 ast_mutex_unlock(&monlock);
06654 return 0;
06655 }
06656
06657 static int skinny_devicestate(void *data)
06658 {
06659 struct skinny_line *l;
06660 char *tmp;
06661
06662 tmp = ast_strdupa(data);
06663
06664 l = find_line_by_name(tmp);
06665
06666 return get_devicestate(l);
06667 }
06668
06669 static struct ast_channel *skinny_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause)
06670 {
06671 struct skinny_line *l;
06672 struct ast_channel *tmpc = NULL;
06673 char tmp[256];
06674 char *dest = data;
06675
06676 if (!(format &= AST_FORMAT_AUDIO_MASK)) {
06677 ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%s'\n", ast_getformatname_multiple(tmp, sizeof(tmp), format));
06678 return NULL;
06679 }
06680
06681 ast_copy_string(tmp, dest, sizeof(tmp));
06682 if (ast_strlen_zero(tmp)) {
06683 ast_log(LOG_NOTICE, "Skinny channels require a device\n");
06684 return NULL;
06685 }
06686 l = find_line_by_name(tmp);
06687 if (!l) {
06688 ast_log(LOG_NOTICE, "No available lines on: %s\n", dest);
06689 return NULL;
06690 }
06691 ast_verb(3, "skinny_request(%s)\n", tmp);
06692 tmpc = skinny_new(l, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL);
06693 if (!tmpc) {
06694 ast_log(LOG_WARNING, "Unable to make channel for '%s'\n", tmp);
06695 }
06696 restart_monitor();
06697 return tmpc;
06698 }
06699
06700 #define TYPE_GENERAL 1
06701 #define TYPE_DEF_DEVICE 2
06702 #define TYPE_DEF_LINE 4
06703 #define TYPE_DEVICE 8
06704 #define TYPE_LINE 16
06705
06706 #define CLINE_OPTS ((struct skinny_line_options *)item)
06707 #define CLINE ((struct skinny_line *)item)
06708 #define CDEV_OPTS ((struct skinny_device_options *)item)
06709 #define CDEV ((struct skinny_device *)item)
06710
06711 static void config_parse_variables(int type, void *item, struct ast_variable *vptr)
06712 {
06713 struct ast_variable *v;
06714 int lineInstance = 1;
06715 int speeddialInstance = 1;
06716
06717 while(vptr) {
06718 v = vptr;
06719 vptr = vptr->next;
06720
06721 if (type & (TYPE_GENERAL)) {
06722 char newcontexts[AST_MAX_CONTEXT];
06723 char oldcontexts[AST_MAX_CONTEXT];
06724 char *stringp, *context, *oldregcontext;
06725 if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) {
06726 v = v->next;
06727 continue;
06728 }
06729 if (!strcasecmp(v->name, "bindaddr")) {
06730 if (!(hp = ast_gethostbyname(v->value, &ahp))) {
06731 ast_log(LOG_WARNING, "Invalid address: %s\n", v->value);
06732 } else {
06733 memcpy(&bindaddr.sin_addr, hp->h_addr, sizeof(bindaddr.sin_addr));
06734 }
06735 continue;
06736 } else if (!strcasecmp(v->name, "keepalive")) {
06737 keep_alive = atoi(v->value);
06738 continue;
06739 } else if (!strcasecmp(v->name, "authtimeout")) {
06740 int timeout = atoi(v->value);
06741
06742 if (timeout < 1) {
06743 ast_log(LOG_WARNING, "Invalid authtimeout value '%s', using default value\n", v->value);
06744 auth_timeout = DEFAULT_AUTH_TIMEOUT;
06745 } else {
06746 auth_timeout = timeout;
06747 }
06748 continue;
06749 } else if (!strcasecmp(v->name, "authlimit")) {
06750 int limit = atoi(v->value);
06751
06752 if (limit < 1) {
06753 ast_log(LOG_WARNING, "Invalid authlimit value '%s', using default value\n", v->value);
06754 auth_limit = DEFAULT_AUTH_LIMIT;
06755 } else {
06756 auth_limit = limit;
06757 }
06758 continue;
06759 } else if (!strcasecmp(v->name, "regcontext")) {
06760 ast_copy_string(newcontexts, v->value, sizeof(newcontexts));
06761 stringp = newcontexts;
06762
06763 ast_copy_string(oldcontexts, regcontext, sizeof(oldcontexts));
06764 oldregcontext = oldcontexts;
06765
06766 cleanup_stale_contexts(stringp, oldregcontext);
06767
06768 while ((context = strsep(&stringp, "&"))) {
06769 ast_copy_string(used_context, context, sizeof(used_context));
06770 ast_context_find_or_create(NULL, NULL, context, "Skinny");
06771 }
06772 ast_copy_string(regcontext, v->value, sizeof(regcontext));
06773 continue;
06774 } else if (!strcasecmp(v->name, "dateformat")) {
06775 memcpy(date_format, v->value, sizeof(date_format));
06776 continue;
06777 } else if (!strcasecmp(v->name, "tos")) {
06778 if (ast_str2tos(v->value, &qos.tos))
06779 ast_log(LOG_WARNING, "Invalid tos value at line %d, refer to QoS documentation\n", v->lineno);
06780 continue;
06781 } else if (!strcasecmp(v->name, "tos_audio")) {
06782 if (ast_str2tos(v->value, &qos.tos_audio))
06783 ast_log(LOG_WARNING, "Invalid tos_audio value at line %d, refer to QoS documentation\n", v->lineno);
06784 continue;
06785 } else if (!strcasecmp(v->name, "tos_video")) {
06786 if (ast_str2tos(v->value, &qos.tos_video))
06787 ast_log(LOG_WARNING, "Invalid tos_video value at line %d, refer to QoS documentation\n", v->lineno);
06788 continue;
06789 } else if (!strcasecmp(v->name, "cos")) {
06790 if (ast_str2cos(v->value, &qos.cos))
06791 ast_log(LOG_WARNING, "Invalid cos value at line %d, refer to QoS documentation\n", v->lineno);
06792 continue;
06793 } else if (!strcasecmp(v->name, "cos_audio")) {
06794 if (ast_str2cos(v->value, &qos.cos_audio))
06795 ast_log(LOG_WARNING, "Invalid cos_audio value at line %d, refer to QoS documentation\n", v->lineno);
06796 continue;
06797 } else if (!strcasecmp(v->name, "cos_video")) {
06798 if (ast_str2cos(v->value, &qos.cos_video))
06799 ast_log(LOG_WARNING, "Invalid cos_video value at line %d, refer to QoS documentation\n", v->lineno);
06800 continue;
06801 } else if (!strcasecmp(v->name, "bindport")) {
06802 if (sscanf(v->value, "%5d", &ourport) == 1) {
06803 bindaddr.sin_port = htons(ourport);
06804 } else {
06805 ast_log(LOG_WARNING, "Invalid bindport '%s' at line %d of %s\n", v->value, v->lineno, config);
06806 }
06807 continue;
06808 } else if (!strcasecmp(v->name, "allow")) {
06809 ast_parse_allow_disallow(&default_prefs, &default_capability, v->value, 1);
06810 continue;
06811 } else if (!strcasecmp(v->name, "disallow")) {
06812 ast_parse_allow_disallow(&default_prefs, &default_capability, v->value, 0);
06813 continue;
06814 }
06815 }
06816
06817 if (!strcasecmp(v->name, "transfer")) {
06818 if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06819 CDEV_OPTS->transfer = ast_true(v->value);
06820 continue;
06821 } else if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06822 CLINE_OPTS->transfer = ast_true(v->value);
06823 continue;
06824 }
06825 } else if (!strcasecmp(v->name, "callwaiting")) {
06826 if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06827 CDEV_OPTS->callwaiting = ast_true(v->value);
06828 continue;
06829 } else if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06830 CLINE_OPTS->callwaiting = ast_true(v->value);
06831 continue;
06832 }
06833 } else if (!strcasecmp(v->name, "directmedia") || !strcasecmp(v->name, "canreinvite")) {
06834 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06835 CLINE_OPTS->directmedia = ast_true(v->value);
06836 continue;
06837 }
06838 } else if (!strcasecmp(v->name, "nat")) {
06839 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06840 CLINE_OPTS->nat = ast_true(v->value);
06841 continue;
06842 }
06843 } else if (!strcasecmp(v->name, "context")) {
06844 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06845 ast_copy_string(CLINE_OPTS->context, v->value, sizeof(CLINE_OPTS->context));
06846 continue;
06847 }
06848 }else if (!strcasecmp(v->name, "vmexten")) {
06849 if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06850 ast_copy_string(CDEV_OPTS->vmexten, v->value, sizeof(CDEV_OPTS->vmexten));
06851 continue;
06852 } else if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06853 ast_copy_string(CLINE_OPTS->vmexten, v->value, sizeof(CLINE_OPTS->vmexten));
06854 continue;
06855 }
06856 } else if (!strcasecmp(v->name, "mwiblink")) {
06857 if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06858 CDEV_OPTS->mwiblink = ast_true(v->value);
06859 continue;
06860 } else if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06861 CLINE_OPTS->mwiblink = ast_true(v->value);
06862 continue;
06863 }
06864 } else if (!strcasecmp(v->name, "linelabel")) {
06865 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06866 ast_copy_string(CLINE_OPTS->label, v->value, sizeof(CLINE_OPTS->label));
06867 continue;
06868 }
06869 } else if (!strcasecmp(v->name, "callerid")) {
06870 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06871 if (!strcasecmp(v->value, "asreceived")) {
06872 CLINE_OPTS->cid_num[0] = '\0';
06873 CLINE_OPTS->cid_name[0] = '\0';
06874 } else {
06875 ast_callerid_split(v->value, CLINE_OPTS->cid_name, sizeof(CLINE_OPTS->cid_name), CLINE_OPTS->cid_num, sizeof(CLINE_OPTS->cid_num));
06876 }
06877 continue;
06878 }
06879 } else if (!strcasecmp(v->name, "amaflags")) {
06880 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06881 int tempamaflags = ast_cdr_amaflags2int(v->value);
06882 if (tempamaflags < 0) {
06883 ast_log(LOG_WARNING, "Invalid AMA flags: %s at line %d\n", v->value, v->lineno);
06884 } else {
06885 CLINE_OPTS->amaflags = tempamaflags;
06886 }
06887 continue;
06888 }
06889 } else if (!strcasecmp(v->name, "regexten")) {
06890 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06891 ast_copy_string(CLINE_OPTS->regexten, v->value, sizeof(CLINE_OPTS->regexten));
06892 continue;
06893 }
06894 } else if (!strcasecmp(v->name, "language")) {
06895 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06896 ast_copy_string(CLINE_OPTS->language, v->value, sizeof(CLINE_OPTS->language));
06897 continue;
06898 }
06899 } else if (!strcasecmp(v->name, "accountcode")) {
06900 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06901 ast_copy_string(CLINE_OPTS->accountcode, v->value, sizeof(CLINE_OPTS->accountcode));
06902 continue;
06903 }
06904 } else if (!strcasecmp(v->name, "mohinterpret") || !strcasecmp(v->name, "musiconhold")) {
06905 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06906 ast_copy_string(CLINE_OPTS->mohinterpret, v->value, sizeof(CLINE_OPTS->mohinterpret));
06907 continue;
06908 }
06909 } else if (!strcasecmp(v->name, "mohsuggest")) {
06910 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06911 ast_copy_string(CLINE_OPTS->mohsuggest, v->value, sizeof(CLINE_OPTS->mohsuggest));
06912 continue;
06913 }
06914 } else if (!strcasecmp(v->name, "callgroup")) {
06915 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06916 CLINE_OPTS->callgroup = ast_get_group(v->value);
06917 continue;
06918 }
06919 } else if (!strcasecmp(v->name, "pickupgroup")) {
06920 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06921 CLINE_OPTS->pickupgroup = ast_get_group(v->value);
06922 continue;
06923 }
06924 } else if (!strcasecmp(v->name, "immediate")) {
06925 if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE | TYPE_DEF_LINE | TYPE_LINE)) {
06926 CLINE_OPTS->immediate = ast_true(v->value);
06927 continue;
06928 }
06929 } else if (!strcasecmp(v->name, "cancallforward")) {
06930 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06931 CLINE_OPTS->cancallforward = ast_true(v->value);
06932 continue;
06933 }
06934 } else if (!strcasecmp(v->name, "mailbox")) {
06935 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06936 ast_copy_string(CLINE_OPTS->mailbox, v->value, sizeof(CLINE_OPTS->mailbox));
06937 continue;
06938 }
06939 } else if ( !strcasecmp(v->name, "parkinglot")) {
06940 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06941 ast_copy_string(CLINE_OPTS->parkinglot, v->value, sizeof(CLINE_OPTS->parkinglot));
06942 continue;
06943 }
06944 } else if (!strcasecmp(v->name, "hasvoicemail")) {
06945 if (type & (TYPE_LINE)) {
06946 if (ast_true(v->value) && ast_strlen_zero(CLINE->mailbox)) {
06947 ast_copy_string(CLINE->mailbox, CLINE->name, sizeof(CLINE->mailbox));
06948 }
06949 continue;
06950 }
06951 } else if (!strcasecmp(v->name, "callreturn")) {
06952 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06953 CLINE_OPTS->callreturn = ast_true(v->value);
06954 continue;
06955 }
06956 } else if (!strcasecmp(v->name, "threewaycalling")) {
06957 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
06958 CLINE_OPTS->threewaycalling = ast_true(v->value);
06959 continue;
06960 }
06961 } else if (!strcasecmp(v->name, "setvar")) {
06962 if (type & (TYPE_LINE)) {
06963 CLINE->chanvars = add_var(v->value, CLINE->chanvars);
06964 continue;
06965 }
06966 } else if (!strcasecmp(v->name, "earlyrtp")) {
06967 if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
06968 CDEV_OPTS->earlyrtp = ast_true(v->value);
06969 continue;
06970 }
06971 } else if (!strcasecmp(v->name, "host")) {
06972 if (type & (TYPE_DEVICE)) {
06973 struct ast_sockaddr CDEV_addr_tmp;
06974
06975 CDEV_addr_tmp.ss.ss_family = AF_INET;
06976 if (ast_get_ip(&CDEV_addr_tmp, v->value)) {
06977 ast_log(LOG_WARNING, "Bad IP '%s' at line %d.\n", v->value, v->lineno);
06978 }
06979 ast_sockaddr_to_sin(&CDEV_addr_tmp,
06980 &CDEV->addr);
06981 continue;
06982 }
06983 } else if (!strcasecmp(v->name, "port")) {
06984 if (type & (TYPE_DEF_DEVICE)) {
06985 CDEV->addr.sin_port = htons(atoi(v->value));
06986 continue;
06987 }
06988 } else if (!strcasecmp(v->name, "device")) {
06989 if (type & (TYPE_DEVICE)) {
06990 ast_copy_string(CDEV_OPTS->id, v->value, sizeof(CDEV_OPTS->id));
06991 continue;
06992 }
06993 } else if (!strcasecmp(v->name, "permit") || !strcasecmp(v->name, "deny")) {
06994 if (type & (TYPE_DEVICE)) {
06995 CDEV->ha = ast_append_ha(v->name, v->value, CDEV->ha, NULL);
06996 continue;
06997 }
06998 } else if (!strcasecmp(v->name, "allow")) {
06999 if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
07000 ast_parse_allow_disallow(&CDEV_OPTS->confprefs, &CDEV_OPTS->confcapability, v->value, 1);
07001 continue;
07002 }
07003 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
07004 ast_parse_allow_disallow(&CLINE_OPTS->confprefs, &CLINE_OPTS->confcapability, v->value, 1);
07005 continue;
07006 }
07007 } else if (!strcasecmp(v->name, "disallow")) {
07008 if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
07009 ast_parse_allow_disallow(&CDEV_OPTS->confprefs, &CDEV_OPTS->confcapability, v->value, 0);
07010 continue;
07011 }
07012 if (type & (TYPE_DEF_LINE | TYPE_LINE)) {
07013 ast_parse_allow_disallow(&CLINE_OPTS->confprefs, &CLINE_OPTS->confcapability, v->value, 0);
07014 continue;
07015 }
07016 } else if (!strcasecmp(v->name, "version")) {
07017 if (type & (TYPE_DEF_DEVICE | TYPE_DEVICE)) {
07018 ast_copy_string(CDEV_OPTS->version_id, v->value, sizeof(CDEV_OPTS->version_id));
07019 continue;
07020 }
07021 } else if (!strcasecmp(v->name, "line")) {
07022 if (type & (TYPE_DEVICE)) {
07023 struct skinny_line *l;
07024 AST_LIST_TRAVERSE(&lines, l, all) {
07025 if (!strcasecmp(v->value, l->name) && !l->prune) {
07026
07027
07028 struct skinny_device *d;
07029 struct skinny_line *l2;
07030 int lineinuse = 0;
07031 AST_LIST_TRAVERSE(&devices, d, list) {
07032 AST_LIST_TRAVERSE(&d->lines, l2, list) {
07033 if (l2 == l && strcasecmp(d->id, CDEV->id)) {
07034 ast_log(LOG_WARNING, "Line %s already used by %s. Not connecting to %s.\n", l->name, d->name, CDEV->name);
07035 lineinuse++;
07036 }
07037 }
07038 }
07039 if (!lineinuse) {
07040 if (!AST_LIST_FIRST(&CDEV->lines)) {
07041 CDEV->activeline = l;
07042 }
07043 lineInstance++;
07044 AST_LIST_INSERT_HEAD(&CDEV->lines, l, list);
07045 }
07046 break;
07047 }
07048 }
07049 continue;
07050 }
07051 } else if (!strcasecmp(v->name, "speeddial")) {
07052 if (type & (TYPE_DEVICE)) {
07053 struct skinny_speeddial *sd;
07054 if (!(sd = ast_calloc(1, sizeof(*sd)))) {
07055 ast_log(LOG_WARNING, "Unable to allocate memory for speeddial %s. Ignoring speeddial.\n", v->name);
07056 continue;
07057 } else {
07058 char buf[256];
07059 char *stringp = buf, *exten, *context, *label;
07060 ast_copy_string(buf, v->value, sizeof(buf));
07061 exten = strsep(&stringp, ",");
07062 if ((context = strchr(exten, '@'))) {
07063 *context++ = '\0';
07064 }
07065 label = stringp;
07066 ast_mutex_init(&sd->lock);
07067 ast_copy_string(sd->exten, exten, sizeof(sd->exten));
07068 if (!ast_strlen_zero(context)) {
07069 sd->isHint = 1;
07070 sd->instance = lineInstance++;
07071 ast_copy_string(sd->context, context, sizeof(sd->context));
07072 } else {
07073 sd->isHint = 0;
07074 sd->instance = speeddialInstance++;
07075 sd->context[0] = '\0';
07076 }
07077 ast_copy_string(sd->label, S_OR(label, exten), sizeof(sd->label));
07078 sd->parent = CDEV;
07079 AST_LIST_INSERT_HEAD(&CDEV->speeddials, sd, list);
07080 }
07081 continue;
07082 }
07083 } else if (!strcasecmp(v->name, "addon")) {
07084 if (type & (TYPE_DEVICE)) {
07085 struct skinny_addon *a;
07086 if (!(a = ast_calloc(1, sizeof(*a)))) {
07087 ast_log(LOG_WARNING, "Unable to allocate memory for addon %s. Ignoring addon.\n", v->name);
07088 continue;
07089 } else {
07090 ast_mutex_init(&a->lock);
07091 ast_copy_string(a->type, v->value, sizeof(a->type));
07092 AST_LIST_INSERT_HEAD(&CDEV->addons, a, list);
07093 }
07094 continue;
07095 }
07096
07097 } else {
07098 ast_log(LOG_WARNING, "Don't know keyword '%s' at line %d\n", v->name, v->lineno);
07099 continue;
07100 }
07101 ast_log(LOG_WARNING, "Invalid category used: %s at line %d\n", v->name, v->lineno);
07102 }
07103 }
07104
07105 static struct skinny_line *config_line(const char *lname, struct ast_variable *v)
07106 {
07107 struct skinny_line *l, *temp;
07108 int update = 0;
07109
07110 ast_log(LOG_NOTICE, "Configuring skinny line %s.\n", lname);
07111
07112
07113
07114 AST_LIST_LOCK(&lines);
07115 AST_LIST_TRAVERSE(&lines, temp, all) {
07116 if (!strcasecmp(lname, temp->name) && temp->prune) {
07117 update = 1;
07118 break;
07119 }
07120 }
07121
07122 if (!(l=ast_calloc(1, sizeof(*l)))) {
07123 ast_verb(1, "Unable to allocate memory for line %s.\n", lname);
07124 AST_LIST_UNLOCK(&lines);
07125 return NULL;
07126 }
07127
07128 memcpy(l, default_line, sizeof(*default_line));
07129 ast_mutex_init(&l->lock);
07130 ast_copy_string(l->name, lname, sizeof(l->name));
07131 AST_LIST_INSERT_TAIL(&lines, l, all);
07132
07133 ast_mutex_lock(&l->lock);
07134 AST_LIST_UNLOCK(&lines);
07135
07136 config_parse_variables(TYPE_LINE, l, v);
07137
07138 if (!ast_strlen_zero(l->mailbox)) {
07139 char *cfg_mailbox, *cfg_context;
07140 cfg_context = cfg_mailbox = ast_strdupa(l->mailbox);
07141 ast_verb(3, "Setting mailbox '%s' on line %s\n", cfg_mailbox, l->name);
07142 strsep(&cfg_context, "@");
07143 if (ast_strlen_zero(cfg_context))
07144 cfg_context = "default";
07145 l->mwi_event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, "skinny MWI subsciption", l,
07146 AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, cfg_mailbox,
07147 AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, cfg_context,
07148 AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_EXISTS,
07149 AST_EVENT_IE_END);
07150 }
07151
07152 ast_mutex_unlock(&l->lock);
07153
07154
07155
07156
07157
07158 ast_verb(3, "%s config for line '%s'\n", update ? "Updated" : (skinnyreload ? "Reloaded" : "Created"), l->name);
07159
07160 return l;
07161 }
07162
07163 static struct skinny_device *config_device(const char *dname, struct ast_variable *v)
07164 {
07165 struct skinny_device *d, *temp;
07166 struct skinny_line *l, *ltemp;
07167 struct skinny_subchannel *sub;
07168 int update = 0;
07169
07170 ast_log(LOG_NOTICE, "Configuring skinny device %s.\n", dname);
07171
07172 AST_LIST_LOCK(&devices);
07173 AST_LIST_TRAVERSE(&devices, temp, list) {
07174 if (!strcasecmp(dname, temp->name) && temp->prune) {
07175 update = 1;
07176 break;
07177 }
07178 }
07179
07180 if (!(d = ast_calloc(1, sizeof(*d)))) {
07181 ast_verb(1, "Unable to allocate memory for device %s.\n", dname);
07182 AST_LIST_UNLOCK(&devices);
07183 return NULL;
07184 }
07185 memcpy(d, default_device, sizeof(*default_device));
07186 ast_mutex_init(&d->lock);
07187 ast_copy_string(d->name, dname, sizeof(d->name));
07188 AST_LIST_INSERT_TAIL(&devices, d, list);
07189
07190 ast_mutex_lock(&d->lock);
07191 AST_LIST_UNLOCK(&devices);
07192
07193 config_parse_variables(TYPE_DEVICE, d, v);
07194
07195 if (!AST_LIST_FIRST(&d->lines)) {
07196 ast_log(LOG_ERROR, "A Skinny device must have at least one line!\n");
07197 ast_mutex_unlock(&d->lock);
07198 return NULL;
07199 }
07200 if (!ntohs(d->addr.sin_port)) {
07201 d->addr.sin_port = htons(DEFAULT_SKINNY_PORT);
07202 }
07203
07204 if (skinnyreload){
07205 AST_LIST_LOCK(&devices);
07206 AST_LIST_TRAVERSE(&devices, temp, list) {
07207 if (strcasecmp(d->id, temp->id) || !temp->prune || !temp->session) {
07208 continue;
07209 }
07210 ast_mutex_lock(&d->lock);
07211 d->session = temp->session;
07212 d->session->device = d;
07213
07214 AST_LIST_LOCK(&d->lines);
07215 AST_LIST_TRAVERSE(&d->lines, l, list){
07216 l->device = d;
07217
07218 AST_LIST_LOCK(&temp->lines);
07219 AST_LIST_TRAVERSE(&temp->lines, ltemp, list) {
07220 if (strcasecmp(l->name, ltemp->name)) {
07221 continue;
07222 }
07223 ast_mutex_lock(<emp->lock);
07224 l->instance = ltemp->instance;
07225 l->hookstate = ltemp->hookstate;
07226 if (!AST_LIST_EMPTY(<emp->sub)) {
07227 ast_mutex_lock(&l->lock);
07228 l->sub = ltemp->sub;
07229 AST_LIST_TRAVERSE(&l->sub, sub, list) {
07230 sub->parent = l;
07231 }
07232 ast_mutex_unlock(&l->lock);
07233 }
07234 ast_mutex_unlock(<emp->lock);
07235 }
07236 AST_LIST_UNLOCK(&temp->lines);
07237 }
07238 AST_LIST_UNLOCK(&d->lines);
07239 ast_mutex_unlock(&d->lock);
07240 }
07241 AST_LIST_UNLOCK(&devices);
07242 }
07243
07244 ast_mutex_unlock(&d->lock);
07245
07246 ast_verb(3, "%s config for device '%s'\n", update ? "Updated" : (skinnyreload ? "Reloaded" : "Created"), d->name);
07247
07248 return d;
07249
07250 }
07251
07252 static int config_load(void)
07253 {
07254 int on = 1;
07255 struct ast_config *cfg;
07256 char *cat;
07257 int oldport = ntohs(bindaddr.sin_port);
07258 struct ast_flags config_flags = { 0 };
07259
07260 ast_log(LOG_NOTICE, "Configuring skinny from %s\n", config);
07261
07262 if (gethostname(ourhost, sizeof(ourhost))) {
07263 ast_log(LOG_WARNING, "Unable to get hostname, Skinny disabled.\n");
07264 return 0;
07265 }
07266 cfg = ast_config_load(config, config_flags);
07267
07268
07269 if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
07270 ast_log(LOG_NOTICE, "Unable to load config %s, Skinny disabled.\n", config);
07271 return -1;
07272 }
07273 memset(&bindaddr, 0, sizeof(bindaddr));
07274 memset(&default_prefs, 0, sizeof(default_prefs));
07275
07276
07277 memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
07278
07279
07280 cat = ast_category_browse(cfg, "general");
07281 config_parse_variables(TYPE_GENERAL, NULL, ast_variable_browse(cfg, "general"));
07282
07283 if (ntohl(bindaddr.sin_addr.s_addr)) {
07284 __ourip = bindaddr.sin_addr;
07285 } else {
07286 hp = ast_gethostbyname(ourhost, &ahp);
07287 if (!hp) {
07288 ast_log(LOG_WARNING, "Unable to get our IP address, Skinny disabled\n");
07289 ast_config_destroy(cfg);
07290 return 0;
07291 }
07292 memcpy(&__ourip, hp->h_addr, sizeof(__ourip));
07293 }
07294 if (!ntohs(bindaddr.sin_port)) {
07295 bindaddr.sin_port = htons(DEFAULT_SKINNY_PORT);
07296 }
07297 bindaddr.sin_family = AF_INET;
07298
07299
07300 default_line->confcapability = default_capability;
07301 default_line->confprefs = default_prefs;
07302 config_parse_variables(TYPE_DEF_LINE, default_line, ast_variable_browse(cfg, "lines"));
07303 cat = ast_category_browse(cfg, "lines");
07304 while (cat && strcasecmp(cat, "general") && strcasecmp(cat, "devices")) {
07305 config_line(cat, ast_variable_browse(cfg, cat));
07306 cat = ast_category_browse(cfg, cat);
07307 }
07308
07309
07310 default_device->confcapability = default_capability;
07311 default_device->confprefs = default_prefs;
07312 config_parse_variables(TYPE_DEF_DEVICE, default_device, ast_variable_browse(cfg, "devices"));
07313 cat = ast_category_browse(cfg, "devices");
07314 while (cat && strcasecmp(cat, "general") && strcasecmp(cat, "lines")) {
07315 config_device(cat, ast_variable_browse(cfg, cat));
07316 cat = ast_category_browse(cfg, cat);
07317 }
07318
07319 ast_mutex_lock(&netlock);
07320 if ((skinnysock > -1) && (ntohs(bindaddr.sin_port) != oldport)) {
07321 close(skinnysock);
07322 skinnysock = -1;
07323 }
07324 if (skinnysock < 0) {
07325 skinnysock = socket(AF_INET, SOCK_STREAM, 0);
07326 if(setsockopt(skinnysock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
07327 ast_log(LOG_ERROR, "Set Socket Options failed: errno %d, %s\n", errno, strerror(errno));
07328 ast_config_destroy(cfg);
07329 ast_mutex_unlock(&netlock);
07330 return 0;
07331 }
07332 if (skinnysock < 0) {
07333 ast_log(LOG_WARNING, "Unable to create Skinny socket: %s\n", strerror(errno));
07334 } else {
07335 if (bind(skinnysock, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) {
07336 ast_log(LOG_WARNING, "Failed to bind to %s:%d: %s\n",
07337 ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port),
07338 strerror(errno));
07339 close(skinnysock);
07340 skinnysock = -1;
07341 ast_config_destroy(cfg);
07342 ast_mutex_unlock(&netlock);
07343 return 0;
07344 }
07345 if (listen(skinnysock, DEFAULT_SKINNY_BACKLOG)) {
07346 ast_log(LOG_WARNING, "Failed to start listening to %s:%d: %s\n",
07347 ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port),
07348 strerror(errno));
07349 close(skinnysock);
07350 skinnysock = -1;
07351 ast_config_destroy(cfg);
07352 ast_mutex_unlock(&netlock);
07353 return 0;
07354 }
07355 ast_verb(2, "Skinny listening on %s:%d\n",
07356 ast_inet_ntoa(bindaddr.sin_addr), ntohs(bindaddr.sin_port));
07357 ast_netsock_set_qos(skinnysock, qos.tos, qos.cos, "Skinny");
07358 ast_pthread_create_background(&accept_t, NULL, accept_thread, NULL);
07359 }
07360 }
07361 ast_mutex_unlock(&netlock);
07362 ast_config_destroy(cfg);
07363 return 1;
07364 }
07365
07366 static void delete_devices(void)
07367 {
07368 struct skinny_device *d;
07369 struct skinny_line *l;
07370 struct skinny_speeddial *sd;
07371 struct skinny_addon *a;
07372
07373 AST_LIST_LOCK(&devices);
07374 AST_LIST_LOCK(&lines);
07375
07376
07377 while ((d = AST_LIST_REMOVE_HEAD(&devices, list))) {
07378
07379 while ((l = AST_LIST_REMOVE_HEAD(&d->lines, list))) {
07380 AST_LIST_REMOVE(&lines, l, all);
07381 free(l);
07382 }
07383
07384 while ((sd = AST_LIST_REMOVE_HEAD(&d->speeddials, list))) {
07385 free(sd);
07386 }
07387
07388 while ((a = AST_LIST_REMOVE_HEAD(&d->addons, list))) {
07389 free(a);
07390 }
07391 free(d);
07392 }
07393 AST_LIST_UNLOCK(&lines);
07394 AST_LIST_UNLOCK(&devices);
07395 }
07396
07397 int skinny_reload(void)
07398 {
07399 struct skinny_device *d;
07400 struct skinny_line *l;
07401 struct skinny_speeddial *sd;
07402 struct skinny_addon *a;
07403 struct skinny_req *req;
07404
07405 if (skinnyreload) {
07406 ast_verb(3, "Chan_skinny is already reloading.\n");
07407 return 0;
07408 }
07409
07410 skinnyreload = 1;
07411
07412
07413 AST_LIST_LOCK(&devices);
07414 AST_LIST_TRAVERSE(&devices, d, list) {
07415 d->prune = 1;
07416 }
07417 AST_LIST_UNLOCK(&devices);
07418
07419 AST_LIST_LOCK(&lines);
07420 AST_LIST_TRAVERSE(&lines, l, all) {
07421 l->prune = 1;
07422 }
07423 AST_LIST_UNLOCK(&lines);
07424
07425 config_load();
07426
07427
07428 AST_LIST_LOCK(&devices);
07429 AST_LIST_TRAVERSE_SAFE_BEGIN(&devices, d, list) {
07430 if (!d->prune) {
07431 continue;
07432 }
07433 ast_verb(3, "Removing device '%s'\n", d->name);
07434
07435
07436
07437 while ((l = AST_LIST_REMOVE_HEAD(&d->lines, list))) {
07438 }
07439
07440 while ((sd = AST_LIST_REMOVE_HEAD(&d->speeddials, list))) {
07441 free(sd);
07442 }
07443
07444 while ((a = AST_LIST_REMOVE_HEAD(&d->addons, list))) {
07445 free(a);
07446 }
07447 AST_LIST_REMOVE_CURRENT(list);
07448 free(d);
07449 }
07450 AST_LIST_TRAVERSE_SAFE_END;
07451 AST_LIST_UNLOCK(&devices);
07452
07453 AST_LIST_LOCK(&lines);
07454 AST_LIST_TRAVERSE_SAFE_BEGIN(&lines, l, all) {
07455 if (l->prune) {
07456 AST_LIST_REMOVE_CURRENT(all);
07457 free(l);
07458 }
07459 }
07460 AST_LIST_TRAVERSE_SAFE_END;
07461 AST_LIST_UNLOCK(&lines);
07462
07463 AST_LIST_TRAVERSE(&devices, d, list) {
07464
07465
07466 if (d->session) {
07467 ast_verb(3, "Restarting device '%s'\n", d->name);
07468 if ((req = req_alloc(sizeof(struct reset_message), RESET_MESSAGE))) {
07469 req->data.reset.resetType = 2;
07470 transmit_response(d, req);
07471 }
07472 }
07473 }
07474
07475 skinnyreload = 0;
07476 return 0;
07477 }
07478
07479 static int load_module(void)
07480 {
07481 int res = 0;
07482
07483 for (; res < ARRAY_LEN(soft_key_template_default); res++) {
07484 soft_key_template_default[res].softKeyEvent = htolel(soft_key_template_default[res].softKeyEvent);
07485 }
07486
07487 res = config_load();
07488 if (res == -1) {
07489 return AST_MODULE_LOAD_DECLINE;
07490 }
07491
07492
07493 if (ast_channel_register(&skinny_tech)) {
07494 ast_log(LOG_ERROR, "Unable to register channel class 'Skinny'\n");
07495 return -1;
07496 }
07497
07498 ast_rtp_glue_register(&skinny_rtp_glue);
07499 ast_cli_register_multiple(cli_skinny, ARRAY_LEN(cli_skinny));
07500
07501 ast_manager_register_xml("SKINNYdevices", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_devices);
07502 ast_manager_register_xml("SKINNYshowdevice", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_device);
07503 ast_manager_register_xml("SKINNYlines", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_lines);
07504 ast_manager_register_xml("SKINNYshowline", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_skinny_show_line);
07505
07506 sched = sched_context_create();
07507 if (!sched) {
07508 ast_log(LOG_WARNING, "Unable to create schedule context\n");
07509 }
07510 io = io_context_create();
07511 if (!io) {
07512 ast_log(LOG_WARNING, "Unable to create I/O context\n");
07513 }
07514
07515 restart_monitor();
07516
07517 return AST_MODULE_LOAD_SUCCESS;
07518 }
07519
07520 static int unload_module(void)
07521 {
07522 struct skinnysession *s;
07523 struct skinny_device *d;
07524 struct skinny_line *l;
07525 struct skinny_subchannel *sub;
07526 struct ast_context *con;
07527
07528 ast_rtp_glue_unregister(&skinny_rtp_glue);
07529 ast_channel_unregister(&skinny_tech);
07530 ast_cli_unregister_multiple(cli_skinny, ARRAY_LEN(cli_skinny));
07531
07532 ast_manager_unregister("SKINNYdevices");
07533 ast_manager_unregister("SKINNYshowdevice");
07534 ast_manager_unregister("SKINNYlines");
07535 ast_manager_unregister("SKINNYshowline");
07536
07537 AST_LIST_LOCK(&sessions);
07538
07539 while((s = AST_LIST_REMOVE_HEAD(&sessions, list))) {
07540 d = s->device;
07541 AST_LIST_TRAVERSE(&d->lines, l, list){
07542 ast_mutex_lock(&l->lock);
07543 AST_LIST_TRAVERSE(&l->sub, sub, list) {
07544 ast_mutex_lock(&sub->lock);
07545 if (sub->owner) {
07546 sub->alreadygone = 1;
07547 ast_softhangup(sub->owner, AST_SOFTHANGUP_APPUNLOAD);
07548 }
07549 ast_mutex_unlock(&sub->lock);
07550 }
07551 if (l->mwi_event_sub)
07552 ast_event_unsubscribe(l->mwi_event_sub);
07553 ast_mutex_unlock(&l->lock);
07554 manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: Skinny\r\nPeer: Skinny/%s@%s\r\nPeerStatus: Unregistered\r\n", l->name, d->name);
07555 unregister_exten(l);
07556 }
07557 if (s->fd > -1)
07558 close(s->fd);
07559 pthread_cancel(s->t);
07560 pthread_kill(s->t, SIGURG);
07561 pthread_join(s->t, NULL);
07562 free(s);
07563 }
07564 AST_LIST_UNLOCK(&sessions);
07565
07566 delete_devices();
07567
07568 ast_mutex_lock(&monlock);
07569 if ((monitor_thread != AST_PTHREADT_NULL) && (monitor_thread != AST_PTHREADT_STOP)) {
07570 pthread_cancel(monitor_thread);
07571 pthread_kill(monitor_thread, SIGURG);
07572 pthread_join(monitor_thread, NULL);
07573 }
07574 monitor_thread = AST_PTHREADT_STOP;
07575 ast_mutex_unlock(&monlock);
07576
07577 ast_mutex_lock(&netlock);
07578 if (accept_t && (accept_t != AST_PTHREADT_STOP)) {
07579 pthread_cancel(accept_t);
07580 pthread_kill(accept_t, SIGURG);
07581 pthread_join(accept_t, NULL);
07582 }
07583 accept_t = AST_PTHREADT_STOP;
07584 ast_mutex_unlock(&netlock);
07585
07586 close(skinnysock);
07587 if (sched)
07588 sched_context_destroy(sched);
07589
07590 con = ast_context_find(used_context);
07591 if (con)
07592 ast_context_destroy(con, "Skinny");
07593
07594 return 0;
07595 }
07596
07597 static int reload(void)
07598 {
07599 skinny_reload();
07600 return 0;
07601 }
07602
07603 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Skinny Client Control Protocol (Skinny)",
07604 .load = load_module,
07605 .unload = unload_module,
07606 .reload = reload,
07607 .load_pri = AST_MODPRI_CHANNEL_DRIVER,
07608 );