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