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