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