1 /*!
2 * \file sccp_conference.c
3 * \brief SCCP Conference for asterisk 10
4 * \author Marcello Ceschia <marcelloceschia [at] users.sorceforge.net>
5 * \note Reworked, but based on chan_sccp code.
6 *
7 */
8
9 #include "config.h"
10 #include "common.h"
11 #include "sccp_conference.h"
12 #include "sccp_channel.h"
13 #include "sccp_atomic.h"
14 #include "sccp_device.h"
15 #include "sccp_indicate.h"
16 #include "sccp_line.h"
17 #include "sccp_linedevice.h"
18 #include "sccp_utils.h"
19 #include "sccp_labels.h"
20 #include "sccp_threadpool.h"
21 #include <asterisk/say.h>
22
23 /*** DOCUMENTATION
24 <manager name="SCCPConference" language="en_US">
25 <synopsis>Control sccp conference and it's participants.</synopsis>
26 <syntax>
27 <xi:include href="../core-en_US.xml" parse="xml"
28 xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])"/>
29 <parameter name="ConferenceId" required="true">
30 <para>Id of the conference</para>
31 </parameter>
32 <parameter name="ParticipantId">
33 <para>Id of the participant</para>
34 </parameter>
35 <parameter name="Command" required="true">
36 <para>Command to be executed on this <replaceable>ConferenceId</replaceable> and <replaceable>ParticipantId</replaceable> combination</para>
37 <enumlist>
38 <enum name="EndConf">
39 <para>End the conference specified via <replaceable>ConferenceId</replaceable>.</para>
40 </enum>
41 <enum name="Kick">
42 <para>Kick a <replaceable>ParticipantId</replaceable> out of <replaceable>ConferenceId</replaceable>.</para>
43 </enum>
44 <enum name="Mute">
45 <para>Mute <replaceable>ParticipantId</replaceable> on <replaceable>ConferenceId</replaceable>.</para>
46 </enum>
47 <enum name="Moderate">
48 <para>Promote <replaceable>ParticipantId</replaceable> on <replaceable>ConferenceId</replaceable> to moderator status.</para>
49 </enum>
50 <enum name="Invite">
51 <para>Invite a new participant to this <replaceable>ConferenceId</replaceable> (Not implemented).</para>
52 </enum>
53 </enumlist>
54 </parameter>
55 </syntax>
56 <description>
57 <para>Control sccp conference and it's participants.</para>
58 </description>
59 </manager>
60 ***/
61
62 #ifdef CS_SCCP_CONFERENCE
63
64 #if ASTERISK_VERSION_GROUP < 112
65 #include <asterisk/bridging.h>
66 #include <asterisk/bridging_features.h>
67 #else
68 #include <asterisk/bridge.h>
69 #include <asterisk/bridge_channel.h>
70 #include <asterisk/bridge_features.h>
71 #include <asterisk/bridge_technology.h>
72 #endif
73 #ifdef HAVE_PBX_BRIDGING_ROLES_H
74 #include <asterisk/bridging_roles.h>
75 #endif
76 #include <asterisk/callerid.h>
77 #include <asterisk/causes.h> // for AST_CAUSE_NORMAL_CLEARING
78 #if ASTERISK_VERSION_GROUP >= 113
79 #include <asterisk/format_cap.h> // for AST_FORMAT_CAP_NAMES_LEN
80 #endif
81
82 #define sccp_participant_retain(_x) sccp_refcount_retain_type(sccp_participant_t, _x)
83 #define sccp_participant_release(_x) sccp_refcount_release_type(sccp_participant_t, _x)
84 #define sccp_participant_refreplace(_x, _y) sccp_refcount_refreplace_type(sccp_participant_t, _x, _y)
85
86 SCCP_FILE_VERSION(__FILE__, "");
87 static uint32_t lastConferenceID = 99;
88 static const uint32_t appID = APPID_CONFERENCE;
89 typedef struct sccp_participant sccp_participant_t; /*!< SCCP Conference Participant Structure */
90
91 /* structures */
92 struct sccp_conference {
93 ast_mutex_t lock; /*!< mutex */
94 uint32_t id; /*!< conference id */
95 int32_t num_moderators; /*!< Number of moderators for this conference */
96 const char *linkedid; /*!< Conference LinkedId */
97 struct ast_bridge *bridge; /*!< Shared Ast_Bridge used by this conference */
98 struct {
99 ast_mutex_t lock; /*!< Mutex Lock for playing back sound files */
100 char language[SCCP_MAX_LANGUAGE]; /*!< Language to be used during playback */
101 PBX_CHANNEL_TYPE *channel; /*!< Channel to playback sound file on */
102 } playback;
103
104 SCCP_RWLIST_HEAD (, sccp_participant_t) participants; /*!< participants in conference */
105 SCCP_LIST_ENTRY (sccp_conference_t) list; /*!< Linked List Entry */
106
107 volatile int finishing; /*!< Indicates the conference is closing down */
108 boolean_t isLocked; /*!< Indicates that no new participants are allowed */
109 boolean_t isOnHold;
110 boolean_t mute_on_entry; /*!< Mute new participant when they enter the conference */
111 boolean_t playback_announcements; /*!< general hear announcements */
112 }; /*!< SCCP Conference Structure */
113
114 struct sccp_participant {
115 boolean_t pendingRemoval; /*!< Pending Removal */
116 uint32_t id; /*!< Numeric participant id. */
117 sccp_channel_t *channel; /*!< sccp channel, non-null if the participant resides on an SCCP device */
118 sccp_device_t *device; /*!< sccp device, non-null if the participant resides on an SCCP device */
119 PBX_CHANNEL_TYPE *conferenceBridgePeer; /*!< the asterisk channel which joins the conference bridge */
120 struct ast_bridge_channel *bridge_channel; /*!< Asterisk Conference Bridge Channel */
121 pthread_t joinThread; /*!< Running in this Thread */
122 sccp_conference_t *conference; /*!< Conference this participant belongs to */
123 char *final_announcement; /*!< Announcement playedback to participant after leaving the bridge */
124 boolean_t isModerator; /*!< Is Participant a Moderator */
125 boolean_t onMusicOnHold; /*!< Participant is listening to Music on Hold */
126 boolean_t playback_announcements; /*!< Does the Participant want to hear announcements */
127 uint32_t callReference; /* used to push/update conflist */
128 uint32_t lineInstance; /* used to push/update conflist */
129 uint32_t transactionID; /* used to push/update conflist */
130
131 SCCP_RWLIST_ENTRY (sccp_participant_t) list; /*!< Linked List Entry */
132
133 char PartyName[StationMaxNameSize];
134 char PartyNumber[StationMaxDirnumSize];
135
136 struct ast_bridge_features features; /*!< Enabled features information */
137 }; /*!< SCCP Conference Participant Structure */
138
139 static SCCP_LIST_HEAD (, sccp_conference_t) conferences; /*!< our list of conferences */
140
141 #define participantPtr sccp_participant_t *const
142 #define constParticipantPtr const sccp_participant_t *const
143
144 static void *sccp_conference_thread(void *data);
145 void sccp_conference_update_callInfo(constChannelPtr channel, PBX_CHANNEL_TYPE * pbxChannel, constParticipantPtr participant, uint32_t conferenceID);
146 int playback_to_channel(participantPtr participant, const char *filename, int say_number);
147 int playback_to_conference(conferencePtr conference, const char *filename, int say_number);
148 sccp_conference_t *sccp_conference_findByID(uint32_t identifier);
149 sccp_participant_t *sccp_participant_findByID(constConferencePtr conference, uint32_t identifier);
150 sccp_participant_t *sccp_participant_findByChannel(constConferencePtr conference, constChannelPtr channel);
151 sccp_participant_t *sccp_participant_findByDevice(constConferencePtr conference, constDevicePtr device);
152 sccp_participant_t *sccp_participant_findByPBXChannel(constConferencePtr conference, PBX_CHANNEL_TYPE * channel);
153 void sccp_conference_play_music_on_hold_to_participant(constConferencePtr conference, participantPtr participant, boolean_t start);
154 static sccp_participant_t *sccp_conference_createParticipant(constConferencePtr conference);
155 static void sccp_conference_addParticipant_toList(constConferencePtr conference, constParticipantPtr participant);
156 void pbx_builtin_setvar_int_helper(PBX_CHANNEL_TYPE * channel, const char *var_name, int intvalue);
157 //static void sccp_conference_connect_bridge_channels_to_participants(constConferencePtr conference);
158 static void sccp_conference_update_conflist(conferencePtr conference);
159 void __sccp_conference_hide_list(participantPtr participant);
160 void sccp_conference_invite_participant(constConferencePtr conference, constParticipantPtr moderator);
161 void sccp_conference_kick_participant(constConferencePtr conference, participantPtr participant);
162 void *sccp_participant_kicker(void *data);
163 void sccp_conference_toggle_mute_participant(constConferencePtr conference, participantPtr participant);
164 void sccp_conference_promote_demote_participant(conferencePtr conference, participantPtr participant, constParticipantPtr moderator);
165
166 /*!
167 * \brief Start Conference Module
168 */
sccp_conference_module_start(void)169 void sccp_conference_module_start(void)
170 {
171 SCCP_LIST_HEAD_INIT(&conferences);
172 }
173
174 /*!
175 * \brief Start Conference Module
176 */
sccp_conference_module_stop(void)177 void sccp_conference_module_stop(void)
178 {
179 SCCP_LIST_HEAD_DESTROY(&conferences);
180 }
181
182 /*
183 * \brief Cleanup after conference refcount goes to zero (refcount destroy)
184 */
__sccp_conference_destroy(const void * data)185 static int __sccp_conference_destroy(const void *data)
186 {
187 conferencePtr conference = (conferencePtr) data;
188 if (!conference) {
189 return -1;
190 }
191
192 if (conference->playback.channel) {
193 sccp_log_and((DEBUGCAT_CONFERENCE + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: Destroying conference playback channel\n", conference->id);
194 #if ASTERISK_VERSION_GROUP < 112
195 PBX_CHANNEL_TYPE *underlying_channel = NULL;
196 if ((underlying_channel = iPbx.get_underlying_channel(conference->playback.channel))) {
197 pbx_hangup(underlying_channel);
198 pbx_hangup(conference->playback.channel);
199 pbx_channel_unref(underlying_channel);
200 }
201 #else
202 sccpconf_announce_channel_depart(conference->playback.channel);
203 pbx_hangup(conference->playback.channel);
204 #endif
205 conference->playback.channel = NULL;
206 }
207 sccp_log((DEBUGCAT_CONFERENCE + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: Destroying conference\n", conference->id);
208 sccp_free(conference->linkedid);
209 if (conference->bridge) {
210 pbx_bridge_destroy(conference->bridge, AST_CAUSE_NORMAL_CLEARING);
211 }
212 SCCP_RWLIST_HEAD_DESTROY(&conference->participants);
213 pbx_mutex_destroy(&conference->playback.lock);
214
215 #ifdef CS_MANAGER_EVENTS
216 if (GLOB(callevents)) {
217 manager_event(EVENT_FLAG_USER, "SCCPConfEnd", "ConfId: %d\r\n", conference->id);
218 }
219 #endif
220 return 0;
221 }
222
223 /*
224 * \brief Cleanup after participant refcount goes to zero (refcount destroy)
225 */
__sccp_participant_destroy(const void * data)226 static int __sccp_participant_destroy(const void *data)
227 {
228 sccp_participant_t * participant = (sccp_participant_t *)data;
229 sccp_log_and((DEBUGCAT_CONFERENCE + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: Destroying participant %d %p\n", participant->conference->id, participant->id, participant);
230
231 if (participant->isModerator && participant->conference) {
232 participant->conference->num_moderators--;
233 }
234 pbx_bridge_features_cleanup(&participant->features);
235 #ifdef CS_MANAGER_EVENTS
236 if (GLOB(callevents)) {
237 /*
238 manager_event(EVENT_FLAG_CALL, "SCCPConfLeave", "ConfId: %d\r\n" "PartId: %d\r\n" "Channel: %s\r\n" "Uniqueid: %s\r\n",
239 participant->conference ? participant->conference->id : -1,
240 participant->id,
241 participant->conferenceBridgePeer ? pbx_channel_name(participant->conferenceBridgePeer) : "NULL",
242 participant->conferenceBridgePeer ? pbx_channel_uniqueid(participant->conferenceBridgePeer) : "NULL"
243 );
244 */
245 manager_event(EVENT_FLAG_CALL, "SCCPConfLeave", "ConfId: %d\r\n" "PartId: %d\r\n", participant->conference ? participant->conference->id : 0, participant->id);
246 }
247 #endif
248 if (participant->channel) {
249 #if CS_REFCOUNT_DEBUG
250 sccp_refcount_removeRelationship(participant->channel, participant);
251 #endif
252 participant->channel->conference_id = 0;
253 participant->channel->conference_participant_id = 0;
254 if (participant->channel->conference) {
255 sccp_conference_release(&participant->channel->conference); /* explicit release */
256 }
257 sccp_channel_release(&participant->channel); /* explicit release */
258 }
259 if (participant->device) {
260 #if CS_REFCOUNT_DEBUG
261 sccp_refcount_removeRelationship(participant->device, participant);
262 #endif
263 participant->device->conferencelist_active = FALSE;
264 if (participant->device->conference) {
265 sccp_conference_release(&participant->device->conference); /* explicit release */
266 }
267 sccp_device_release(&participant->device); /* explicit release */
268 }
269 sccp_conference_release(&participant->conference); /* explicit release */
270 return 0;
271 }
272
273 /* ============================================================================================================================ Conference Functions === */
274 /*!
275 * \brief Create a new conference on sccp_channel_t
276 */
sccp_conference_create(devicePtr device,channelPtr channel)277 sccp_conference_t *sccp_conference_create(devicePtr device, channelPtr channel)
278 {
279 sccp_conference_t *conference = NULL;
280 char conferenceIdentifier[REFCOUNT_INDENTIFIER_SIZE];
281 int conferenceID = ++lastConferenceID;
282 uint32_t bridgeCapabilities = 0;
283
284 sccp_log((DEBUGCAT_CORE + DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_3 "SCCP: Creating new conference SCCPCONF/%04d\n", conferenceID);
285
286 /** create conference */
287 snprintf(conferenceIdentifier, REFCOUNT_INDENTIFIER_SIZE, "SCCPCONF/%04d", conferenceID);
288 conference = (conferencePtr) sccp_refcount_object_alloc(sizeof(sccp_conference_t), SCCP_REF_CONFERENCE, conferenceIdentifier, __sccp_conference_destroy);
289 if (!conference) {
290 pbx_log(LOG_ERROR, "SCCPCONF/%04d: cannot alloc memory for new conference.\n", conferenceID);
291 return NULL;
292 }
293 /** initialize new conference */
294 memset(conference, 0, sizeof(sccp_conference_t));
295 conference->id = conferenceID;
296 conference->finishing = FALSE;
297 conference->isLocked = FALSE;
298 conference->isOnHold = FALSE;
299 conference->linkedid = pbx_strdup(iPbx.getChannelLinkedId(channel));
300 if (device->conf_mute_on_entry) {
301 sccp_log((DEBUGCAT_CORE + DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_3 "SCCP: Device: %s Mute on Entry: On -> All participant of conference: SCCPCONF/%04d, will be muted\n", DEV_ID_LOG(device), conferenceID);
302 conference->mute_on_entry = device->conf_mute_on_entry;
303 }
304 conference->playback_announcements = device->conf_play_general_announce;
305 sccp_copy_string(conference->playback.language, pbx_channel_language(channel->owner), sizeof(conference->playback.language));
306 SCCP_RWLIST_HEAD_INIT(&conference->participants);
307
308 //bridgeCapabilities = AST_BRIDGE_CAPABILITY_1TO1MIX; /* bridge_multiplexed */
309 bridgeCapabilities = AST_BRIDGE_CAPABILITY_MULTIMIX; /* bridge_softmix */
310 #ifdef CS_BRIDGE_CAPABILITY_MULTITHREADED
311 bridgeCapabilities |= AST_BRIDGE_CAPABILITY_MULTITHREADED; /* bridge_softmix */
312 #endif
313 #if defined(CS_SCCP_VIDEO)
314 #if ASTERISK_VERSION_GROUP < 112
315 bridgeCapabilities |= AST_BRIDGE_CAPABILITY_VIDEO;
316 #endif
317 #endif
318 /* using the SMART flag results in issues when removing forgeign participant, because it try to create a new conference and merge into it. Which seems to be more complex then necessary */
319 #if ASTERISK_VERSION_GROUP >= 112
320 conference->bridge = pbx_bridge_new(bridgeCapabilities, AST_BRIDGE_FLAG_DISSOLVE_EMPTY | AST_BRIDGE_FLAG_MASQUERADE_ONLY | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED, channel->designator, conferenceIdentifier, NULL);
321 #else
322 conference->bridge = pbx_bridge_new(bridgeCapabilities, 0, channel->designator, conferenceIdentifier, NULL);
323 #endif
324
325 #if defined(CS_SCCP_VIDEO) && ASTERISK_VERSION_GROUP >= 112
326 ast_bridge_set_talker_src_video_mode(conference->bridge);
327 #endif
328 if (!conference->bridge) {
329 pbx_log(LOG_WARNING, "%s: Creating conference bridge failed, cancelling conference\n", channel->designator);
330 sccp_conference_release(&conference); /* explicit release */
331 return NULL;
332 }
333
334 /*
335 pbx_bridge_set_internal_sample_rate(conference_bridge->bridge, auto);
336 pbx_bridge_set_mixing_interval(conference->bridge,40);
337 */
338 /* Add to conference List */
339 {
340 sccp_conference_t *tmpConference = NULL;
341 SCCP_LIST_LOCK(&conferences);
342 if ((tmpConference = sccp_conference_retain(conference))) {
343 SCCP_RWLIST_INSERT_HEAD(&conferences, tmpConference, list);
344 }
345 SCCP_LIST_UNLOCK(&conferences);
346 }
347
348 /* init playback lock */
349 pbx_mutex_init(&conference->playback.lock);
350
351 /* create new conference moderator channel */
352 sccp_log((DEBUGCAT_CORE + DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_3 "SCCP: Adding moderator channel to SCCPCONF/%04d\n", conferenceID);
353
354 AUTO_RELEASE(sccp_participant_t, participant , sccp_conference_createParticipant(conference));
355
356 if (participant) {
357 #if CS_REFCOUNT_DEBUG
358 sccp_refcount_addRelationship(device, participant);
359 sccp_refcount_addRelationship(channel, participant);
360 #endif
361 conference->num_moderators = 1;
362 participant->channel = sccp_channel_retain(channel);
363 participant->device = sccp_device_retain(device);
364 participant->conferenceBridgePeer = channel->owner;
365 sccp_conference_update_callInfo(channel, participant->conferenceBridgePeer, participant, conference->id);
366 //ast_set_flag(&(participant->features.feature_flags), AST_BRIDGE_CHANNEL_FLAG_DISSOLVE_HANGUP);
367
368 if (pbx_pthread_create_background(&participant->joinThread, NULL, sccp_conference_thread, participant) < 0) {
369 channel->hangupRequest(channel);
370 return NULL;
371 }
372 channel->hangupRequest = sccp_astgenwrap_requestHangup; // moderator channel not running in a ast_pbx_start thread, but in a local thread => use hard hangup
373 sccp_conference_addParticipant_toList(conference, participant);
374 participant->channel->conference = sccp_conference_retain(conference);
375 participant->channel->conference_id = conference->id;
376 participant->channel->conference_participant_id = participant->id;
377 participant->playback_announcements = device->conf_play_part_announce;
378
379 sccp_conference_update_callInfo(channel, participant->conferenceBridgePeer, participant, conference->id);
380 channel->calltype=SKINNY_CALLTYPE_INBOUND;
381 iPbx.setChannelLinkedId(participant->channel, conference->linkedid);
382 channel->calltype=SKINNY_CALLTYPE_OUTBOUND;
383 participant->isModerator = TRUE;
384 device->conferencelist_active = device->conf_show_conflist; // Activate conflist
385 pbx_builtin_setvar_int_helper(channel->owner, "__SCCP_CONFERENCE_ID", conference->id);
386 pbx_builtin_setvar_int_helper(channel->owner, "__SCCP_CONFERENCE_PARTICIPANT_ID", participant->id);
387 sccp_log((DEBUGCAT_CORE + DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: Added Moderator %d (Channel: %s)\n", conference->id, participant->id, pbx_channel_name(participant->conferenceBridgePeer));
388 }
389
390 /** we return the pointer, so do not release conference (should be retained in d->conference or rather l->conference/c->conference. d->conference limits us to one conference per phone */
391 #ifdef CS_MANAGER_EVENTS
392 if (GLOB(callevents)) {
393 manager_event(EVENT_FLAG_USER, "SCCPConfStart", "ConfId: %d\r\n" "SCCPDevice: %s\r\n", conferenceID, DEV_ID_LOG(device));
394 }
395 #endif
396 //conference = sccp_conference_retain(conference); // return retained
397 return conference;
398 }
399
400 /*!
401 * \brief Generic Function to create a new participant
402 */
sccp_conference_createParticipant(constConferencePtr conference)403 static sccp_participant_t *sccp_conference_createParticipant(constConferencePtr conference)
404 {
405 if (!conference) {
406 pbx_log(LOG_ERROR, "SCCPCONF: no conference / participantChannel provided.\n");
407 return NULL;
408 }
409
410 sccp_participant_t *participant = NULL;
411 int participantID = SCCP_RWLIST_GETSIZE(&conference->participants) + 1;
412 char participantIdentifier[REFCOUNT_INDENTIFIER_SIZE];
413
414 sccp_log((DEBUGCAT_CORE + DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_3 "SCCPCONF/%04d: Creating new conference-participant %d\n", conference->id, participantID);
415
416 snprintf(participantIdentifier, REFCOUNT_INDENTIFIER_SIZE, "SCCPCONF/%04d/PART/%04d", conference->id, participantID);
417 participant = (sccp_participant_t *) sccp_refcount_object_alloc(sizeof(sccp_participant_t), SCCP_REF_PARTICIPANT, participantIdentifier, __sccp_participant_destroy);
418 if (!participant) {
419 pbx_log(LOG_ERROR, "SCCPCONF/%04d: cannot alloc memory for new conference participant.\n", conference->id);
420 return NULL;
421 }
422 #if CS_REFCOUNT_DEBUG
423 sccp_refcount_addRelationship(conference, participant);
424 # endif
425 pbx_bridge_features_init(&participant->features);
426 //ast_set_flag(&(participant->features.feature_flags), AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE);
427
428 participant->id = participantID;
429 participant->conference = sccp_conference_retain(conference);
430 participant->conferenceBridgePeer = NULL;
431 participant->playback_announcements = conference->playback_announcements; // default
432 participant->onMusicOnHold = FALSE;
433 if (conference->mute_on_entry) {
434 sccp_log((DEBUGCAT_CORE + DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_3 "SCCP: Participant: %d will be muted on entry\n", participant->id);
435 participant->features.mute = 1;
436 participant->features.dtmf_passthrough = 1;
437 }
438
439 return participant;
440 }
441
sccp_conference_connect_bridge_channels_to_participants(constConferencePtr conference)442 static void sccp_conference_connect_bridge_channels_to_participants(constConferencePtr conference)
443 {
444 struct ast_bridge *bridge = conference->bridge;
445 struct ast_bridge_channel *bridge_channel = NULL;
446
447 # ifndef CS_BRIDGE_BASE_NEW
448 sccp_log((DEBUGCAT_HIGH + DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: Searching Bridge Channel(num_channels: %d).\n", conference->id, conference->bridge->num);
449 # else
450 sccp_log((DEBUGCAT_HIGH + DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: Searching Bridge Channel(num_channels: %d).\n", conference->id, conference->bridge->num_channels);
451 # endif
452 ao2_lock(bridge);
453 AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
454 sccp_log((DEBUGCAT_HIGH + DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: Bridge Channel %p.\n", conference->id, bridge_channel);
455 AUTO_RELEASE(sccp_participant_t, participant , sccp_participant_findByPBXChannel(conference, bridge_channel->chan));
456
457 if (participant && participant->bridge_channel != bridge_channel) {
458 sccp_log((DEBUGCAT_CORE + DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: Connecting Bridge Channel %p to Participant %d.\n", conference->id, bridge_channel, participant->id);
459 participant->bridge_channel = bridge_channel;
460 if(participant->isModerator) {
461 sccp_device_t * device = participant->device;
462 sccp_channel_t * channel = participant->channel;
463 if(device && channel) {
464 sccp_indicate(device, channel, SCCP_CHANNELSTATE_CONNECTEDCONFERENCE);
465 sccp_dev_set_keyset(device, sccp_device_find_index_for_line(device, channel->line->name), channel->callid, KEYMODE_CONNCONF);
466 }
467 }
468 }
469 }
470 ao2_unlock(bridge);
471 }
472
473 /*!
474 * \brief Allocate a temp channel(participant->conferenceBridgePeer) to take the place of the participant_ast_channel in the old channel bridge (masquerade).
475 * The resulting "bridge-free" participant_ast_channel can then be inserted into the conference
476 * The temp channel will be hungup
477 */
sccp_conference_masqueradeChannel(PBX_CHANNEL_TYPE * participant_ast_channel,sccp_conference_t * conference,sccp_participant_t * participant)478 static boolean_t sccp_conference_masqueradeChannel(PBX_CHANNEL_TYPE * participant_ast_channel, sccp_conference_t * conference, sccp_participant_t * participant)
479 {
480 if (participant && participant_ast_channel) {
481 if (!(iPbx.allocTempPBXChannel(participant_ast_channel, &participant->conferenceBridgePeer))) {
482 pbx_log(LOG_ERROR, "SCCPCONF/%04d: Creation of Temp Channel Failed. Exiting.\n", conference->id);
483 pbx_hangup(participant->conferenceBridgePeer);
484 pbx_channel_unref(participant_ast_channel);
485 return FALSE;
486 }
487 if (!iPbx.masqueradeHelper(participant_ast_channel, participant->conferenceBridgePeer)) {
488 pbx_log(LOG_ERROR, "SCCPCONF/%04d: Failed to Masquerade TempChannel.\n", conference->id);
489 pbx_hangup(participant->conferenceBridgePeer);
490 pbx_channel_unref(participant_ast_channel);
491 return FALSE;
492 }
493 pbx_channel_ref(participant->conferenceBridgePeer);
494 if (pbx_pthread_create_background(&participant->joinThread, NULL, sccp_conference_thread, participant) < 0) {
495 pbx_hangup(participant->conferenceBridgePeer);
496 pbx_channel_unref(participant->conferenceBridgePeer);
497 return FALSE;
498 }
499 sccp_log((DEBUGCAT_CORE + DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: Added Participant %d (Channel: %s)\n", conference->id, participant->id, pbx_channel_name(participant->conferenceBridgePeer));
500
501 return TRUE;
502 }
503 return FALSE;
504 }
505
506 /*!
507 * \brief Add the Participant to conference->participants
508 */
sccp_conference_addParticipant_toList(constConferencePtr conference,constParticipantPtr participant)509 static void sccp_conference_addParticipant_toList(constConferencePtr conference, constParticipantPtr participant)
510 {
511 // add to participant list
512 sccp_participant_t *tmpParticipant = NULL;
513
514 SCCP_RWLIST_WRLOCK(&(((conferencePtr)conference)->participants));
515 if ((tmpParticipant = sccp_participant_retain(participant))) {
516 SCCP_RWLIST_INSERT_TAIL(&(((conferencePtr)conference)->participants), tmpParticipant, list);
517 }
518 SCCP_RWLIST_UNLOCK(&(((conferencePtr)conference)->participants));
519 }
520
521 /*!
522 * \brief Update the callinfo on the sccp channel
523 */
sccp_conference_update_callInfo(constChannelPtr channel,PBX_CHANNEL_TYPE * pbxChannel,constParticipantPtr participant,uint32_t conferenceID)524 void sccp_conference_update_callInfo(constChannelPtr channel, PBX_CHANNEL_TYPE * pbxChannel, constParticipantPtr participant, uint32_t conferenceID)
525 {
526 char moderator_cidname[StationMaxNameSize] = "";
527 char moderator_cidnum[StationMaxNameSize] = "";
528 sccp_callinfo_t *ci = sccp_channel_getCallInfo(channel);
529
530 switch (channel->calltype) {
531 case SKINNY_CALLTYPE_INBOUND:
532 iCallInfo.Getter(ci,
533 SCCP_CALLINFO_CALLEDPARTY_NAME, moderator_cidname,
534 SCCP_CALLINFO_CALLEDPARTY_NUMBER, moderator_cidnum,
535 SCCP_CALLINFO_CALLINGPARTY_NAME, &participant->PartyName,
536 SCCP_CALLINFO_CALLINGPARTY_NUMBER, &participant->PartyNumber,
537 SCCP_CALLINFO_KEY_SENTINEL);
538 iCallInfo.Setter(ci,
539 SCCP_CALLINFO_ORIG_CALLINGPARTY_NAME, participant->PartyName,
540 SCCP_CALLINFO_ORIG_CALLINGPARTY_NUMBER, participant->PartyNumber,
541 SCCP_CALLINFO_CALLINGPARTY_NAME, moderator_cidname,
542 SCCP_CALLINFO_CALLINGPARTY_NUMBER, moderator_cidnum,
543 SCCP_CALLINFO_KEY_SENTINEL);
544 break;
545 case SKINNY_CALLTYPE_OUTBOUND:
546 case SKINNY_CALLTYPE_FORWARD:
547 iCallInfo.Getter(ci,
548 SCCP_CALLINFO_CALLINGPARTY_NAME, moderator_cidname,
549 SCCP_CALLINFO_CALLINGPARTY_NUMBER, moderator_cidnum,
550 SCCP_CALLINFO_CALLEDPARTY_NAME, &participant->PartyName,
551 SCCP_CALLINFO_CALLEDPARTY_NUMBER, &participant->PartyNumber,
552 SCCP_CALLINFO_KEY_SENTINEL);
553 iCallInfo.Setter(ci,
554 SCCP_CALLINFO_ORIG_CALLEDPARTY_NAME, participant->PartyName,
555 SCCP_CALLINFO_ORIG_CALLEDPARTY_NUMBER, participant->PartyNumber,
556 SCCP_CALLINFO_CALLEDPARTY_NAME, moderator_cidname,
557 SCCP_CALLINFO_CALLEDPARTY_NUMBER, moderator_cidnum,
558 SCCP_CALLINFO_KEY_SENTINEL);
559 break;
560 case SKINNY_CALLTYPE_SENTINEL:
561 break;
562 }
563
564 /* this is just a workaround to update sip and other channels also -MC */
565 /** @todo we should fix this workaround -MC */
566 #if ASTERISK_VERSION_GROUP > 106
567 struct ast_party_connected_line connected;
568 struct ast_set_party_connected_line update_connected;
569
570 memset(&update_connected, 0, sizeof(update_connected));
571 ast_party_connected_line_init(&connected);
572
573 update_connected.id.number = 1;
574 connected.id.number.valid = 1;
575 connected.id.number.str = moderator_cidnum;
576 connected.id.number.presentation = AST_PRES_ALLOWED_NETWORK_NUMBER;
577
578 update_connected.id.name = 1;
579 connected.id.name.valid = 1;
580 connected.id.name.str = moderator_cidname;
581 connected.id.name.presentation = AST_PRES_ALLOWED_NETWORK_NUMBER;
582 #if ASTERISK_VERSION_GROUP > 110
583 ast_set_party_id_all(&update_connected.priv);
584 #endif
585 connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
586 if (pbxChannel) {
587 ast_channel_set_connected_line(pbxChannel, &connected, &update_connected);
588 }
589 #endif
590 iPbx.set_connected_line(channel, moderator_cidnum, moderator_cidname, AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER);
591 }
592
593 /*!
594 * \brief Used to set the ConferenceId and ParticipantId on the pbx channel for outside use
595 * \todo Has to be moved to pbx_impl
596 */
pbx_builtin_setvar_int_helper(PBX_CHANNEL_TYPE * channel,const char * var_name,int intvalue)597 void pbx_builtin_setvar_int_helper(PBX_CHANNEL_TYPE * channel, const char *var_name, int intvalue)
598 {
599 char valuestr[8] = "";
600
601 snprintf(valuestr, 8, "%d", intvalue);
602 pbx_builtin_setvar_helper(channel, var_name, valuestr);
603 }
604
605 /*!
606 * \brief Take the channel bridge peer and add it to the conference. (used for the bridge peer channels which are on hold on the moderators phone)
607 */
608 #if HAVE_PBX_CEL_H
609 #include <asterisk/cel.h>
610 #endif
sccp_conference_addParticipatingChannel(conferencePtr conference,constChannelPtr conferenceSCCPChannel,constChannelPtr originalSCCPChannel,PBX_CHANNEL_TYPE * pbxChannel)611 boolean_t sccp_conference_addParticipatingChannel(conferencePtr conference, constChannelPtr conferenceSCCPChannel, constChannelPtr originalSCCPChannel, PBX_CHANNEL_TYPE * pbxChannel)
612 {
613 boolean_t res = FALSE;
614 pbx_assert(conference != NULL);
615 if (!conference->isLocked) {
616 AUTO_RELEASE(sccp_participant_t, participant , sccp_conference_createParticipant(conference));
617
618 if (participant) {
619 sccp_log((DEBUGCAT_CORE + DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: Adding participant %d (Channel %s)\n", conference->id, participant->id, pbx_channel_name(pbxChannel));
620
621 sccp_conference_update_callInfo(originalSCCPChannel, pbxChannel, participant, conference->id); // Update CallerId on originalChannel before masquerade
622
623 // if peer is sccp then retain peer_sccp_channel
624 AUTO_RELEASE(sccp_channel_t, channel, get_sccp_channel_from_pbx_channel(pbxChannel));
625 AUTO_RELEASE(sccp_device_t, device, channel ? sccp_channel_getDevice(channel) : NULL);
626 if(device) {
627 participant->playback_announcements = device->conf_play_part_announce;
628 iPbx.setChannelLinkedId(channel, conference->linkedid);
629 #if CS_REFCOUNT_DEBUG
630 sccp_refcount_addRelationship(device, participant);
631 sccp_refcount_addRelationship(channel, participant);
632 #endif
633 } else {
634 participant->playback_announcements = conference->playback_announcements;
635 }
636 pbx_channel_ref(pbxChannel);
637 if (sccp_conference_masqueradeChannel(pbxChannel, conference, participant)) {
638 sccp_conference_addParticipant_toList(conference, participant);
639 if (channel && device) { // SCCP Channel
640 participant->channel = sccp_channel_retain(channel);
641 participant->device = sccp_device_retain(device);
642 //participant->device->conference = !participant->device->conference ? sccp_conference_retain(conference);
643 participant->channel->conference = sccp_conference_retain(conference);
644 participant->channel->conference_id = conference->id;
645 participant->channel->conference_participant_id = participant->id;
646 participant->playback_announcements = device->conf_play_part_announce;
647 //device->conferencelist_active = device->conf_show_conflist; // Activate conflist on all sccp participants
648 } else { // PBX Channel
649 iPbx.setPBXChannelLinkedId(participant->conferenceBridgePeer, conference->linkedid);
650 }
651 pbx_builtin_setvar_int_helper(participant->conferenceBridgePeer, "__SCCP_CONFERENCE_ID", conference->id);
652 pbx_builtin_setvar_int_helper(participant->conferenceBridgePeer, "__SCCP_CONFERENCE_PARTICIPANT_ID", participant->id);
653 #if ASTERISK_VERSION_GROUP>106
654 pbx_indicate(participant->conferenceBridgePeer, AST_CONTROL_CONNECTED_LINE);
655 #endif
656 res = TRUE;
657 } else {
658 // Masq Error
659 }
660 }
661 } else {
662 sccp_log((DEBUGCAT_CORE + DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: Conference is locked. Participant Denied.\n", conference->id);
663 if (pbxChannel) {
664 pbx_stream_and_wait(pbxChannel, "conf-locked", "");
665 }
666 }
667 return res;
668 }
669
670 /*!
671 * \brief Remove a specific participant from a conference
672 */
sccp_conference_removeParticipant(conferencePtr conference,participantPtr participant)673 static void sccp_conference_removeParticipant(conferencePtr conference, participantPtr participant)
674 {
675 int num_participants = 0;
676
677 if (!conference || !participant) {
678 return;
679 }
680
681 sccp_log((DEBUGCAT_CORE + DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: Removing Participant %d.\n", conference->id, participant->id);
682
683 SCCP_RWLIST_RDLOCK(&(((conferencePtr)conference)->participants));
684 AUTO_RELEASE(sccp_participant_t, tmp_participant, SCCP_RWLIST_REMOVE(&conference->participants, (sccp_participant_t *)participant, list));
685 num_participants = SCCP_RWLIST_GETSIZE(&conference->participants);
686 SCCP_RWLIST_UNLOCK(&(((conferencePtr)conference)->participants));
687
688 if (!ATOMIC_FETCH(&conference->finishing, &conference->lock)) {
689 if ((tmp_participant->isModerator && conference->num_moderators <= 1) || num_participants <= 1) {
690 sccp_conference_end(conference);
691 } else if (!tmp_participant->isModerator) {
692 playback_to_conference(conference, "conf-hasleft", participant->id);
693 }
694 }
695 sccp_conference_update_conflist(conference);
696 sccp_log((DEBUGCAT_CORE + DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: Hanging up Participant %d\n", conference->id, tmp_participant->id);
697 }
698
699 /*!
700 * \brief Every participant is running one of the threads as long as they are joined to the conference
701 * When the thread is cancelled they will clean-up after them selves using the removeParticipant function
702 */
sccp_conference_thread(void * data)703 static void *sccp_conference_thread(void *data)
704 {
705 AUTO_RELEASE(sccp_participant_t, participant , sccp_participant_retain(data));
706
707 if (participant && participant->conference && participant->conference->bridge) {
708 sccp_log_and((DEBUGCAT_CONFERENCE + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: entering join thread.\n", participant->conference->id);
709 #ifdef CS_MANAGER_EVENTS
710 if (GLOB(callevents)) {
711 manager_event(EVENT_FLAG_CALL, "SCCPConfEntered", "ConfId: %d\r\n" "PartId: %d\r\n" "Channel: %s\r\n" "Uniqueid: %s\r\n", participant->conference ? participant->conference->id : 0, participant->id, participant->conferenceBridgePeer ? pbx_channel_name(participant->conferenceBridgePeer) : "NULL", participant->conferenceBridgePeer ? pbx_channel_uniqueid(participant->conferenceBridgePeer) : "NULL");
712 }
713 #endif
714 // Join the bridge
715 sccp_log_and((DEBUGCAT_CONFERENCE + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: Entering pbx_bridge_join: %s as %d\n", participant->conference->id, pbx_channel_name(participant->conferenceBridgePeer), participant->id);
716
717 /*
718 char buffer[2000];
719 iPbx.dumpchan(participant->conferenceBridgePeer, buffer, sizeof buffer);
720 pbx_log(LOG_NOTICE, "SCCPCONF/%04d: channel: %s\n", participant->conference->id, buffer);
721 struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
722 pbx_log(LOG_NOTICE, "SCCPCONF/%04d: (sccp_conference_thread) nativeformats=%s\n", participant->conference->id, ast_format_cap_get_names(ast_channel_nativeformats(participant->conferenceBridgePeer), &codec_buf));
723 */
724
725 #if ASTERISK_VERSION_GROUP >= 113
726 enum ast_bridge_join_flags flags = (enum ast_bridge_join_flags) 0; //AST_BRIDGE_JOIN_PASS_REFERENCE & AST_BRIDGE_JOIN_INHIBIT_JOIN_COLP;
727 //enum ast_bridge_join_flags flags = AST_BRIDGE_JOIN_PASS_REFERENCE & AST_BRIDGE_JOIN_INHIBIT_JOIN_COLP;
728 pbx_bridge_join(participant->conference->bridge, participant->conferenceBridgePeer, NULL, &participant->features, NULL, flags);
729 #else
730 pbx_bridge_join(participant->conference->bridge, participant->conferenceBridgePeer, NULL, &participant->features, NULL, (enum ast_bridge_join_flags)0);
731 #endif
732 participant->pendingRemoval = TRUE;
733
734 sccp_log_and((DEBUGCAT_CONFERENCE + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: Leaving pbx_bridge_join: %s as %d\n", participant->conference->id, pbx_channel_name(participant->conferenceBridgePeer), participant->id);
735 #ifdef CS_MANAGER_EVENTS
736 if (GLOB(callevents)) {
737 manager_event(EVENT_FLAG_CALL, "SCCPConfLeft", "ConfId: %d\r\n" "PartId: %d\r\n" "Channel: %s\r\n" "Uniqueid: %s\r\n", participant->conference ? participant->conference->id : 0, participant->id, participant->conferenceBridgePeer ? pbx_channel_name(participant->conferenceBridgePeer) : "NULL", participant->conferenceBridgePeer ? pbx_channel_uniqueid(participant->conferenceBridgePeer) : "NULL");
738 }
739 #endif
740 if (participant->channel && participant->device) {
741 __sccp_conference_hide_list(participant);
742 }
743
744 if (participant->conferenceBridgePeer) {
745 if (participant->final_announcement) {
746 pbx_stream_and_wait(participant->conferenceBridgePeer, participant->final_announcement, "");
747 sccp_free(participant->final_announcement);
748 }
749 if (pbx_test_flag(pbx_channel_flags(participant->conferenceBridgePeer), AST_FLAG_BLOCKING)) {
750 ast_softhangup(participant->conferenceBridgePeer, AST_SOFTHANGUP_DEV);
751 } else {
752 pbx_hangup(participant->conferenceBridgePeer);
753 }
754 participant->conferenceBridgePeer = NULL;
755 }
756 sccp_conference_removeParticipant(participant->conference, participant);
757 participant->joinThread = AST_PTHREADT_NULL;
758 } else {
759 pbx_log(LOG_WARNING, "SCCP: Conference thread could not be started because of missing conference (%d), participant (%d) or conference->bridge\n", (participant && participant->conference) ? participant->conference->id : 0, participant ? participant->id : 0);
760 }
761 return NULL;
762 }
763
sccp_conference_update(constConferencePtr conference)764 void sccp_conference_update(constConferencePtr conference)
765 {
766 usleep(500); /* need time to settle into bridge, before updating links */
767 sccp_conference_connect_bridge_channels_to_participants(conference);
768 //sccp_conference_update_conflist(conference);
769 }
770
771 /*!
772 * \brief This function is called when the minimal number of occupants of a confernce is reached or when the last moderator hangs-up
773 */
sccp_conference_start(conferencePtr conference)774 void sccp_conference_start(conferencePtr conference)
775 {
776 pbx_assert(conference != NULL);
777 sccp_conference_update_conflist(conference);
778 playback_to_conference(conference, "conf-placeintoconf", -1);
779 sccp_conference_update(conference);
780 #ifdef CS_MANAGER_EVENTS
781 if (GLOB(callevents)) {
782 manager_event(EVENT_FLAG_CALL, "SCCPConfStarted", "ConfId: %d\r\n", conference->id);
783 }
784 #endif
785 }
786
787 /*!k
788 * \brief This function is called when the minimal number of occupants of a confernce is reached or when the last moderator hangs-up
789 */
sccp_conference_end(sccp_conference_t * conference)790 void sccp_conference_end(sccp_conference_t * conference)
791 {
792 sccp_participant_t *participant = NULL;
793
794 if (ATOMIC_INCR(&conference->finishing, TRUE, &conference->lock)) { // already ending
795 return;
796 }
797
798 sccp_log((DEBUGCAT_CORE + DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: Ending Conference.\n", conference->id);
799
800 /* remove remaining participants / moderators */
801 SCCP_RWLIST_RDLOCK(&conference->participants);
802 int num_participants = SCCP_RWLIST_GETSIZE(&conference->participants);
803 if (num_participants > 2) {
804 playback_to_conference(conference, "conf-leaderhasleft", -1);
805 }
806 if (num_participants > 0) {
807 SCCP_RWLIST_TRAVERSE_SAFE_BEGIN(&conference->participants, participant, list) {
808 if (!participant->isModerator && !participant->pendingRemoval) { // remove the participants first
809 if (pbx_bridge_remove(participant->conference->bridge, participant->conferenceBridgePeer)) {
810 pbx_log(LOG_ERROR, "SCCPCONF/%04d: Failed to remove channel from conference\n", conference->id);
811 }
812 }
813 }
814 SCCP_RWLIST_TRAVERSE_SAFE_END;
815 SCCP_RWLIST_TRAVERSE_SAFE_BEGIN(&conference->participants, participant, list) {
816 if (participant->isModerator && !participant->pendingRemoval) { // and then remove the moderators
817 pbx_bridge_remove(participant->conference->bridge, participant->conferenceBridgePeer);
818 }
819 }
820 SCCP_RWLIST_TRAVERSE_SAFE_END;
821 }
822 SCCP_RWLIST_UNLOCK(&conference->participants);
823
824 /* remove conference */
825 sccp_conference_t *tmp_conference = NULL;
826 int conference_id = conference->id;
827
828 SCCP_LIST_LOCK(&conferences);
829 tmp_conference = SCCP_RWLIST_REMOVE(&conferences, conference, list);
830 sccp_conference_release(&tmp_conference); /* explicit release */
831 SCCP_LIST_UNLOCK(&conferences);
832 sccp_log((DEBUGCAT_CORE + DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_3 "SCCPCONF/%04d: Conference Ended.\n", conference_id);
833 }
834
835 /* ========================================================================================================================== Conference Hold/Resume === */
836 /*!
837 * \brief Handle putting on hold a conference
838 */
sccp_conference_hold(conferencePtr conference)839 void sccp_conference_hold(conferencePtr conference)
840 {
841 sccp_participant_t *participant = NULL;
842
843 if (!conference || conference->isOnHold) {
844 return;
845 }
846 sccp_log((DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_3 "SCCPCONF/%04d: Putting conference on hold.\n", conference->id);
847
848 /* play music on hold to participants, if there is no moderator, currently active to the conference */
849 if (conference->num_moderators >= 1) {
850 conference->isOnHold = TRUE;
851 SCCP_RWLIST_RDLOCK(&(((conferencePtr)conference)->participants));
852 SCCP_RWLIST_TRAVERSE(&conference->participants, participant, list) {
853 if (participant->isModerator == FALSE) {
854 sccp_conference_play_music_on_hold_to_participant(conference, participant, TRUE);
855 } else {
856 participant->device->conferencelist_active = FALSE;
857 }
858 }
859 SCCP_RWLIST_UNLOCK(&(((conferencePtr)conference)->participants));
860 }
861 }
862
863 /*!
864 * \brief Handle resuming a conference
865 */
sccp_conference_resume(conferencePtr conference)866 void sccp_conference_resume(conferencePtr conference)
867 {
868 sccp_participant_t *participant = NULL;
869
870 sccp_log((DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_3 "SCCPCONF/%04d: Resuming conference.\n", conference->id);
871 if (!conference) {
872 return;
873 }
874
875 /* stop play music on hold to participants. */
876 if (conference->isOnHold) {
877 SCCP_RWLIST_RDLOCK(&(((conferencePtr)conference)->participants));
878 SCCP_RWLIST_TRAVERSE(&conference->participants, participant, list) {
879 if (participant->isModerator == FALSE) {
880 sccp_conference_play_music_on_hold_to_participant(conference, participant, FALSE);
881 }
882 }
883 SCCP_RWLIST_UNLOCK(&(((conferencePtr)conference)->participants));
884 conference->isOnHold = FALSE;
885 sccp_conference_update_conflist(conference);
886 }
887 }
888
889 /* =============================================================================================================== Playback to Conference/Participant === */
890 /*!
891 * \brief This helper-function is used to playback either a file or number sequence
892 */
stream_and_wait(PBX_CHANNEL_TYPE * playback_channel,const char * filename,int say_number)893 static int stream_and_wait(PBX_CHANNEL_TYPE * playback_channel, const char *filename, int say_number)
894 {
895 if (!sccp_strlen_zero(filename) && !pbx_fileexists(filename, NULL, NULL)) {
896 pbx_log(LOG_WARNING, "File %s does not exists in any format\n", !sccp_strlen_zero(filename) ? filename : "<unknown>");
897 return 0;
898 }
899 if (playback_channel) {
900 if (!sccp_strlen_zero(filename)) {
901 sccp_log_and((DEBUGCAT_CONFERENCE + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "Playing '%s' to Conference\n", filename);
902 pbx_stream_and_wait(playback_channel, filename, "");
903 } else if (say_number >= 0) {
904 sccp_log_and((DEBUGCAT_CONFERENCE + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "Saying '%d' to Conference\n", say_number);
905 pbx_say_number(playback_channel, say_number, "", pbx_channel_language(playback_channel), NULL);
906 }
907 }
908 return 1;
909 }
910
911 /*!
912 * \brief This function is used to playback either a file or number sequence to a specific participant only
913 */
playback_to_channel(participantPtr participant,const char * filename,int say_number)914 int playback_to_channel(participantPtr participant, const char *filename, int say_number)
915 {
916 int res = 0;
917
918 if (!participant->playback_announcements) {
919 sccp_log((DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: Playback for participant %d suppressed\n", participant->conference->id, participant->id);
920 return 1;
921 }
922 if (participant->bridge_channel) {
923 sccp_log((DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: Playback %s %d for participant %d\n", participant->conference->id, filename, say_number, participant->id);
924 //participant->bridge_channel->suspended = 1;
925 pbx_bridge_lock(participant->conference->bridge);
926 res = pbx_bridge_suspend(participant->conference->bridge, participant->conferenceBridgePeer);
927 pbx_bridge_unlock(participant->conference->bridge);
928 if (!res) {
929 //pbx_bridge_change_state(participant->bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
930 if (stream_and_wait(participant->bridge_channel->chan, filename, say_number)) {
931 res = 1;
932 } else {
933 pbx_log(LOG_WARNING, "Failed to play %s or '%d'!\n", filename, say_number);
934 }
935 pbx_bridge_lock(participant->conference->bridge);
936 pbx_bridge_unsuspend(participant->conference->bridge, participant->conferenceBridgePeer);
937 pbx_bridge_unlock(participant->conference->bridge);
938 }
939 //participant->bridge_channel->suspended = 0;
940 //pbx_bridge_change_state(participant->bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
941 } else {
942 sccp_log((DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: No bridge channel for playback\n", participant->conference->id);
943 }
944 return res;
945 }
946
947 /*!
948 * \brief This function is used to playback either a file or number sequence to all conference participants. Used for announcing
949 * The playback channel is created once, and imparted on the conference when necessary on follow-up calls
950 */
951 #if ASTERISK_VERSION_GROUP >= 112
playback_to_conference(conferencePtr conference,const char * filename,int say_number)952 int playback_to_conference(conferencePtr conference, const char *filename, int say_number)
953 {
954 pbx_assert(conference != NULL);
955 if (!conference->playback_announcements) {
956 sccp_log((DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: Playback on conference suppressed\n", conference->id);
957 return 1;
958 }
959
960 pbx_mutex_lock(&conference->playback.lock);
961
962 if (filename && !sccp_strlen_zero(filename) && !pbx_fileexists(filename, NULL, NULL)) {
963 pbx_log(LOG_WARNING, "File %s does not exists in any format\n", !sccp_strlen_zero(filename) ? filename : "<unknown>");
964 return 1;
965 }
966
967 if (!(conference->playback.channel)) {
968 char data[14];
969
970 snprintf(data, sizeof(data), "SCCPCONF/%04d", conference->id);
971 if (!(conference->playback.channel = iPbx.requestAnnouncementChannel(AST_FORMAT_ALAW, NULL, data))) {
972 pbx_mutex_unlock(&conference->playback.lock);
973 return 1;
974 }
975 if (!sccp_strlen_zero(conference->playback.language)) {
976 iPbx.set_language(conference->playback.channel, conference->playback.language);
977 }
978 }
979 sccp_log_and((DEBUGCAT_CONFERENCE + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: Attaching Announcer from Conference\n", conference->id);
980 if (sccpconf_announce_channel_push(conference->playback.channel, conference->bridge)) {
981 pbx_mutex_unlock(&conference->playback.lock);
982 return 1;
983 }
984
985 /* The channel is all under our control, in goes the prompt */
986 if (say_number >= 0) {
987 pbx_say_number(conference->playback.channel, say_number, 0, conference->playback.language, "n");
988 }
989 if (filename && !sccp_strlen_zero(filename)) {
990 pbx_stream_and_wait(conference->playback.channel, filename, "");
991 }
992
993 sccp_log_and((DEBUGCAT_CONFERENCE + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: Detaching Announcer from Conference\n", conference->id);
994 sccpconf_announce_channel_depart(conference->playback.channel);
995
996 pbx_mutex_unlock(&conference->playback.lock);
997
998 return 0;
999 }
1000 #else
playback_to_conference(conferencePtr conference,const char * filename,int say_number)1001 int playback_to_conference(conferencePtr conference, const char *filename, int say_number)
1002 {
1003 PBX_CHANNEL_TYPE * underlying_channel = NULL;
1004 int res = 0;
1005
1006 if (!conference || !conference->playback_announcements) {
1007 sccp_log((DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_4 "SCCPCONF: Playback on conference suppressed\n");
1008 return 1;
1009 }
1010
1011 pbx_mutex_lock(&conference->playback.lock);
1012
1013 if (!sccp_strlen_zero(filename) && !pbx_fileexists(filename, NULL, NULL)) {
1014 pbx_log(LOG_WARNING, "File %s does not exists in any format\n", !sccp_strlen_zero(filename) ? filename : "<unknown>");
1015 return 0;
1016 }
1017
1018 if (!(conference->playback.channel)) {
1019 char data[14];
1020
1021 snprintf(data, sizeof(data), "SCCPCONF/%04d", conference->id);
1022 if (!(conference->playback.channel = iPbx.requestAnnouncementChannel(AST_FORMAT_SLINEAR, NULL, data))) {
1023 pbx_mutex_unlock(&conference->playback.lock);
1024 return 0;
1025 }
1026 if (!sccp_strlen_zero(conference->playback.language)) {
1027 iPbx.set_language(conference->playback.channel, conference->playback.language);
1028 }
1029 pbx_channel_set_bridge(conference->playback.channel, conference->bridge);
1030
1031 if (ast_call(conference->playback.channel, "", 0)) {
1032 pbx_hangup(conference->playback.channel);
1033 conference->playback.channel = NULL;
1034 pbx_mutex_unlock(&conference->playback.lock);
1035 return 0;
1036 }
1037
1038 sccp_log_and((DEBUGCAT_CONFERENCE + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: Created Playback Channel\n", conference->id);
1039 if ((underlying_channel = iPbx.get_underlying_channel(conference->playback.channel))) {
1040 // Update CDR to prevent nasty ast warning when hanging up this channel (confbridge does not set the cdr correctly)
1041 pbx_cdr_start(pbx_channel_cdr(conference->playback.channel));
1042 #if ASTERISK_VERSION_GROUP < 110
1043 conference->playback.channel->cdr->answer = ast_tvnow();
1044 underlying_channel->cdr->answer = ast_tvnow();
1045 #endif
1046 pbx_cdr_update(conference->playback.channel);
1047 } else {
1048 pbx_log(LOG_ERROR, "SCCPCONF/%04d: Could not get Underlying channel from newly created playback channel\n", conference->id);
1049 }
1050 } else {
1051 /* Channel was already available so we just need to add it back into the bridge */
1052 if ((underlying_channel = iPbx.get_underlying_channel(conference->playback.channel))) {
1053 sccp_log_and((DEBUGCAT_CONFERENCE + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: Attaching '%s' to Conference\n", conference->id, pbx_channel_name(underlying_channel));
1054 if (pbx_bridge_impart(conference->bridge, underlying_channel, NULL, NULL, 0)) {
1055 sccp_log_and((DEBUGCAT_CONFERENCE + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: Impart playback channel failed\n", conference->id);
1056 if (underlying_channel) {
1057 pbx_channel_unref(underlying_channel);
1058 underlying_channel = NULL;
1059 }
1060 }
1061 } else {
1062 pbx_log(LOG_ERROR, "SCCPCONF/%04d: Could not get Underlying channel via bridge\n", conference->id);
1063 }
1064 }
1065 if (underlying_channel) {
1066 if (say_number >= 0) {
1067 pbx_say_number(conference->playback.channel, say_number, 0, conference->playback.language, "n");
1068 }
1069 if (filename && !sccp_strlen_zero(filename)) {
1070 pbx_stream_and_wait(conference->playback.channel, filename, "");
1071 }
1072 sccp_log_and((DEBUGCAT_CONFERENCE + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: Detaching '%s' from Conference\n", conference->id, pbx_channel_name(underlying_channel));
1073 pbx_bridge_depart(conference->bridge, underlying_channel);
1074 pbx_channel_unref(underlying_channel);
1075 } else {
1076 pbx_log(LOG_ERROR, "SCCPCONF/%04d: No Underlying channel available to use for playback\n", conference->id);
1077 }
1078 pbx_mutex_unlock(&conference->playback.lock);
1079 return res;
1080 }
1081 #endif
1082
1083 /* ============================================================================================================================= List Find Functions === */
1084 /*!
1085 * \brief Find conference by ID
1086 */
sccp_conference_findByID(uint32_t identifier)1087 sccp_conference_t *sccp_conference_findByID(uint32_t identifier)
1088 {
1089 sccp_conference_t *conference = NULL;
1090
1091 if (identifier == 0) {
1092 return NULL;
1093 }
1094 SCCP_LIST_LOCK(&conferences);
1095 SCCP_LIST_TRAVERSE(&conferences, conference, list) {
1096 if (conference->id == identifier) {
1097 conference = sccp_conference_retain(conference);
1098 break;
1099 }
1100 }
1101 SCCP_LIST_UNLOCK(&conferences);
1102 return conference;
1103 }
1104
1105 /*!
1106 * \brief Find participant by ID
1107 */
sccp_participant_findByID(constConferencePtr conference,uint32_t identifier)1108 sccp_participant_t *sccp_participant_findByID(constConferencePtr conference, uint32_t identifier)
1109 {
1110 sccp_participant_t *participant = NULL;
1111
1112 if (!conference || identifier == 0) {
1113 return NULL;
1114 }
1115 SCCP_RWLIST_RDLOCK(&(((conferencePtr)conference)->participants));
1116 SCCP_RWLIST_TRAVERSE(&conference->participants, participant, list) {
1117 if (participant->id == identifier) {
1118 participant = sccp_participant_retain(participant);
1119 break;
1120 }
1121 }
1122 SCCP_RWLIST_UNLOCK(&(((conferencePtr)conference)->participants));
1123 return participant;
1124 }
1125
1126 /*!
1127 * \brief Find participant by sccp channel
1128 */
sccp_participant_findByChannel(constConferencePtr conference,constChannelPtr channel)1129 sccp_participant_t *sccp_participant_findByChannel(constConferencePtr conference, constChannelPtr channel)
1130 {
1131 sccp_participant_t *participant = NULL;
1132
1133 if (!conference || !channel) {
1134 return NULL;
1135 }
1136 SCCP_RWLIST_RDLOCK(&(((conferencePtr)conference)->participants));
1137 SCCP_RWLIST_TRAVERSE(&conference->participants, participant, list) {
1138 if (participant->channel == channel) {
1139 participant = sccp_participant_retain(participant);
1140 break;
1141 }
1142 }
1143 SCCP_RWLIST_UNLOCK(&(((conferencePtr)conference)->participants));
1144 return participant;
1145 }
1146
1147 /*!
1148 * \brief Find participant by sccp device
1149 */
sccp_participant_findByDevice(constConferencePtr conference,constDevicePtr device)1150 sccp_participant_t *sccp_participant_findByDevice(constConferencePtr conference, constDevicePtr device)
1151 {
1152 sccp_participant_t *participant = NULL;
1153
1154 if (!conference || !device) {
1155 return NULL;
1156 }
1157 SCCP_RWLIST_RDLOCK(&(((conferencePtr)conference)->participants));
1158 SCCP_RWLIST_TRAVERSE(&conference->participants, participant, list) {
1159 if (participant->device == device) {
1160 participant = sccp_participant_retain(participant);
1161 break;
1162 }
1163 }
1164 SCCP_RWLIST_UNLOCK(&(((conferencePtr)conference)->participants));
1165 return participant;
1166 }
1167
1168 /*!
1169 * \brief Find participant by PBX channel
1170 */
sccp_participant_findByPBXChannel(constConferencePtr conference,PBX_CHANNEL_TYPE * channel)1171 sccp_participant_t *sccp_participant_findByPBXChannel(constConferencePtr conference, PBX_CHANNEL_TYPE * channel)
1172 {
1173 sccp_participant_t *participant = NULL;
1174
1175 if (!conference || !channel) {
1176 return NULL;
1177 }
1178 SCCP_RWLIST_RDLOCK(&(((conferencePtr)conference)->participants));
1179 SCCP_RWLIST_TRAVERSE(&conference->participants, participant, list) {
1180 if (participant->conferenceBridgePeer == channel) {
1181 participant = sccp_participant_retain(participant);
1182 break;
1183 }
1184 }
1185
1186 SCCP_RWLIST_UNLOCK(&(((conferencePtr)conference)->participants));
1187 return participant;
1188 }
1189
1190 /* ======================================================================================================================== ConfList (XML) Functions === */
1191
1192 /*!
1193 * \brief Show ConfList
1194 *
1195 * Cisco URL reference
1196 * UserData:INTEGER:STRING
1197 * UserDataSoftKey:STRING:INTEGER:STRING
1198 * UserCallDataSoftKey:STRING:INTEGER0:INTEGER1:INTEGER2:INTEGER3:STRING
1199 * UserCallData:INTEGER0:INTEGER1:INTEGER2:INTEGER3:STRING
1200 */
sccp_conference_show_list(constConferencePtr conference,constChannelPtr channel)1201 void sccp_conference_show_list(constConferencePtr conference, constChannelPtr channel)
1202 {
1203 int use_icon = 0;
1204
1205 if (!conference) {
1206 pbx_log(LOG_WARNING, "SCCPCONF: No conference available to display list for\n");
1207 return;
1208 }
1209
1210 //AUTO_RELEASE(sccp_channel_t, channel , sccp_channel_retain(c));
1211
1212 if (!channel) { // only send this list to sccp phones
1213 pbx_log(LOG_WARNING, "SCCPCONF/%04d: No channel available to display conferencelist for\n", conference->id);
1214 return;
1215 }
1216
1217 AUTO_RELEASE(sccp_participant_t, participant , sccp_participant_findByChannel(conference, channel));
1218
1219 if (!participant) {
1220 pbx_log(LOG_WARNING, "SCCPCONF/%04d: Channel %s is not a participant in this conference\n", conference->id, pbx_channel_name(channel->owner));
1221 return;
1222 }
1223 if (SCCP_RWLIST_GETSIZE(&conference->participants) < 1) {
1224 pbx_log(LOG_WARNING, "SCCPCONF/%04d: Conference does not have enough participants\n", conference->id);
1225 return;
1226 }
1227 if (participant->device) {
1228 participant->device->conferencelist_active = TRUE;
1229 if (!participant->callReference) {
1230 participant->callReference = channel->callid;
1231 participant->lineInstance = conference->id;
1232 participant->transactionID = sccp_random() % 1000;
1233 }
1234
1235 pbx_str_t *xmlStr = pbx_str_alloca(2048);
1236
1237 //snprintf(xmlTmp, sizeof(xmlTmp), "<CiscoIPPhoneIconMenu appId=\"%d\" onAppFocusLost=\"\" onAppFocusGained=\"\" onAppClosed=\"\">", appID);
1238 if (participant->device->protocolversion >= 15) {
1239 if (participant->device->hasEnhancedIconMenuSupport()) {
1240 pbx_str_append(&xmlStr, 0, "<CiscoIPPhoneIconFileMenu appId=\"%d\" onAppClosed=\"%d\">", appID, appID);
1241 if (conference->isLocked) {
1242 pbx_str_append(&xmlStr, 0, "<Title IconIndex=\"5\">Conference %d</Title>\n", conference->id);
1243 } else {
1244 pbx_str_append(&xmlStr, 0, "<Title IconIndex=\"4\">Conference %d</Title>\n", conference->id);
1245 }
1246 } else {
1247 pbx_str_append(&xmlStr, 0, "<CiscoIPPhoneIconFileMenu>");
1248 pbx_str_append(&xmlStr, 0, "<Title>Conference %d</Title>\n", conference->id);
1249 }
1250 } else {
1251 pbx_str_append(&xmlStr, 0, "<CiscoIPPhoneIconMenu>");
1252 pbx_str_append(&xmlStr, 0, "<Title>Conference %d</Title>\n", conference->id);
1253 }
1254 pbx_str_append(&xmlStr, 0, "<Prompt>Make Your Selection</Prompt>\n");
1255
1256 // MenuItems
1257
1258 sccp_participant_t *part = NULL;
1259
1260 SCCP_RWLIST_RDLOCK(&(((conferencePtr)conference)->participants));
1261 SCCP_RWLIST_TRAVERSE(&conference->participants, part, list) {
1262 if (part->pendingRemoval) {
1263 continue;
1264 }
1265 pbx_str_append(&xmlStr, 0, "<MenuItem>");
1266
1267 if (part->isModerator) {
1268 use_icon = 0;
1269 } else {
1270 use_icon = 2;
1271 }
1272 if (part->features.mute) {
1273 ++use_icon;
1274 }
1275 pbx_str_append(&xmlStr, 0, "<IconIndex>");
1276 pbx_str_append(&xmlStr, 0, "%d", use_icon);
1277 pbx_str_append(&xmlStr, 0, "</IconIndex>");
1278
1279 pbx_str_append(&xmlStr, 0, "<Name>");
1280 pbx_str_append(&xmlStr, 0, "%d:%s", part->id, part->PartyName);
1281 if (!sccp_strlen_zero(part->PartyNumber)) {
1282 pbx_str_append(&xmlStr, 0, " (%s)", part->PartyNumber);
1283 }
1284 pbx_str_append(&xmlStr, 0, "</Name>");
1285 pbx_str_append(&xmlStr, 0, "<URL>UserCallData:%d:%d:%d:%d:%d</URL>", appID, participant->lineInstance, participant->callReference, participant->transactionID, part->id);
1286 pbx_str_append(&xmlStr, 0, "</MenuItem>\n");
1287 }
1288 SCCP_RWLIST_UNLOCK(&(((conferencePtr)conference)->participants));
1289
1290 // SoftKeys
1291 if (participant->isModerator) {
1292 pbx_str_append(&xmlStr, 0, "<SoftKeyItem>");
1293 pbx_str_append(&xmlStr, 0, "<Name>EndConf</Name>");
1294 pbx_str_append(&xmlStr, 0, "<Position>1</Position>");
1295 // pbx_str_append(&xmlStr, 0, "<URL>UserDataSoftKey:Select:%d:ENDCONF/%d/%d/%d/</URL>", 1, appID, participant->lineInstance, participant->transactionID);
1296 pbx_str_append(&xmlStr, 0, "<URL>UserDataSoftKey:Select:%d:ENDCONF/%d</URL>", appID, participant->transactionID);
1297 pbx_str_append(&xmlStr, 0, "</SoftKeyItem>\n");
1298 pbx_str_append(&xmlStr, 0, "<SoftKeyItem>");
1299 pbx_str_append(&xmlStr, 0, "<Name>Mute</Name>");
1300 pbx_str_append(&xmlStr, 0, "<Position>2</Position>");
1301 // pbx_str_append(&xmlStr, 0, "<URL>UserDataSoftKey:Select:%d:MUTE/%d/%d/%d/</URL>", 2, appID, participant->lineInstance, participant->transactionID);
1302 pbx_str_append(&xmlStr, 0, "<URL>UserDataSoftKey:Select:%d:MUTE/%d</URL>", appID, participant->transactionID);
1303 pbx_str_append(&xmlStr, 0, "</SoftKeyItem>\n");
1304
1305 pbx_str_append(&xmlStr, 0, "<SoftKeyItem>");
1306 pbx_str_append(&xmlStr, 0, "<Name>Kick</Name>");
1307 pbx_str_append(&xmlStr, 0, "<Position>3</Position>");
1308 // pbx_str_append(&xmlStr, 0, "<URL>UserDataSoftKey:Select:%d:KICK/%d/%d/%d/</URL>", 3, appID, participant->lineInstance, participant->transactionID);
1309 pbx_str_append(&xmlStr, 0, "<URL>UserDataSoftKey:Select:%d:KICK/%d</URL>", appID, participant->transactionID);
1310 pbx_str_append(&xmlStr, 0, "</SoftKeyItem>\n");
1311 }
1312 pbx_str_append(&xmlStr, 0, "<SoftKeyItem>");
1313 pbx_str_append(&xmlStr, 0, "<Name>Exit</Name>");
1314 pbx_str_append(&xmlStr, 0, "<Position>4</Position>");
1315 pbx_str_append(&xmlStr, 0, "<URL>SoftKey:Exit</URL>");
1316 pbx_str_append(&xmlStr, 0, "</SoftKeyItem>\n");
1317 if (participant->isModerator) {
1318 pbx_str_append(&xmlStr, 0, "<SoftKeyItem>");
1319 pbx_str_append(&xmlStr, 0, "<Name>Moderate</Name>");
1320 pbx_str_append(&xmlStr, 0, "<Position>5</Position>");
1321 pbx_str_append(&xmlStr, 0, "<URL>UserDataSoftKey:Select:%d:MODERATE/%d</URL>", appID, participant->transactionID);
1322 pbx_str_append(&xmlStr, 0, "</SoftKeyItem>\n");
1323 #if 0 /* INVITE */
1324 pbx_str_append(&xmlStr, 0, "<SoftKeyItem>");
1325 pbx_str_append(&xmlStr, 0, "<Name>Invite</Name>");
1326 pbx_str_append(&xmlStr, 0, "<Position>6</Position>");
1327 pbx_str_append(&xmlStr, 0, "<URL>UserDataSoftKey:Select:%d:INVITE/%d/%d</URL>", appID, participant->lineInstance, participant->transactionID);
1328 pbx_str_append(&xmlStr, 0, "</SoftKeyItem>\n");
1329 #endif
1330 }
1331 // CiscoIPPhoneIconMenu Icons
1332 if (participant->device->protocolversion >= 15) {
1333 if (participant->device->hasEnhancedIconMenuSupport()) {
1334 pbx_str_append(&xmlStr, 0, "<IconItem><Index>0</Index><URL>Resource:Icon.Connected</URL></IconItem>"); // moderator
1335 pbx_str_append(&xmlStr, 0, "<IconItem><Index>1</Index><URL>Resource:AnimatedIcon.Hold</URL></IconItem>"); // muted moderator
1336 pbx_str_append(&xmlStr, 0, "<IconItem><Index>2</Index><URL>Resource:AnimatedIcon.StreamRxTx</URL></IconItem>"); // participant
1337 pbx_str_append(&xmlStr, 0, "<IconItem><Index>3</Index><URL>Resource:AnimatedIcon.Hold</URL></IconItem>"); // muted participant
1338 pbx_str_append(&xmlStr, 0, "<IconItem><Index>4</Index><URL>Resource:Icon.Speaker</URL></IconItem>"); // unlocked conference
1339 pbx_str_append(&xmlStr, 0, "<IconItem><Index>5</Index><URL>Resource:Icon.SecureCall</URL></IconItem>\n"); // locked conference
1340 } else {
1341 pbx_str_append(&xmlStr, 0, "<IconItem><Index>0</Index><URL>TFTP:Icon.Connected.png</URL></IconItem>"); // moderator
1342 pbx_str_append(&xmlStr, 0, "<IconItem><Index>1</Index><URL>TFTP:AnimatedIcon.Hold.png</URL></IconItem>"); // muted moderator
1343 pbx_str_append(&xmlStr, 0, "<IconItem><Index>2</Index><URL>TFTP:AnimatedIcon.StreamRxTx.png</URL></IconItem>"); // participant
1344 pbx_str_append(&xmlStr, 0, "<IconItem><Index>3</Index><URL>TFTP:AnimatedIcon.Hold.png</URL></IconItem>"); // muted participant
1345 pbx_str_append(&xmlStr, 0, "<IconItem><Index>4</Index><URL>TFTP:Icon.Speaker.png</URL></IconItem>"); // unlocked conference
1346 pbx_str_append(&xmlStr, 0, "<IconItem><Index>5</Index><URL>TFTP:Icon.SecureCall.png</URL></IconItem>\n"); // locked conference
1347 }
1348 } else {
1349 pbx_str_append(&xmlStr, 0, "<IconItem><Index>0</Index><Height>10</Height><Width>16</Width><Depth>2</Depth><Data>C3300000FF0F0000F3F30000F3FC0300F3FC0300FFF30000F30F0000FCF30300F0FC0F0000FF3F00</Data></IconItem>"); // moderator
1350 pbx_str_append(&xmlStr, 0, "<IconItem><Index>1</Index><Height>10</Height><Width>16</Width><Depth>2</Depth><Data>C3300C00FF0F3C30F3F3F03CF3FCC333F3FC330FFFF3F03CF30FF0F3FCF333CFF0FC0F3C00FF3F30</Data></IconItem>"); // muted moderator
1351 pbx_str_append(&xmlStr, 0, "<IconItem><Index>2</Index><Height>10</Height><Width>16</Width><Depth>2</Depth><Data>000000000000000000F30000C0FC0300C0FC030000F300000000000000F30300C0FC0F0030FF3F00</Data></IconItem>"); // participant
1352 pbx_str_append(&xmlStr, 0, "<IconItem><Index>3</Index><Height>10</Height><Width>16</Width><Depth>2</Depth><Data>00000C0000003C3000F3F03CC0FCC333C0FC330F00F3F03C0000F0F300F333CFC0FC0F3C30FF3F30</Data></IconItem>\n"); // muted participant
1353 }
1354
1355 if (participant->device->protocolversion >= 15) {
1356 pbx_str_append(&xmlStr, 0, "</CiscoIPPhoneIconFileMenu>\n");
1357 } else {
1358 pbx_str_append(&xmlStr, 0, "</CiscoIPPhoneIconMenu>\n");
1359 }
1360 sccp_log((DEBUGCAT_CONFERENCE + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: ShowList appID %d, lineInstance %d, callReference %d, transactionID %d\n", conference->id, appID, participant->callReference, participant->lineInstance, participant->transactionID);
1361 sccp_log((DEBUGCAT_CONFERENCE + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: XML-message:\n%s\n", conference->id, pbx_str_buffer(xmlStr));
1362
1363 participant->device->protocol->sendUserToDeviceDataVersionMessage(participant->device, appID, participant->callReference, participant->lineInstance, participant->transactionID, pbx_str_buffer(xmlStr), 2);
1364 }
1365 }
1366
1367 /*!
1368 * \brief Hide ConfList
1369 */
__sccp_conference_hide_list(participantPtr participant)1370 void __sccp_conference_hide_list(participantPtr participant)
1371 {
1372 if (participant->channel && participant->device && participant->conference) {
1373 if (participant->device->conferencelist_active) {
1374 sccp_log((DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: Hide Conf List for participant: %d\n", participant->conference->id, participant->id);
1375 char xmlData[512] = "";
1376 if (participant->device->protocolversion >= 15 /* && participant->device->hasEnhancedIconMenuSupport() */) {
1377 snprintf(xmlData, sizeof(xmlData), "<CiscoIPPhoneExecute><ExecuteItem Priority=\"0\" URL=\"App:Close:0\"/></CiscoIPPhoneExecute>");
1378 } else {
1379 snprintf(xmlData, sizeof(xmlData), "<CiscoIPPhoneExecute><ExecuteItem Priority=\"0\" URL=\"Init:Services\"/></CiscoIPPhoneExecute>");
1380 }
1381
1382 participant->device->protocol->sendUserToDeviceDataVersionMessage(participant->device, appID, participant->callReference, participant->lineInstance, participant->transactionID, xmlData, 2);
1383 participant->device->conferencelist_active = FALSE;
1384 }
1385 }
1386 }
1387
sccp_conference_hide_list_ByDevice(constDevicePtr device)1388 void sccp_conference_hide_list_ByDevice(constDevicePtr device)
1389 {
1390 sccp_conference_t *conference = NULL;
1391
1392 SCCP_LIST_LOCK(&conferences);
1393 SCCP_LIST_TRAVERSE(&conferences, conference, list) {
1394 if (device) {
1395 AUTO_RELEASE(sccp_participant_t, participant , sccp_participant_findByDevice(conference, device));
1396
1397 if (participant && participant->channel && participant->device) {
1398 __sccp_conference_hide_list(participant);
1399 }
1400 }
1401 }
1402 SCCP_LIST_UNLOCK(&conferences);
1403 }
1404
1405 /*!
1406 * \brief Update ConfList on all phones displaying the list
1407 */
sccp_conference_update_conflist(conferencePtr conference)1408 static void sccp_conference_update_conflist(conferencePtr conference)
1409 {
1410 sccp_participant_t *participant = NULL;
1411
1412 if (!conference || ATOMIC_FETCH(&(conference)->finishing, &conference->lock)) {
1413 return;
1414 }
1415 SCCP_RWLIST_RDLOCK(&(conference->participants));
1416 SCCP_RWLIST_TRAVERSE(&(conference->participants), participant, list) {
1417 if (participant->channel && participant->device && (participant->device->conferencelist_active || (participant->isModerator && !conference->isOnHold))) {
1418 sccp_conference_show_list(conference, participant->channel);
1419 }
1420 }
1421 SCCP_RWLIST_UNLOCK(&(conference->participants));
1422 }
1423
1424 /*!
1425 * \brief Handle ButtonPresses from ConfList
1426 */
sccp_conference_handle_device_to_user(devicePtr d,uint32_t callReference,uint32_t transactionID,uint32_t conferenceID,uint32_t participantID)1427 void sccp_conference_handle_device_to_user(devicePtr d, uint32_t callReference, uint32_t transactionID, uint32_t conferenceID, uint32_t participantID)
1428 {
1429 if (d && d->dtu_softkey.transactionID == transactionID) {
1430 sccp_log((DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_4 "%s: Handle DTU SoftKey Button Press for CallID %d, Transaction %d, Conference %d, Participant:%d, Action %s\n", d->id, callReference, transactionID, conferenceID, participantID, d->dtu_softkey.action);
1431
1432 AUTO_RELEASE(sccp_conference_t, conference , sccp_conference_findByID(conferenceID));
1433
1434 if (!conference) {
1435 pbx_log(LOG_WARNING, "%s: Conference not found\n", DEV_ID_LOG(d));
1436 goto EXIT;
1437 }
1438 AUTO_RELEASE(sccp_participant_t, participant , sccp_participant_findByID(conference, participantID));
1439
1440 if (!participant) {
1441 pbx_log(LOG_WARNING, "SCCPCONF/%04d: %s: Participant not found\n", conference->id, DEV_ID_LOG(d));
1442 goto EXIT;
1443 }
1444 AUTO_RELEASE(sccp_participant_t, moderator , sccp_participant_findByDevice(conference, d));
1445
1446 if (!moderator) {
1447 pbx_log(LOG_WARNING, "SCCPCONF/%04d: %s: Moderator not found\n", conference->id, DEV_ID_LOG(d));
1448 goto EXIT;
1449 }
1450 sccp_log((DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_3 "SCCPCONF/%04d: DTU Softkey Executing Action %s (%s)\n", conference->id, d->dtu_softkey.action, DEV_ID_LOG(d));
1451 if (!strcmp(d->dtu_softkey.action, "ENDCONF")) {
1452 sccp_conference_end(conference);
1453 } else if (!strcmp(d->dtu_softkey.action, "MUTE")) {
1454 sccp_conference_toggle_mute_participant(conference, participant);
1455 } else if (!strcmp(d->dtu_softkey.action, "KICK")) {
1456 if (participant->isModerator) {
1457 sccp_log((DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_3 "SCCPCONF/%04d: Moderators cannot be kicked (%s)\n", conference->id, DEV_ID_LOG(d));
1458 sccp_dev_set_message(d, "cannot kick a moderator", 5, FALSE, FALSE);
1459 } else {
1460 sccp_threadpool_add_work(GLOB(general_threadpool), sccp_participant_kicker, participant);
1461 }
1462 } else if (!strcmp(d->dtu_softkey.action, "EXIT")) {
1463 d->conferencelist_active = FALSE;
1464 #if 0 /* INVITE */
1465 } else if (!strcmp(d->dtu_softkey.action, "INVITE")) {
1466 sccp_conference_invite_participant(conference, moderator);
1467 #endif
1468 } else if(strcmp(d->dtu_softkey.action, "MODERATE") == 0) {
1469 sccp_conference_promote_demote_participant(conference, participant, moderator);
1470 }
1471 } else {
1472 pbx_log(LOG_WARNING, "%s: DTU TransactionID does not match or device not found (%d)\n", DEV_ID_LOG(d), transactionID);
1473 }
1474 EXIT:
1475 /* reset softkey state for next button press */
1476 if (d) {
1477 if (d->dtu_softkey.action) {
1478 sccp_free(d->dtu_softkey.action);
1479 }
1480 d->dtu_softkey.transactionID = 0;
1481 }
1482 }
1483
1484 /*!
1485 * \brief Kick Participant out of the conference
1486 */
sccp_conference_kick_participant(constConferencePtr conference,participantPtr participant)1487 void sccp_conference_kick_participant(constConferencePtr conference, participantPtr participant)
1488 {
1489 sccp_log((DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_3 "SCCPCONF/%04d: Kick Participant %d\n", conference->id, participant->id);
1490 participant->pendingRemoval = TRUE;
1491
1492 pbx_bridge_lock(participant->conference->bridge);
1493 pbx_bridge_suspend(participant->conference->bridge, participant->conferenceBridgePeer);
1494 pbx_bridge_unlock(participant->conference->bridge);
1495
1496 participant->final_announcement = pbx_strdup("conf-kicked");
1497 //pbx_stream_and_wait(participant->conferenceBridgePeer, "conf-kicked", "");
1498 //ast_streamfile(participant->conferenceBridgePeer, "conf-kicked", conference->playback.language);
1499 if (pbx_bridge_remove(participant->conference->bridge, participant->conferenceBridgePeer)) {
1500 pbx_log(LOG_ERROR, "SCCPCONF/%04d: Failed to remove channel from conference\n", conference->id);
1501 participant->pendingRemoval = FALSE;
1502 return;
1503 }
1504 #ifdef CS_MANAGER_EVENTS
1505 if (GLOB(callevents)) {
1506 manager_event(EVENT_FLAG_CALL, "SCCPConfParticipantKicked", "ConfId: %d\r\n" "PartId: %d\r\n", conference->id, participant->id);
1507 }
1508 #endif
1509 }
1510
sccp_participant_kicker(void * data)1511 void *sccp_participant_kicker(void *data)
1512 {
1513 AUTO_RELEASE(sccp_participant_t, participant , sccp_participant_retain(data));
1514 if (participant) {
1515 sccp_conference_kick_participant(participant->conference, participant);
1516 }
1517 return NULL;
1518 }
1519
1520 /*!
1521 * \brief Toggle Conference Lock
1522 * \note Not Used at the moment -> Commented out
1523 */
1524 #if 0
1525 static void sccp_conference_toggle_lock_conference(conferencePtr conference, constParticipantPtr participant)
1526 {
1527 sccp_log((DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_3 "SCCPCONF/%04d: Toggle Conference Lock\n", conference->id);
1528 conference->isLocked = (!conference->isLocked ? 1 : 0);
1529 playback_to_channel(participant, (conference->isLocked ? "conf-lockednow" : "conf-unlockednow"), -1);
1530 #ifdef CS_MANAGER_EVENTS
1531 if (GLOB(callevents)) {
1532 manager_event(EVENT_FLAG_CALL, "SCCPConfLock", "ConfId: %d\r\n" "Enabled: %s\r\n", conference->id, conference->isLocked ? "Yes" : "No");
1533 }
1534 #endif
1535 sccp_conference_update_conflist(conference);
1536 }
1537 #endif
1538
1539 /*!
1540 * \brief Toggle Participant Mute Status
1541 */
sccp_conference_toggle_mute_participant(constConferencePtr conference,participantPtr participant)1542 void sccp_conference_toggle_mute_participant(constConferencePtr conference, participantPtr participant)
1543 {
1544 sccp_log((DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_3 "SCCPCONF/%04d: Mute Participant %d\n", conference->id, participant->id);
1545 if (!participant->features.mute) {
1546 participant->features.mute = 1;
1547 participant->features.dtmf_passthrough = 1;
1548 playback_to_channel(participant, "conf-muted", -1);
1549 //if (participant->channel) {
1550 //participant->channel->setMicrophone(participant->channel, FALSE);
1551 //}
1552 } else {
1553 participant->features.mute = 0;
1554 participant->features.dtmf_passthrough = 0;
1555 playback_to_channel(participant, "conf-unmuted", -1);
1556 //if (participant->channel) {
1557 //participant->channel->setMicrophone(participant->channel, TRUE);
1558 //}
1559 }
1560 if (participant->channel && participant->device) {
1561 sccp_dev_set_message(participant->device, participant->features.mute ? "You are muted" : "You are unmuted", 5, FALSE, FALSE);
1562 }
1563 #ifdef CS_MANAGER_EVENTS
1564 if (GLOB(callevents)) {
1565 manager_event(EVENT_FLAG_CALL, "SCCPConfParticipantMute", "ConfId: %d\r\n" "PartId: %d\r\n" "Mute: %s\r\n", conference->id, participant->id, participant->features.mute ? "Yes" : "No");
1566 }
1567 #endif
1568 sccp_conference_update_conflist((conferencePtr)conference);
1569 }
1570
1571 /*!
1572 * \brief Toggle Music on Hold to a specific participant
1573 */
sccp_conference_play_music_on_hold_to_participant(constConferencePtr conference,participantPtr participant,boolean_t start)1574 void sccp_conference_play_music_on_hold_to_participant(constConferencePtr conference, participantPtr participant, boolean_t start)
1575 {
1576 if (!participant->channel || !participant->device) {
1577 return;
1578 }
1579 if (start) {
1580 sccp_log((DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_3 "SCCPCONF/%04d: Start Playing Music on hold to Participant %d\n", conference->id, participant->id);
1581 if (participant->onMusicOnHold == FALSE) {
1582 if (!sccp_strlen_zero(participant->device->conf_music_on_hold_class)) {
1583 pbx_bridge_lock(participant->conference->bridge);
1584 int res = pbx_bridge_suspend(participant->conference->bridge, participant->conferenceBridgePeer);
1585 pbx_bridge_unlock(participant->conference->bridge);
1586 if (!res) {
1587 iPbx.moh_start(participant->conferenceBridgePeer, participant->device->conf_music_on_hold_class, NULL);
1588 participant->onMusicOnHold = TRUE;
1589 //pbx_set_flag(participant->conferenceBridgePeer, AST_FLAG_MOH);
1590 pbx_bridge_lock(((conferencePtr)conference)->bridge);
1591 pbx_bridge_unsuspend(((conferencePtr)conference)->bridge, participant->conferenceBridgePeer);
1592 pbx_bridge_unlock(((conferencePtr)conference)->bridge);
1593 }
1594 } else {
1595 sccp_conference_toggle_mute_participant(conference, participant);
1596 }
1597 }
1598 } else {
1599 sccp_log((DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_3 "SCCPCONF/%04d: Stop Playing Music on hold to Participant %d\n", conference->id, participant->id);
1600 if (!sccp_strlen_zero(participant->device->conf_music_on_hold_class)) {
1601 pbx_bridge_lock(participant->conference->bridge);
1602 int res = pbx_bridge_suspend(participant->conference->bridge, participant->conferenceBridgePeer);
1603 pbx_bridge_unlock(participant->conference->bridge);
1604 if (!res) {
1605 iPbx.moh_stop(participant->conferenceBridgePeer);
1606 participant->onMusicOnHold = FALSE;
1607 //pbx_clear_flag(participant->conferenceBridgePeer, AST_FLAG_MOH);
1608 pbx_bridge_lock(((conferencePtr)conference)->bridge);
1609 pbx_bridge_unsuspend(((conferencePtr)conference)->bridge, participant->conferenceBridgePeer);
1610 pbx_bridge_unlock(((conferencePtr)conference)->bridge);
1611 }
1612 } else {
1613 sccp_conference_toggle_mute_participant(conference, participant);
1614 }
1615 }
1616 if (!conference->isOnHold) {
1617 sccp_conference_update_conflist((conferencePtr)conference);
1618 }
1619 }
1620
1621 /*!
1622 * \brief Promote Participant to Moderator
1623 *
1624 * paramater moderator can be provided as NULL (cli/ami actions)
1625 */
sccp_conference_promote_demote_participant(conferencePtr conference,participantPtr participant,constParticipantPtr moderator)1626 void sccp_conference_promote_demote_participant(conferencePtr conference, participantPtr participant, constParticipantPtr moderator)
1627 {
1628 if (participant->device && participant->channel) {
1629 if (!participant->isModerator) { // promote
1630 participant->isModerator = TRUE;
1631 //ast_set_flag(&(participant->features.feature_flags), AST_BRIDGE_CHANNEL_FLAG_DISSOLVE_HANGUP);
1632 conference->num_moderators++;
1633 participant->device->conferencelist_active = TRUE;
1634 participant->device->conference = sccp_conference_retain(conference);
1635 sccp_softkey_setSoftkeyState(participant->device, KEYMODE_CONNCONF, SKINNY_LBL_JOIN, TRUE);
1636 sccp_softkey_setSoftkeyState(participant->device, KEYMODE_CONNTRANS, SKINNY_LBL_JOIN, TRUE);
1637 sccp_indicate(participant->device, participant->channel, SCCP_CHANNELSTATE_CONNECTEDCONFERENCE);
1638 } else {
1639 if (conference->num_moderators > 1) { // demote
1640 participant->isModerator = FALSE;
1641 //ast_clear_flag(&(participant->features.feature_flags), AST_BRIDGE_CHANNEL_FLAG_DISSOLVE_HANGUP);
1642 conference->num_moderators++;
1643 sccp_conference_release(&participant->device->conference); // explicit release
1644 sccp_softkey_setSoftkeyState(participant->device, KEYMODE_CONNCONF, SKINNY_LBL_JOIN, FALSE);
1645 sccp_softkey_setSoftkeyState(participant->device, KEYMODE_CONNTRANS, SKINNY_LBL_JOIN, FALSE);
1646 sccp_indicate(participant->device, participant->channel, SCCP_CHANNELSTATE_CONNECTED);
1647 } else {
1648 sccp_log((DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_3 "SCCPCONF/%04d: Not enough moderators left in the conference. Promote someone else first.\n", conference->id);
1649 if (moderator) {
1650 sccp_dev_set_message(moderator->device, "Promote someone first", 5, FALSE, FALSE);
1651 }
1652 }
1653 }
1654 sccp_dev_set_message(participant->device, participant->isModerator ? "You have been Promoted" : "You have been Demoted", 5, FALSE, FALSE);
1655 #ifdef CS_MANAGER_EVENTS
1656 if (GLOB(callevents)) {
1657 manager_event(EVENT_FLAG_CALL, "SCCPConfParticipantPromotion", "ConfId: %d\r\n" "PartId: %d\r\n" "Moderator: %s\r\n", conference->id, participant->id, participant->isModerator ? "Yes" : "No");
1658 }
1659 #endif
1660 } else {
1661 sccp_log((DEBUGCAT_CONFERENCE)) (VERBOSE_PREFIX_3 "SCCPCONF/%04d: Only SCCP Channels can be moderators\n", conference->id);
1662 if (moderator) {
1663 sccp_dev_set_message(moderator->device, "Only sccp phones can be moderator", 5, FALSE, FALSE);
1664 }
1665 }
1666 sccp_conference_update_conflist(conference);
1667 }
1668
1669 /*!
1670 * \brief Invite a new participant (XML function)
1671 * usage: enter a phone number via conflist menu, number will be dialed and included into the conference without any further action
1672 *
1673 * \todo To Be Implemented
1674 *
1675 * Cisco URL reference
1676 * UserData:INTEGER:STRING
1677 * UserDataSoftKey:STRING:INTEGER:STRING
1678 * UserCallDataSoftKey:STRING:INTEGER0:INTEGER1:INTEGER2:INTEGER3:STRING
1679 * UserCallData:INTEGER0:INTEGER1:INTEGER2:INTEGER3:STRING
1680 *
1681 * \note use ast_pbx_outgoing_app to make the call
1682 * ast_pbx_outgoing_app: Synchronously or asynchronously make an outbound call and execute an application on the channel
1683 */
sccp_conference_invite_participant(constConferencePtr conference,constParticipantPtr moderator)1684 void sccp_conference_invite_participant(constConferencePtr conference, constParticipantPtr moderator)
1685 {
1686 //sccp_channel_t *channel = NULL;
1687 if (!conference) {
1688 pbx_log(LOG_WARNING, "SCCPCONF: No conference\n");
1689 return;
1690 }
1691 if (!moderator) {
1692 pbx_log(LOG_WARNING, "SCCPCONF/%04d: No moderator\n", conference->id);
1693 return;
1694 }
1695 if (conference->isLocked) {
1696 pbx_log(LOG_WARNING, "SCCPCONF/%04d: Conference is currently locked\n", conference->id);
1697 if (moderator->device) {
1698 sccp_dev_set_message(moderator->device, "Conference is locked", 5, FALSE, FALSE);
1699 }
1700 return;
1701 }
1702
1703 if (moderator->channel && moderator->device) {
1704 pbx_str_t *xmlStr = pbx_str_alloca(2048);
1705
1706 if (moderator->device->protocolversion >= 15) {
1707 pbx_str_append(&xmlStr, 0, "<CiscoIPPhoneInput appId=\"%d\">\n", appID);
1708 } else {
1709 pbx_str_append(&xmlStr, 0, "<CiscoIPPhoneInput>\n");
1710 }
1711 pbx_str_append(&xmlStr, 0, "<Title>Conference %d Invite</Title>\n", conference->id);
1712 pbx_str_append(&xmlStr, 0, "<Prompt>Enter the phone number to invite</Prompt>\n");
1713 // pbx_str_append(&xmlStr, 0, "<URL>UserCallData:%d:%d:%d:%d:%d</URL>\n", APPID_CONFERENCE_INVITE, moderator->lineInstance, moderator->callReference, moderator->transactionID, moderator->id);
1714 // pbx_str_append(&xmlStr, 0, "<URL>UserCallData:%d:%d:%d:%d</URL>\n", APPID_CONFERENCE_INVITE, moderator->lineInstance, moderator->callReference, moderator->transactionID);
1715 pbx_str_append(&xmlStr, 0, "<URL>UserData:%d:%s</URL>\n", appID, "invite");
1716
1717 pbx_str_append(&xmlStr, 0, "<InputItem>\n");
1718 pbx_str_append(&xmlStr, 0, " <DisplayName>Phone Number</DisplayName>\n");
1719 pbx_str_append(&xmlStr, 0, " <QueryStringParam>NUMBER</QueryStringParam>\n");
1720 pbx_str_append(&xmlStr, 0, " <InputFlags>T</InputFlags>\n");
1721 pbx_str_append(&xmlStr, 0, "</InputItem>\n");
1722
1723 // SoftKeys
1724 // pbx_str_append(&xmlStr, 0, "<SoftKeyItem>\n");
1725 // pbx_str_append(&xmlStr, 0, " <Name>Exit</Name>\n");
1726 // pbx_str_append(&xmlStr, 0, " <Position>4</Position>\n");
1727 // pbx_str_append(&xmlStr, 0, " <URL>SoftKey:Exit</URL>\n");
1728 // pbx_str_append(&xmlStr, 0, "</SoftKeyItem>\n");
1729
1730 pbx_str_append(&xmlStr, 0, "</CiscoIPPhoneInput>\n");
1731
1732 sccp_log((DEBUGCAT_CONFERENCE + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: ShowList appID %d, lineInstance %d, callReference %d, transactionID %d\n", conference->id, appID, moderator->callReference, moderator->lineInstance, moderator->transactionID);
1733 sccp_log((DEBUGCAT_CONFERENCE + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_4 "SCCPCONF/%04d: XML-message:\n%s\n", conference->id, pbx_str_buffer(xmlStr));
1734
1735 moderator->device->protocol->sendUserToDeviceDataVersionMessage(moderator->device, APPID_CONFERENCE_INVITE, moderator->callReference, moderator->lineInstance, moderator->transactionID, pbx_str_buffer(xmlStr), 2);
1736 }
1737 }
1738
1739 /* =================================================================================================================================== CLI Functions === */
1740 #include <asterisk/cli.h>
1741 /*!
1742 * \brief Complete Conference
1743 * \param line Line as char
1744 * \param word Word as char
1745 * \param pos Pos as int
1746 * \param state State as int
1747 * \return Result as char
1748 *
1749 * \called_from_asterisk
1750 *
1751 */
sccp_complete_conference(OLDCONST char * line,OLDCONST char * word,int pos,int state)1752 char *sccp_complete_conference(OLDCONST char *line, OLDCONST char *word, int pos, int state)
1753 {
1754 int conference_id = 0;
1755 int wordlen = strlen(word);
1756
1757 int which = 0;
1758 uint i = 0;
1759 char *ret = NULL;
1760 char tmpname[21];
1761 char *actions[5] = { "EndConf", "Kick", "Mute", "Invite", "Moderate" };
1762
1763 switch (pos) {
1764 case 2: // action
1765 {
1766 for (i = 0; i < ARRAY_LEN(actions); i++) {
1767 if (!strncasecmp(word, actions[i], wordlen) && ++which > state) {
1768 return pbx_strdup(actions[i]);
1769 }
1770 }
1771 break;
1772 }
1773 case 3: // conferenceid
1774 {
1775 sccp_conference_t *conference = NULL;
1776
1777 SCCP_LIST_LOCK(&conferences);
1778 SCCP_LIST_TRAVERSE(&conferences, conference, list) {
1779 snprintf(tmpname, sizeof(tmpname), "%d", conference->id);
1780 if (!strncasecmp(word, tmpname, wordlen) && ++which > state) {
1781 ret = pbx_strdup(tmpname);
1782 break;
1783 }
1784 }
1785 SCCP_LIST_UNLOCK(&conferences);
1786 break;
1787 }
1788 case 4: // participantid
1789 {
1790 sccp_participant_t *participant = NULL;
1791
1792 if (sscanf(line, "sccp conference %20s %d", tmpname, &conference_id) > 0) {
1793 AUTO_RELEASE(sccp_conference_t, conference , sccp_conference_findByID(conference_id));
1794
1795 if (conference) {
1796 SCCP_RWLIST_RDLOCK(&(((conferencePtr)conference)->participants));
1797 SCCP_RWLIST_TRAVERSE(&conference->participants, participant, list) {
1798 snprintf(tmpname, sizeof(tmpname), "%d", participant->id);
1799 if (!strncasecmp(word, tmpname, wordlen) && ++which > state) {
1800 ret = pbx_strdup(tmpname);
1801 break;
1802 }
1803 }
1804 SCCP_RWLIST_UNLOCK(&(((conferencePtr)conference)->participants));
1805 }
1806 }
1807 break;
1808 }
1809 default:
1810 break;
1811 }
1812 return ret;
1813 }
1814
1815 /*!
1816 * \brief List Conferences
1817 * \param fd Fd as int
1818 * \param totals Total number of lines as int
1819 * \param s AMI Session
1820 * \param m Message
1821 * \param argc Argc as int
1822 * \param argv[] Argv[] as char
1823 * \return Result as int
1824 *
1825 * \called_from_asterisk
1826 */
sccp_cli_show_conferences(int fd,sccp_cli_totals_t * totals,struct mansession * s,const struct message * m,int argc,char * argv[])1827 int sccp_cli_show_conferences(int fd, sccp_cli_totals_t *totals, struct mansession *s, const struct message *m, int argc, char *argv[])
1828 {
1829 int local_line_total = 0;
1830 sccp_conference_t *conference = NULL;
1831
1832 // table definition
1833 #define CLI_AMI_TABLE_NAME Conferences
1834 #define CLI_AMI_TABLE_PER_ENTRY_NAME Conference
1835
1836 #define CLI_AMI_TABLE_LIST_ITER_HEAD &conferences
1837 #define CLI_AMI_TABLE_LIST_ITER_VAR conference
1838 #define CLI_AMI_TABLE_LIST_LOCK SCCP_LIST_LOCK
1839 #define CLI_AMI_TABLE_LIST_ITERATOR SCCP_LIST_TRAVERSE
1840 #define CLI_AMI_TABLE_LIST_UNLOCK SCCP_LIST_UNLOCK
1841
1842 #define CLI_AMI_TABLE_FIELDS \
1843 CLI_AMI_TABLE_FIELD(Id, "3.3", d, 3, conference->id) \
1844 CLI_AMI_TABLE_FIELD(Participants, "-12.12", d, 12, SCCP_RWLIST_GETSIZE(&conference->participants)) \
1845 CLI_AMI_TABLE_FIELD(Moderators, "-12.12", d, 12, conference->num_moderators) \
1846 CLI_AMI_TABLE_FIELD(Announce, "-12.12", s, 12, conference->playback_announcements ? "Yes" : "No") \
1847 CLI_AMI_TABLE_FIELD(MuteOnEntry, "-12.12", s, 12, conference->mute_on_entry ? "Yes" : "No") \
1848
1849 #include "sccp_cli_table.h"
1850 if (s) {
1851 totals->lines = local_line_total;
1852 totals->tables = 1;
1853 }
1854 return RESULT_SUCCESS;
1855 }
1856
1857 /*!
1858 * \brief Show Conference Participants
1859 * \param fd Fd as int
1860 * \param totals Total number of lines as int
1861 * \param s AMI Session
1862 * \param m Message
1863 * \param argc Argc as int
1864 * \param argv[] Argv[] as char
1865 * \return Result as int
1866 *
1867 * \called_from_asterisk
1868 */
sccp_cli_show_conference(int fd,sccp_cli_totals_t * totals,struct mansession * s,const struct message * m,int argc,char * argv[])1869 int sccp_cli_show_conference(int fd, sccp_cli_totals_t *totals, struct mansession *s, const struct message *m, int argc, char *argv[])
1870 {
1871 int local_line_total = 0;
1872 int confid = 0;
1873
1874 if (argc < 4 || argc > 5 || sccp_strlen_zero(argv[3])) {
1875 pbx_log(LOG_WARNING, "At least ConferenceId needs to be supplied\n");
1876 CLI_AMI_RETURN_ERROR(fd, s, m, "At least ConferenceId needs to be supplied\n %s", "");
1877 }
1878 if (!sccp_strIsNumeric(argv[3]) || (confid = sccp_atoi(argv[3], strlen(argv[3]))) <= 0) {
1879 pbx_log(LOG_WARNING, "At least a valid ConferenceId needs to be supplied\n");
1880 CLI_AMI_RETURN_ERROR(fd, s, m, "At least valid ConferenceId needs to be supplied\n %s", "");
1881 }
1882
1883 AUTO_RELEASE(sccp_conference_t, conference , sccp_conference_findByID(confid));
1884
1885 if (conference) {
1886 sccp_participant_t *participant = NULL;
1887
1888 if (!s) {
1889 CLI_AMI_OUTPUT(fd, s, "\n--- SCCP conference ----------------------------------------------------------------------------------\n");
1890 } else {
1891 astman_send_listack(s, m, argv[0], "start");
1892 CLI_AMI_OUTPUT_PARAM("Event", CLI_AMI_LIST_WIDTH, "%s", argv[0]);
1893 }
1894 CLI_AMI_OUTPUT_PARAM("ConfId", CLI_AMI_LIST_WIDTH, "%d", conference->id);
1895 #define CLI_AMI_TABLE_NAME Participants
1896 #define CLI_AMI_TABLE_PER_ENTRY_NAME Participant
1897 #define CLI_AMI_TABLE_LIST_ITER_HEAD &conference->participants
1898 #define CLI_AMI_TABLE_LIST_ITER_VAR participant
1899 #define CLI_AMI_TABLE_LIST_LOCK SCCP_RWLIST_RDLOCK
1900 #define CLI_AMI_TABLE_LIST_ITERATOR SCCP_RWLIST_TRAVERSE
1901 #define CLI_AMI_TABLE_LIST_UNLOCK SCCP_RWLIST_UNLOCK
1902 #define CLI_AMI_TABLE_FIELDS \
1903 CLI_AMI_TABLE_FIELD(Id, "3.3", d, 3, participant->id) \
1904 CLI_AMI_TABLE_FIELD(ChannelName, "-20.20", s, 20, participant->conferenceBridgePeer ? pbx_channel_name(participant->conferenceBridgePeer) : "NULL") \
1905 CLI_AMI_TABLE_FIELD(Moderator, "-11.11", s, 11, participant->isModerator ? "Yes" : "No") \
1906 CLI_AMI_TABLE_FIELD(Muted, "-5.5", s, 5, participant->features.mute ? "Yes" : "No") \
1907 CLI_AMI_TABLE_FIELD(Announce, "-8.8", s, 8, participant->playback_announcements ? "Yes" : "No") \
1908 CLI_AMI_TABLE_FIELD(ConfList, "-8.8", s, 8, (participant->device && participant->device->conferencelist_active) ? "YES" : "NO")
1909
1910 #include "sccp_cli_table.h"
1911 } else {
1912 pbx_log(LOG_WARNING, "At least a valid ConferenceId needs to be supplied\n");
1913 CLI_AMI_RETURN_ERROR(fd, s, m, "At least valid ConferenceId needs to be supplied\n %s", "");
1914 }
1915 if (s) {
1916 totals->lines = local_line_total;
1917 totals->tables = 1;
1918 }
1919 return RESULT_SUCCESS;
1920 }
1921
1922 /*!
1923 * \brief Conference Command CLI/AMI
1924 * \param fd Fd as int
1925 * \param totals Total number of lines as int
1926 * \param s AMI Session
1927 * \param m Message
1928 * \param argc Argc as int
1929 * \param argv[] Argv[] as char
1930 * \return Result as int
1931 *
1932 * \called_from_asterisk
1933 */
sccp_cli_conference_command(int fd,sccp_cli_totals_t * totals,struct mansession * s,const struct message * m,int argc,char * argv[])1934 int sccp_cli_conference_command(int fd, sccp_cli_totals_t *totals, struct mansession *s, const struct message *m, int argc, char *argv[])
1935 {
1936 int confid = 0;
1937
1938 int partid = 0;
1939 int local_line_total = 0;
1940 int res = RESULT_SUCCESS;
1941 char error[100];
1942
1943 sccp_log((DEBUGCAT_CORE)) (VERBOSE_PREFIX_2 "Conference Command:%s, Conference %s, Participant %s\n", argv[2], argv[3], argc >= 5 ? argv[4] : "");
1944
1945 if (argc < 4 || argc > 5) {
1946 return RESULT_SHOWUSAGE;
1947 }
1948
1949 if (sccp_strlen_zero(argv[2]) || sccp_strlen_zero(argv[3])) {
1950 return RESULT_SHOWUSAGE;
1951 }
1952
1953 if (sccp_strIsNumeric(argv[3]) && (confid = sccp_atoi(argv[3], strlen(argv[3]))) > 0) {
1954 AUTO_RELEASE(sccp_conference_t, conference , sccp_conference_findByID(confid));
1955
1956 if (conference) {
1957 if (!strncasecmp(argv[2], "EndConf", 7)) { // EndConf Command
1958 sccp_conference_end(conference);
1959 } else if (argc >= 5) {
1960 if (sccp_strIsNumeric(argv[4]) && (partid = sccp_atoi(argv[4], strlen(argv[4]))) > 0) {
1961 AUTO_RELEASE(sccp_participant_t, participant , sccp_participant_findByID(conference, partid));
1962
1963 if (participant) {
1964 if (!strncasecmp(argv[2], "Kick", 4)) { // Kick Command
1965 sccp_threadpool_add_work(GLOB(general_threadpool), sccp_participant_kicker, participant);
1966 } else if (!strncasecmp(argv[2], "Mute", 4)) { // Mute Command
1967 sccp_conference_toggle_mute_participant(conference, participant);
1968 } else if (!strncasecmp(argv[2], "Invite", 5)) { // Invite Command
1969 sccp_conference_invite_participant(conference, participant);
1970 } else if (!strncasecmp(argv[2], "Moderate", 8)) { // Moderate Command
1971 sccp_conference_promote_demote_participant(conference, participant, NULL);
1972 } else {
1973 pbx_log(LOG_WARNING, "Unknown Command %s\n", argv[2]);
1974 snprintf(error, sizeof(error), "Unknown Command\n %s", argv[2]);
1975 res = RESULT_FAILURE;
1976 }
1977 } else {
1978 pbx_log(LOG_WARNING, "Participant %s not found in conference %s\n", argv[4], argv[3]);
1979 snprintf(error, sizeof(error), "Participant %s not found in conference\n", argv[4]);
1980 res = RESULT_FAILURE;
1981 }
1982 } else {
1983 pbx_log(LOG_WARNING, "At least a valid ParticipantId needs to be supplied\n");
1984 snprintf(error, sizeof(error), "At least valid ParticipantId needs to be supplied\n %s", "");
1985 res = RESULT_FAILURE;
1986 }
1987 } else {
1988 pbx_log(LOG_WARNING, "Not enough parameters provided for action %s\n", argv[2]);
1989 snprintf(error, sizeof(error), "Not enough parameters provided for action %s\n", argv[2]);
1990 res = RESULT_FAILURE;
1991 }
1992 if (res == RESULT_SUCCESS) {
1993 sccp_conference_update_conflist(conference);
1994 }
1995 } else {
1996 pbx_log(LOG_WARNING, "Conference %s not found\n", argv[3]);
1997 snprintf(error, sizeof(error), "Conference %s not found\n", argv[3]);
1998 res = RESULT_FAILURE;
1999 }
2000 } else {
2001 pbx_log(LOG_WARNING, "At least a valid ConferenceId needs to be supplied\n");
2002 snprintf(error, sizeof(error), "At least valid ConferenceId needs to be supplied\n %s", "");
2003 res = RESULT_FAILURE;
2004 }
2005
2006 if (res == RESULT_FAILURE && !sccp_strlen_zero(error)) {
2007 CLI_AMI_RETURN_ERROR(fd, s, m, "%s\n", error);
2008 }
2009 if (s) {
2010 totals->lines = local_line_total;
2011 }
2012 return res;
2013 }
2014
2015 #endif
2016
2017 // kate: indent-width 8; replace-tabs off; indent-mode cstyle; auto-insert-doxygen on; line-numbers on; tab-indents on; keep-extra-spaces off; auto-brackets off;
2018