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