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