1 /*!
2  * \file        sccp_indicate.c
3  * \brief       SCCP Indicate Class
4  * \author      Sergio Chersovani <mlists [at] c-net.it>
5  * \note        Reworked, but based on chan_sccp code.
6  *              The original chan_sccp driver that was made by Zozo which itself was derived from the chan_skinny driver.
7  *              Modified by Jan Czmok and Julien Goodwin
8  * \note        This program is free software and may be modified and distributed under the terms of the GNU Public License.
9  *
10  */
11 
12 #include "config.h"
13 #include "common.h"
14 #include "sccp_channel.h"
15 #include "sccp_conference.h"
16 #include "sccp_actions.h"
17 #include "sccp_device.h"
18 #include "sccp_indicate.h"
19 #include "sccp_line.h"
20 #include "sccp_linedevice.h"
21 #include "sccp_utils.h"
22 #include "sccp_labels.h"
23 
24 SCCP_FILE_VERSION(__FILE__, "");
25 
26 static void __sccp_indicate_remote_device(constDevicePtr device, channelPtr c, linePtr line, const sccp_channelstate_t state);
27 
28 /*!
29  * \brief Indicate Without Lock
30  * \param device SCCP Device
31  * \param c *locked* SCCP Channel
32  * \param state State as uint8_t
33  * \param debug Debug as uint8_t
34  * \param file File as char
35  * \param line Line Number as int
36  * \param file Pretty Function as char
37  * \param pretty_function
38  *
39  * \callgraph
40  * \callergraph
41  *
42  * \warning
43  *  - line->devices is not always locked
44  *
45  */
46 //void __sccp_indicate(sccp_device_t * _device, sccp_channel_t * c, uint8_t state, uint8_t debug, char *file, int line, const char *pretty_function)
__sccp_indicate(constDevicePtr maybe_device,channelPtr c,const sccp_channelstate_t state,boolean_t force,const uint8_t debug,const char * file,const int line,const char * pretty_function)47 void __sccp_indicate (constDevicePtr maybe_device, channelPtr c, const sccp_channelstate_t state, boolean_t force, const uint8_t debug, const char * file, const int line, const char * pretty_function)
48 {
49 	if (debug) {
50 		sccp_log((DEBUGCAT_INDICATE)) (VERBOSE_PREFIX_1 "SCCP: [INDICATE] state '%d' in file '%s', on line %d (%s)\n", state, file, line, pretty_function);
51 	}
52 
53 	AUTO_RELEASE(sccp_device_t, d , (maybe_device) ? sccp_device_retain(maybe_device) : sccp_channel_getDevice(c));
54 	if (!d) {
55 		sccp_log((DEBUGCAT_INDICATE)) (VERBOSE_PREFIX_1 "SCCP: The channel %d does not have a device\n", c->callid);
56 		return;
57 	}
58 
59 	AUTO_RELEASE(sccp_line_t, l , sccp_line_retain(c->line));
60 	if (!l) {
61 		pbx_log(LOG_ERROR, "SCCP: The channel %d does not have a line\n", c->callid);
62 		return;
63 	}
64 	uint16_t lineInstance = sccp_device_find_index_for_line(d, l->name);
65 
66 	/* all the check are ok. We can safely run all the dev functions with no more checks */
67 	sccp_log((DEBUGCAT_INDICATE + DEBUGCAT_DEVICE + DEBUGCAT_LINE)) (VERBOSE_PREFIX_3 "%s: Indicate SCCP new state:%s, current channel state:%s on call:%s, lineInstance:%d (previous channelstate:%s)\n", d->id, sccp_channelstate2str(state), sccp_channelstate2str(c->state), c->designator, lineInstance, sccp_channelstate2str(c->previousChannelState));
68 	sccp_channel_setChannelstate(c, state);
69 	sccp_callinfo_t * const ci = sccp_channel_getCallInfo(c);
70 
71 	if (SCCP_CHANNELSTATE_Idling(state) || SCCP_CHANNELSTATE_IsTerminating(state)) {
72 		sccp_device_indicateMWI(d);
73 	} else {
74 		sccp_device_suppressMWI(d);
75 	}
76 	switch (state) {
77 		case SCCP_CHANNELSTATE_DOWN:
78 			{
79 				//iPbx.set_callstate(c, AST_STATE_DOWN);
80 			}
81 			break;
82 		case SCCP_CHANNELSTATE_OFFHOOK:
83 			{
84 			// should use d->indicate->offhook instead
85 			sccp_dev_set_speaker(d, SKINNY_STATIONSPEAKER_ON);
86 			sccp_device_setLamp(d, SKINNY_STIMULUS_LINE, lineInstance, SKINNY_LAMP_ON);
87 			sccp_device_sendcallstate(d, lineInstance, c->callid, SKINNY_CALLSTATE_OFFHOOK, SKINNY_CALLPRIORITY_LOW, SKINNY_CALLINFO_VISIBILITY_DEFAULT);
88 			sccp_dev_set_cplane(d, lineInstance, 1);
89 			if(SCCP_CHANNELSTATE_DOWN == c->previousChannelState) {                                        // new call
90 				sccp_dev_displayprompt(d, lineInstance, c->callid, SKINNY_DISP_ENTER_NUMBER, GLOB(digittimeout));
91 				c->setTone(c, l->initial_dialtone_tone, SKINNY_TONEDIRECTION_USER);
92 				if(!(d->hasMWILight()) && d->voicemailStatistic.newmsgs) {
93 					sccp_log((DEBUGCAT_INDICATE | DEBUGCAT_MWI))(VERBOSE_PREFIX_2 "%s: Device does not have MWI-light and does have messages waiting -> stutter\n", d->id);
94 					c->setTone(c, SKINNY_TONE_PARTIALDIALTONE, SKINNY_TONEDIRECTION_USER); /* Add Stutter Pattern for ATA/Analog devices */
95 				}
96 			}
97 				sccp_dev_set_keyset(d, lineInstance, c->callid, KEYMODE_OFFHOOK);
98 			}
99 			break;
100 		case SCCP_CHANNELSTATE_GETDIGITS:
101 			{
102 				c->state = SCCP_CHANNELSTATE_OFFHOOK;
103 				sccp_dev_set_speaker(d, SKINNY_STATIONSPEAKER_ON);
104 				sccp_device_sendcallstate(d, lineInstance, c->callid, SKINNY_CALLSTATE_OFFHOOK, SKINNY_CALLPRIORITY_LOW, SKINNY_CALLINFO_VISIBILITY_DEFAULT);
105 				sccp_dev_displayprompt(d, lineInstance, c->callid, SKINNY_DISP_ENTER_NUMBER, GLOB(digittimeout));
106 				sccp_dev_set_keyset(d, lineInstance, c->callid, KEYMODE_DIGITSFOLL);
107 				sccp_dev_set_cplane(d, lineInstance, 1);
108 				c->setTone(c, SKINNY_TONE_ZIP, SKINNY_TONEDIRECTION_USER);
109 			}
110 			break;
111 		case SCCP_CHANNELSTATE_DIGITSFOLL:
112 			{
113 				int lenDialed = sccp_strlen(c->dialedNumber);
114 				int lenSecDialtoneDigits = sccp_strlen(l->secondary_dialtone_digits);
115 				skinny_tone_t secondary_dialtone_tone = l->secondary_dialtone_tone;
116 				sccp_log(DEBUGCAT_INDICATE)(VERBOSE_PREFIX_3 "%s: digitsfollow: dialed:%s, lenDialed:%d, secDialtoneDigits:%s, lenSecDialtoneDigits:%d, secondary_dialtine:%d\n",
117 					c->designator,
118 					c->dialedNumber,
119 					lenDialed,
120 					l->secondary_dialtone_digits,
121 					lenSecDialtoneDigits,
122 					secondary_dialtone_tone);
123 				if (lenSecDialtoneDigits > 0 && lenDialed == lenSecDialtoneDigits && !strncmp(c->dialedNumber, l->secondary_dialtone_digits, lenSecDialtoneDigits)) {
124 					c->setTone(c, secondary_dialtone_tone, SKINNY_TONEDIRECTION_USER);
125 				} else if (lenDialed > 0) {
126 					c->setTone(c, SKINNY_TONE_SILENCE, SKINNY_TONEDIRECTION_USER);
127 				}
128 				sccp_dev_set_keyset(d, lineInstance, c->callid, KEYMODE_DIGITSFOLL);
129 				break;
130 			}
131 		case SCCP_CHANNELSTATE_SPEEDDIAL:
132 			{
133 				c->state = SCCP_CHANNELSTATE_OFFHOOK;
134 				/* clear all the display buffers */
135 				sccp_dev_cleardisplaynotify(d);
136 				sccp_dev_clearprompt(d, 0, 0);
137 
138 				sccp_dev_set_ringer(d, SKINNY_RINGTYPE_OFF, SKINNY_RINGDURATION_NORMAL, lineInstance, c->callid);
139 				sccp_dev_set_speaker(d, SKINNY_STATIONSPEAKER_ON);
140 				sccp_device_sendcallstate(d, lineInstance, c->callid, SKINNY_CALLSTATE_OFFHOOK, SKINNY_CALLPRIORITY_LOW, SKINNY_CALLINFO_VISIBILITY_DEFAULT);
141 				//sccp_dev_displayprompt(d, lineInstance, c->callid, SKINNY_DISP_ENTER_NUMBER, GLOB(digittimeout));
142 				sccp_dev_set_keyset(d, lineInstance, c->callid, KEYMODE_OFFHOOK);
143 				sccp_dev_set_cplane(d, lineInstance, 1);
144 			}
145 			break;
146 		case SCCP_CHANNELSTATE_DIALING:
147 			{
148 				if (c->previousChannelState == SCCP_CHANNELSTATE_DIALING) {
149 					break;
150 				}
151 				d->indicate->dialing(d, lineInstance, c->callid, c->calltype, ci, c->dialedNumber);
152 			}
153 			break;
154 		case SCCP_CHANNELSTATE_RINGOUT:
155 			{
156 				// we already send out the ringing state before */
157 				// first ringout indicate (before connected line update */
158 				sccp_device_sendcallstate(d, lineInstance, c->callid, SKINNY_CALLSTATE_PROCEED, SKINNY_CALLPRIORITY_LOW, SKINNY_CALLINFO_VISIBILITY_DEFAULT);
159 				sccp_dev_displayprompt(d, lineInstance, c->callid, SKINNY_DISP_RING_OUT, GLOB(digittimeout));
160 				if (!(sccp_rtp_getState (&c->rtp.audio, SCCP_RTP_RECEPTION) & SCCP_RTP_STATUS_ACTIVE)) {
161 					c->setTone (c, SKINNY_TONE_ALERTINGTONE, SKINNY_TONEDIRECTION_USER);
162 				}
163 				sccp_dev_set_keyset(d, lineInstance, c->callid, KEYMODE_RINGOUT);
164 				iCallInfo.Send(ci, c->callid, c->calltype, lineInstance, d, FALSE);
165 			}
166 			break;
167 		case SCCP_CHANNELSTATE_RINGOUT_ALERTING:
168 			/* send by connected line update, to show that we know the remote end, we can now update the callinfo */
169 			sccp_device_sendcallstate(d, lineInstance, c->callid, SKINNY_CALLSTATE_RINGOUT, SKINNY_CALLPRIORITY_NORMAL, SKINNY_CALLINFO_VISIBILITY_DEFAULT);
170 			iCallInfo.Send(ci, c->callid, c->calltype, lineInstance, d, TRUE);
171 			break;
172 		case SCCP_CHANNELSTATE_RINGING:
173 			{
174 				sccp_dev_cleardisplaynotify(d);
175 				sccp_dev_clearprompt(d, lineInstance, 0);
176 
177 				sccp_device_sendcallstate(d, lineInstance, c->callid, SKINNY_CALLSTATE_RINGIN, SKINNY_CALLPRIORITY_LOW, SKINNY_CALLINFO_VISIBILITY_DEFAULT);
178 				iCallInfo.Send(ci, c->callid, c->calltype, lineInstance, d, TRUE);
179 				sccp_device_setLamp(d, SKINNY_STIMULUS_LINE, lineInstance, SKINNY_LAMP_BLINK);
180 
181 				if ((d->dndFeature.enabled && d->dndFeature.status == SCCP_DNDMODE_SILENT && c->ringermode != SKINNY_RINGTYPE_URGENT)) {
182 					sccp_log((DEBUGCAT_INDICATE + DEBUGCAT_CHANNEL)) (VERBOSE_PREFIX_3 "%s: DND is active on device\n", d->id);
183 					sccp_dev_set_ringer(d, SKINNY_RINGTYPE_SILENT, SKINNY_RINGDURATION_NORMAL, lineInstance, c->callid);
184 					if (GLOB(dnd_tone) && d->dndFeature.status == SCCP_DNDMODE_SILENT) {
185 						sccp_dev_starttone(d, GLOB(dnd_tone), 0, 0, SKINNY_TONEDIRECTION_USER);
186 					}
187 				} else {
188 					sccp_linedevice_t * ownlinedevice = NULL;
189 					sccp_device_t *remoteDevice = NULL;
190 
191 					SCCP_LIST_TRAVERSE(&l->devices, ownlinedevice, list) {
192 						remoteDevice = ownlinedevice->device;
193 
194 						if (d && remoteDevice && remoteDevice == d) {
195 							sccp_log((DEBUGCAT_INDICATE + DEBUGCAT_CHANNEL))(VERBOSE_PREFIX_3 "%s: Found matching ld. Aux parameter = %s\n", d->id, ownlinedevice->subscriptionId.aux);
196 							if (0 == strncmp(ownlinedevice->subscriptionId.aux, "silent", 6)) {
197 								sccp_dev_set_ringer(d, SKINNY_RINGTYPE_SILENT, SKINNY_RINGDURATION_NORMAL, lineInstance, c->callid);
198 								sccp_log((DEBUGCAT_INDICATE + DEBUGCAT_CHANNEL))(VERBOSE_PREFIX_3 "%s: Forcing silent ring for specific device.\n", d->id);
199 							} else {
200 								sccp_dev_set_ringer(d, c->ringermode, SKINNY_RINGDURATION_NORMAL, lineInstance, c->callid);
201 								sccp_log((DEBUGCAT_INDICATE + DEBUGCAT_CHANNEL)) (VERBOSE_PREFIX_3 "%s: Normal ring occurred.\n", d->id);
202 							}
203 						}
204 					}
205 				}
206 
207 				sccp_dev_set_keyset(d, lineInstance, c->callid, KEYMODE_RINGIN);
208 				char prompt[100];
209 
210 				char orig_called_name[StationMaxNameSize] = {0};
211 				char orig_called_num[StationMaxDirnumSize] = {0};
212 				char calling_name[StationMaxNameSize] = {0};
213 				char calling_num[StationMaxDirnumSize] = {0};
214 				iCallInfo.Getter(ci,
215 					SCCP_CALLINFO_ORIG_CALLEDPARTY_NAME, &orig_called_name,
216 					SCCP_CALLINFO_ORIG_CALLEDPARTY_NUMBER, &orig_called_num,
217 					SCCP_CALLINFO_CALLINGPARTY_NAME, &calling_name,
218 					SCCP_CALLINFO_CALLINGPARTY_NUMBER, &calling_num,
219 					SCCP_CALLINFO_KEY_SENTINEL);
220 
221 				/* GPL - Modify below to expand possible data shown on phone */
222 				char caller[100];
223 				if (!sccp_strlen_zero(calling_name)) {
224 					if (!sccp_strlen_zero(calling_num)) {
225 						snprintf(caller,sizeof(caller), "%s (%s)", calling_name, calling_num);
226 					} else {
227 						snprintf(caller,sizeof(caller), "%s", calling_name);
228 					}
229 				} else {
230 					if (!sccp_strlen_zero(calling_num)) {
231 						snprintf(caller,sizeof(caller), "%s", calling_num);
232 					} else {
233 						snprintf(caller,sizeof(caller), "%s", SKINNY_DISP_UNKNOWN_NUMBER);
234 					}
235 				}
236 				snprintf(prompt, sizeof(prompt), "%s%s", (c->ringermode == SKINNY_RINGTYPE_URGENT) ? SKINNY_DISP_FLASH : SKINNY_DISP_FROM, caller);
237 				sccp_dev_displayprompt(d, lineInstance, c->callid, prompt, GLOB(digittimeout));
238 			}
239 			break;
240 		case SCCP_CHANNELSTATE_PROGRESS:
241 			sccp_log(DEBUGCAT_RTP)(VERBOSE_PREFIX_3 "%s (%s) wantsEarlyRTP:%s, progressSent:%s\n", c->designator, __func__, c->wantsEarlyRTP() ? "yes" : "no", c->progressSent() ? "yes" : "no");
242 			if(!c->wantsEarlyRTP()) {
243 				//  work around : FreePBX sometimes goes from DIALING->AST_CONTROL_PROGRESS skipping RINGING (for EarlyRTP=off)
244 				if(c->previousChannelState == SCCP_CHANNELSTATE_DIALING) {
245 					sccp_indicate(d, c, SCCP_CHANNELSTATE_RINGOUT);
246 				} else if(c->previousChannelState == SCCP_CHANNELSTATE_RINGOUT) {
247 					sccp_indicate(d, c, SCCP_CHANNELSTATE_RINGOUT_ALERTING);
248 				}
249 			} else if(!c->progressSent()) {
250 				c->makeProgress(c);
251 			}
252 			sccp_dev_displayprompt(d, lineInstance, c->callid, SKINNY_DISP_CALL_PROGRESS, GLOB(digittimeout));
253 			break;
254 		case SCCP_CHANNELSTATE_PROCEED:
255 			{
256 				if (c->previousChannelState == SCCP_CHANNELSTATE_CONNECTED) {		// this is a bug of asterisk 1.6 (it sends progress after a call is answered then diverted to some extensions with dial app)
257 					sccp_log((DEBUGCAT_INDICATE + DEBUGCAT_CHANNEL)) (VERBOSE_PREFIX_3 "SCCP: Asterisk requests to change state to (Progress) after (Connected). Ignoring\n");
258 					break;
259 				}
260 				d->indicate->proceed(d, lineInstance, c->callid, c->calltype, ci);
261 			}
262 			break;
263 		case SCCP_CHANNELSTATE_CALLREMOTEMULTILINE:
264 			{
265 				sccp_dev_set_ringer(d, SKINNY_RINGTYPE_OFF, SKINNY_RINGDURATION_NORMAL, lineInstance, c->callid);
266 				sccp_dev_clearprompt(d, lineInstance, c->callid);
267 				sccp_device_sendcallstate(d, lineInstance, c->callid, SKINNY_CALLSTATE_CONNECTED, SKINNY_CALLPRIORITY_NORMAL, SKINNY_CALLINFO_VISIBILITY_DEFAULT);	/** send connected, so it is not listed as missed call */
268 				sccp_device_sendcallstate(d, lineInstance, c->callid, SKINNY_CALLSTATE_CALLREMOTEMULTILINE, SKINNY_CALLPRIORITY_NORMAL, SKINNY_CALLINFO_VISIBILITY_DEFAULT);
269 				sccp_dev_set_keyset(d, lineInstance, c->callid, KEYMODE_ONHOOKSTEALABLE);
270 			}
271 			break;
272 		case SCCP_CHANNELSTATE_CONNECTED:
273 			{
274 				d->indicate->connected(d, lineInstance, c->callid, c->calltype, ci);
275 				sccp_rtp_setCallback(&c->rtp.audio, SCCP_RTP_RECEPTION, sccp_channel_startMediaTransmission);
276 				if(!sccp_rtp_getState(&c->rtp.audio, SCCP_RTP_RECEPTION)) {
277 					sccp_channel_openReceiveChannel(c);
278 				} else if(!sccp_rtp_getState(&c->rtp.audio, SCCP_RTP_TRANSMISSION)) {
279 					/* this looks a little confusing, maybe we should not have two but only one rtp callback */
280 					sccp_rtp_runCallback(&c->rtp.audio, SCCP_RTP_RECEPTION, c);
281 				} else {
282 					sccp_rtp_setCallback(&c->rtp.audio, SCCP_RTP_RECEPTION, NULL);
283 				}
284 				c->setTone (c, SKINNY_TONE_SILENCE, SKINNY_TONEDIRECTION_USER);
285 				sccp_dev_set_keyset(d, lineInstance, c->callid, KEYMODE_CONNECTED);
286 			}
287 			break;
288 		case SCCP_CHANNELSTATE_BUSY:
289 			{
290 			if(!sccp_rtp_getState(&c->rtp.audio, SCCP_RTP_RECEPTION)) {
291 				c->setTone(c, SKINNY_TONE_LINEBUSYTONE, SKINNY_TONEDIRECTION_USER);
292 			}
293 				sccp_dev_displayprompt(d, lineInstance, c->callid, SKINNY_DISP_BUSY, GLOB(digittimeout));
294 			}
295 			break;
296 		case SCCP_CHANNELSTATE_HOLD:
297 			{
298 				sccp_channel_closeAllMediaTransmitAndReceive(c);
299 				if (d->session) {
300 					sccp_handle_time_date_req(d->session, d, NULL);
301 				}
302 				sccp_device_setLamp(d, SKINNY_STIMULUS_LINE, lineInstance, SKINNY_LAMP_WINK);
303 				sccp_device_sendcallstate(d, lineInstance, c->callid, SKINNY_CALLSTATE_HOLD, SKINNY_CALLPRIORITY_LOW, SKINNY_CALLINFO_VISIBILITY_DEFAULT);	/* send connected, so it is not listed as missed call */
304 				sccp_dev_displayprompt(d, lineInstance, c->callid, SKINNY_DISP_HOLD, GLOB(digittimeout));
305 				iCallInfo.Send(ci, c->callid, c->calltype, lineInstance, d, TRUE);
306 				sccp_dev_set_speaker(d, SKINNY_STATIONSPEAKER_OFF);
307 #if CS_SCCP_CONFERENCE
308 				if (c->conference && d->conference) {
309 					sccp_dev_set_keyset(d, lineInstance, c->callid, KEYMODE_HOLDCONF);
310 				} else
311 #endif
312 				{
313 					sccp_dev_set_keyset(d, lineInstance, c->callid, KEYMODE_ONHOLD);
314 				}
315 			}
316 			break;
317 		case SCCP_CHANNELSTATE_CALLWAITING:
318 			{
319 				/* When dialing a shared line which you also have registered, we don't want the outgoing call to show up on our own device as a callwaiting call */
320 				AUTO_RELEASE(sccp_channel_t, activeChannel, sccp_device_getActiveChannel(d));
321 				if(activeChannel) {
322 					if((sccp_strequals(iPbx.getChannelLinkedId(activeChannel), iPbx.getChannelLinkedId(c)))) {
323 						sccp_log(DEBUGCAT_INDICATE)(VERBOSE_PREFIX_3 "%s: (SCCP_CHANNELSTATE_CALLWAITING) Already Own Part of the Call: Skipping\n", DEV_ID_LOG(d));
324 						sccp_log_and(DEBUGCAT_INDICATE + DEBUGCAT_HIGH)(VERBOSE_PREFIX_3 "%s: LinkedId: %s / %s: LinkedId Remote: %s\n", DEV_ID_LOG(d), iPbx.getChannelLinkedId(c), DEV_ID_LOG(d),
325 												iPbx.getChannelLinkedId(activeChannel));
326 						break;
327 					}
328 					c->setTone(activeChannel, GLOB(callwaiting_tone), SKINNY_TONEDIRECTION_USER);
329 				}
330 				sccp_log((DEBUGCAT_INDICATE)) (VERBOSE_PREFIX_3 "%s: SCCP_CHANNELSTATE_CALLWAITING (%s)\n", DEV_ID_LOG(d), sccp_channelstate2str(c->previousChannelState));
331 				// sccp_channel_callwaiting_tone_interval(d, c);
332 				// c->setTone(c, GLOB(callwaiting_tone), SKINNY_TONEDIRECTION_USER);
333 				sccp_device_sendcallstate(d, lineInstance, c->callid, SKINNY_CALLSTATE_RINGIN, SKINNY_CALLPRIORITY_LOW, SKINNY_CALLINFO_VISIBILITY_DEFAULT);
334 				iCallInfo.Send(ci, c->callid, c->calltype, lineInstance, d, TRUE);
335 				sccp_dev_displayprompt(d, lineInstance, c->callid, SKINNY_DISP_CALL_WAITING, GLOB(digittimeout));
336 				sccp_dev_set_ringer(d, SKINNY_RINGTYPE_SILENT, SKINNY_RINGDURATION_SINGLE, lineInstance, c->callid);
337 				sccp_dev_set_keyset(d, lineInstance, c->callid, KEYMODE_RINGIN);
338 
339 #ifdef CS_SCCP_CONFERENCE
340 				if (d->conferencelist_active) {
341 					sccp_conference_hide_list_ByDevice(d);
342 				}
343 #endif
344 			}
345 			break;
346 		case SCCP_CHANNELSTATE_CALLPARK:
347 			{
348 				sccp_device_sendcallstate(d, lineInstance, c->callid, SKINNY_CALLSTATE_CALLPARK, SKINNY_CALLPRIORITY_LOW, SKINNY_CALLINFO_VISIBILITY_DEFAULT);
349 			}
350 			break;
351 		case SCCP_CHANNELSTATE_CALLTRANSFER:
352 			{
353 				sccp_dev_displayprompt(d, lineInstance, c->callid, SKINNY_DISP_TRANSFER, GLOB(digittimeout));
354 				sccp_dev_set_ringer(d, SKINNY_RINGTYPE_OFF, SKINNY_RINGDURATION_NORMAL, lineInstance, c->callid);
355 				sccp_device_sendcallstate(d, lineInstance, c->callid, SKINNY_CALLSTATE_CALLTRANSFER, SKINNY_CALLPRIORITY_LOW, SKINNY_CALLINFO_VISIBILITY_DEFAULT);
356 				iCallInfo.Send(ci, c->callid, c->calltype, lineInstance, d, FALSE);
357 			}
358 			break;
359 		case SCCP_CHANNELSTATE_BLINDTRANSFER:							// \todo SCCP_CHANNELSTATE_BLINDTRANSFER To be implemented
360 			{
361 				sccp_log((DEBUGCAT_INDICATE)) (VERBOSE_PREFIX_3 "%s: SCCP_CHANNELSTATE_BLINDTRANSFER (%s)\n", d->id, sccp_channelstate2str(c->previousChannelState));
362 			}
363 			break;
364 		case SCCP_CHANNELSTATE_CALLCONFERENCE:
365 			{
366 				// sccp_device_sendcallstate(d, lineInstance, c->callid, SCCP_CHANNELSTATE_CALLCONFERENCE, SKINNY_CALLPRIORITY_LOW, SKINNY_CALLINFO_VISIBILITY_DEFAULT);
367 			}
368 			break;
369 		case SCCP_CHANNELSTATE_CONNECTEDCONFERENCE:
370 			{
371 				d->indicate->connected(d, lineInstance, c->callid, c->calltype, ci);
372 				sccp_rtp_setCallback(&c->rtp.audio, SCCP_RTP_RECEPTION, sccp_channel_startMediaTransmission);
373 				if(!sccp_rtp_getState(&c->rtp.audio, SCCP_RTP_RECEPTION)) {
374 					sccp_channel_openReceiveChannel(c);
375 				} else if(!sccp_rtp_getState(&c->rtp.audio, SCCP_RTP_TRANSMISSION)) {
376 					sccp_rtp_runCallback(&c->rtp.audio, SCCP_RTP_TRANSMISSION, c);
377 				} else {
378 					sccp_rtp_setCallback(&c->rtp.audio, SCCP_RTP_RECEPTION, NULL);
379 				}
380 				sccp_dev_set_keyset(d, lineInstance, c->callid, KEYMODE_CONNCONF);
381 			}
382 			break;
383 		case SCCP_CHANNELSTATE_INVALIDCONFERENCE:
384 			{
385 				/*! \todo SCCP_CHANNELSTATE_INVALIDCONFERENCE To be implemented */
386 				sccp_log((DEBUGCAT_INDICATE)) (VERBOSE_PREFIX_3 "%s: SCCP_CHANNELSTATE_INVALIDCONFERENCE (%s)\n", d->id, sccp_channelstate2str(c->previousChannelState));
387 			}
388 			break;
389 		case SCCP_CHANNELSTATE_INVALIDNUMBER:
390 			{
391 				/* this is for the earlyrtp. The 7910 does not play tones if a rtp stream is open */
392 				sccp_dev_displayprompt(d, lineInstance, c->callid, SKINNY_DISP_UNKNOWN_NUMBER, GLOB(digittimeout));
393 				sccp_channel_closeAllMediaTransmitAndReceive(c);
394 				c->setTone(c, SKINNY_TONE_REORDERTONE, SKINNY_TONEDIRECTION_USER);
395 				sccp_channel_schedule_hangup(c, SCCP_HANGUP_TIMEOUT);			// wait 15 seconds, then hangup automatically
396 			}
397 			break;
398 		case SCCP_CHANNELSTATE_CONGESTION:
399 			{
400 			if(!sccp_rtp_getState(&c->rtp.audio, SCCP_RTP_RECEPTION)) {
401 				/* congestion will be emulated if the rtp audio stream is not yet open */
402 				c->setTone(c, SKINNY_TONE_REORDERTONE, SKINNY_TONEDIRECTION_USER);
403 			}
404 			iCallInfo.Send(ci, c->callid, c->calltype, lineInstance, d, FALSE);
405 			sccp_dev_displayprompt(d, lineInstance, c->callid, SKINNY_DISP_TEMP_FAIL, GLOB(digittimeout));
406 			sccp_channel_schedule_hangup(c, SCCP_HANGUP_TIMEOUT);                                        // wait 15 seconds, then hangup automatically
407 			}
408 			break;
409 		case SCCP_CHANNELSTATE_ONHOOK:
410 			{
411 				c->state = SCCP_CHANNELSTATE_DOWN;
412 				if (c->answered_elsewhere && d->indicate->callhistory) {
413 					iCallInfo.Send(ci, c->callid, c->calltype, lineInstance, d, TRUE);
414 					d->indicate->callhistory(d, lineInstance, c->callid, d->callhistory_answered_elsewhere);
415 				}
416 				if (d->indicate && d->indicate->onhook) {
417 					d->indicate->onhook(d, lineInstance, c->callid);
418 				}
419 			}
420 			break;
421 		default:										//! \todo SCCP_CHANNELSTATE:default To be implemented
422 			sccp_log((DEBUGCAT_INDICATE)) (VERBOSE_PREFIX_3 "%s: SCCP_CHANNELSTATE:default  %s (%d) -> %s (%d)\n", d->id, sccp_channelstate2str(c->previousChannelState), c->previousChannelState, sccp_channelstate2str(c->state), c->state);
423 			break;
424 	}
425 
426 	/* if channel state has changed, notify the others */
427 	if (d && (c->state != c->previousChannelState || force)) {
428 		/* if it is a shared line and a state of interest */
429 		if ((SCCP_RWLIST_GETSIZE (&l->devices) > 1) && !c->conference
430 		    && (c->state == SCCP_CHANNELSTATE_OFFHOOK || c->state == SCCP_CHANNELSTATE_DOWN || c->state == SCCP_CHANNELSTATE_ONHOOK || c->state == SCCP_CHANNELSTATE_CONNECTED
431 			|| c->state == SCCP_CHANNELSTATE_CONNECTEDCONFERENCE || c->state == SCCP_CHANNELSTATE_HOLD || c->state == SCCP_CHANNELSTATE_CALLPARK)) {
432 			/* notify all remote devices */
433 			__sccp_indicate_remote_device(d, c, l, state);
434 		}
435 
436 		/* notify features (sccp_feat_channelstateChanged = empty function, skipping) */
437 		// sccp_feat_channelstateChanged(d, c);
438 		// sccp_log((DEBUGCAT_INDICATE + DEBUGCAT_CHANNEL)) (VERBOSE_PREFIX_4 "%s (sccp_indicate) Sending LineStatus Changed Event to State: %s(%d)\n", c->designator, sccp_channelstate2str(c->state), c->state);
439 		sccp_event_t *event = sccp_event_allocate(SCCP_EVENT_LINESTATUS_CHANGED);
440 		if (event) {
441 			event->lineStatusChanged.line = sccp_line_retain(l);
442 			event->lineStatusChanged.optional_device = sccp_device_retain(d);
443 			event->lineStatusChanged.state = c->state;
444 			sccp_event_fire(event);
445 		}
446 	}
447 
448 	sccp_log((DEBUGCAT_INDICATE + DEBUGCAT_CHANNEL)) (VERBOSE_PREFIX_3 "%s: Finish to indicate channel state:%s on call:%s, lineInstance:%d. New channel state:%s\n", d->id, sccp_channelstate2str(state), c->designator, lineInstance, sccp_channelstate2str(c->state));
449 	//sccp_do_backtrace();
450 }
451 
452 /*!
453  * \brief Indicate to Remote Device
454  * \param device SCCP Device
455  * \param c SCCP Channel
456  * \param line SCCP Line
457  * \param state State as int
458  *
459  * \warning
460  *  - line->devices is not always locked
461  */
__sccp_indicate_remote_device(constDevicePtr device,channelPtr c,linePtr line,const sccp_channelstate_t state)462 static void __sccp_indicate_remote_device(constDevicePtr device, channelPtr c, linePtr line, const sccp_channelstate_t state)
463 {
464 	int lineInstance = 0;
465 
466 	if (!c || !line) {
467 		return;
468 	}
469 
470 	/** \todo move this to channel->privacy */
471 
472 	/* do not propagate status of hotline */
473 	if (line == GLOB(hotline)->line) {
474 		sccp_log((DEBUGCAT_INDICATE)) (VERBOSE_PREFIX_3 "SCCP: (__sccp_indicate_remote_device) I'm a hotline, do not notify me!\n");
475 		return;
476 	}
477 	sccp_linedevice_t * ld = NULL;
478 
479 	/* copy temp variables, information to be send to remote device (in another thread) */
480 	const uint32_t callid = c->callid;
481 	const skinny_calltype_t calltype = c->calltype;
482 	char dialedNumber[SCCP_MAX_EXTENSION];
483 	sccp_copy_string(dialedNumber, c->dialedNumber, SCCP_MAX_EXTENSION);
484 	sccp_callinfo_t * ci = iCallInfo.CopyConstructor(sccp_channel_getCallInfo(c));
485 
486 	sccp_log((DEBUGCAT_INDICATE)) (VERBOSE_PREFIX_3 "%s: Remote Indicate state %s (%d) with reason: %s (%d) on remote devices for channel %s\n", DEV_ID_LOG(device), sccp_channelstate2str(state), state, sccp_channelstatereason2str(c->channelStateReason), c->channelStateReason, c->designator);
487 	SCCP_LIST_TRAVERSE(&line->devices, ld, list) {
488 		if(!ld->device) {
489 			pbx_log(LOG_NOTICE, "Strange to find a ld (%p) here without a valid device connected to it !", ld);
490 			continue;
491 		}
492 
493 		if(ld->device == device) {
494 			// skip self
495 			continue;
496 		}
497 
498 		/* check if we have one part of the remote channel */
499 		AUTO_RELEASE(sccp_device_t, remoteDevice, sccp_device_retain(ld->device));
500 
501 		if (remoteDevice) {
502 			sccp_callerid_presentation_t presenceParameter = CALLERID_PRESENTATION_ALLOWED;
503 			iCallInfo.Getter(ci, SCCP_CALLINFO_PRESENTATION, &presenceParameter, SCCP_CALLINFO_KEY_SENTINEL);
504 			skinny_callinfo_visibility_t stateVisibility = (c->privacy || !presenceParameter) ? SKINNY_CALLINFO_VISIBILITY_HIDDEN : SKINNY_CALLINFO_VISIBILITY_DEFAULT;
505 
506 			/* Remarking the next piece out, solves the transfer issue when using sharedline as default on the transferer. Don't know why though (yet) */
507 			if (state != SCCP_CHANNELSTATE_ONHOOK) {
508 				AUTO_RELEASE(sccp_channel_t, activeChannel , sccp_device_getActiveChannel(remoteDevice));
509 
510 				if (activeChannel && (sccp_strequals(iPbx.getChannelLinkedId(activeChannel), iPbx.getChannelLinkedId(c)) || (activeChannel->conference_id && activeChannel->conference_id == c->conference_id))) {
511 					sccp_log(DEBUGCAT_INDICATE) (VERBOSE_PREFIX_3 "%s: (indicate_remote_device) Already Own Part of the Call: Skipped\n", DEV_ID_LOG(device));
512 					//sccp_log_and(DEBUGCAT_INDICATE + DEBUGCAT_HIGH) (VERBOSE_PREFIX_3 "%s: LinkedId: %s / %s: LinkedId Remote: %s\n", DEV_ID_LOG(device), iPbx.getChannelLinkedId(c), DEV_ID_LOG(remoteDevice), iPbx.getChannelLinkedId(activeChannel));
513 					continue;
514 				}
515 			}
516 
517 			if(ld) {
518 				lineInstance = ld->lineInstance;                                        // sccp_device_find_index_for_line(remoteDevice, line->name);
519 			}
520 			switch (state) {
521 				case SCCP_CHANNELSTATE_DOWN:
522 				case SCCP_CHANNELSTATE_ONHOOK:
523 					sccp_log(DEBUGCAT_INDICATE) (VERBOSE_PREFIX_3 "%s -> %s: indicate remote onhook (lineInstance: %d, callid: %d %s)\n", DEV_ID_LOG(device), DEV_ID_LOG(remoteDevice), lineInstance, c->callid, c->answered_elsewhere ? ", answered elsewhere" :"");
524 					if (SKINNY_CALLTYPE_INBOUND == c->calltype && c->answered_elsewhere && remoteDevice->indicate->callhistory) {
525 						remoteDevice->indicate->callhistory(remoteDevice, lineInstance, c->callid, remoteDevice->callhistory_answered_elsewhere);
526 					}
527 					remoteDevice->indicate->remoteOnhook(remoteDevice, lineInstance, callid);
528 					break;
529 
530 				case SCCP_CHANNELSTATE_CONNECTEDCONFERENCE:
531 				case SCCP_CHANNELSTATE_CONNECTED:
532 					sccp_log(DEBUGCAT_INDICATE) (VERBOSE_PREFIX_3 "%s -> %s: indicate remote connected (lineInstance: %d, callid: %d %s)\n", DEV_ID_LOG(device), DEV_ID_LOG(remoteDevice), lineInstance, c->callid, c->answered_elsewhere ? ", answered elsewhere" : "");
533 					if (SKINNY_CALLTYPE_INBOUND == c->calltype && remoteDevice->indicate->callhistory) {
534 						//remoteDevice->indicate->callhistory(remoteDevice, lineInstance, c->callid, remoteDevice->callhistory_answered_elsewhere);
535 						remoteDevice->indicate->callhistory(remoteDevice, lineInstance, c->callid, SKINNY_CALL_HISTORY_DISPOSITION_IGNORE);
536 					}
537 
538 					/* if line is not currently active on remote device, collapse the callstate */
539 					// if (remoteDevice->currentLine && ld->line != remoteDevice->currentLine && !(c->privacy || !presenceParameter)) {
540 					if((c->privacy || presenceParameter == CALLERID_PRESENTATION_FORBIDDEN)
541 					   || (!sccp_softkey_isSoftkeyInSoftkeySet(remoteDevice, KEYMODE_ONHOOKSTEALABLE, SKINNY_LBL_INTRCPT)
542 					       && !sccp_softkey_isSoftkeyInSoftkeySet(remoteDevice, KEYMODE_ONHOOKSTEALABLE, SKINNY_LBL_BARGE))) {
543 						stateVisibility = SKINNY_CALLINFO_VISIBILITY_COLLAPSED;
544 					}
545 					if (c->channelStateReason == SCCP_CHANNELSTATEREASON_BARGE || c->isBarging || c->isBarged) {
546 						stateVisibility = SKINNY_CALLINFO_VISIBILITY_HIDDEN;
547 					}
548 					remoteDevice->indicate->remoteConnected(remoteDevice, lineInstance, callid, stateVisibility);
549 					iCallInfo.Send(ci, callid, calltype, lineInstance, remoteDevice, TRUE);
550 					break;
551 
552 				case SCCP_CHANNELSTATE_HOLD:
553 					if (c->channelStateReason == SCCP_CHANNELSTATEREASON_NORMAL) {
554 						remoteDevice->indicate->remoteHold(remoteDevice, lineInstance, callid, SKINNY_CALLPRIORITY_NORMAL, stateVisibility);
555 						iCallInfo.Send(ci, callid, calltype, lineInstance, remoteDevice, TRUE);
556 					} else {
557 						sccp_log((DEBUGCAT_INDICATE)) (VERBOSE_PREFIX_3 "%s: Skipped Remote Hold Indication for reason: %s\n", DEV_ID_LOG(device), sccp_channelstatereason2str(c->channelStateReason));
558 					}
559 					break;
560 
561 				case SCCP_CHANNELSTATE_CALLPARK:
562 					if (c->channelStateReason == SCCP_CHANNELSTATEREASON_NORMAL) {
563 						remoteDevice->indicate->remoteHold (remoteDevice, lineInstance, callid, SKINNY_CALLPRIORITY_NORMAL, stateVisibility);
564 						iCallInfo.Send (ci, callid, calltype, lineInstance, remoteDevice, TRUE);
565 					}
566 				default:
567 					break;
568 
569 			}
570 			sccp_log((DEBUGCAT_INDICATE)) (VERBOSE_PREFIX_3 "%s: Finish Indicating state %s (%d) with reason: %s (%d) on remote device %s for channel %s\n", DEV_ID_LOG(device), sccp_channelstate2str(state), state, sccp_channelstatereason2str(c->channelStateReason), c->channelStateReason, DEV_ID_LOG(remoteDevice), c->designator);
571 		}
572 	}
573 	iCallInfo.Destructor(&ci);
574 }
575 
576 // 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;
577