1 /*!
2  * \file        sccp_management.c
3  * \brief       SCCP Management Class
4  * \author      Marcello Ceschia <marcello [at] ceschia.de>
5  * \note        This program is free software and may be modified and distributed under the terms of the GNU Public License.
6  *              See the LICENSE file at the top of the source tree.
7  */
8 #include "config.h"
9 #ifdef CS_SCCP_MANAGER
10 #include "common.h"
11 #include "sccp_channel.h"
12 #include "sccp_actions.h"
13 #include "sccp_config.h"
14 #include "sccp_device.h"
15 #include "sccp_feature.h"
16 #include "sccp_line.h"
17 #	include "sccp_linedevice.h"
18 #	include "sccp_management.h"
19 #	include "sccp_session.h"
20 #	include "sccp_utils.h"
21 #	include "sccp_labels.h"
22 #	include "sccp_featureParkingLot.h"
23 #	include <asterisk/threadstorage.h>
24 #	include <asterisk/localtime.h>
25 
26 SCCP_FILE_VERSION(__FILE__, "");
27 
28 /*** DOCUMENTATION
29 	<manager name="SCCPAnswerCall" language="en_US">
30 		<synopsis>Answer an inbound call on a device.</synopsis>
31 		<syntax>
32 			<xi:include href="../core-en_US.xml" parse="xml"
33 				xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])"/>
34 			<parameter name="DeviceName" required="true">
35 				<para>DeviceId of the device with the incoming/ringing call.</para>
36 			</parameter>
37 			<parameter name="ChannelId" required="true">
38 				<para>CallId of the ringing channel. Only the interger part (last part) of the <replaceable>ChannelId</replaceable> should be provided.</para>
39 			</parameter>
40 		</syntax>
41 		<description>
42 			<para>Answer an inbound call on a skinny device with <replaceable>DeviceId</replaceable>.</para>
43 			<note>
44 				<para>The inbound call must be in the Ring-in state at the time of issuing this command.</para>
45 			</note>
46 			<warning>
47 				<para>Deprecated in favor of SCCPAnswerCall1</para>
48 			</warning>
49 		</description>
50 		<see-also>
51 			<ref type="manager">SCCPAnswerCall1</ref>
52 		</see-also>
53 	</manager>
54 	<manager name="SCCPDeviceAddLine" language="en_US">
55 		<synopsis>Add a existing line to an active device.</synopsis>
56 		<syntax>
57 			<xi:include href="../core-en_US.xml" parse="xml"
58 				xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])"/>
59 			<parameter name="DeviceName" required="true">
60 				<para>DeviceId to add a new line to.</para>
61 			</parameter>
62 			<parameter name="LineName" required="true">
63 				<para>LineName of an existing line that has to be added to this device.</para>
64 			</parameter>
65 		</syntax>
66 		<description>
67 			<para>Add a existing line to an active device.</para>
68 			<para>The device will have to be restarted for the new line to appear.</para>
69 			<note>
70 				<para>The effect of this action is temporary and not persisted.</para>
71 			</note>
72 			<warning>
73 				<para>Deprecated</para>
74 			</warning>
75 		</description>
76 		<see-also>
77 			<ref type="manager">SCCPDeviceRestart</ref>
78 		</see-also>
79 	</manager>
80 	<manager name="SCCPDeviceRestart" language="en_US">
81 		<synopsis>Send a restart message to a registered device.</synopsis>
82 		<syntax>
83 			<xi:include href="../core-en_US.xml" parse="xml"
84 				xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])"/>
85 			<parameter name="DeviceName" required="true">
86 				<para><replaceable>DeviceId</replaceable> of the device that should be restarted.</para>
87 			</parameter>
88 			<parameter name="Type" required="true" default="restart">
89 				<para>Type of restart to be performed.</para>
90 				<enumlist>
91 					<enum name="full">
92 						<para>Full reboot/reset.</para>
93 					</enum>
94 					<enum name="reset">
95 						<para>Full reboot/reset.</para>
96 					</enum>
97 					<enum name="applyConfig">
98 						<para>Reload sep.cnf.xml file, connection is maintained if changes are only minor.</para>
99 					</enum>
100 					<enum name="restart">
101 						<para>Quick restart.</para>
102 					</enum>
103 				</enumlist>
104 			</parameter>
105 		</syntax>
106 		<description>
107 			<para>Send a restart message to a registered device. The type of restart can be specified.</para>
108 			<note>
109 				<para>Equivalent to <astcli>sccp restart</astcli>.</para>
110 			</note>
111 		</description>
112 	</manager>
113 	<manager name="SCCPDeviceSetDND" language="en_US">
114 		<synopsis>Set do not disturb status for a particular device.</synopsis>
115 		<syntax>
116 			<xi:include href="../core-en_US.xml" parse="xml"
117 				xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])"/>
118 			<parameter name="Devicename" required="true">
119 				<para>DeviceId of the Device, for which to set Do Not Disturb.</para>
120 			</parameter>
121 			<parameter name="DNDState" required="true">
122 				<enumlist>
123 					<enum name="reject">
124 						<para>Reject the call and signal Busy to the caller.</para>
125 					</enum>
126 					<enum name="silent">
127 						<para>Incoming Call is displayed on the destination, but does not ring.</para>
128 					</enum>
129 					<enum name="off">
130 						<para>Do not disturb is turned of.</para>
131 					</enum>
132 				</enumlist>
133 			</parameter>
134 		</syntax>
135 		<description>
136 			<para>Change the do not disturb status (<replaceable>DNDState</replaceable>) for a device denoted by <replaceable>DeviceId</replaceable>.</para>
137 			<warning>
138 				<para>Deprecated</para>
139 			</warning>
140 		</description>
141 		<see-also>
142 			<ref type="manager">SCCPDndDevice</ref>
143 		</see-also>
144 	</manager>
145 	<manager name="SCCPStartCall" language="en_US">
146 		<synopsis>Description: start a new call on a device/line.</synopsis>
147 		<syntax>
148 			<xi:include href="../core-en_US.xml" parse="xml"
149 				xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])"/>
150 			<parameter name="DeviceId" required="true">
151 				<para>Name of the <replaceable>DeviceId</replaceable> to use to make the new call.</para>
152 			</parameter>
153 			<parameter name="LineName">
154 				<para>Name of the line to use on the device to make this call.</para>
155 				<para>If the <replaceable>LineName</replaceable> is not provided the default line of the device will be used.</para>
156 			</parameter>
157 			<parameter name="Number" required="true">
158 				<para>Extension to call.</para>
159 			</parameter>
160 			<parameter name="ChannelId">
161 				<para>The <replaceable>ChannelId</replaceable> that will be copied to the uniqueid of the channel that will be created to make this call.</para>
162 				<para>This is available to ease the developement of automated scripts.</para>
163 				<para>Note: Only available on asterisk-12 and higher.</para>
164 			</parameter>
165 		</syntax>
166 		<description>
167 			<para>Make a new call to an extension on the device and line combination supplied.</para>
168 		</description>
169 	</manager>
170 ***/
171 
172 /*
173  * Pre Declarations
174  */
175 void sccp_manager_eventListener(const sccp_event_t * event);
176 static int sccp_manager_startCall(struct mansession * s, const struct message * m);
177 static const char * startCall_command = "SCCPStartCall";
178 static const char * answerCall_command = "SCCPAnswerCall";
179 static const char * deviceAddLine_command = "SCCPDeviceAddLine";
180 static const char * configMetaData_command = "SCCPConfigMetaData";
181 static const char * deviceRestart_command = "SCCPDeviceRestart";
182 static const char * deviceSetDND_command = "SCCPDeviceSetDND";
183 
184 /* old */
185 static int sccp_manager_show_devices(struct mansession * s, const struct message * m);
186 static int sccp_manager_show_lines(struct mansession * s, const struct message * m);
187 static int sccp_manager_restart_device(struct mansession * s, const struct message * m);
188 static int sccp_manager_device_add_line(struct mansession * s, const struct message * m);
189 static int sccp_manager_device_update(struct mansession * s, const struct message * m);
190 static int sccp_manager_device_set_dnd(struct mansession * s, const struct message * m);
191 static int sccp_manager_line_fwd_update(struct mansession * s, const struct message * m);
192 static int sccp_manager_answerCall2(struct mansession * s, const struct message * m);
193 static int sccp_manager_hangupCall(struct mansession * s, const struct message * m);
194 static int sccp_manager_holdCall(struct mansession * s, const struct message * m);
195 
196 /*
197  * Descriptions
198  */
199 static char management_show_devices_desc[] = "Description: Lists SCCP devices in text format with details on current status. (DEPRECATED in favor of SCCPShowDevices)\n" "\n" "DevicelistComplete.\n" "Variables: \n" "  ActionID: <id>	Action ID for this transaction. Will be returned.\n";
200 static char management_show_lines_desc[] = "Description: Lists SCCP lines in text format with details on current status. (DEPRECATED in favor of SCCPShowLines)\n" "\n" "LinelistComplete.\n" "Variables: \n" "  ActionID: <id>	Action ID for this transaction. Will be returned.\n";
201 static char management_device_update_desc[] = "Description: restart a given device\n" "\n" "Variables:\n" "   Devicename: Name of device\n";
202 static char management_line_fwd_update_desc[] = "Description: update forward status for line\n" "\n" "Variables:\n" "  Devicename: Name of device\n" "  Linename: Name of line\n" "  Forwardtype: type of cfwd (all | busy | noAnswer)\n" "  Disable: yes Disable call forward (optional)\n" "  Number: number to forward calls (optional)";
203 static char management_hangupcall_desc[] = "Description: hangup a channel/call\n" "\n" "Variables:\n" "  channelId: Id of the Channel to hangup\n";
204 static char management_hold_desc[] = "Description: hold/resume a call\n" "\n" "Variables:\n" "  channelId: Id of the channel to hold/unhold\n" "  hold: hold=true / resume=false\n" "  Devicename: Name of the Device\n" "  SwapChannels: Swap channels when resuming and an active channel is present (true/false)\n";
205 
206 #if HAVE_PBX_MANAGER_HOOK_H
207 static int sccp_asterisk_managerHookHelper(int category, const char *event, char *content);
208 boolean_t  hook_registered = FALSE;
209 
210 static struct manager_custom_hook sccp_manager_hook = {
211 	.file = "chan_sccp",
212 	.helper = sccp_asterisk_managerHookHelper,
213 };
214 #	endif
215 
216 /*!
217  * \brief Register management commands
218  * \note deprecated
219  */
sccp_register_management(void)220 int sccp_register_management(void)
221 {
222 	int result = 0;
223 
224 	/* Register manager commands */
225 #if ASTERISK_VERSION_NUMBER < 10600
226 #define _MAN_FLAGS	EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG
227 #else
228 #define _MAN_FLAGS	(EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG | EVENT_FLAG_REPORTING)
229 #endif
230 
231 	result = pbx_manager_register("SCCPListDevices", _MAN_FLAGS, sccp_manager_show_devices, "List SCCP devices", management_show_devices_desc);
232 	result |= pbx_manager_register("SCCPListLines", _MAN_FLAGS, sccp_manager_show_lines, "List SCCP lines", management_show_lines_desc);
233 	result |= pbx_manager_register("SCCPDeviceUpdate", _MAN_FLAGS, sccp_manager_device_update, "add a line to device", management_device_update_desc);
234 	result |= pbx_manager_register("SCCPLineForwardUpdate", _MAN_FLAGS, sccp_manager_line_fwd_update, "set call-forward on a line", management_line_fwd_update_desc);
235 	result |= pbx_manager_register("SCCPHangupCall", _MAN_FLAGS, sccp_manager_hangupCall, "hangup a channel", management_hangupcall_desc);
236 	result |= pbx_manager_register("SCCPHoldCall", _MAN_FLAGS, sccp_manager_holdCall, "hold/unhold a call", management_hold_desc);
237 	result |= iPbx.register_manager(deviceAddLine_command, _MAN_FLAGS, sccp_manager_device_add_line, NULL, NULL);
238 	result |= iPbx.register_manager(startCall_command, _MAN_FLAGS, sccp_manager_startCall, NULL, NULL);
239 	result |= iPbx.register_manager(answerCall_command, _MAN_FLAGS, sccp_manager_answerCall2, NULL, NULL);
240 	result |= iPbx.register_manager(configMetaData_command, _MAN_FLAGS, sccp_manager_config_metadata, NULL, NULL);
241 	result |= iPbx.register_manager(deviceRestart_command, _MAN_FLAGS, sccp_manager_restart_device, NULL, NULL);
242 	result |= iPbx.register_manager(deviceSetDND_command, _MAN_FLAGS, sccp_manager_device_set_dnd, NULL, NULL);
243 #	undef _MAN_FLAGS
244 
245 #	if HAVE_PBX_MANAGER_HOOK_H
246 #		if CS_AST_MANAGER_CHECK_ENABLED
247 	if (ast_manager_check_enabled())
248 #		endif
249 	{
250 		ast_manager_register_hook(&sccp_manager_hook);
251 		hook_registered = TRUE;
252 	}
253 #	else
254 #		warning "manager_custom_hook not found, monitor indication does not work properly"
255 #	endif
256 	return result;
257 }
258 
259 /*!
260  * \brief Unregister management commands
261  */
sccp_unregister_management(void)262 int sccp_unregister_management(void)
263 {
264 	int result = 0;
265 
266 	result = pbx_manager_unregister("SCCPListDevices");
267 	result |= pbx_manager_unregister("SCCPListLines");
268 	result |= pbx_manager_unregister("SCCPDeviceUpdate");
269 	result |= pbx_manager_unregister("SCCPLineForwardUpdate");
270 	result |= pbx_manager_unregister("SCCPHangupCall");
271 	result |= pbx_manager_unregister("SCCPHoldCall");
272 
273 	result |= pbx_manager_unregister(deviceAddLine_command);
274 	result |= pbx_manager_unregister(startCall_command);
275 	result |= pbx_manager_unregister(answerCall_command);
276 	result |= pbx_manager_unregister(configMetaData_command);
277 	result |= pbx_manager_unregister(deviceRestart_command);
278 	result |= pbx_manager_unregister(deviceSetDND_command);
279 #	if HAVE_PBX_MANAGER_HOOK_H
280 	if (hook_registered) {
281 		ast_manager_unregister_hook(&sccp_manager_hook);
282 	}
283 #	endif
284 	return result;
285 }
286 
287 /*!
288  * \brief starting manager-module
289  */
sccp_manager_module_start(void)290 void sccp_manager_module_start(void)
291 {
292 	sccp_event_subscribe(SCCP_EVENT_DEVICE_ATTACHED | SCCP_EVENT_DEVICE_PREREGISTERED | SCCP_EVENT_DEVICE_REGISTERED | SCCP_EVENT_FEATURE_CHANGED, sccp_manager_eventListener, TRUE);
293 	sccp_event_subscribe(SCCP_EVENT_DEVICE_DETACHED | SCCP_EVENT_DEVICE_UNREGISTERED, sccp_manager_eventListener, FALSE);
294 }
295 
296 /*!
297  * \brief stop manager-module
298  *
299  */
sccp_manager_module_stop(void)300 void sccp_manager_module_stop(void)
301 {
302 	sccp_event_unsubscribe(SCCP_EVENT_DEVICE_ATTACHED | SCCP_EVENT_DEVICE_DETACHED | SCCP_EVENT_DEVICE_PREREGISTERED | SCCP_EVENT_DEVICE_REGISTERED | SCCP_EVENT_DEVICE_UNREGISTERED, sccp_manager_eventListener);
303 }
304 
305 /*!
306  * \brief Event Listener
307  *
308  * Handles the manager events that need to be posted when an event happens
309  */
sccp_manager_eventListener(const sccp_event_t * event)310 void sccp_manager_eventListener(const sccp_event_t * event)
311 {
312 	sccp_device_t * device = NULL;
313 	sccp_linedevice_t * ld = NULL;
314 
315 	if (!event) {
316 		return;
317 	}
318 	switch (event->type) {
319 		case SCCP_EVENT_DEVICE_REGISTERED:
320 			device = event->deviceRegistered.device;						// already retained in the event
321 			manager_event(EVENT_FLAG_CALL, "DeviceStatus", "ChannelType: SCCP\r\nChannelObjectType: Device\r\nDeviceStatus: %s\r\nSCCPDevice: %s\r\n", "REGISTERED", DEV_ID_LOG(device));
322 			break;
323 
324 		case SCCP_EVENT_DEVICE_UNREGISTERED:
325 			device = event->deviceRegistered.device;						// already retained in the event
326 			manager_event(EVENT_FLAG_CALL, "DeviceStatus", "ChannelType: SCCP\r\nChannelObjectType: Device\r\nDeviceStatus: %s\r\nSCCPDevice: %s\r\n", "UNREGISTERED", DEV_ID_LOG(device));
327 			break;
328 
329 		case SCCP_EVENT_DEVICE_PREREGISTERED:
330 			device = event->deviceRegistered.device;						// already retained in the event
331 			manager_event(EVENT_FLAG_CALL, "DeviceStatus", "ChannelType: SCCP\r\nChannelObjectType: Device\r\nDeviceStatus: %s\r\nSCCPDevice: %s\r\n", "PREREGISTERED", DEV_ID_LOG(device));
332 			break;
333 
334 		case SCCP_EVENT_DEVICE_ATTACHED:
335 			device = event->deviceAttached.ld->device;                                        // already retained in the event
336 			ld = event->deviceAttached.ld;                                                    // already retained in the event
337 			manager_event(EVENT_FLAG_CALL, "PeerStatus",
338 				      "ChannelType: SCCP\r\nChannelObjectType: DeviceLine\r\nPeerStatus: %s\r\nSCCPDevice: %s\r\nSCCPLine: %s\r\nSCCPLineName: %s\r\nSubscriptionId: %s\r\nSubscriptionName: %s\r\n", "ATTACHED",
339 				      DEV_ID_LOG(device), ld && ld->line ? ld->line->name : "(null)", (ld && ld->line && ld->line->label) ? ld->line->label : "(null)", ld->subscriptionId.number, ld->subscriptionId.name);
340 			break;
341 
342 		case SCCP_EVENT_DEVICE_DETACHED:
343 			device = event->deviceAttached.ld->device;                                        // already retained in the event
344 			ld = event->deviceAttached.ld;                                                    // already retained in the event
345 			manager_event(EVENT_FLAG_CALL, "PeerStatus",
346 				      "ChannelType: SCCP\r\nChannelObjectType: DeviceLine\r\nPeerStatus: %s\r\nSCCPDevice: %s\r\nSCCPLine: %s\r\nSCCPLineName: %s\r\nSubscriptionId: %s\r\nSubscriptionName: %s\r\n", "DETACHED",
347 				      DEV_ID_LOG(device), ld && ld->line ? ld->line->name : "(null)", (ld && ld->line && ld->line->label) ? ld->line->label : "(null)", ld->subscriptionId.number, ld->subscriptionId.name);
348 			break;
349 
350 		case SCCP_EVENT_FEATURE_CHANGED:
351 			device = event->featureChanged.device;						// already retained in the event
352 			ld = event->featureChanged.optional_linedevice;                                        // either NULL or already retained in the event
353 			sccp_feature_type_t featureType = event->featureChanged.featureType;
354 			sccp_cfwd_t cfwd_type = SCCP_CFWD_NONE;
355 
356 			switch(featureType) {
357 				case SCCP_FEATURE_DND:
358 					manager_event(EVENT_FLAG_CALL, "DND", "ChannelType: SCCP\r\nChannelObjectType: Device\r\nFeature: %s\r\nStatus: %s\r\nSCCPDevice: %s\r\n", sccp_feature_type2str(SCCP_FEATURE_DND), sccp_dndmode2str((sccp_dndmode_t)device->dndFeature.status), DEV_ID_LOG(device));
359 					break;
360 				case SCCP_FEATURE_CFWDALL:
361 					cfwd_type = SCCP_CFWD_ALL;
362 					break;
363 				case SCCP_FEATURE_CFWDBUSY:
364 					cfwd_type = SCCP_CFWD_BUSY;
365 					break;
366 				case SCCP_FEATURE_CFWDNOANSWER:
367 					cfwd_type = SCCP_CFWD_NOANSWER;
368 					break;
369 				case SCCP_FEATURE_CFWDNONE:
370 					cfwd_type = SCCP_CFWD_NONE;
371 					manager_event(EVENT_FLAG_CALL, "CallForward", "ChannelType: SCCP\r\nChannelObjectType: DeviceLine\r\nFeature: %s\r\nStatus: Off\r\nSCCPLine: %s\r\nSCCPDevice: %s\r\n",
372 						      sccp_feature_type2str(featureType), (ld && ld->line) ? ld->line->name : "(null)", DEV_ID_LOG(device));
373 					break;
374 				default:
375 					break;
376 			}
377 			if(ld && cfwd_type != SCCP_CFWD_NONE) {
378 				manager_event(EVENT_FLAG_CALL, "CallForward", "ChannelType: SCCP\r\nChannelObjectType: DeviceLine\r\nFeature: %s\r\nStatus: %s\r\nExtension: %s\r\nSCCPLine: %s\r\nSCCPDevice: %s\r\n",
379 					      sccp_feature_type2str(featureType), ld->cfwd[cfwd_type].enabled ? "On" : "Off", ld->cfwd[cfwd_type].number, (ld->line) ? ld->line->name : "(null)", DEV_ID_LOG(device));
380 			}
381 
382 			break;
383 
384 		default:
385 			break;
386 	}
387 }
388 
389 /*!
390  * \brief Show Devices Command
391  * \param s Management Session
392  * \param m Message
393  * \return Success as int
394  *
395  * \called_from_asterisk
396  * \note deprecated
397  *
398  */
sccp_manager_show_devices(struct mansession * s,const struct message * m)399 static int sccp_manager_show_devices(struct mansession *s, const struct message *m)
400 {
401 	const char *id = astman_get_header(m, "ActionID");
402 	sccp_device_t *device = NULL;
403 	char idtext[256] = "";
404 	int total = 0;
405 	char regtime[25];
406 	char clientAddress[INET6_ADDRSTRLEN];
407 
408 	snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
409 
410 	pbxman_send_listack(s, m, "Device status list will follow", "start");
411 	// List the peers in separate manager events
412 	SCCP_RWLIST_RDLOCK(&GLOB(devices));
413 	SCCP_RWLIST_TRAVERSE(&GLOB(devices), device, list) {
414 		struct ast_tm tm;
415 		struct timeval when = { device->registrationTime, 0 };
416 		ast_localtime (&when, &tm, NULL);
417 
418 		struct sockaddr_storage sas = { 0 };
419 		if (sccp_session_getSas(device->session, &sas)) {
420 			sccp_copy_string(clientAddress, sccp_netsock_stringify(&sas), sizeof(clientAddress));
421 		} else {
422 			sccp_copy_string(clientAddress, "--", sizeof(clientAddress));
423 		}
424 
425 		ast_strftime (regtime, sizeof (regtime), "%c ", &tm);
426 		astman_append(s, "Event: DeviceEntry\r\n%s", idtext);
427 		astman_append(s, "ChannelType: SCCP\r\n");
428 		astman_append(s, "ObjectId: %s\r\n", device->id);
429 		astman_append(s, "ObjectType: device\r\n");
430 		astman_append(s, "Description: %s\r\n", device->description  ? device->description : "<not set>");
431 		astman_append(s, "IPaddress: %s\r\n", clientAddress);
432 		astman_append(s, "Reg_Status: %s\r\n", skinny_registrationstate2str(sccp_device_getRegistrationState(device)));
433 		astman_append(s, "Reg_Time: %s\r\n", regtime);
434 		astman_append(s, "Active: %s\r\n", (device->active_channel) ? "Yes" : "No");
435 		astman_append(s, "NumLines: %d\r\n\r\n", device->configurationStatistic.numberOfLines);
436 		total++;
437 	}
438 
439 	SCCP_RWLIST_UNLOCK(&GLOB(devices));
440 
441 	// Send final confirmation
442 	astman_append(s, "Event: SCCPListDevicesComplete\r\n" "EventList: Complete\r\n" "ListItems: %d\r\n" "\r\n", total);
443 
444 	return 0;
445 }
446 
447 /*!
448  * \brief Show Lines Command
449  * \param s Management Session
450  * \param m Message
451  * \return Success as int
452  *
453  * \called_from_asterisk
454  * \note deprecated
455  *
456  */
sccp_manager_show_lines(struct mansession * s,const struct message * m)457 static int sccp_manager_show_lines(struct mansession *s, const struct message *m)
458 {
459 	const char *id = astman_get_header(m, "ActionID");
460 	sccp_line_t *line = NULL;
461 	char idtext[256] = "";
462 	int total = 0;
463 
464 	snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
465 
466 	pbxman_send_listack(s, m, "Device status list will follow", "start");
467 	/* List the peers in separate manager events */
468 	SCCP_RWLIST_RDLOCK(&GLOB(lines));
469 	SCCP_RWLIST_TRAVERSE(&GLOB(lines), line, list) {
470 		astman_append(s, "Event: LineEntry\r\n%s", idtext);
471 		astman_append(s, "ChannelType: SCCP\r\n");
472 		astman_append(s, "ObjectId: %s\r\n", line->id);
473 		astman_append(s, "ObjectType: line\r\n");
474 		astman_append(s, "Name: %s\r\n", line->name);
475 		astman_append(s, "Description: %s\r\n", line->description  ? line->description : "<not set>");
476 		astman_append(s, "Num_Channels: %d\r\n\r\n", SCCP_RWLIST_GETSIZE(&line->channels));
477 		total++;
478 	}
479 
480 	SCCP_RWLIST_UNLOCK(&GLOB(lines));
481 
482 	/* Send final confirmation */
483 	astman_append(s, "Event: SCCPListLinesComplete\r\n" "EventList: Complete\r\n" "ListItems: %d\r\n" "\r\n", total);
484 	return 0;
485 }
486 
487 /*!
488  * \brief Restart Command
489  * \param s Management Session
490  * \param m Message
491  * \return Success as int
492  *
493  * \called_from_asterisk
494  */
sccp_manager_restart_device(struct mansession * s,const struct message * m)495 static int sccp_manager_restart_device(struct mansession *s, const struct message *m)
496 {
497 	// sccp_list_t *hintList = NULL;
498 	const char *deviceName = astman_get_header(m, "Devicename");
499 	const char *type = astman_get_header(m, "Type");
500 
501 	if (sccp_strlen_zero(deviceName)) {
502 		astman_send_error(s, m, "Please specify the name of device to be reset");
503 		return 0;
504 	}
505 
506 	if (sccp_strlen_zero(type)) {
507 		pbx_log(LOG_WARNING, "Type not specified [reset|restart|applyconfig], using restart");
508 		type = "restart";
509 	}
510 
511 	AUTO_RELEASE(sccp_device_t, d , sccp_device_find_byid(deviceName, FALSE));
512 
513 	if (!d) {
514 		astman_send_error(s, m, "Device not found");
515 		return 0;
516 	}
517 
518 	if (!d->session) {
519 		astman_send_error(s, m, "Device not registered");
520 		return 0;
521 	}
522 
523 	if (!strncasecmp(type, "full", 4) || !strncasecmp(type, "reset", 5)) {
524 		sccp_device_sendReset(d, SKINNY_RESETTYPE_RESET);
525 	} else if (!strncasecmp(type, "applyconfig", 11)) {
526 		sccp_device_sendReset(d, SKINNY_RESETTYPE_APPLYCONFIG);
527 	} else {
528 		sccp_device_sendReset(d, SKINNY_RESETTYPE_RESTART);
529 	}
530 
531 	astman_send_ack(s, m, "Device restarted");
532 	//astman_append(s, "Send %s restart to device %s\r\n", type, deviceName);
533 	//astman_append(s, "\r\n");
534 
535 	return 0;
536 }
537 
538 /*!
539  * \brief Add Device Command
540  * \param s Management Session
541  * \param m Message
542  * \return Success as int
543  *
544  * \called_from_asterisk
545  */
sccp_manager_device_add_line(struct mansession * s,const struct message * m)546 static int sccp_manager_device_add_line(struct mansession *s, const struct message *m)
547 {
548 	const char *deviceName = astman_get_header(m, "Devicename");
549 	const char *lineName = astman_get_header(m, "Linename");
550 
551 	pbx_log(LOG_WARNING, "Attempt to get device %s\n", deviceName);
552 
553 	if (sccp_strlen_zero(deviceName)) {
554 		astman_send_error(s, m, "Please specify the name of device");
555 		return 0;
556 	}
557 
558 	if (sccp_strlen_zero(lineName)) {
559 		astman_send_error(s, m, "Please specify the name of line to be added");
560 		return 0;
561 	}
562 
563 	AUTO_RELEASE(sccp_device_t, d , sccp_device_find_byid(deviceName, FALSE));
564 
565 	if (!d) {
566 		astman_send_error(s, m, "Device not found");
567 		return 0;
568 	}
569 
570 	AUTO_RELEASE(sccp_line_t, line , sccp_line_find_byname(lineName, TRUE));
571 
572 	if (!line) {
573 		astman_send_error(s, m, "Line not found");
574 		return 0;
575 	}
576 	if (sccp_config_addButton(&d->buttonconfig, -1, LINE, line->name, NULL, NULL) == SCCP_CONFIG_CHANGE_CHANGED) {
577 		d->pendingUpdate = 1;
578 		sccp_config_addButton(&d->buttonconfig, -1, LINE, line->name, NULL, NULL);
579 		sccp_device_check_update(d);
580 		astman_append(s, "Done\r\n");
581 		astman_append(s, "\r\n");
582 	} else {
583 		astman_send_error(s, m, "Adding line button to device failed");
584 	}
585 	return 0;
586 }
587 
588 /*!
589  * \brief Update Line Forward Command
590  * \param s Management Session
591  * \param m Message
592  * \return Success as int
593  *
594  * \called_from_asterisk
595  */
sccp_manager_line_fwd_update(struct mansession * s,const struct message * m)596 static int sccp_manager_line_fwd_update(struct mansession *s, const struct message *m)
597 {
598 	const char *deviceName = astman_get_header(m, "Devicename");
599 	const char *lineName = astman_get_header(m, "Linename");
600 	const char *forwardType = astman_get_header(m, "Forwardtype");
601 	const char *Disable = astman_get_header(m, "Disable");
602 	const char *number = astman_get_header(m, "Number");
603 	sccp_cfwd_t cfwd_type = SCCP_CFWD_NONE;
604 	char cbuf[64] = "";
605 
606 	AUTO_RELEASE(sccp_device_t, d , sccp_device_find_byid(deviceName, FALSE));
607 
608 	if (!d) {
609 		pbx_log(LOG_WARNING, "%s: Device not found\n", deviceName);
610 		astman_send_error(s, m, "Device not found");
611 		return 0;
612 	}
613 
614 	AUTO_RELEASE(sccp_line_t, line , sccp_line_find_byname(lineName, TRUE));
615 
616 	if (!line) {
617 		pbx_log(LOG_WARNING, "%s: Line %s not found\n", deviceName, lineName);
618 		astman_send_error(s, m, "Line not found");
619 		return 0;
620 	}
621 
622 	if (SCCP_LIST_GETSIZE(&line->devices) > 1) {
623 		pbx_log(LOG_WARNING, "%s: Callforwarding on shared lines is not supported at the moment\n", deviceName);
624 		astman_send_error(s, m, "Callforwarding on shared lines is not supported at the moment");
625 		return 0;
626 	}
627 
628 	if (!forwardType) {
629 		pbx_log(LOG_WARNING, "%s: Forwardtype is not optional [all | busy | noanswer]\n", deviceName);
630 		astman_send_error(s, m, "Forwardtype is not optional [all | busy | noanswer]"); /* NoAnswer to be added later on */
631 		return 0;
632 	}
633 
634 	if (!Disable) {
635 		Disable = "no";
636 	}
637 
638 	if (line) {
639 		AUTO_RELEASE(sccp_linedevice_t, ld, sccp_linedevice_find(d, line));
640 
641 		if(ld) {
642 			if(!sccp_strlen_zero(forwardType) && sccp_true(Disable)) {
643 				for(uint x = SCCP_CFWD_ALL; x < SCCP_CFWD_SENTINEL; x++) {
644 					cfwd_type = (sccp_cfwd_t)x;
645 					ld->cfwd[cfwd_type].enabled = FALSE;
646 					sccp_copy_string(ld->cfwd[cfwd_type].number, "", sizeof(ld->cfwd[cfwd_type].number));
647 					sccp_feat_changed(ld->device, ld, sccp_cfwd2feature(cfwd_type));
648 				}
649 			} else {
650 				if(sccp_strcaseequals("all", forwardType)) {
651 					cfwd_type = SCCP_CFWD_ALL;
652 				} else if(sccp_strcaseequals("busy", forwardType)) {
653 					cfwd_type = SCCP_CFWD_BUSY;
654 				} else if(sccp_strcaseequals("noanswer", forwardType)) {
655 					cfwd_type = SCCP_CFWD_NOANSWER;
656 				}
657 				if(cfwd_type != SCCP_CFWD_NONE) {
658 					ld->cfwd[cfwd_type].enabled = sccp_true(Disable);
659 					const char * destination = ld->cfwd[cfwd_type].enabled ? number : "";
660 					sccp_copy_string(ld->cfwd[cfwd_type].number, destination, sizeof(ld->cfwd[cfwd_type].number));
661 					sccp_feat_changed(ld->device, ld, sccp_cfwd2feature(cfwd_type));
662 					snprintf(cbuf, sizeof(cbuf), "Line %s CallForward %s set to %s", lineName, sccp_cfwd2str(cfwd_type), destination);
663 				}
664 			}
665 			sccp_dev_forward_status(line, ld->lineInstance, ld->device);
666 		} else {
667 			pbx_log(LOG_WARNING, "%s: LineDevice not found for line %s (Device not registeed ?)\n", deviceName, lineName);
668 			astman_send_error(s, m, "LineDevice not found (Device not registered ?)");
669 			return 0;
670 		}
671 	}
672 	astman_send_ack(s, m, cbuf);
673 	return 0;
674 }
675 
676 /*!
677  * \brief Update Device Command
678  * \param s Management Session
679  * \param m Message
680  * \return Success as int
681  *
682  * \called_from_asterisk
683  */
sccp_manager_device_update(struct mansession * s,const struct message * m)684 static int sccp_manager_device_update(struct mansession *s, const struct message *m)
685 {
686 	const char *deviceName = astman_get_header(m, "Devicename");
687 
688 	if (sccp_strlen_zero(deviceName)) {
689 		astman_send_error(s, m, "Please specify the name of device");
690 		return 0;
691 	}
692 
693 	AUTO_RELEASE(sccp_device_t, d , sccp_device_find_byid(deviceName, FALSE));
694 
695 	if (!d) {
696 		astman_send_error(s, m, "Device not found");
697 		return 0;
698 	}
699 
700 	if (!d->session) {
701 		astman_send_error(s, m, "Device not active");
702 		return 0;
703 	}
704 
705 	sccp_handle_soft_key_template_req(d->session, d, NULL);
706 
707 	sccp_handle_button_template_req(d->session, d, NULL);
708 
709 	astman_send_ack(s, m, "Done");
710 	return 0;
711 }
712 
713 /*!
714  * \brief Set DND State on Device
715  * \param s Management Session
716  * \param m Message
717  * \return Success as int
718  *
719  * \called_from_asterisk
720  */
sccp_manager_device_set_dnd(struct mansession * s,const struct message * m)721 static int sccp_manager_device_set_dnd(struct mansession *s, const struct message *m)
722 {
723 	const char *deviceName = astman_get_header(m, "Devicename");
724 	const char *DNDState = astman_get_header(m, "DNDState");
725 	uint prevStatus = 0;
726 	char retValStr[64] = "";
727 
728 	/** we need the device for resuming calls */
729 	if (sccp_strlen_zero(deviceName)) {
730 		astman_send_error(s, m, "Devicename variable is required.");
731 		return 0;
732 	}
733 	if (sccp_strlen_zero(DNDState)) {
734 		astman_send_error(s, m, "DNDState variable is required.");
735 		return 0;
736 	}
737 	//astman_append(s, "remove channel '%s' from hold\n", channelId);
738 	AUTO_RELEASE(sccp_device_t, d , sccp_device_find_byid(deviceName, FALSE));
739 
740 	if (d) {
741 		if (d->dndFeature.enabled) {
742 			prevStatus = d->dndFeature.status;
743 			if (sccp_strcaseequals("reject", DNDState)) {
744 				d->dndFeature.status = SCCP_DNDMODE_REJECT;
745 			} else if (sccp_strcaseequals("silent", DNDState)) {
746 				d->dndFeature.status = SCCP_DNDMODE_SILENT;
747 			} else if (sccp_strcaseequals("off", DNDState)) {
748 				d->dndFeature.status = SCCP_DNDMODE_OFF;
749 			} else {
750 				astman_send_error(s, m, "DNDState Variable has to be one of (on/off/reject/silent).");
751 			}
752 
753 			if (d->dndFeature.status != prevStatus) {
754 				snprintf(retValStr, sizeof(retValStr), "Device %s DND has been set to %s", d->id, sccp_dndmode2str((sccp_dndmode_t)d->dndFeature.status));
755 				sccp_feat_changed(d, NULL, SCCP_FEATURE_DND);
756 				sccp_dev_check_displayprompt(d);
757 			} else {
758 				snprintf(retValStr, sizeof(retValStr), "Device %s DND state unchanged", d->id);
759 			}
760 		} else {
761 			astman_send_error(s, m, "DND Feature not enabled on this device.");
762 		}
763 	} else {
764 		astman_send_error(s, m, "Device could not be found.");
765 		return 0;
766 	}
767 
768 	astman_send_ack(s, m, retValStr);
769 	return 0;
770 }
771 
772 /*!
773  * \brief Start Call on Device, Line to Number
774  * \param s Management Session
775  * \param m Message
776  * \return Success as int
777  *
778  * \called_from_asterisk
779  */
sccp_manager_startCall(struct mansession * s,const struct message * m)780 static int sccp_manager_startCall(struct mansession *s, const struct message *m)
781 {
782 	const char *deviceName = astman_get_header(m, "Devicename");
783 	const char *lineName = astman_get_header(m, "Linename");
784 	const char *number = astman_get_header(m, "number");
785 
786 	AUTO_RELEASE(sccp_device_t, d , sccp_device_find_byid(deviceName, FALSE));
787 
788 	if (!d) {
789 		astman_send_error(s, m, "Device not found");
790 		return 0;
791 	}
792 
793 	AUTO_RELEASE(sccp_line_t, line, lineName ? sccp_line_find_byname(lineName, FALSE) : NULL);
794 	if(!line) {
795 		if (d && d->defaultLineInstance > 0) {
796 			line = sccp_line_find_byid(d, d->defaultLineInstance) /*ref_replace*/;
797 		} else {
798 			line = sccp_dev_getActiveLine(d) /*ref_replace*/;
799 		}
800 	}
801 
802 	if (!line) {
803 		astman_send_error(s, m, "Line not found");
804 		return 0;
805 	}
806 
807 
808 #if ASTERISK_VERSION_GROUP >= 112
809 	struct ast_assigned_ids ids = {
810 		.uniqueid = astman_get_header(m, "ChannelId"),
811 		//.uniqueid2 = astman_get_header(m, "OtherChannelId")
812 	};
813 	if ((ids.uniqueid && AST_MAX_PUBLIC_UNIQUEID < sccp_strlen(ids.uniqueid))
814 	    //|| (ids.uniqueid2 && AST_MAX_PUBLIC_UNIQUEID < sccp_strlen(ids.uniqueid2))
815 	    ) {
816 		astman_send_error_va(s, m, "Uniqueid length exceeds maximum of %d\n", AST_MAX_PUBLIC_UNIQUEID);
817 		return 0;
818 	}
819 	AUTO_RELEASE(sccp_channel_t, new_channel, sccp_channel_newcall(line, d, sccp_strlen_zero(number) ? NULL : (char *)number, SKINNY_CALLTYPE_OUTBOUND, NULL, (ids.uniqueid) ? &ids : NULL));
820 #	else
821 	AUTO_RELEASE(sccp_channel_t, new_channel, sccp_channel_newcall(line, d, sccp_strlen_zero(number) ? NULL : (char *)number, SKINNY_CALLTYPE_OUTBOUND, NULL, NULL));
822 #	endif
823 	astman_send_ack(s, m, "Call Started");
824 	return 0;
825 }
826 
827 /*!
828  * \brief Answer Call of ChannelId on Device
829  * \param s Management Session
830  * \param m Message
831  * \return Success as int
832  *
833  * \called_from_asterisk
834  */
sccp_manager_answerCall2(struct mansession * s,const struct message * m)835 static int sccp_manager_answerCall2(struct mansession *s, const struct message *m)
836 {
837 	char retValStr[64] = "";
838 
839 	const char *deviceName = astman_get_header(m, "Devicename");
840 	const char *channelId = astman_get_header(m, "channelId");
841 	int channelIntId = sccp_atoi(channelId, strlen(channelId));
842 
843 	if (channelIntId == 0) {
844 		snprintf(retValStr, sizeof(retValStr), "Channel Id has to be a number. You have provided: '%s'\r\n", channelId);
845 		astman_send_error(s, m, retValStr);
846 		return 0;
847 	}
848 
849 	AUTO_RELEASE(sccp_channel_t, c , sccp_channel_find_byid(channelIntId));
850 
851 	if (c) {
852 		AUTO_RELEASE(sccp_device_t, d, sccp_strlen_zero(deviceName) ? sccp_channel_getDevice(c) : sccp_device_find_byid(deviceName, FALSE));
853 		if (d) {
854 			if (c->state == SCCP_CHANNELSTATE_RINGING) {
855 				sccp_channel_answer(d, c);
856 				if (c->owner) {
857 					iPbx.queue_control(c->owner, AST_CONTROL_ANSWER);
858 				}
859 				snprintf(retValStr, sizeof(retValStr), "Answered channel '%s' on device '%s'\r\n", channelId, deviceName);
860 				astman_send_ack(s, m, retValStr);
861 			} else {
862 				astman_send_error(s, m, "Call is not ringing\r\n");
863 			}
864 		} else {
865 			astman_send_error(s, m, "Device not found");
866 		}
867 	} else {
868 		astman_send_error(s, m, "Call not found\r\n");
869 	}
870 	return 0;
871 }
872 
873 /*!
874  * \brief Hangup Call of ChannelId
875  * \param s Management Session
876  * \param m Message
877  * \return Success as int
878  *
879  * \called_from_asterisk
880  */
sccp_manager_hangupCall(struct mansession * s,const struct message * m)881 static int sccp_manager_hangupCall(struct mansession *s, const struct message *m)
882 {
883 	const char *channelId = astman_get_header(m, "channelId");
884 	int channelIntId = sccp_atoi(channelId, strlen(channelId));
885 
886 	if (channelIntId == 0) {
887 		astman_send_error(s, m, "Channel Id has to be a number.");
888 		return 0;
889 	}
890 
891 	AUTO_RELEASE(sccp_channel_t, c , sccp_channel_find_byid(channelIntId));
892 
893 	if (!c) {
894 		astman_send_error(s, m, "Call not found.");
895 		return 0;
896 	}
897 	//astman_append(s, "Hangup call '%s'\r\n", channelId);
898 	sccp_channel_endcall(c);
899 	astman_send_ack(s, m, "Call was hungup");
900 	return 0;
901 }
902 
903 /*!
904  * \brief Put ChannelId Call on Hold (on/off)
905  * \param s Management Session
906  * \param m Message
907  * \return Success as int
908  *
909  * \called_from_asterisk
910  */
sccp_manager_holdCall(struct mansession * s,const struct message * m)911 static int sccp_manager_holdCall(struct mansession *s, const struct message *m)
912 {
913 	const char *channelId = astman_get_header(m, "channelId");
914 	int channelIntId = sccp_atoi(channelId, strlen(channelId));
915 	const char *hold = astman_get_header(m, "hold");
916 	const char *deviceName = astman_get_header(m, "Devicename");
917 	const char *swap = astman_get_header(m, "SwapChannels");
918 	static char *retValStr = "Channel was resumed";
919 	boolean_t errorMessage = TRUE;
920 
921 	if (channelIntId == 0) {
922 		astman_send_error(s, m, "Channel Id has to be a number\r\n");
923 		return 0;
924 	}
925 
926 	AUTO_RELEASE(sccp_channel_t, c , sccp_channel_find_byid(channelIntId));
927 
928 	if (!c) {
929 		astman_send_error(s, m, "Call not found\r\n");
930 		return 0;
931 	}
932 	if (sccp_strcaseequals("on", hold)) {									/* check to see if enable hold */
933 		sccp_channel_hold(c);
934 		retValStr = "Channel was put on hold";
935 		errorMessage = FALSE;
936 
937 	} else if (sccp_strcaseequals("off", hold)) {								/* check to see if disable hold */
938 
939 		/** we need the device for resuming calls */
940 		if (sccp_strlen_zero(deviceName)) {
941 			retValStr = "To resume a channel, you need to specify the device that resumes call using Devicename variable.";
942 			goto SEND_RESPONSE;
943 		}
944 		AUTO_RELEASE(sccp_device_t, d , sccp_device_find_byid(deviceName, FALSE));
945 
946 		if (d) {
947 			if (sccp_strcaseequals("yes", swap)) {
948 				sccp_channel_resume(d, c, TRUE);
949 			} else {
950 				sccp_channel_resume(d, c, FALSE);
951 			}
952 			retValStr = "Channel was resumed";
953 			errorMessage = FALSE;
954 		} else {
955 			retValStr = "Device to hold/resume could not be found.";
956 		}
957 	} else {
958 		retValStr = "Invalid value for hold, use 'on' or 'off' only.";
959 	}
960 
961 SEND_RESPONSE:
962 	if (errorMessage) {
963 		astman_send_error(s, m, retValStr);
964 	} else {
965 		astman_send_ack(s, m, retValStr);
966 	}
967 	return 0;
968 }
969 
970 #if HAVE_PBX_MANAGER_HOOK_H
971 /*!
972  * \brief parse string from management hook to struct message
973  * \note side effect: this function changes/consumes the str pointer
974  */
sccp_asterisk_parseStrToAstMessage(char * str,struct message * m)975 static char * sccp_asterisk_parseStrToAstMessage(char *str, struct message *m)
976 {
977 	int x = 0;
978 	int curlen = 0;
979 
980 	curlen = sccp_strlen(str);
981 	for (x = 0; x < curlen; x++) {
982 		int cr = 0; /* set if we have \r */
983 
984 		if (str[x] == '\r' && x + 1 < curlen && str[x + 1] == '\n') {
985 			cr = 2;											/* Found. Update length to include \r\n */
986 		} else if (str[x] == '\n') {
987 			cr = 1;											/* also accept \n only */
988 		} else {
989 			continue;
990 		}
991 		/* don't keep empty lines */
992 		if (x && m->hdrcount < ARRAY_LEN(m->headers)) {
993 			/* ... but trim \r\n and terminate the header string */
994 			str[x] = '\0';
995 			m->headers[m->hdrcount++] = str;
996 		}
997 		x += cr;
998 		curlen -= x;											/* remaining size */
999 		str += x;											/* update pointer */
1000 		x = -1;												/* reset loop */
1001 	}
1002 	return str;
1003 }
1004 
1005 /*!
1006  * \brief HookHelper to parse AMI events
1007  * Used to check for Monitor Stop/Start events
1008  */
sccp_asterisk_managerHookHelper(int category,const char * event,char * content)1009 static int sccp_asterisk_managerHookHelper(int category, const char *event, char *content)
1010 {
1011 	char * str = NULL;
1012 
1013 	char * dupStr = NULL;
1014 
1015 	if (EVENT_FLAG_CALL == category) {
1016 		if (!strcasecmp("MonitorStart", event) || !strcasecmp("MonitorStop", event)) {
1017 			AUTO_RELEASE(sccp_channel_t, channel , NULL);
1018 			struct message m = { 0 };
1019 
1020 			str = dupStr = pbx_strdupa(content); /** need a dup, because converter to message structure will modify the str */
1021 			sccp_log(DEBUGCAT_CORE)("SCCP: (managerHookHelper) MonitorStart/MonitorStop Received\ncontent:[%s]\n", content);	/* temp */
1022 
1023 			sccp_asterisk_parseStrToAstMessage(str, &m); /** convert to message structure to use the astman_get_header function */
1024 			const char *channelName = astman_get_header(&m, "Channel");
1025 
1026 			PBX_CHANNEL_TYPE *pbxchannel = pbx_channel_get_by_name(channelName);							/* returns reffed */
1027 			if (pbxchannel) {
1028 				PBX_CHANNEL_TYPE *pbxBridge = NULL;
1029 				if ((CS_AST_CHANNEL_PVT_IS_SCCP(pbxchannel))) {
1030 					channel = get_sccp_channel_from_pbx_channel(pbxchannel) /*ref_replace*/;
1031 				} else if ( (pbxBridge = pbx_channel_get_by_name(pbx_builtin_getvar_helper(pbxchannel, "BRIDGEPEER"))) ) {
1032 					if ((CS_AST_CHANNEL_PVT_IS_SCCP(pbxBridge))) {
1033 						channel = get_sccp_channel_from_pbx_channel(pbxBridge) /*ref_replace*/;
1034 					}
1035 #if ASTERISK_VERSION_GROUP == 106
1036 					pbx_channel_unlock(pbxBridge);
1037 #else
1038 					pbxBridge = ast_channel_unref(pbxBridge);
1039 #endif
1040 				}
1041 #if ASTERISK_VERSION_GROUP == 106
1042 				pbx_channel_unlock(pbxchannel);
1043 #else
1044 				pbxchannel = ast_channel_unref(pbxchannel);
1045 #endif
1046 			}
1047 
1048 			if (channel) {
1049 				sccp_log(DEBUGCAT_CORE)("%s: (managerHookHelper) MonitorStart/MonitorStop Received\n", channel->designator);	/* temp */
1050 				AUTO_RELEASE(sccp_device_t, d , sccp_channel_getDevice(channel));
1051 				if (d) {
1052 					sccp_log(DEBUGCAT_CORE)("%s: (managerHookHelper) MonitorStart/MonitorStop on Device: %s\n", channel->designator, d->id);	/* temp */
1053 					if (!strcasecmp("MonitorStart", event)) {
1054 						d->monitorFeature.status |= SCCP_FEATURE_MONITOR_STATE_ACTIVE;
1055 					} else {
1056 						d->monitorFeature.status &= ~SCCP_FEATURE_MONITOR_STATE_ACTIVE;
1057 					}
1058 					sccp_msg_t *msg_out = NULL;
1059 					REQ(msg_out, RecordingStatusMessage);
1060 					msg_out->data.RecordingStatusMessage.lel_callReference = htolel(channel->callid);
1061 					msg_out->data.RecordingStatusMessage.lel_status = (d->monitorFeature.status & SCCP_FEATURE_MONITOR_STATE_ACTIVE) ? htolel(1) : htolel(0);
1062 					sccp_dev_send(d, msg_out);
1063 
1064 					sccp_feat_changed(d, NULL, SCCP_FEATURE_MONITOR);
1065 				}
1066 			}
1067 #ifdef CS_SCCP_PARK
1068 		} else if (sccp_strcaseequals("ParkedCall", event) || sccp_strcaseequals("UnParkedCall", event) || sccp_strcaseequals("ParkedCallGiveUp", event) || sccp_strcaseequals("ParkedCallTimeout", event)) {
1069 			if (iParkingLot.addSlot && iParkingLot.removeSlot) {
1070 				sccp_log_and((DEBUGCAT_PARKINGLOT & DEBUGCAT_HIGH))("SCCP: (managerHookHelper) %s Received\ncontent:[%s]\n", event, content);
1071 
1072 				str = dupStr = pbx_strdupa(content);
1073 				struct message m = { 0 };
1074 				sccp_asterisk_parseStrToAstMessage(str, &m);
1075 
1076 				const char *parkinglot = astman_get_header(&m, "Parkinglot");
1077 				const char *extension = astman_get_header(&m, PARKING_SLOT);
1078 				int exten = sccp_atoi(extension, strlen(extension));
1079 
1080 				/*
1081 								//const char *from = astman_get_header(&m, PARKING_FROM);
1082 								if (sccp_strcaseequals("ParkedCall", event) && !sccp_strlen_zero(from)) {
1083 									AUTO_RELEASE(sccp_line_t, l, sccp_line_find_byname(from, FALSE));
1084 									if (l) {
1085 										sccp_linedevice_t * ld = NULL;
1086 										char extstr[20] = "";
1087 										snprintf(extstr, sizeof(extstr), "%c%c %.16s", 128, SKINNY_LBL_CALL_PARK_AT, extension);
1088 										SCCP_LIST_LOCK(&l->devices);
1089 										SCCP_LIST_TRAVERSE(&l->devices, ld, list) {
1090 											if (ld->line == l) {
1091 												sccp_dev_displayprinotify(ld->device, extstr, SCCP_MESSAGE_PRIORITY_TIMEOUT, 20);
1092 											}
1093 										}
1094 										SCCP_LIST_UNLOCK(&l->devices);
1095 									}
1096 								}*/
1097 				if (parkinglot && exten) {
1098 					if (sccp_strcaseequals("ParkedCall", event)) {
1099 						iParkingLot.addSlot(parkinglot, exten, &m);
1100 					} else {
1101 						iParkingLot.removeSlot(parkinglot, exten);
1102 					}
1103 				}
1104 			}
1105 #endif
1106 		//} else {
1107 		//	sccp_log(DEBUGCAT_CORE)("SCCP: (managerHookHelper) %s Received\ncontent:[%s]\n", event, content);
1108 		}
1109 	}
1110 	return 0;
1111 }
1112 
1113 AST_THREADSTORAGE(hookresult_threadbuf);
1114 #define HOOKRESULT_INITSIZE DEFAULT_PBX_STR_BUFFERSIZE*2
1115 
1116 #if ASTERISK_VERSION_GROUP >= 108
1117 /*
1118  * \brief helper function to concatenate the result from a ami hook send action using a threadlocal buffer
1119  */
__sccp_manager_hookresult(int category,const char * event,char * content)1120 static int __sccp_manager_hookresult(int category, const char *event, char *content) {
1121         struct ast_str *buf = ast_str_thread_get(&hookresult_threadbuf, HOOKRESULT_INITSIZE);;
1122 	if (buf) {
1123 		pbx_str_append(&buf, 0, "%s", content);
1124 	}
1125 	return 0;
1126 }
1127 #endif
1128 /*!
1129  * \brief Call an AMI/Manager Function and Wait for the Result
1130  *
1131  * @param manager_command	const char * containing Something like "Action: ParkedCalls\r\n"
1132  * @param outStr		unallocated char * (will be allocated if successfull, must be freed after call)
1133  * @return int (-1 on failure | return value from called function)
1134  *
1135  * \todo implement using thread_local instead
1136  */
sccp_manager_action2str(const char * manager_command,char ** outStr)1137 boolean_t sccp_manager_action2str(const char *manager_command, char **outStr)
1138 {
1139 #if ASTERISK_VERSION_GROUP >= 108
1140         int failure = 0;
1141 	struct ast_str * buf = NULL;
1142 
1143 	if(!outStr || sccp_strlen_zero(manager_command) || !(buf = ast_str_thread_get(&hookresult_threadbuf, HOOKRESULT_INITSIZE))) {
1144 		pbx_log(LOG_ERROR, "SCCP: No OutStr or Command Provided\n");
1145         	return -2;
1146 	}
1147 
1148 	struct manager_custom_hook hook = {__FILE__, __sccp_manager_hookresult};
1149         failure = ast_hook_send_action(&hook, manager_command);							/* "Action: ParkedCalls\r\n" */
1150         if (!failure) {
1151 		sccp_log(DEBUGCAT_CORE)("SCCP: Sending AMI Result String: %s\n", pbx_str_buffer(buf));
1152         	*outStr = pbx_strdup(pbx_str_buffer(buf));
1153         }
1154        	ast_str_reset(buf);
1155         return !failure ? TRUE : FALSE;
1156 #else
1157 	sccp_log(DEBUGCAT_CORE)("SCCP: ast_hook_send_action is not available in asterisk-1.6\n");
1158 	return FALSE;
1159 #endif
1160 }
1161 
1162 /*
1163 <response type='object' id='(null)'><(null) response='Success' message='Parked calls will follow' /></response>
1164 <response type='object' id='(null)'><(null) event='ParkedCall' parkinglot='default' exten='701' channel='IAX2/iaxuser-2343' from='SCCP/98031-00000001' timeout='41' duration='4' calleridnum='100011' calleridname='Diederik de Groot (10001)' connectedlinenum='' connectedlinename='' /></response>
1165 <response type='object' id='(null)'><(null) event='ParkedCallsComplete' total='1' /></response>
1166 */
1167 
1168 #if defined(CS_EXPERIMENTAL)
sccp_manager_retrieve_parkedcalls_cxml(char ** out)1169 char * sccp_manager_retrieve_parkedcalls_cxml(char ** out)
1170 {
1171 	char *parkedcalls_messageStr = NULL;
1172 	char *manager_command = "Action: ParkedCalls\r\n";
1173 
1174 	if (sccp_manager_action2str(manager_command, &parkedcalls_messageStr) && parkedcalls_messageStr) {
1175 		pbx_str_t *tmpPbxStr = ast_str_create(DEFAULT_PBX_STR_BUFFERSIZE);
1176 		struct message m = {0};
1177 		const char *event = "";
1178 
1179 		sccp_log(DEBUGCAT_CORE)(VERBOSE_PREFIX_2 "SCCP: (sccp_manager_retrieve_parkedcalls_cxml) content=%s\n",  parkedcalls_messageStr);
1180 		pbx_str_append(&tmpPbxStr, 0, "<?xml version=\"1.0\"?>");
1181 		pbx_str_append(&tmpPbxStr, 0, "<CiscoIPPhoneDirectory>");
1182 		pbx_str_append(&tmpPbxStr, 0, "<Title>Parked Calls</Title>");
1183 		pbx_str_append(&tmpPbxStr, 0, "<Prompt>Please Choose on of the parking lots</Prompt>");
1184 		pbx_str_append(&tmpPbxStr, 0, "<DirectoryEntry>");
1185 		char *strptr = parkedcalls_messageStr;
1186 		char *token = NULL;
1187 		char *rest = strptr;
1188 		while (sscanf(strptr, "%[^\r\n]\r\n\r\n%s", token, rest) && token) {
1189 			sccp_log(DEBUGCAT_CORE)(VERBOSE_PREFIX_2 "SCCP: (sccp_manager_retrieve_parkedcalls_cxml) token='%s', rest='%s'\n", token, rest);
1190 			usleep(500);
1191 
1192 			token = sccp_asterisk_parseStrToAstMessage(token, &m);
1193 			event = astman_get_header(&m, "Event");
1194 			if (sccp_strcaseequals(event, "ParkedCallsComplete")) {
1195 				break;
1196 			} else if(sccp_strcaseequals(event, "ParkedCall")) {
1197 				pbx_str_append(&tmpPbxStr, 0, "<Name>%s (%s) by %s</Name><Telephone>%s</Telephone>",
1198 					astman_get_header((const struct message *)&m, "CallerIdName"),
1199 					astman_get_header((const struct message *)&m, "CallerIdNum"),
1200 					astman_get_header((const struct message *)&m, "ConnectedLineName"),
1201 					astman_get_header((const struct message *)&m, "Exten")
1202 				);
1203 				sccp_log(DEBUGCAT_CORE)(VERBOSE_PREFIX_3 "SCCP: Found ParkedCall: %s on %s@%s\n", astman_get_header((const struct message *)&m, "Channel"), astman_get_header((const struct message *)&m, "Exten"), astman_get_header((const struct message *)&m, "ParkingLot"));
1204 			}
1205 			memset(&m, 0, sizeof(m));
1206 			strptr = rest;
1207 		}
1208 		pbx_str_append(&tmpPbxStr, 0, "</DirectoryEntry>");
1209 		pbx_str_append(&tmpPbxStr, 0, "</CiscoIPPhoneDirectory>");
1210 
1211 		*out = pbx_strdup(pbx_str_buffer(tmpPbxStr));
1212 
1213 		sccp_free(tmpPbxStr);
1214 		sccp_free(parkedcalls_messageStr);
1215 	}
1216 	sccp_log(DEBUGCAT_CORE)(VERBOSE_PREFIX_2 "SCCP: (sccp_manager_retrieve_parkedcalls_cxml) cxml=%s\n", *out);
1217 	return *out;
1218 }
1219 #endif
1220 #endif														// HAVE_PBX_MANAGER_HOOK_H
1221 #endif														// CS_SCCP_MANAGER
1222 // 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;
1223