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