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