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