1 /*!
2  * \file        sccp_line.c
3  * \brief       SCCP Line
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  *              See the LICENSE file at the top of the source tree.
10  *
11  */
12 
13 #include "config.h"
14 #include "common.h"
15 #include "sccp_channel.h"
16 #include "sccp_device.h"
17 #include "sccp_line.h"
18 #include "sccp_config.h"
19 #include "sccp_feature.h"
20 #include "sccp_linedevice.h"
21 #include "sccp_mwi.h"
22 #include "sccp_utils.h"
23 
24 SCCP_FILE_VERSION(__FILE__, "");
25 
26 int __sccp_line_destroy(const void *ptr);
27 
28 /*!
29  * \brief run before reload is start on lines * \note See \ref sccp_config_reload
30  *
31  * \callgraph
32  * \callergraph
33  *
34  */
sccp_line_pre_reload(void)35 void sccp_line_pre_reload(void)
36 {
37 	sccp_line_t *l = NULL;
38 	SCCP_RWLIST_TRAVERSE_SAFE_BEGIN(&GLOB(lines), l, list) {
39 		if(GLOB(hotline)->line == l) { /* always remove hotline from ld */
40 			sccp_log((DEBUGCAT_CONFIG + DEBUGCAT_LINE)) (VERBOSE_PREFIX_3 "%s: Removing Hotline from Device\n", l->name);
41 			sccp_linedevice_remove(NULL, l);
42 		} else { /* Don't want to include the hotline line */
43 #ifdef CS_SCCP_REALTIME
44 			if (l->realtime == FALSE)
45 #endif
46 			{
47 				sccp_log((DEBUGCAT_CONFIG + DEBUGCAT_LINE)) (VERBOSE_PREFIX_3 "%s: Setting Line to Pending Delete=1\n", l->name);
48 				l->pendingDelete = 1;
49 			}
50 		}
51 		l->pendingUpdate = 0;
52 	}
53 	SCCP_LIST_TRAVERSE_SAFE_END;
54 }
55 
56 /*!
57  * \brief run after the new line config is loaded during the reload process
58  * \note See \ref sccp_config_reload
59  * \todo to be implemented correctly (***)
60  *
61  * \callgraph
62  * \callergraph
63  *
64  */
sccp_line_post_reload(void)65 void sccp_line_post_reload(void)
66 {
67 	sccp_line_t *line = NULL;
68 
69 	SCCP_RWLIST_TRAVERSE_SAFE_BEGIN(&GLOB(lines), line, list) {
70 		if (!line->pendingDelete && !line->pendingUpdate) {
71 			continue;
72 		}
73 		AUTO_RELEASE(sccp_line_t, l , sccp_line_retain(line));
74 
75 		if (l) {
76 			// existing lines
77 			sccp_linedevice_t * ld = NULL;
78 			SCCP_LIST_LOCK(&l->devices);
79 			SCCP_LIST_TRAVERSE(&l->devices, ld, list) {
80 				ld->device->pendingUpdate = 1;
81 				sccp_log((DEBUGCAT_CONFIG + DEBUGCAT_LINE))(VERBOSE_PREFIX_3 "%s: LineDevice (line_post_reload) update:%d, delete:%d\n", l->name, ld->device->pendingUpdate, ld->device->pendingDelete);
82 			}
83 			SCCP_LIST_UNLOCK(&l->devices);
84 			if(l->pendingDelete) {
85 				sccp_log((DEBUGCAT_CONFIG + DEBUGCAT_LINE)) (VERBOSE_PREFIX_3 "%s: Deleting Line (post_reload)\n", l->name);
86 				sccp_line_clean(l, TRUE);
87 			} else {
88 				sccp_log((DEBUGCAT_CONFIG + DEBUGCAT_LINE)) (VERBOSE_PREFIX_3 "%s: Cleaning Line (post_reload)\n", l->name);
89 				sccp_line_clean(l, FALSE);
90 			}
91 			sccp_log((DEBUGCAT_CONFIG + DEBUGCAT_LINE)) (VERBOSE_PREFIX_3 "%s: Line (line_post_reload) update:%d, delete:%d\n", l->name, l->pendingUpdate, l->pendingDelete);
92 		}
93 	}
94 	SCCP_RWLIST_TRAVERSE_SAFE_END;
95 }
96 
97 /*!
98  * \brief Build Default SCCP Line.
99  *
100  * Creates an SCCP Line with default/global values
101  *
102  * \return Default SCCP Line
103  *
104  * \callgraph
105  * \callergraph
106  */
sccp_line_create(const char * name)107 linePtr sccp_line_create(const char * name)
108 {
109 	sccp_line_t *l = NULL;
110 
111 	if ((l = sccp_line_find_byname(name, FALSE))) {
112 		sccp_log((DEBUGCAT_CORE)) (VERBOSE_PREFIX_3 "SCCP: Line '%s' already exists.\n", name);
113 		sccp_line_release(&l);						/* explicit release of found line */
114 		return NULL;
115 	}
116 
117 	l = (sccp_line_t *) sccp_refcount_object_alloc(sizeof(sccp_line_t), SCCP_REF_LINE, name, __sccp_line_destroy);
118 	if (!l) {
119 		pbx_log(LOG_ERROR, SS_Memory_Allocation_Error, name);
120 		return NULL;
121 	}
122 	memset(l, 0, sizeof(sccp_line_t));
123 	sccp_copy_string(l->name, name, sizeof(l->name));
124 
125 	SCCP_LIST_HEAD_INIT(&l->channels);
126 	SCCP_LIST_HEAD_INIT(&l->devices);
127 	SCCP_LIST_HEAD_INIT(&l->mailboxes);
128 	return l;
129 }
130 
131 /*!
132  * Add a line to global line list.
133  * \param line line pointer
134  * \since 20091202 - MC
135  *
136  * \note needs to be called with a retained line
137  * \note adds a retained line to the list (refcount + 1)
138  */
sccp_line_addToGlobals(constLinePtr line)139 void sccp_line_addToGlobals(constLinePtr line)
140 {
141 	AUTO_RELEASE(sccp_line_t, l , sccp_line_retain(line));
142 	if (l) {
143 		/* add to list */
144 		SCCP_RWLIST_WRLOCK(&GLOB(lines));
145 		sccp_line_retain(l);										/* add retained line to the list */
146 		SCCP_RWLIST_INSERT_SORTALPHA(&GLOB(lines), l, list, cid_num);
147 		sccp_log((DEBUGCAT_CORE)) (VERBOSE_PREFIX_3 "Added line '%s' to Glob(lines)\n", l->name);
148 		SCCP_RWLIST_UNLOCK(&GLOB(lines));
149 
150 		/* emit event */
151 		sccp_event_t *event = sccp_event_allocate(SCCP_EVENT_LINEINSTANCE_CREATED);
152 		if (event) {
153 			event->lineInstance.line = sccp_line_retain(l);
154 			sccp_event_fire(event);
155 		}
156 	} else {
157 		pbx_log(LOG_ERROR, "Adding null to global line list is not allowed!\n");
158 	}
159 }
160 
161 /*!
162  * Remove a line from the global line list.
163  * \param line SCCP line pointer
164  *
165  * \note needs to be called with a retained line
166  * \note removes the retained line withing the list (refcount - 1)
167  */
sccp_line_removeFromGlobals(sccp_line_t * line)168 void sccp_line_removeFromGlobals(sccp_line_t * line)
169 {
170 	sccp_line_t *removed_line = NULL;
171 	if (line) {
172 		SCCP_RWLIST_WRLOCK(&GLOB(lines));
173 		removed_line = SCCP_RWLIST_REMOVE(&GLOB(lines), line, list);
174 		SCCP_RWLIST_UNLOCK(&GLOB(lines));
175 
176 		sccp_log((DEBUGCAT_CORE)) (VERBOSE_PREFIX_3 "Removed line '%s' from Glob(lines)\n", removed_line->name);
177 
178 		sccp_line_release(&removed_line);								/* explicit release */
179 	} else {
180 		pbx_log(LOG_ERROR, "Removing null from global line list is not allowed!\n");
181 	}
182 }
183 
184 /*!
185  * \brief create a hotline
186  *
187  */
sccp_create_hotline(void)188 void *sccp_create_hotline(void)
189 {
190 
191 	GLOB(hotline) = (sccp_hotline_t *) sccp_malloc(sizeof(sccp_hotline_t));
192 	if (!GLOB(hotline)) {
193 		pbx_log(LOG_ERROR, SS_Memory_Allocation_Error, "SCCP");
194 		return NULL;
195 	}
196 	memset(GLOB(hotline), 0, sizeof(sccp_hotline_t));
197 
198 	AUTO_RELEASE(sccp_line_t, hotline , sccp_line_create("Hotline"));
199 	if (hotline) {
200 #ifdef CS_SCCP_REALTIME
201 		hotline->realtime = TRUE;
202 #endif
203 		hotline->label = pbx_strdup("Hotline");
204 		hotline->context = pbx_strdup("default");
205 		sccp_copy_string(hotline->cid_name, "hotline", sizeof(hotline->cid_name));
206 		sccp_copy_string(hotline->cid_num, "hotline", sizeof(hotline->cid_name));
207 		*(sccp_line_t **)&(GLOB(hotline)->line) = sccp_line_retain(hotline);                                        // retain line inside hotline (const cast to emplace)
208 		sccp_line_addToGlobals(hotline);								// retain line inside GlobalsList
209 	}
210 	return NULL;
211 }
212 
213 /*!
214  * \brief Kill all Channels of a specific Line
215  * \param l SCCP Line
216  *
217  * \callgraph
218  * \callergraph
219  *
220  */
sccp_line_kill_channels(linePtr l)221 void sccp_line_kill_channels(linePtr l)
222 {
223 	sccp_channel_t *c = NULL;
224 
225 	if (!l) {
226 		return;
227 	}
228 	SCCP_LIST_LOCK(&l->channels);
229 	while ((c = SCCP_LIST_REMOVE_HEAD(&l->channels, list))) {
230 		sccp_channel_endcall(c);
231 		sccp_channel_release(&c);									// explicit release channel retain in list
232 	}
233 	SCCP_LIST_UNLOCK(&l->channels);
234 }
235 
236 /*!
237  * \brief Clean Line
238  *
239  *  clean up memory allocated by the line.
240  *  if destroy is true, line will be removed from global device list
241  *
242  * \param l SCCP Line
243  * \param remove_from_global as boolean_t
244  *
245  * \callgraph
246  * \callergraph
247  *
248  */
sccp_line_clean(linePtr l,boolean_t remove_from_global)249 void sccp_line_clean(linePtr l, boolean_t remove_from_global)
250 {
251 	sccp_line_kill_channels(l);
252 	sccp_linedevice_remove(NULL, l);                                        // removing all devices from this line.
253 	if (remove_from_global) {
254 		sccp_event_t *event = sccp_event_allocate(SCCP_EVENT_LINEINSTANCE_DESTROYED);
255 		if(event) {
256 			event->lineInstance.line = sccp_line_retain(l);
257 			sccp_event_syncFire(event);
258 		}
259 		sccp_line_removeFromGlobals(l);					// final release
260 	}
261 }
262 
263 /*!
264  * \brief Free a Line as scheduled command
265  * \param ptr SCCP Line Pointer
266  * \return success as int
267  *
268  * \callgraph
269  * \callergraph
270  *
271  */
__sccp_line_destroy(const void * ptr)272 int __sccp_line_destroy(const void *ptr)
273 {
274 	sccp_mailbox_t *mailbox = NULL;
275 	sccp_line_t *l = (sccp_line_t *) ptr;
276 	if (!l) {
277 		return -1;
278 	}
279 
280 	sccp_log((DEBUGCAT_LINE + DEBUGCAT_CONFIG)) (VERBOSE_PREFIX_1 "%s: Line FREE\n", l->name);
281 
282 	// cleaning l->channels
283 	sccp_line_kill_channels(l);
284 
285 	// cleaning l->devices
286 	sccp_linedevice_remove(NULL, l);
287 
288 	// cleanup mailboxes (l->mailboxes)
289 	{
290 		SCCP_LIST_LOCK(&l->mailboxes);
291 		while ((mailbox = SCCP_LIST_REMOVE_HEAD(&l->mailboxes, list))) {
292 			//sccp_mwi_unsubscribeMailbox(mailbox);
293 			sccp_free(mailbox);
294 		}
295 		SCCP_LIST_UNLOCK(&l->mailboxes);
296 		if (!SCCP_LIST_EMPTY(&l->mailboxes)) {
297 			pbx_log(LOG_WARNING, "%s: (line_destroy) there are connected mailboxes left during line destroy\n", l->name);
298 		}
299 		SCCP_LIST_HEAD_DESTROY(&l->mailboxes);
300 	}
301 
302 	// cleanup variables
303 	if (l->variables) {
304 		pbx_variables_destroy(l->variables);
305 		l->variables = NULL;
306 	}
307 
308 	// cleanup dynamically allocated memory by sccp_config
309 	sccp_config_cleanup_dynamically_allocated_memory(l, SCCP_CONFIG_LINE_SEGMENT);
310 
311 	// cleanup regcontext
312 	if (l->regcontext) {
313 		sccp_free(l->regcontext);
314 	}
315 
316 	// destroy attached channels (if any / should be none)
317 	SCCP_LIST_LOCK(&l->channels);
318 	sccp_channel_t *channel;
319 	while ((channel = SCCP_LIST_REMOVE_HEAD(&l->channels, list))) {
320 		sccp_channel_release(&channel);
321 	}
322 	if (!SCCP_LIST_EMPTY(&l->channels)) {
323 		pbx_log(LOG_WARNING, "%s: (line_destroy) there are connected channels left during line destroy\n", l->name);
324 	}
325 	SCCP_LIST_UNLOCK(&l->channels);
326 	SCCP_LIST_HEAD_DESTROY(&l->channels);
327 
328 	// destroy attached devices (if any / should be none)
329 	SCCP_LIST_LOCK(&l->devices);
330 	sccp_linedevice_t *linedevice;
331 	while ((linedevice = SCCP_LIST_REMOVE_HEAD(&l->devices, list))) {
332 		sccp_linedevice_release(&linedevice);
333 	}
334 	if (!SCCP_LIST_EMPTY(&l->devices)) {
335 		pbx_log(LOG_WARNING, "%s: (line_destroy) there are connected linedevices left during line destroy\n", l->name);
336 	}
337 	SCCP_LIST_UNLOCK(&l->devices);
338 	SCCP_LIST_HEAD_DESTROY(&l->devices);
339 
340 	return 0;
341 }
342 
sccp_line_copyCodecSetsFromLineToChannel(constLinePtr l,constDevicePtr maybe_d,channelPtr c)343 void sccp_line_copyCodecSetsFromLineToChannel(constLinePtr l, constDevicePtr maybe_d, channelPtr c)
344 {
345 	if(!l || !c) {
346 		return;
347 	}
348 	/* work already done in sccp_line_copyCodecSetsFromDeviceToLine during sccp_line_addDevice */
349 	if (!l->preferences_set_on_line_level && maybe_d) {
350 		memcpy(&c->preferences.audio, &maybe_d->preferences.audio, sizeof(c->preferences.audio));
351 		memcpy(&c->preferences.video, &maybe_d->preferences.video, sizeof(c->preferences.video));
352 	} else {
353 		memcpy(&c->preferences.audio, &l->preferences.audio, sizeof(c->preferences.audio));
354 		memcpy(&c->preferences.video, &l->preferences.video, sizeof(c->preferences.video));
355 	}
356 	if (maybe_d) {
357 		memcpy(&c->capabilities.audio, &maybe_d->capabilities.audio, sizeof(c->capabilities.audio));
358 		memcpy(&c->capabilities.video, &maybe_d->capabilities.video, sizeof(c->capabilities.video));
359 	} else {
360 		memcpy(&c->capabilities.audio, &l->capabilities.audio, sizeof(c->capabilities.audio));
361 		memcpy(&c->capabilities.video, &l->capabilities.video, sizeof(c->capabilities.video));
362 	}
363 
364 	// use a minimal default set, all devices should be able to support (last resort)
365 	if (c->preferences.audio[0] == SKINNY_CODEC_NONE) {
366 		pbx_log(LOG_WARNING, "%s: (updatePreferencesFromDevicesToLine) Could not retrieve preferences from line or device. Using Fallback Preferences from Global\n", c->designator);
367 		memcpy(&c->preferences.audio, &GLOB(global_preferences), sizeof(c->preferences.audio));
368 		memcpy(&c->preferences.video, &GLOB(global_preferences), sizeof(c->preferences.video));
369 	}
370 
371 	char s1[512];
372 
373 	char s2[512];
374 	sccp_log_and((DEBUGCAT_LINE + DEBUGCAT_CODEC)) (VERBOSE_PREFIX_3 "%s: (copyCodecSetsFromLineToChannel) channel capabilities:%s\n", c->designator, sccp_codec_multiple2str(s1, sizeof(s1) - 1, c->capabilities.audio, SKINNY_MAX_CAPABILITIES));
375 	sccp_log_and((DEBUGCAT_LINE + DEBUGCAT_CODEC)) (VERBOSE_PREFIX_3 "%s: (copyCodecSetsFromLineToChannel) channel preferences:%s\n", c->designator, sccp_codec_multiple2str(s2, sizeof(s2) - 1, c->preferences.audio, SKINNY_MAX_CAPABILITIES));
376 }
377 
sccp_line_updatePreferencesFromDevicesToLine(sccp_line_t * l)378 void sccp_line_updatePreferencesFromDevicesToLine(sccp_line_t * l)
379 {
380 	sccp_linedevice_t * ld = NULL;
381 	int numMembers = 0;
382 	boolean_t first=TRUE;
383 	if(!l) {
384 		return;
385 	}
386 	// sccp_log(DEBUGCAT_CODEC)(VERBOSE_PREFIX_3 "%s: Update line preferences\n", l->name);
387 	// combine all preferences
388 	SCCP_LIST_LOCK(&l->devices);
389 	SCCP_LIST_TRAVERSE(&l->devices, ld, list) {
390 		if (first) {
391 			if (!l->preferences_set_on_line_level) {
392 				memcpy(&l->preferences.audio, &ld->device->preferences.audio, sizeof(l->preferences.audio));
393 				memcpy(&l->preferences.video, &ld->device->preferences.video, sizeof(l->preferences.video));
394 			}
395 			first = FALSE;
396 		} else {
397 			if (!l->preferences_set_on_line_level) {
398 				skinny_codec_t temp[SKINNY_MAX_CAPABILITIES] = {SKINNY_CODEC_NONE};
399 				if(sccp_codec_getReducedSet(l->preferences.audio, ld->device->preferences.audio, temp) == 0) {
400 					// zero matching codecs we have to combine
401 					sccp_codec_combineSets(l->preferences.audio, ld->device->preferences.audio);
402 				} else {
403 					memcpy(&l->preferences.audio, &temp, sizeof *temp);
404 				}
405 				memset(&temp, SKINNY_CODEC_NONE, sizeof *temp);
406 				if(sccp_codec_getReducedSet(l->preferences.video, ld->device->preferences.video, temp) == 0) {
407 					// zero matching codecs we have to combine
408 					sccp_codec_combineSets(l->preferences.video, ld->device->preferences.video);
409 				} else {
410 					memcpy(&l->preferences.video, &temp, sizeof *temp);
411 				}
412 			}
413 		}
414 		numMembers++;
415 	}
416 	l->isShared = numMembers > 1 ? TRUE : FALSE;
417 	SCCP_LIST_UNLOCK(&l->devices);
418 
419 	char s1[512];
420 	sccp_log_and((DEBUGCAT_LINE + DEBUGCAT_CODEC)) (VERBOSE_PREFIX_3 "%s: (updatePreferencesFromDevicesToLine) line preferences:%s\n", l->name, sccp_codec_multiple2str(s1, sizeof(s1) - 1, l->preferences.audio, SKINNY_MAX_CAPABILITIES));
421 }
422 
sccp_line_updateCapabilitiesFromDevicesToLine(linePtr l)423 void sccp_line_updateCapabilitiesFromDevicesToLine(linePtr l)
424 {
425 	sccp_linedevice_t * ld = NULL;
426 	boolean_t first=TRUE;
427 	if (!l) {
428 		return;
429 	}
430 	// sccp_log(DEBUGCAT_CODEC)(VERBOSE_PREFIX_3 "%s: Update line capabilities \n", l->name);
431 	// combine all capabilities
432 	SCCP_LIST_LOCK(&l->devices);
433 	SCCP_LIST_TRAVERSE(&l->devices, ld, list) {
434 		if (first) {
435 			memcpy(&l->capabilities.audio, &ld->device->capabilities.audio, sizeof(l->capabilities.audio));
436 			memcpy(&l->capabilities.video, &ld->device->capabilities.video, sizeof(l->capabilities.video));
437 			first = FALSE;
438 		} else {
439 			sccp_codec_combineSets(l->capabilities.audio, ld->device->capabilities.audio);
440 			sccp_codec_combineSets(l->capabilities.video, ld->device->capabilities.video);
441 		}
442 	}
443 	SCCP_LIST_UNLOCK(&l->devices);
444 	// use a minimal default set, all devices should be able to support (last resort)
445 	if (l->capabilities.audio[0] == SKINNY_CODEC_NONE) {
446 		sccp_log((DEBUGCAT_LINE | DEBUGCAT_CODEC))(VERBOSE_PREFIX_3 "%s: (updateCapabilitiesFromDevicesToLine) Could not retrieve capabilities from line or device.\nUsing Fallback Capabilities Alaw/Ulaw\n", l->name);
447 		l->capabilities.audio[0] = SKINNY_CODEC_G711_ALAW_64K;
448 		l->capabilities.audio[1] = SKINNY_CODEC_G711_ALAW_56K;
449 		l->capabilities.audio[2] = SKINNY_CODEC_G711_ULAW_64K;
450 		l->capabilities.audio[3] = SKINNY_CODEC_G711_ULAW_56K;
451 		l->capabilities.audio[4] = SKINNY_CODEC_NONE;
452 	}
453 
454 	char s1[512];
455 	sccp_log_and((DEBUGCAT_LINE + DEBUGCAT_CODEC)) (VERBOSE_PREFIX_3 "%s: (updateCapabilitiesFromDevicesToLine) line capabilities:%s\n", l->name, sccp_codec_multiple2str(s1, sizeof(s1) - 1, l->capabilities.audio, SKINNY_MAX_CAPABILITIES));
456 }
457 
sccp_line_updateLineCapabilitiesByDevice(constDevicePtr d)458 void sccp_line_updateLineCapabilitiesByDevice(constDevicePtr d)
459 {
460 	if (!d) {
461 		return;
462 	}
463 	int instance = 0;
464 	for (instance = SCCP_FIRST_LINEINSTANCE; instance < d->lineButtons.size; instance++) {
465 		if (d->lineButtons.instance[instance]) {
466 			AUTO_RELEASE(sccp_linedevice_t, ld, sccp_linedevice_retain(d->lineButtons.instance[instance]));
467 			if(ld && ld->line) {
468 				sccp_line_updateCapabilitiesFromDevicesToLine(ld->line);
469 			}
470 		}
471 	}
472 }
473 
474 /*!
475  * \brief Set a Call Forward on a specific Line
476  * \param line SCCP Line
477  * \param device device that requested the forward
478  * \param type Call Forward Type as uint8_t
479  * \param number Number to which should be forwarded
480  * \todo we should check, that extension is reachable on line
481  *
482  * \callgraph
483  * \callergraph
484  *
485  * \todo implement cfwd_noanswer
486  */
sccp_line_cfwd(constLinePtr line,constDevicePtr device,sccp_cfwd_t type,char * number)487 void sccp_line_cfwd(constLinePtr line, constDevicePtr device, sccp_cfwd_t type, char * number)
488 {
489 	if (!line || !device) {
490 		return;
491 	}
492 
493 	AUTO_RELEASE(sccp_linedevice_t, ld, sccp_linedevice_find(device, line));
494 	if(ld) {
495 		sccp_linedevice_cfwd(ld, type, number);
496 	} else {
497 		pbx_log(LOG_ERROR, "%s: Device does not have line configured (ld not found)\n", DEV_ID_LOG(device));
498 	}
499 }
500 
501 /*! * \brief Add a Channel to a Line
502  *
503  * \param line SCCP Line
504  * \param channel SCCP Channel
505  *
506  * \warning
507  *  - line->channels is not always locked
508  *
509  */
sccp_line_addChannel(constLinePtr line,constChannelPtr channel)510 void sccp_line_addChannel(constLinePtr line, constChannelPtr channel)
511 {
512 	if (!line || !channel) {
513 		return;
514 	}
515 	sccp_channel_t *c = NULL;
516 
517 	AUTO_RELEASE(sccp_line_t, l , sccp_line_retain(line));
518 
519 	if (l) {
520 		//l->statistic.numberOfActiveChannels++;
521 		SCCP_LIST_LOCK(&l->channels);
522 		if ((c = sccp_channel_retain(channel))) {							// Add into list retained
523 #if CS_REFCOUNT_DEBUG
524 			sccp_refcount_addRelationship(c, l);
525 #endif
526 			sccp_log((DEBUGCAT_LINE)) (VERBOSE_PREFIX_1 "SCCP: Adding channel %d to line %s\n", c->callid, l->name);
527 			if (GLOB(callanswerorder) == SCCP_ANSWER_OLDEST_FIRST) {
528 				SCCP_LIST_INSERT_TAIL(&l->channels, c, list);					// add to list
529 			} else {
530 				SCCP_LIST_INSERT_HEAD(&l->channels, c, list);					// add to list
531 			}
532 		}
533 		SCCP_LIST_UNLOCK(&l->channels);
534 	}
535 }
536 
537 /*!
538  * \brief Remove a Channel from a Line
539  *
540  * \param line SCCP Line
541  * \param channel SCCP Channel
542  *
543  * \warning
544  *  - line->channels is not always locked
545  *
546  */
sccp_line_removeChannel(constLinePtr line,sccp_channel_t * channel)547 void sccp_line_removeChannel(constLinePtr line, sccp_channel_t * channel)
548 {
549 	if (!line || !channel) {
550 		return;
551 	}
552 	sccp_channel_t *c = NULL;
553 	AUTO_RELEASE(sccp_line_t, l , sccp_line_retain(line));
554 	if (l) {
555 		SCCP_LIST_LOCK(&l->channels);
556 		if ((c = SCCP_LIST_REMOVE(&l->channels, channel, list))) {
557 #if CS_REFCOUNT_DEBUG
558 			sccp_refcount_removeRelationship(c, l);
559 #endif
560 			if (c->state == SCCP_CHANNELSTATE_HOLD) {
561 				c->line->statistic.numberOfHeldChannels--;
562 			}
563 			sccp_log((DEBUGCAT_LINE)) (VERBOSE_PREFIX_1 "SCCP: Removing channel %d from line %s\n", c->callid, l->name);
564 			sccp_channel_release(&c);					/* explicit release of channel from list */
565 		}
566 		SCCP_LIST_UNLOCK(&l->channels);
567 	}
568 }
569 
570 /*=================================================================================== MWI EVENT HANDLING ==============*/
sccp_line_setMWI(constLinePtr l,int newmsgs,int oldmsgs)571 void sccp_line_setMWI(constLinePtr l, int newmsgs, int oldmsgs)
572 {
573 	AUTO_RELEASE(sccp_line_t, line, sccp_line_retain(l));
574 	if(line) {
575 		sccp_log((DEBUGCAT_MWI))(VERBOSE_PREFIX_3 "%s: (sccp_line_setMWI), newmsgs:%d, oldmsgs:%d\n", line->name, newmsgs, oldmsgs);
576 		if(line->voicemailStatistic.newmsgs != newmsgs || line->voicemailStatistic.oldmsgs != oldmsgs) {
577 			line->voicemailStatistic.newmsgs = newmsgs;
578 			line->voicemailStatistic.oldmsgs = oldmsgs;
579 		}
580 	}
581 }
582 /*=================================================================================== FIND FUNCTIONS ==============*/
583 
584 /*!
585  * \brief Find Line by Name
586  *
587  * \callgraph
588  * \callergraph
589  *
590  */
sccp_line_find_byname(const char * name,uint8_t useRealtime)591 linePtr sccp_line_find_byname(const char * name, uint8_t useRealtime)
592 {
593 	sccp_line_t *l = NULL;
594 
595 	SCCP_RWLIST_RDLOCK(&GLOB(lines));
596 	l = SCCP_RWLIST_FIND(&GLOB(lines), sccp_line_t, tmpl, list, (sccp_strcaseequals(tmpl->name, name)), TRUE, __FILE__, __LINE__, __PRETTY_FUNCTION__);
597 	SCCP_RWLIST_UNLOCK(&GLOB(lines));
598 #ifdef CS_SCCP_REALTIME
599 	if (!l && useRealtime) {
600 		l = sccp_line_find_realtime_byname(name);
601 	}
602 #endif
603 
604 	if (!l) {
605 		sccp_log((DEBUGCAT_LINE)) (VERBOSE_PREFIX_3 "SCCP: Line '%s' not found.\n", name);
606 		return NULL;
607 	}
608 	return l;
609 }
610 
611 #ifdef CS_SCCP_REALTIME
612 
613 /*!
614  * \brief Find Line via Realtime
615  *
616  * \callgraph
617  * \callergraph
618  */
619 #if DEBUG
620 /*!
621  * \param name Line Name
622  * \param filename Debug FileName
623  * \param lineno Debug LineNumber
624  * \param func Debug Function Name
625  * \return SCCP Line
626  */
__sccp_line_find_realtime_byname(const char * name,const char * filename,int lineno,const char * func)627 linePtr __sccp_line_find_realtime_byname(const char * name, const char * filename, int lineno, const char * func)
628 #	else
629 /*!
630  * \param name Line Name
631  * \return SCCP Line
632  */
633 linePtr sccp_line_find_realtime_byname(const char * name)
634 #	endif
635 {
636 	sccp_line_t *l = NULL;
637 	PBX_VARIABLE_TYPE *v = NULL, *variable = NULL;
638 
639 	if (sccp_strlen_zero(GLOB(realtimelinetable)) || sccp_strlen_zero(name)) {
640 		return NULL;
641 	}
642 
643 	if (sccp_strlen_zero(name)) {
644 		sccp_log((DEBUGCAT_LINE)) (VERBOSE_PREFIX_3 "SCCP: Not allowed to search for line with name ''\n");
645 		return NULL;
646 	}
647 
648 	if ((variable = pbx_load_realtime(GLOB(realtimelinetable), "name", name, NULL))) {
649 		v = variable;
650 		sccp_log((DEBUGCAT_LINE + DEBUGCAT_REALTIME)) (VERBOSE_PREFIX_3 "SCCP: Line '%s' found in realtime table '%s'\n", name, GLOB(realtimelinetable));
651 
652 		sccp_log((DEBUGCAT_LINE)) (VERBOSE_PREFIX_4 "SCCP: creating realtime line '%s'\n", name);
653 
654 		if ((l = sccp_line_create(name))) {								/* already retained */
655 			sccp_config_applyLineConfiguration(l, variable);
656 			l->realtime = TRUE;
657 			sccp_line_addToGlobals(l);								// can return previous instance on doubles
658 			pbx_variables_destroy(v);
659 		} else {
660 			pbx_log(LOG_ERROR, "SCCP: Unable to build realtime line '%s'\n", name);
661 		}
662 		return l;
663 	}
664 
665 	sccp_log((DEBUGCAT_LINE + DEBUGCAT_REALTIME)) (VERBOSE_PREFIX_3 "SCCP: Line '%s' not found in realtime table '%s'\n", name, GLOB(realtimelinetable));
666 	return NULL;
667 }
668 #endif
669 
670 /*!
671  * \brief Find Line by Instance on device
672  *
673  * \todo No ID Specified only instance, should this function be renamed ?
674  *
675  * \callgraph
676  * \callergraph
677  *
678  */
679 #if DEBUG
680 /*!
681  * \param d SCCP Device
682  * \param instance line instance as int
683  * \param filename Debug FileName
684  * \param lineno Debug LineNumber
685  * \param func Debug Function Name
686  * \return SCCP Line (can be null)
687  */
__sccp_line_find_byid(constDevicePtr d,uint16_t instance,const char * filename,int lineno,const char * func)688 linePtr __sccp_line_find_byid(constDevicePtr d, uint16_t instance, const char * filename, int lineno, const char * func)
689 #else
690 /*!
691  * \param d SCCP Device
692  * \param instance line instance as int
693  * \return SCCP Line (can be null)
694  */
695 linePtr sccp_line_find_byid(constDevicePtr d, uint16_t instance)
696 #endif
697 {
698 	sccp_line_t *l = NULL;
699 
700 	if (!d || instance == 0) {
701 		return NULL;
702 	}
703 
704 	sccp_log((DEBUGCAT_LINE + DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Looking for line with instance %d.\n", DEV_ID_LOG(d), instance);
705 
706 	if (0 < instance && instance < d->lineButtons.size && d->lineButtons.instance[instance] && d->lineButtons.instance[instance]->line) {
707 #if DEBUG
708 		l = (sccp_line_t *)sccp_refcount_retain(d->lineButtons.instance[instance]->line, filename, lineno, func);
709 #else
710 		l = sccp_line_retain(d->lineButtons.instance[instance]->line);
711 #endif
712 	}
713 	if (!l) {
714 		sccp_log((DEBUGCAT_LINE + DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: No line found with instance %d.\n", DEV_ID_LOG(d), instance);
715 		return NULL;
716 	}
717 
718 	sccp_log((DEBUGCAT_LINE + DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Found line %s\n", "SCCP", l->name);
719 
720 	return l;
721 }
722 
723 /*!
724  * \brief Find Line by ButtonIndex on device
725  *
726  * \todo No ID Specified only instance, should this function be renamed ?
727  *
728  * \callgraph
729  * \callergraph
730  *
731  */
732 #if DEBUG
733 /*!
734  * \param d SCCP Device
735  * \param buttonIndex Button Index as uint16_t
736  * \param filename Debug FileName
737  * \param lineno Debug LineNumber
738  * \param func Debug Function Name
739  * \return SCCP Line (can be null)
740  */
__sccp_line_find_byButtonIndex(constDevicePtr d,uint16_t buttonIndex,const char * filename,int lineno,const char * func)741 linePtr __sccp_line_find_byButtonIndex(constDevicePtr d, uint16_t buttonIndex, const char * filename, int lineno, const char * func)
742 #else
743 /*!
744  * \param d SCCP Device
745  * \param instance line instance as int
746  * \return SCCP Line (can be null)
747  */
748 linePtr sccp_line_find_byButtonIndex(constDevicePtr d, uint16_t buttonIndex)
749 #endif
750 {
751 	sccp_line_t *l = NULL;
752 
753 	if (!d || buttonIndex == 0) {
754 		return NULL;
755 	}
756 
757 	sccp_log((DEBUGCAT_LINE + DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Looking for line with buttonIndex %d.\n", DEV_ID_LOG(d), buttonIndex);
758 
759 	if (buttonIndex > 0 && buttonIndex < StationMaxButtonTemplateSize && d->buttonTemplate[buttonIndex - 1].type == SKINNY_BUTTONTYPE_LINE && d->buttonTemplate[buttonIndex - 1].ptr ) {
760 #if DEBUG
761 		l = (sccp_line_t *)sccp_refcount_retain(d->buttonTemplate[buttonIndex - 1].ptr, filename, lineno, func);
762 #else
763 		l = sccp_line_retain(d->buttonTemplate[buttonIndex - 1].ptr);
764 #endif
765 	}
766 	if (!l) {
767 		sccp_log((DEBUGCAT_LINE + DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: No line found with buttonIndex %d.\n", DEV_ID_LOG(d), buttonIndex);
768 		return NULL;
769 	}
770 
771 	sccp_log((DEBUGCAT_LINE + DEBUGCAT_DEVICE)) (VERBOSE_PREFIX_3 "%s: Found line %s\n", "SCCP", l->name);
772 
773 	return l;
774 }
775 // 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;
776