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