1 /*!
2  * \file        sccp_appfunctions.c
3  * \brief       SCCP application / dialplan functions Class
4  * \author      Diederik de Groot (ddegroot [at] sourceforge.net)
5  * \date        18-03-2011
6  * \note        Reworked, but based on chan_sccp code.
7  *              The original chan_sccp driver that was made by Zozo which itself was derived from the chan_skinny driver.
8  *              Modified by Jan Czmok and Julien Goodwin
9  * \note        This program is free software and may be modified and distributed under the terms of the GNU Public License.
10  *              See the LICENSE file at the top of the source tree.
11  *
12  */
13 #include "config.h"
14 #include "common.h"
15 #include "sccp_appfunctions.h"
16 #include "sccp_channel.h"
17 #include "sccp_device.h"
18 #include "sccp_line.h"
19 #include "sccp_linedevice.h"
20 #include "sccp_mwi.h"
21 #include "sccp_conference.h"
22 #include "sccp_session.h"
23 #include "sccp_utils.h"
24 
25 SCCP_FILE_VERSION(__FILE__, "");
26 
27 #include <asterisk/callerid.h>
28 #include <asterisk/module.h>                                        // ast_register_application2
29 #ifdef HAVE_PBX_APP_H
30 #	include <asterisk/app.h>
31 #endif
32 
33 /*** DOCUMENTATION
34         <function name="SCCPDevice" language="en_US">
35                 <synopsis>
36                         Retrieves information about an SCCP Device.
37                 </synopsis>
38                 <syntax argsep=",">
39                         <parameter name="deviceId" required="true">
40                                 <para>Device to be queried.</para>
41                                 <optionlist>
42                                         <option name="current"><para>The current device.</para></option>
43                                         <option name="SEPxxxxxxxxxx"><para>string containing the SEP+Mac-address of the device to be queried.</para></option>
44                                 </optionlist>
45                         </parameter>
46                         <parameter name="option" required="true" multiple="true">
47                                 <para>One of the following options:</para>
48                                 <optionlist>
49                                         <option name="ip"><para>Ip-Address of the device (string).</para></option>
50                                         <option name="id"><para>Device id (string).</para></option>
51                                         <option name="status"><para>Current Device State (string).</para></option>
52                                         <option name="description"><para>Device Description (string).</para></option>
53                                         <option name="config_type"><para>Config Type (string).</para></option>
54                                         <option name="skinny_type"><para>Skinny Type (string).</para></option>
55                                         <option name="tz_offset"><para>Timezone Offset (integer).</para></option>
56                                         <option name="image_version"><para>Loaded image version (string).</para></option>
57                                         <option name="accessory_status"><para>Accessory Status (string).</para></option>
58                                         <option name="registration_state"><para>Registration State (string).</para></option>
59                                         <option name="codecs"><para>Codec Preferences (string).</para></option>
60                                         <option name="capability"><para>Codec Capabilities (string).</para></option>
61                                         <option name="lines_registered"><para>Has Registered Lines (boolean).</para></option>
62                                         <option name="lines_count"><para>Number of lines (integer).</para></option>
63                                         <option name="last_number"><para>Last number dialed (extension).</para></option>
64                                         <option name="early_rtp"><para>Early RTP Setting (string).</para></option>
65                                         <option name="supported_protocol_version"><para>Supported Protocol by Device (integer).</para></option>
66                                         <option name="used_protocol_version"><para>Currently Used Protocol (integer).</para></option>
67                                         <option name="dnd_feature"><para>DND Feature (boolean).</para></option>
68                                         <option name="dnd_state"><para>DND State (string).</para></option>
69                                         <option name="dnd_action"><para>DND Action (string).</para></option>
70                                         <option name="dynamic"><para>Is Realtime Device (boolean).</para></option>
71                                         <option name="realtime"><para>Is Realtime Device (boolean).</para></option>
72                                         <option name="active_channel"><para>CallID of active Channel (integer).</para></option>
73                                         <option name="transfer_channel"><para>CallID of channel being transfered (integer).</para></option>
74                                         <option name="allow_conference"><para>Allow Conference (boolean).</para></option>
75                                         <option name="conf_play_general_announce"><para>Play General Announcements (boolean).</para></option>
76                                         <option name="conf_play_part_announce"><para>Play Announcement to participants (boolean).</para></option>
77                                         <option name="conf_mute_on_entry"><para>Are all participant muted when entering the conference (boolean).</para></option>
78                                         <option name="conf_music_on_hold_class"><para>Name of the Music on Hold Class used for conferences (string).</para></option>
79                                         <option name="conf_show_conflist"><para>Should the conference list be displayed when conference is used (boolean).</para></option>
80                                         <option name="conflist_active"><para>Is the conference list currently actively shown (boolean).</para></option>
81                                         <option name="current_line"><para>Currently Active Line ID (integer).</para></option>
82                                         <option name="button_config"><para>Array of buttons associated with this device (comma seperated string).</para></option>
83                                         <option name="pending_delete"><para>Reload is active and device is going to be removed (boolean).</para></option>
84                                         <option name="pending_update"><para>Reload is active and device is going to be updated (boolean).</para></option>
85                                         <option name="rtpqos"><para>Aggregated Call Statistics for this device (string).</para></option>
86                                         <option name="peerip"><para>IP-Address/port Associated with the device session (NO-NAT).</para></option>
87                                         <option name="recvip"><para>IP-Address/Port Actual Source IP-Address Reported by the phone upon registration (NAT).</para></option>
88                                         <option name="chanvar[setvar]" hasparams="true">
89                                                 <argument name="setvar" required="true">
90                                                         <para>Name of the <replaceable>setvar</replaceable>, associated with this device, to be queried (string).</para>
91                                                 </argument>
92                                         </option>
93                                         <option name="codec[codec]" hasparams="true">
94                                                 <argument name="codec" required="true">
95                                                         <para>Number of Skinny <replaceable>codec</replaceable> to be queried (integer).</para>
96                                                 </argument>
97                                         </option>
98                                 </optionlist>
99                         </parameter>
100                 </syntax>
101                 <description>
102                         <para>This function is used to query sccp device information from insde the dialplan.</para>
103                         <example>
104                                 Set(ipaddress=${SCCPDevice(SEPxxxxxxxxxx,codecs)});
105                         </example>
106                         <para>This function can also be used together with Hash() and Array();</para>
107                         <para>In this case, the device is only queried once and the results are stored in a Hash Table/Array.</para>
108                         <example>
109                                 Set(HASH(_SCCP_DEVICE)=${SCCPDevice(current,ip,description,codec[2])});
110                                 Noop(Desc: ${HASH(SCCP_DEVICE,description)});
111                                 Noop(Ip: ${HASH(SCCP_DEVICE,ip)});
112                                 Noop(Codec2: ${HASH(SCCP_DEVICE,codec[2])});
113                         </example>
114                 </description>
115                 <see-also>
116                         <ref type="application">Array</ref>
117                         <ref type="application">Hash</ref>
118                 </see-also>
119         </function>
120         <function name="SCCPLine" language="en_US">
121                 <synopsis>
122                         Retrieves information about an SCCP Line.
123                 </synopsis>
124                 <syntax argsep=",">
125                         <parameter name="lineName" required="true">
126                                 <para>Line to be queried.</para>
127                                 <optionlist>
128                                         <option name="current"><para>The current line.</para></option>
129                                         <option name="parent"><para>The forwarding line (In case this line was created to forward a call).</para></option>
130                                         <option name="LineName"><para>string specifying the name of the line to be queried.</para></option>
131                                 </optionlist>
132                         </parameter>
133                         <parameter name="option" required="true" multiple="true">
134                                 <para>One of the following options:</para>
135                                 <optionlist>
136                                         <option name="id"><para>Line Identifier (integer).</para></option>
137                                         <option name="name"><para>Line Name (string).</para></option>
138                                         <option name="description"><para>Line Description (string).</para></option>
139                                         <option name="label"><para>Line Label (string).</para></option>
140                                         <option name="vmnum"><para>Voicemail extension to be dialed when the voicemail soft/hard key is pressed (extension).</para></option>
141                                         <option name="trnsfvm"><para>Extension to be dialed when redirecting a call to voicemail (extension).</para></option>
142                                         <option name="meetme"><para>is meetme enabled (boolean).</para></option>
143                                         <option name="meetmenum"><para>Extension dialed when pressing the meetme softkey (extension).</para></option>
144                                         <option name="meetmeopts"><para>Options set when meetme applicaton is started (string).</para></option>
145                                         <option name="context"><para>PBX Context used when this line starts an outbound call (context).</para></option>
146                                         <option name="language"><para>language (string).</para></option>
147                                         <option name="accountcode"><para>accountcode (string).</para></option>
148                                         <option name="musicclass"><para>musicclass (string).</para></option>
149                                         <option name="amaflags"><para>amaflags (string).</para></option>
150                                         <option name="dnd_action"><para>dnd_action (string).</para></option>
151                                         <option name="callgroup"><para>call group this line is part of (integer-array).</para></option>
152                                         <option name="pickupgroup"><para>pickup group this line is part of (integer-array).</para></option>
153                                         <option name="named_callgroup"><para>named call group this line is part of (integer-array).</para></option>
154                                         <option name="named_pickupgroup"><para>named pickup group this line is part of (integer-array).</para></option>
155                                         <option name="cid_name"><para>CallerID Name (string).</para></option>
156                                         <option name="cid_num"><para>CallerID Number (string).</para></option>
157                                         <option name="incoming_limit"><para>Maximum number of inbound calls that will be accepted to this line (integer).</para></option>
158                                         <option name="channel_count"><para>Current number of active channels allocated on this line (integer).</para></option>
159                                         <option name="dynamic"><para>Is Realtime Line (boolean).</para></option>
160                                         <option name="realtime"><para>Is Realtime Line (boolean).</para></option>
161                                         <option name="pending_delete"><para>Reload is active and line is going to be removed (boolean).</para></option>
162                                         <option name="pending_update"><para>Reload is active and line is going to be updated (boolean).</para></option>
163                                         <option name="regexten"><para>Registration Extension (used by Dundi). Allocated in the dialplan when the line is registered (string).</para></option>
164                                         <option name="regcontext"><para>Registration Context (used by Dundi). Allocated in the dialplan when the line is registered (string).</para></option>
165                                         <option name="adhoc_number"><para>If this is a hotline/plar line, this returns the extension to be dialed when the phone goes offhook (extension).</para></option>
166                                         <option name="newmsgs"><para>Number of new voicemail messages (integer).</para></option>
167                                         <option name="oldmsgs"><para>Number of old voicemail messages (integer).</para></option>
168                                         <option name="videomode"><para>Video mode (string).</para></option>
169                                         <option name="num_devices"><para>Number of devices this line has been registed on (integer).</para></option>
170                                         <option name="mailboxes"><para>Returns a comma seperated list of voicemail mailboxes connected to this line (csv).</para></option>
171                                         <option name="cfwd"><para>Returns a comma seperated list of callforward set on this line (csv).</para></option>
172                                         <option name="devices"><para>Returns a comma seperated list of devicesId's that are connected to this line(csv).</para></option>
173                                         <option name="chanvar[setvar]" hasparams="true">
174                                                 <argument name="setvar" required="true">
175                                                         <para>Name of the <replaceable>setvar</replaceable>, associated with this device, to be queried (string).</para>
176                                                 </argument>
177                                         </option>
178                                 </optionlist>
179                         </parameter>
180                 </syntax>
181                 <description>
182                         <para>This function is used to query sccp device information from insde the dialplan.</para>
183                         <example>
184                                 Set(ipaddress=${SCCPLine(12345,codecs)});
185                                 Set(devices=${SCCPLine(current,devices)});
186                                 Set(cfwd=${SCCPLine(parent,cfwd)});
187                         </example>
188                         <para>This function can also be used together with Hash() and Array();</para>
189                         <para>In this case, the line is only queried once and the results are stored in a Hash Table/Array.</para>
190                         <example>
191                                 Set(HASH(_SCCP_LINE)=${SCCPLine(current,id,description,devices)});
192                                 Noop(Desc: ${HASH(SCCP_LINE,description)});
193                                 Noop(Id: ${HASH(SCCP_LINE,id)});
194                                 Noop(Devs: ${HASH(SCCP_LINE,devices)});
195                         </example>
196                 </description>
197                 <see-also>
198                         <ref type="application">Array</ref>
199                         <ref type="application">Hash</ref>
200                         <ref type="application">SCCPDevice</ref>
201                 </see-also>
202         </function>
203         <function name="SCCPChannel" language="en_US">
204                 <synopsis>
205                         Retrieves information about an SCCP Channel.
206                 </synopsis>
207                 <syntax argsep=",">
208                         <parameter name="channel" required="true">
209                                 <para>Line to be queried.</para>
210                                 <optionlist>
211                                         <option name="current"><para>The current channel.</para></option>
212                                         <option name="ChannelName"><para>Retrieve the channel by PBX Channel Name.</para></option>
213                                         <option name="CallId"><para>Retrieve the channel by Call ID.</para></option>
214                                 </optionlist>
215                         </parameter>
216                         <parameter name="option" required="true" multiple="true">
217                                 <para>One of the following options:</para>
218                                 <optionlist>
219                                         <option name="callid"><para>Channel CallID (integer).</para></option>
220                                         <option name="id"><para>Channel ID (integer).</para></option>
221                                         <option name="format"><para>Channel RTP Read Format (integer).</para></option>
222                                         <option name="codecs"><para>Channel RTP Read Codec (string).</para></option>
223                                         <option name="capability"><para>Channel Codec Capabilities (string).</para></option>
224                                         <option name="calledPartyName"><para>Called Party Name (string).</para></option>
225                                         <option name="calledPartyNumber"><para>Called Party Number (string).</para></option>
226                                         <option name="callingPartyName"><para>Calling Party Name (string).</para></option>
227                                         <option name="callingPartyNumber"><para>calling Party Number (string).</para></option>
228                                         <option name="originalCallingPartyName"><para>Original Calling Party Name (string).</para></option>
229                                         <option name="originalCallingPartyNumber"><para>Original Calling Party Number (string).</para></option>
230                                         <option name="originalCalledPartyName"><para>Original Called Party Name (string).</para></option>
231                                         <option name="originalCalledPartyNumber"><para>Original Called Party Number (string).</para></option>
232                                         <option name="lastRedirectingPartyName"><para>Last Redirecting Party Name (string).</para></option>
233                                         <option name="lastRedirectingPartyNumber"><para>Last Redirecting Party Number (string).</para></option>
234                                         <option name="cgpnVoiceMailbox"><para>Calling Party Voice Mailbox (string).</para></option>
235                                         <option name="cdpnVoiceMailbox"><para>Called Party Voice Mailbox (string).</para></option>
236                                         <option name="originalCdpnVoiceMailbox"><para>Original Called Party Voice Mailbox (string).</para></option>
237                                         <option name="lastRedirectingVoiceMailbox"><para>Last Redirecting Voice Mailbox (string).</para></option>
238                                         <option name="passthrupartyid"><para>RTP / Media Passthrough Party Id (integer).</para></option>
239                                         <option name="state"><para>ChannelState (string).</para></option>
240                                         <option name="previous_state"><para>Previous ChannelState (string).</para></option>
241                                         <option name="calltype"><para>Call Type (inbound/outbound) (string).</para></option>
242                                         <option name="dialed_number"><para>Dialed Number (only on outbound call) (extension).</para></option>
243                                         <option name="device"><para>Current DeviceId this channel is attached to (only active calls) (string).</para></option>
244                                         <option name="line"><para>Current LineName this channel is attached to (only active calls) (string).</para></option>
245                                         <option name="answered_elsewhere"><para>This call has been answered somewhere else (boolean).</para></option>
246                                         <option name="privacy"><para>Privacy (boolean).</para></option>
247                                         <option name="ss_action"><para>SoftSwitch Action responsible for this channel (string).</para></option>
248                                         <option name="videomode"><para>Video mode (string).</para></option>
249                                         <option name="conference_id"><para>Conference Id associated to this channel (string).</para></option>
250                                         <option name="conference_participant_id"><para>Conference Participant Id (string).</para></option>
251                                         <option name="parent"><para>Channel Forwarding Parent CallID. When this is a channel originating from a forwarded call, this will link back to the callid of the original call.
252  (integer).</para></option> <option name="bridgepeer"><para>Remote Bridge Peer connected to this channel (string).</para></option> <option name="peerip"><para>IP-Address/port Associated with the device session
253  (NO-NAT).</para></option> <option name="recvip"><para>IP-Address/Port Actual Source IP-Address Reported by the phone upon registration (NAT).</para></option> <option name="rtpqos"><para>Aggregated Call Statistics for this
254  device (string).</para></option> <option name="codec[codec]" hasparams="true"> <argument name="codec" required="true"> <para>Number of Skinny <replaceable>codec</replaceable> to be queried (integer).</para>
255                                                 </argument>
256                                         </option>
257                                 </optionlist>
258                         </parameter>
259                 </syntax>
260                 <description>
261                         <para>This function is used to query sccp device information from insde the dialplan.</para>
262                         <example>
263                                 Set(state=${SCCPChannel(12345,state)});
264                                 Set(device=${SCCPChannel(current,device)});
265                                 Set(calltype=${SCCPChannel(SCCP/12345-00000015,calltype)});
266                         </example>
267                         <para>This function can also be used together with Hash() and Array();</para>
268                         <para>In this case, the channel is only queried once and the results are stored in a Hash Table/Array.</para>
269                         <example>
270                                 Set(HASH(_SCCP_CHANNEL)=${SCCPChannel(current,bridgepeer,peerip,recvip)});
271                                 Noop(PIp: ${HASH(SCCP_CHANNEL,peerip)});
272                                 Noop(RIp: ${HASH(SCCP_CHANNEL,recvip)});
273                                 Noop(Bridge: ${HASH(SCCP_CHANNEL,bridgepeer)});
274                         </example>
275                 </description>
276                 <see-also>
277                         <ref type="application">Array</ref>
278                         <ref type="application">Hash</ref>
279                         <ref type="application">SCCPDevice</ref>
280                         <ref type="application">SCCPLine</ref>
281                 </see-also>
282         </function>
283         <application name="SCCPSetCalledParty" language="en_US">
284                 <synopsis>
285                         Set the callerid of the called party.
286                 </synopsis>
287                 <syntax>
288                         <parameter name="callerid" required="true">
289                                 <para>CallerID String, following the format "'<replaceable>Name</replaceable>' &lt;<replaceable>extension</replaceable>&gt;"</para>
290                         </parameter>
291                 </syntax>
292                 <description>
293                         <para>Usage: SCCPSetCalledParty(\"<replaceable>Name</replaceable>\" &lt;<replaceable>ext</replaceable>&gt;);</para>
294                         <para>Sets the name and number of the called party for use with chan_sccp.</para>
295                         <note>
296                                 <para>DEPRECATED:please use generic 'Set(CHANNEL(calledparty)=\"<replaceable>name</replaceable> &lt;<replaceable>exten</replaceable>&gt;\");' instead</para>
297                         </note>
298                 </description>
299                 <see-also>
300                         <ref type="application">Channel</ref>
301                 </see-also>
302         </application>
303         <application name="SCCPSetCodec" language="en_US">
304                 <synopsis>
305                         Set the prefered codec for the current sccp channel to be used before dialing the destination channel.
306                 </synopsis>
307                 <syntax>
308                         <parameter name="codec" required="true">
309                                 <para>String specifying the codec to be used.</para>
310                         </parameter>
311                 </syntax>
312                 <description>
313                         <para>Usage: SCCPSetCodec(<replaceable>Codec Name</replaceable>);</para>
314                         <para>Example: SCCPSetCodec(alaw);</para>
315                         <note>
316                                 <para>This has to be done before dialing the destination</para>
317                                 <para>DEPRECATED:please use generic 'Set(CHANNEL(codec)=<replaceable>Codec Name</replaceable>);' instead</para>
318                         </note>
319                 </description>
320                 <see-also>
321                         <ref type="application">Channel</ref>
322                 </see-also>
323         </application>
324         <application name="SCCPSetMessage" language="en_US">
325                 <synopsis>
326                         Send a message to the statusline of the phone connected to this channel.
327                 </synopsis>
328                 <syntax>
329                         <parameter name="message" required="true">
330                                 <para>String specifying the message that should be send.</para>
331                         </parameter>
332                         <parameter name="timeout" required="false">
333                                 <para>Number of seconds the message should be displayed.</para>
334                                 <para>If timeout is ommitted, the message will remain until the next/empty message.</para>
335                         </parameter>
336                         <parameter name="priority" required="false">
337                                 <para>Use priority to set/clear priority notifications.</para>
338                                 <para>Higher priority levels overrule lower ones.</para>
339                         </parameter>
340                 </syntax>
341                 <description>
342                         <para>Usage: SCCPSetMessage(<replaceable>Message Text</replaceable>, <replaceable>timeout</replaceable>, <replaceable>priority</replaceable>);</para>
343                         <para>Example: SCCPSetMessage("Test Test", 10);</para>
344                 </description>
345         </application>
346  ***/
347 
348 PBX_THREADSTORAGE(coldata_buf);
349 PBX_THREADSTORAGE(colnames_buf);
350 
351 /*!
352  * \brief ${SCCPDevice()} Dialplan function - reads device data
353  * \param chan Asterisk Channel
354  * \param cmd Command as char
355  * \param data Extra data as char
356  * \param output Buffer as chan*
357  * \param len Lenght as size_t
358  * \return Status as int
359  *
360  * \author Diederik de Groot <ddegroot@users.sourceforce.net>
361  *
362  * \called_from_asterisk
363  */
sccp_func_sccpdevice(PBX_CHANNEL_TYPE * chan,NEWCONST char * cmd,char * data,char * output,size_t len)364 static int sccp_func_sccpdevice(PBX_CHANNEL_TYPE * chan, NEWCONST char * cmd, char * data, char * output, size_t len)
365 {
366 	pbx_str_t * coldata   = pbx_str_thread_get(&coldata_buf, 16);
367 	pbx_str_t * colnames  = pbx_str_thread_get(&colnames_buf, 16);
368 	char *      colname   = NULL;                                        // we should make this a finite length
369 	uint16_t    buf_len   = 1024;
370 	char        buf[1024] = "";
371 	char *      token     = NULL;
372 	int         addcomma  = 0;
373 
374 	if ((colname = strchr(data, ':'))) { /*! \todo Will be deprecated after 1.4 */
375 		static int deprecation_warning = 0;
376 		*colname++                     = '\0';
377 		if (deprecation_warning++ % 10 == 0) {
378 			pbx_log(LOG_WARNING, "SCCPDevice(): usage of ':' to separate arguments is deprecated. Please use ',' instead.\n");
379 		}
380 	} else if ((colname = strchr(data, ','))) {
381 		*colname++ = '\0';
382 	} else {
383 		colname = (char *)sccp_alloca(16);
384 		if (!colname) {
385 			return -1;
386 		}
387 		snprintf(colname, 16, "ip");
388 	}
389 
390 	AUTO_RELEASE(sccp_device_t, d, NULL);
391 
392 	if (!strncasecmp(data, "current", 7)) {
393 		AUTO_RELEASE(sccp_channel_t, c, get_sccp_channel_from_pbx_channel(chan));
394 
395 		if (!c) {
396 			return -1;
397 		}
398 		d = sccp_channel_getDevice(c) /*ref_replace*/;
399 		if (!d) {
400 			pbx_log(LOG_WARNING, "SCCPDevice(): SCCP Device not available\n");
401 			return -1;
402 		}
403 	} else {
404 		d = sccp_device_find_byid(data, FALSE) /*ref_replace*/;
405 		if (!d) {
406 			pbx_log(LOG_WARNING, "SCCPDevice(): SCCP Device not available\n");
407 			return -1;
408 		}
409 	}
410 	pbx_str_reset(colnames);
411 	pbx_str_reset(coldata);
412 	char delims[] = " ,";
413 	if (d) {
414 		char * tokenrest = NULL;
415 		token            = strtok_r(colname, delims, &tokenrest);
416 		while (token != NULL) {
417 			addcomma = 0;
418 			token    = pbx_skip_blanks(token);
419 			if (!strlen(token)) {
420 				continue;
421 			}
422 
423 			/** copy request tokens for HASH() */
424 			if (pbx_str_strlen(colnames)) {
425 				pbx_str_append(&colnames, 0, ",");
426 			}
427 			pbx_str_append_escapecommas(&colnames, 0, token, sccp_strlen(token));
428 			/** */
429 
430 			if (!strcasecmp(token, "ip")) {
431 				sccp_session_t * s = d->session;
432 
433 				if (s) {
434 					struct sockaddr_storage sas = { 0 };
435 					sccp_session_getOurIP(s, &sas, 0);
436 					sccp_copy_string(buf, sccp_netsock_stringify(&sas), buf_len);
437 				}
438 			} else if (!strcasecmp(token, "id")) {
439 				sccp_copy_string(buf, d->id, buf_len);
440 			} else if (!strcasecmp(token, "status")) {
441 				sccp_copy_string(buf, sccp_devicestate2str(sccp_device_getDeviceState(d)), buf_len);
442 			} else if (!strcasecmp(token, "description")) {
443 				sccp_copy_string(buf, d->description, buf_len);
444 			} else if (!strcasecmp(token, "config_type")) {
445 				sccp_copy_string(buf, d->config_type, buf_len);
446 			} else if (!strcasecmp(token, "skinny_type")) {
447 				sccp_copy_string(buf, skinny_devicetype2str(d->skinny_type), buf_len);
448 			} else if (!strcasecmp(token, "tz_offset")) {
449 				snprintf(buf, buf_len, "%d", d->tz_offset);
450 			} else if (!strcasecmp(token, "image_version")) {
451 				sccp_copy_string(buf, d->loadedimageversion, buf_len);
452 			} else if (!strcasecmp(token, "accessory_status")) {
453 				sccp_accessory_t activeAccessory = sccp_device_getActiveAccessory(d);
454 				snprintf(buf, buf_len, "%s:%s", sccp_accessory2str(activeAccessory), sccp_accessorystate2str(sccp_device_getAccessoryStatus(d, activeAccessory)));
455 			} else if (!strcasecmp(token, "registration_state")) {
456 				sccp_copy_string(buf, skinny_registrationstate2str(sccp_device_getRegistrationState(d)), buf_len);
457 			} else if (!strcasecmp(token, "codecs")) {
458 				sccp_codec_multiple2str(buf, buf_len - 1, d->preferences.audio, ARRAY_LEN(d->preferences.audio));
459 			} else if (!strcasecmp(token, "capability")) {
460 				sccp_codec_multiple2str(buf, buf_len - 1, d->capabilities.audio, ARRAY_LEN(d->capabilities.audio));
461 			} else if (!strcasecmp(token, "lines_registered")) {
462 				sccp_copy_string(buf, d->linesRegistered ? "yes" : "no", buf_len);
463 			} else if (!strcasecmp(token, "lines_count")) {
464 				snprintf(buf, buf_len, "%d", d->linesCount);
465 			} else if (!strcasecmp(token, "last_number")) {
466 				sccp_copy_string(buf, d->redialInformation.number, buf_len);
467 			} else if (!strcasecmp(token, "early_rtp")) {
468 				sccp_copy_string(buf, d->earlyrtp ? "yes" : "no", buf_len);
469 			} else if (!strcasecmp(token, "supported_protocol_version")) {
470 				snprintf(buf, buf_len, "%d", d->protocolversion);
471 			} else if (!strcasecmp(token, "used_protocol_version")) {
472 				snprintf(buf, buf_len, "%d", d->inuseprotocolversion);
473 			} else if (!strcasecmp(token, "dnd_feature")) {
474 				sccp_copy_string(buf, (d->dndFeature.enabled) ? "ON" : "OFF", buf_len);
475 			} else if (!strcasecmp(token, "dnd_state")) {
476 				sccp_copy_string(buf, sccp_dndmode2str((sccp_dndmode_t)d->dndFeature.status), buf_len);
477 			} else if (!strcasecmp(token, "dnd_action")) {
478 				sccp_copy_string(buf, sccp_dndmode2str(d->dndmode), buf_len);
479 			} else if (!strcasecmp(token, "dynamic") || !strcasecmp(token, "realtime")) {
480 #ifdef CS_SCCP_REALTIME
481 				sccp_copy_string(buf, d->realtime ? "yes" : "no", buf_len);
482 #else
483 				sccp_copy_string(buf, "not supported", buf_len);
484 #endif
485 			} else if (!strcasecmp(token, "active_channel")) {
486 				snprintf(buf, buf_len, "%d", d->active_channel->callid);
487 			} else if (!strcasecmp(token, "transfer_channel")) {
488 				snprintf(buf, buf_len, "%d", d->transferChannels.transferee->callid);
489 #ifdef CS_SCCP_CONFERENCE
490 				//			} else if (!strcasecmp(token, "conference_id")) {
491 				//				snprintf(buf, buf_len, "%d", d->conference->id);
492 			} else if (!strcasecmp(token, "allow_conference")) {
493 				snprintf(buf, buf_len, "%s", d->allow_conference ? "ON" : "OFF");
494 			} else if (!strcasecmp(token, "conf_play_general_announce")) {
495 				snprintf(buf, buf_len, "%s", d->conf_play_general_announce ? "ON" : "OFF");
496 			} else if (!strcasecmp(token, "conf_play_part_announce")) {
497 				snprintf(buf, buf_len, "%s", d->conf_play_part_announce ? "ON" : "OFF");
498 			} else if (!strcasecmp(token, "conf_mute_on_entry")) {
499 				snprintf(buf, buf_len, "%s", d->conf_mute_on_entry ? "ON" : "OFF");
500 			} else if (!strcasecmp(token, "conf_music_on_hold_class")) {
501 				snprintf(buf, buf_len, "%s", d->conf_music_on_hold_class);
502 			} else if (!strcasecmp(token, "conf_show_conflist")) {
503 				snprintf(buf, buf_len, "%s", d->conf_show_conflist ? "ON" : "OFF");
504 			} else if (!strcasecmp(token, "conflist_active")) {
505 				snprintf(buf, buf_len, "%s", d->conferencelist_active ? "ON" : "OFF");
506 #endif
507 			} else if (!strcasecmp(token, "current_line")) {
508 				sccp_copy_string(buf, d->currentLine->id, buf_len);
509 			} else if (!strcasecmp(token, "button_config")) {
510 				pbx_str_t *           lbuf   = pbx_str_create(DEFAULT_PBX_STR_BUFFERSIZE);
511 				sccp_buttonconfig_t * config = NULL;
512 
513 				SCCP_LIST_LOCK(&d->buttonconfig);
514 				SCCP_LIST_TRAVERSE(&d->buttonconfig, config, list) {
515 					switch (config->type) {
516 						case LINE:
517 							pbx_str_append(&lbuf, 0, "%s[%d,%s,%s]", addcomma++ ? "," : "", config->instance, sccp_config_buttontype2str(config->type),
518 							               config->button.line.name ? config->button.line.name : "");
519 							break;
520 						case SPEEDDIAL:
521 							pbx_str_append(&lbuf, 0, "%s[%d,%s,%s,%s]", addcomma++ ? "," : "", config->instance, sccp_config_buttontype2str(config->type), config->label,
522 							               config->button.speeddial.ext ? config->button.speeddial.ext : "");
523 							break;
524 						case SERVICE:
525 							pbx_str_append(&lbuf, 0, "%s[%d,%s,%s,%s]", addcomma++ ? "," : "", config->instance, sccp_config_buttontype2str(config->type), config->label,
526 							               config->button.service.url ? config->button.service.url : "");
527 							break;
528 						case FEATURE:
529 							pbx_str_append(&lbuf, 0, "%s[%d,%s,%s,%s]", addcomma++ ? "," : "", config->instance, sccp_config_buttontype2str(config->type), config->label,
530 							               config->button.feature.options ? config->button.feature.options : "");
531 							break;
532 						case EMPTY:
533 							pbx_str_append(&lbuf, 0, "%s[%d,%s]", addcomma++ ? "," : "", config->instance, sccp_config_buttontype2str(config->type));
534 							break;
535 						case SCCP_CONFIG_BUTTONTYPE_SENTINEL:
536 							break;
537 					}
538 				}
539 				SCCP_LIST_UNLOCK(&d->buttonconfig);
540 				snprintf(buf, buf_len, "[ %s ]", pbx_str_buffer(lbuf));
541 				sccp_free(lbuf);
542 			} else if (!strcasecmp(token, "pending_delete")) {
543 				sccp_copy_string(buf, d->pendingDelete ? "yes" : "no", buf_len);
544 			} else if (!strcasecmp(token, "pending_update")) {
545 				sccp_copy_string(buf, d->pendingUpdate ? "yes" : "no", buf_len);
546 			} else if (!strcasecmp(token, "peerip")) {                                        // NO-NAT (Ip-Address Associated with the Session->sin)
547 				if (d->session) {
548 					struct sockaddr_storage sas = { 0 };
549 					sccp_session_getOurIP(d->session, &sas, 0);
550 					sccp_copy_string(buf, sccp_netsock_stringify(&sas), len);
551 				}
552 			} else if (!strcasecmp(token, "recvip")) {                                        // NAT (Actual Source IP-Address Reported by the phone upon registration)
553 				if (d->session) {
554 					struct sockaddr_storage sas = { 0 };
555 					sccp_session_getSas(d->session, &sas);
556 					sccp_copy_string(buf, sccp_netsock_stringify(&sas), len);
557 				}
558 			} else if (!strcasecmp(colname, "rtpqos")) {
559 				sccp_call_statistics_t * call_stats = d->call_statistics;
560 				snprintf(buf, buf_len, "Packets sent: %d;rcvd: %d;lost: %d;jitter: %d;latency: %d;MLQK=%.4f;MLQKav=%.4f;MLQKmn=%.4f;MLQKmx=%.4f;MLQKvr=%.2f|ICR=%.4f;CCR=%.4f;ICRmx=%.4f|CS=%d;SCS=%d",
561 				         call_stats[SCCP_CALLSTATISTIC_LAST].packets_sent, call_stats[SCCP_CALLSTATISTIC_LAST].packets_received, call_stats[SCCP_CALLSTATISTIC_LAST].packets_lost,
562 				         call_stats[SCCP_CALLSTATISTIC_LAST].jitter, call_stats[SCCP_CALLSTATISTIC_LAST].latency, call_stats[SCCP_CALLSTATISTIC_LAST].opinion_score_listening_quality,
563 				         call_stats[SCCP_CALLSTATISTIC_LAST].avg_opinion_score_listening_quality, call_stats[SCCP_CALLSTATISTIC_LAST].mean_opinion_score_listening_quality,
564 				         call_stats[SCCP_CALLSTATISTIC_LAST].max_opinion_score_listening_quality, call_stats[SCCP_CALLSTATISTIC_LAST].variance_opinion_score_listening_quality,
565 				         call_stats[SCCP_CALLSTATISTIC_LAST].interval_concealement_ratio, call_stats[SCCP_CALLSTATISTIC_LAST].cumulative_concealement_ratio,
566 				         call_stats[SCCP_CALLSTATISTIC_LAST].max_concealement_ratio, (int)call_stats[SCCP_CALLSTATISTIC_LAST].concealed_seconds,
567 				         (int)call_stats[SCCP_CALLSTATISTIC_LAST].severely_concealed_seconds);
568 			} else if (!strncasecmp(token, "chanvar[", 8)) {
569 				char *              chanvar = token + 8;
570 				PBX_VARIABLE_TYPE * v       = NULL;
571 
572 				chanvar = strsep(&chanvar, "]");
573 				for (v = d->variables; v; v = v->next) {
574 					if (!strcasecmp(v->name, chanvar)) {
575 						sccp_copy_string(buf, v->value, buf_len);
576 					}
577 				}
578 			} else if (!strncasecmp(token, "codec[", 6)) {
579 				char * codecnum = NULL;
580 
581 				codecnum      = token + 6;                                                     // move past the '['
582 				codecnum      = strsep(&codecnum, "]");                                        // trim trailing ']' if any
583 				int codec_int = sccp_atoi(codecnum, strlen(codecnum));
584 				if (skinny_codecs[codec_int].key) {
585 					sccp_copy_string(buf, codec2name((skinny_codec_t)codec_int), buf_len);
586 				} else {
587 					buf[0] = '\0';
588 				}
589 			} else {
590 				pbx_log(LOG_WARNING, "SCCPDevice(%s): unknown colname: %s\n", data, token);
591 				buf[0] = '\0';
592 			}
593 
594 			/** copy buf to coldata */
595 			pbx_str_append_escapecommas(&coldata, 0, buf, buf_len);
596 			token = strtok_r(NULL, delims, &tokenrest);
597 			if (token != NULL) {
598 				pbx_str_append(&coldata, 0, ",");
599 			}
600 			buf[0] = '\0';
601 			/** */
602 		}
603 
604 		pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", pbx_str_buffer(colnames)); /* setvar ODBCFIELDS so that results can be used by HASH() and ARRAY() */
605 		sccp_copy_string(output, pbx_str_buffer(coldata), len);
606 	}
607 	return 0;
608 }
609 
610 /*! \brief Stucture to declare a dialplan function: SCCPDevice */
611 static struct pbx_custom_function sccpdevice_function = {
612 	.name = "SCCPDevice",
613 	.read = sccp_func_sccpdevice,
614 };
615 
616 /*!
617  * \brief  ${SCCPLine()} Dialplan function - reads sccp line data
618  * \param chan Asterisk Channel
619  * \param cmd Command as char
620  * \param data Extra data as char
621  * \param output Buffer as chan*
622  * \param len Lenght as size_t
623  * \return Status as int
624  *
625  * \author Diederik de Groot <ddegroot@users.sourceforce.net>
626  *
627  * \called_from_asterisk
628  *
629  */
sccp_func_sccpline(PBX_CHANNEL_TYPE * chan,NEWCONST char * cmd,char * data,char * output,size_t len)630 static int sccp_func_sccpline(PBX_CHANNEL_TYPE * chan, NEWCONST char * cmd, char * data, char * output, size_t len)
631 {
632 	pbx_str_t * coldata   = pbx_str_thread_get(&coldata_buf, 16);
633 	pbx_str_t * colnames  = pbx_str_thread_get(&colnames_buf, 16);
634 	char *      colname   = NULL;
635 	uint16_t    buf_len   = 1024;
636 	char        buf[1024] = "";
637 	char *      token     = NULL;
638 	int         addcomma  = 0;
639 
640 	if ((colname = strchr(data, ':'))) { /*! \todo Will be deprecated after 1.4 */
641 		static int deprecation_warning = 0;
642 		*colname++                     = '\0';
643 		if (deprecation_warning++ % 10 == 0) {
644 			pbx_log(LOG_WARNING, "SCCPLine(): usage of ':' to separate arguments is deprecated.  Please use ',' instead.\n");
645 		}
646 	} else if ((colname = strchr(data, ','))) {
647 		*colname++ = '\0';
648 	} else {
649 		colname = (char *)sccp_alloca(16);
650 		if (!colname) {
651 			return -1;
652 		}
653 		snprintf(colname, 16, "id");
654 	}
655 	AUTO_RELEASE(sccp_line_t, l, NULL);
656 	AUTO_RELEASE(sccp_channel_t, c, NULL);
657 
658 	if (!strncasecmp(data, "current", 7)) {
659 		c = get_sccp_channel_from_pbx_channel(chan) /*ref_replace*/;
660 		if (!c || !c->line) {
661 			pbx_log(LOG_WARNING, "SCCPLine(): SCCP Line not available\n");
662 			return -1;
663 		}
664 		l = sccp_line_retain(c->line) /*ref_replace*/;
665 	} else if (!strncasecmp(data, "parent", 7)) {
666 		c = get_sccp_channel_from_pbx_channel(chan) /*ref_replace*/;
667 		if (!c || !c->parentChannel || !c->parentChannel->line) {
668 			pbx_log(LOG_WARNING, "SCCPLine(): SCCP Line not available\n");
669 			return -1;
670 		}
671 		l = sccp_line_retain(c->parentChannel->line) /*ref_replace*/;
672 	} else {
673 		l = sccp_line_find_byname(data, TRUE) /*ref_replace*/;
674 		if (!l) {
675 			pbx_log(LOG_WARNING, "SCCPLine(): SCCP Line not available\n");
676 			return -1;
677 		}
678 	}
679 	pbx_str_reset(colnames);
680 	pbx_str_reset(coldata);
681 	char delims[] = " ,";
682 	if (l) {
683 		char * tokenrest = NULL;
684 		token            = strtok_r(colname, delims, &tokenrest);
685 		while (token != NULL) {
686 			addcomma = 0;
687 			token    = pbx_skip_blanks(token);
688 			if (!strlen(token)) {
689 				continue;
690 			}
691 
692 			/** copy request tokens for HASH() */
693 			if (pbx_str_strlen(colnames)) {
694 				pbx_str_append(&colnames, 0, ",");
695 			}
696 			pbx_str_append_escapecommas(&colnames, 0, token, sccp_strlen(token));
697 			/** */
698 
699 			if (!strcasecmp(token, "id")) {
700 				sccp_copy_string(buf, l->id, len);
701 			} else if (!strcasecmp(token, "name")) {
702 				sccp_copy_string(buf, l->name, len);
703 			} else if (!strcasecmp(token, "description")) {
704 				sccp_copy_string(buf, l->description, len);
705 			} else if (!strcasecmp(token, "label")) {
706 				sccp_copy_string(buf, l->label, len);
707 			} else if (!strcasecmp(token, "vmnum")) {
708 				sccp_copy_string(buf, l->vmnum, len);
709 			} else if (!strcasecmp(token, "trnsfvm")) {
710 				sccp_copy_string(buf, l->trnsfvm, len);
711 			} else if (!strcasecmp(token, "meetme")) {
712 				sccp_copy_string(buf, l->meetme ? "on" : "off", len);
713 			} else if (!strcasecmp(token, "meetmenum")) {
714 				sccp_copy_string(buf, l->meetmenum, len);
715 			} else if (!strcasecmp(token, "meetmeopts")) {
716 				sccp_copy_string(buf, l->meetmeopts, len);
717 			} else if (!strcasecmp(token, "context")) {
718 				sccp_copy_string(buf, l->context, len);
719 			} else if (!strcasecmp(token, "language")) {
720 				sccp_copy_string(buf, l->language, len);
721 			} else if (!strcasecmp(token, "accountcode")) {
722 				sccp_copy_string(buf, l->accountcode, len);
723 			} else if (!strcasecmp(token, "musicclass")) {
724 				sccp_copy_string(buf, l->musicclass, len);
725 			} else if (!strcasecmp(token, "amaflags")) {
726 				sccp_copy_string(buf, l->amaflags ? "yes" : "no", len);
727 			} else if (!strcasecmp(token, "dnd_action")) {
728 				sccp_copy_string(buf, sccp_dndmode2str(l->dndmode), buf_len);
729 			} else if (!strcasecmp(token, "callgroup")) {
730 				pbx_print_group(buf, buf_len, l->callgroup);
731 			} else if (!strcasecmp(token, "pickupgroup")) {
732 #ifdef CS_SCCP_PICKUP
733 				pbx_print_group(buf, buf_len, l->pickupgroup);
734 #else
735 				sccp_copy_string(buf, "not supported", len);
736 #endif
737 #ifdef CS_AST_HAS_NAMEDGROUP
738 			} else if (!strcasecmp(token, "named_callgroup")) {
739 #	ifdef CS_SCCP_PICKUP
740 				ast_copy_string(buf, l->namedcallgroup, len);
741 #	else
742 				sccp_copy_string(buf, "not supported", len);
743 #	endif
744 			} else if (!strcasecmp(token, "named_pickupgroup")) {
745 #	ifdef CS_SCCP_PICKUP
746 				ast_copy_string(buf, l->namedpickupgroup, len);
747 #	else
748 				sccp_copy_string(buf, "not supported", len);
749 #	endif
750 #endif
751 			} else if (!strcasecmp(token, "codecs")) {
752 				sccp_codec_multiple2str(buf, buf_len - 1, l->preferences.audio, ARRAY_LEN(l->preferences.audio));
753 			} else if (!strcasecmp(token, "capability")) {
754 				sccp_codec_multiple2str(buf, buf_len - 1, l->capabilities.audio, ARRAY_LEN(l->capabilities.audio));
755 			} else if (!strcasecmp(token, "cid_name")) {
756 				sccp_copy_string(buf, l->cid_name, len);
757 			} else if (!strcasecmp(token, "cid_num")) {
758 				sccp_copy_string(buf, l->cid_num, len);
759 			} else if (!strcasecmp(token, "incoming_limit")) {
760 				snprintf(buf, buf_len, "%d", l->incominglimit);
761 			} else if (!strcasecmp(token, "channel_count")) {
762 				snprintf(buf, buf_len, "%d", SCCP_RWLIST_GETSIZE(&l->channels));
763 			} else if (!strcasecmp(token, "dynamic") || !strcasecmp(token, "realtime")) {
764 #ifdef CS_SCCP_REALTIME
765 				sccp_copy_string(buf, l->realtime ? "Yes" : "No", len);
766 #else
767 				sccp_copy_string(buf, "not supported", len);
768 #endif
769 			} else if (!strcasecmp(token, "pending_delete")) {
770 				sccp_copy_string(buf, l->pendingDelete ? "yes" : "no", len);
771 			} else if (!strcasecmp(token, "pending_update")) {
772 				sccp_copy_string(buf, l->pendingUpdate ? "yes" : "no", len);
773 			} else if (!strcasecmp(token, "regexten")) {
774 				sccp_copy_string(buf, l->regexten ? l->regexten : "Unset", len);
775 			} else if (!strcasecmp(token, "regcontext")) {
776 				sccp_copy_string(buf, l->regcontext ? l->regcontext : "Unset", len);
777 			} else if (!strcasecmp(token, "adhoc_number")) {
778 				sccp_copy_string(buf, l->adhocNumber ? l->adhocNumber : "No", len);
779 			} else if (!strcasecmp(token, "newmsgs")) {
780 				snprintf(buf, buf_len, "%d", l->voicemailStatistic.newmsgs);
781 			} else if (!strcasecmp(token, "oldmsgs")) {
782 				snprintf(buf, buf_len, "%d", l->voicemailStatistic.oldmsgs);
783 			} else if (!strcasecmp(token, "videomode")) {
784 				snprintf(buf, buf_len, "%s", sccp_video_mode2str(l->videomode));
785 			} else if (!strcasecmp(token, "num_devices")) {
786 				snprintf(buf, buf_len, "%d", SCCP_LIST_GETSIZE(&l->devices));
787 			} else if (!strcasecmp(token, "mailboxes")) {
788 				sccp_mailbox_t * mailbox = NULL;
789 				pbx_str_t *      lbuf    = pbx_str_create(DEFAULT_PBX_STR_BUFFERSIZE);
790 				SCCP_LIST_LOCK(&l->mailboxes);
791 				SCCP_LIST_TRAVERSE(&l->mailboxes, mailbox, list) {
792 					pbx_str_append(&lbuf, 0, "%s%s", addcomma++ ? "," : "", mailbox->uniqueid);
793 				}
794 				SCCP_LIST_UNLOCK(&l->mailboxes);
795 				snprintf(buf, buf_len, "%s", pbx_str_buffer(lbuf));
796 				sccp_free(lbuf);
797 			} else if (!strcasecmp(token, "cfwd")) {
798 				sccp_linedevice_t * ld   = NULL;
799 				pbx_str_t *         lbuf = pbx_str_create(DEFAULT_PBX_STR_BUFFERSIZE);
800 
801 				SCCP_LIST_LOCK(&l->devices);
802 				SCCP_LIST_TRAVERSE(&l->devices, ld, list) {
803 					char cfwd_buf[256];
804 					pbx_str_append(&lbuf, 0, "%s[id:%s,cfwd:%s]", addcomma++ ? "," : "", ld->device->id, sccp_linedevice_get_cfwd_string(ld, cfwd_buf, sizeof(cfwd_buf)));
805 				}
806 				SCCP_LIST_UNLOCK(&l->devices);
807 				snprintf(buf, buf_len, "[ %s ]", pbx_str_buffer(lbuf));
808 				sccp_free(lbuf);
809 			} else if (!strcasecmp(token, "devices")) {
810 				sccp_linedevice_t * ld   = NULL;
811 				pbx_str_t *         lbuf = pbx_str_create(DEFAULT_PBX_STR_BUFFERSIZE);
812 
813 				SCCP_LIST_LOCK(&l->devices);
814 				SCCP_LIST_TRAVERSE(&l->devices, ld, list) {
815 					pbx_str_append(&lbuf, 0, "%s%s", addcomma++ ? "," : "", ld->device->id);
816 				}
817 				SCCP_LIST_UNLOCK(&l->devices);
818 				snprintf(buf, buf_len, "[ %s ]", pbx_str_buffer(lbuf));
819 				sccp_free(lbuf);
820 			} else if (!strncasecmp(token, "chanvar[", 8)) {
821 				char * chanvar = token + 8;
822 
823 				PBX_VARIABLE_TYPE * v = NULL;
824 
825 				chanvar = strsep(&chanvar, "]");
826 				for (v = l->variables; v; v = v->next) {
827 					if (!strcasecmp(v->name, chanvar)) {
828 						sccp_copy_string(buf, v->value, len);
829 					}
830 				}
831 			} else {
832 				pbx_log(LOG_WARNING, "SCCPLine(%s): unknown colname: %s\n", data, token);
833 				buf[0] = '\0';
834 			}
835 
836 			/** copy buf to coldata */
837 			pbx_str_append_escapecommas(&coldata, 0, buf, buf_len);
838 			token = strtok_r(NULL, delims, &tokenrest);
839 			if (token != NULL) {
840 				pbx_str_append(&coldata, 0, ",");
841 			}
842 			buf[0] = '\0';
843 			/** */
844 		}
845 
846 		pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", pbx_str_buffer(colnames)); /* setvar ODBCFIELDS so that results can be used by HASH() and ARRAY() */
847 		sccp_copy_string(output, pbx_str_buffer(coldata), len);
848 	}
849 	return 0;
850 }
851 
852 /*! \brief Stucture to declare a dialplan function: SCCPLine */
853 static struct pbx_custom_function sccpline_function = {
854 	.name = "SCCPLine",
855 	.read = sccp_func_sccpline,
856 };
857 
858 /*!
859  * \brief  ${SCCPChannel()} Dialplan function - reads sccp line data
860  * \param chan Asterisk Channel
861  * \param cmd Command as char
862  * \param data Extra data as char
863  * \param output Buffer as chan*
864  * \param len Lenght as size_t
865  * \return Status as int
866  *
867  * \author Diederik de Groot <ddegroot@users.sourceforce.net>
868  *
869  * \called_from_asterisk
870  */
sccp_func_sccpchannel(PBX_CHANNEL_TYPE * chan,NEWCONST char * cmd,char * data,char * output,size_t len)871 static int sccp_func_sccpchannel(PBX_CHANNEL_TYPE * chan, NEWCONST char * cmd, char * data, char * output, size_t len)
872 {
873 	PBX_CHANNEL_TYPE * ast       = NULL;
874 	pbx_str_t *        coldata   = pbx_str_thread_get(&coldata_buf, 16);
875 	pbx_str_t *        colnames  = pbx_str_thread_get(&colnames_buf, 16);
876 	char *             colname   = NULL;
877 	uint16_t           buf_len   = 1024;
878 	char               buf[1024] = "";
879 	char *             token     = NULL;
880 
881 	if ((colname = strchr(data, ':'))) { /*! \todo Will be deprecated after 1.4 */
882 		static int deprecation_warning = 0;
883 		*colname++                     = '\0';
884 		if (deprecation_warning++ % 10 == 0) {
885 			pbx_log(LOG_WARNING, "SCCPChannel(): usage of ':' to separate arguments is deprecated.  Please use ',' instead.\n");
886 		}
887 	} else if ((colname = strchr(data, ','))) {
888 		*colname++ = '\0';
889 	} else {
890 		colname = (char *)sccp_alloca(16);
891 		if (!colname) {
892 			return -1;
893 		}
894 		snprintf(colname, 16, "callid");
895 	}
896 
897 	AUTO_RELEASE(sccp_channel_t, c, NULL);
898 
899 	if (!strncasecmp(data, "current", 7)) {
900 		c = get_sccp_channel_from_pbx_channel(chan) /*ref_replace*/;
901 	} else if (iPbx.getChannelByName(data, &ast) && ast) {
902 		c = get_sccp_channel_from_pbx_channel(ast) /*ref_replace*/;
903 		/* continue with sccp channel */
904 		pbx_channel_unref(ast);
905 	} else {
906 		uint32_t callid = sccp_atoi(data, strlen(data));
907 		c               = sccp_channel_find_byid(callid) /*ref_replace*/;
908 	}
909 	if (!c) {
910 		pbx_log(LOG_WARNING, "SCCPChannel(): SCCP Channel not available\n");
911 		return -1;
912 	}
913 	pbx_str_reset(colnames);
914 	pbx_str_reset(coldata);
915 	char delims[] = " ,";
916 	if (c) {
917 		sccp_callinfo_t * ci        = sccp_channel_getCallInfo(c);
918 		char *            tokenrest = NULL;
919 		token                       = strtok_r(colname, delims, &tokenrest);
920 		while (token != NULL) {
921 			token = pbx_skip_blanks(token);
922 			if (!strlen(token)) {
923 				continue;
924 			}
925 
926 			/** copy request tokens for HASH() */
927 			if (pbx_str_strlen(colnames)) {
928 				pbx_str_append(&colnames, 0, ",");
929 			}
930 			pbx_str_append_escapecommas(&colnames, 0, token, sccp_strlen(token));
931 			/** */
932 
933 			if (!strcasecmp(token, "callid") || !strcasecmp(token, "id")) {
934 				snprintf(buf, buf_len, "%d", c->callid);
935 			} else if (!strcasecmp(token, "format")) {
936 				snprintf(buf, buf_len, "%d", c->rtp.audio.transmission.format);
937 			} else if (!strcasecmp(token, "codecs")) {
938 				sccp_copy_string(buf, codec2name(c->rtp.audio.transmission.format), len);
939 			} else if (!strcasecmp(token, "capability")) {
940 				sccp_codec_multiple2str(buf, buf_len - 1, c->capabilities.audio, ARRAY_LEN(c->capabilities.audio));
941 			} else if (!strcasecmp(token, "calledPartyName")) {
942 				iCallInfo.Getter(ci, SCCP_CALLINFO_CALLEDPARTY_NAME, buf, SCCP_CALLINFO_KEY_SENTINEL);
943 			} else if (!strcasecmp(token, "calledPartyNumber")) {
944 				iCallInfo.Getter(ci, SCCP_CALLINFO_CALLEDPARTY_NUMBER, buf, SCCP_CALLINFO_KEY_SENTINEL);
945 			} else if (!strcasecmp(token, "callingPartyName")) {
946 				iCallInfo.Getter(ci, SCCP_CALLINFO_CALLINGPARTY_NAME, buf, SCCP_CALLINFO_KEY_SENTINEL);
947 			} else if (!strcasecmp(token, "callingPartyNumber")) {
948 				iCallInfo.Getter(ci, SCCP_CALLINFO_CALLINGPARTY_NUMBER, buf, SCCP_CALLINFO_KEY_SENTINEL);
949 			} else if (!strcasecmp(token, "originalCallingPartyName")) {
950 				iCallInfo.Getter(ci, SCCP_CALLINFO_ORIG_CALLINGPARTY_NAME, buf, SCCP_CALLINFO_KEY_SENTINEL);
951 			} else if (!strcasecmp(token, "originalCallingPartyNumber")) {
952 				iCallInfo.Getter(ci, SCCP_CALLINFO_ORIG_CALLINGPARTY_NUMBER, buf, SCCP_CALLINFO_KEY_SENTINEL);
953 			} else if (!strcasecmp(token, "originalCalledPartyName")) {
954 				iCallInfo.Getter(ci, SCCP_CALLINFO_ORIG_CALLEDPARTY_NAME, buf, SCCP_CALLINFO_KEY_SENTINEL);
955 			} else if (!strcasecmp(token, "originalCalledPartyNumber")) {
956 				iCallInfo.Getter(ci, SCCP_CALLINFO_ORIG_CALLEDPARTY_NUMBER, buf, SCCP_CALLINFO_KEY_SENTINEL);
957 			} else if (!strcasecmp(token, "lastRedirectingPartyName")) {
958 				iCallInfo.Getter(ci, SCCP_CALLINFO_LAST_REDIRECTINGPARTY_NAME, buf, SCCP_CALLINFO_KEY_SENTINEL);
959 			} else if (!strcasecmp(token, "lastRedirectingPartyNumber")) {
960 				iCallInfo.Getter(ci, SCCP_CALLINFO_LAST_REDIRECTINGPARTY_NUMBER, buf, SCCP_CALLINFO_KEY_SENTINEL);
961 			} else if (!strcasecmp(token, "cgpnVoiceMailbox")) {
962 				iCallInfo.Getter(ci, SCCP_CALLINFO_CALLINGPARTY_VOICEMAIL, buf, SCCP_CALLINFO_KEY_SENTINEL);
963 			} else if (!strcasecmp(token, "cdpnVoiceMailbox")) {
964 				iCallInfo.Getter(ci, SCCP_CALLINFO_CALLEDPARTY_VOICEMAIL, buf, SCCP_CALLINFO_KEY_SENTINEL);
965 			} else if (!strcasecmp(token, "originalCdpnVoiceMailbox")) {
966 				iCallInfo.Getter(ci, SCCP_CALLINFO_ORIG_CALLEDPARTY_VOICEMAIL, buf, SCCP_CALLINFO_KEY_SENTINEL);
967 			} else if (!strcasecmp(token, "lastRedirectingVoiceMailbox")) {
968 				iCallInfo.Getter(ci, SCCP_CALLINFO_LAST_REDIRECTINGPARTY_VOICEMAIL, buf, SCCP_CALLINFO_KEY_SENTINEL);
969 			} else if (!strcasecmp(token, "passthrupartyid")) {
970 				snprintf(buf, buf_len, "%d", c->passthrupartyid);
971 			} else if (!strcasecmp(token, "state")) {
972 				sccp_copy_string(buf, sccp_channelstate2str(c->state), len);
973 			} else if (!strcasecmp(token, "previous_state")) {
974 				sccp_copy_string(buf, sccp_channelstate2str(c->previousChannelState), len);
975 			} else if (!strcasecmp(token, "calltype")) {
976 				sccp_copy_string(buf, skinny_calltype2str(c->calltype), len);
977 			} else if (!strcasecmp(token, "ringtype")) {
978 				sccp_copy_string(buf, skinny_ringtype2str(c->ringermode), len);
979 			} else if (!strcasecmp(token, "dialed_number")) {
980 				sccp_copy_string(buf, c->dialedNumber, len);
981 			} else if (!strcasecmp(token, "device")) {
982 				sccp_copy_string(buf, c->currentDeviceId, len);
983 			} else if (!strcasecmp(token, "line")) {
984 				sccp_copy_string(buf, c->line->name, len);
985 			} else if (!strcasecmp(token, "answered_elsewhere")) {
986 				sccp_copy_string(buf, c->answered_elsewhere ? "yes" : "no", len);
987 			} else if (!strcasecmp(token, "privacy")) {
988 				sccp_copy_string(buf, c->privacy ? "yes" : "no", len);
989 			} else if (!strcasecmp(token, "softswitch_action")) {
990 				snprintf(buf, buf_len, "%s (%d)", sccp_softswitch2str(c->softswitch_action), c->softswitch_action);
991 				// } else if (!strcasecmp(token, "monitorEnabled")) {
992 				// sccp_copy_string(buf, c->monitorEnabled ? "yes" : "no", len);
993 			} else if (!strcasecmp(token, "videomode")) {
994 				snprintf(buf, buf_len, "%s", sccp_video_mode2str(c->videomode));
995 #ifdef CS_SCCP_CONFERENCE
996 			} else if (!strcasecmp(token, "conference_id")) {
997 				snprintf(buf, buf_len, "%d", c->conference_id);
998 			} else if (!strcasecmp(token, "conference_participant_id")) {
999 				snprintf(buf, buf_len, "%d", c->conference_participant_id);
1000 #endif
1001 			} else if (!strcasecmp(token, "parent")) {
1002 				snprintf(buf, buf_len, "%d", c->parentChannel->callid);
1003 			} else if (!strcasecmp(token, "bridgepeer")) {
1004 				PBX_CHANNEL_TYPE * bridgechannel = NULL;
1005 				if (c->owner && (bridgechannel = iPbx.get_bridged_channel(c->owner))) {
1006 					snprintf(buf, buf_len, "%s", pbx_channel_name(bridgechannel));
1007 					pbx_channel_unref(bridgechannel);
1008 				} else {
1009 					snprintf(buf, buf_len, "<unknown>");
1010 				}
1011 			} else if (!strcasecmp(token, "peerip")) {                                        // NO-NAT (Ip-Address Associated with the Session->sin)
1012 				AUTO_RELEASE(sccp_device_t, d, sccp_channel_getDevice(c));
1013 				if (d) {
1014 					struct sockaddr_storage sas = { 0 };
1015 					sccp_session_getOurIP(d->session, &sas, 0);
1016 					sccp_copy_string(buf, sccp_netsock_stringify(&sas), len);
1017 				}
1018 			} else if (!strcasecmp(token, "recvip")) {                                        // NAT (Actual Source IP-Address Reported by the phone upon registration)
1019 				AUTO_RELEASE(sccp_device_t, d, sccp_channel_getDevice(c));
1020 				if (d) {
1021 					struct sockaddr_storage sas = { 0 };
1022 					sccp_session_getSas(d->session, &sas);
1023 					sccp_copy_string(buf, sccp_netsock_stringify(&sas), len);
1024 				}
1025 			} else if (!strcasecmp(colname, "rtpqos")) {
1026 				AUTO_RELEASE(sccp_device_t, d, sccp_channel_getDevice(c));
1027 				if (d) {
1028 					sccp_call_statistics_t * call_stats = d->call_statistics;
1029 					snprintf(buf, buf_len, "Packets sent: %d;rcvd: %d;lost: %d;jitter: %d;latency: %d;MLQK=%.4f;MLQKav=%.4f;MLQKmn=%.4f;MLQKmx=%.4f;MLQKvr=%.2f|ICR=%.4f;CCR=%.4f;ICRmx=%.4f|CS=%d;SCS=%d",
1030 					         call_stats[SCCP_CALLSTATISTIC_LAST].packets_sent, call_stats[SCCP_CALLSTATISTIC_LAST].packets_received, call_stats[SCCP_CALLSTATISTIC_LAST].packets_lost,
1031 					         call_stats[SCCP_CALLSTATISTIC_LAST].jitter, call_stats[SCCP_CALLSTATISTIC_LAST].latency, call_stats[SCCP_CALLSTATISTIC_LAST].opinion_score_listening_quality,
1032 					         call_stats[SCCP_CALLSTATISTIC_LAST].avg_opinion_score_listening_quality, call_stats[SCCP_CALLSTATISTIC_LAST].mean_opinion_score_listening_quality,
1033 					         call_stats[SCCP_CALLSTATISTIC_LAST].max_opinion_score_listening_quality, call_stats[SCCP_CALLSTATISTIC_LAST].variance_opinion_score_listening_quality,
1034 					         call_stats[SCCP_CALLSTATISTIC_LAST].interval_concealement_ratio, call_stats[SCCP_CALLSTATISTIC_LAST].cumulative_concealement_ratio,
1035 					         call_stats[SCCP_CALLSTATISTIC_LAST].max_concealement_ratio, (int)call_stats[SCCP_CALLSTATISTIC_LAST].concealed_seconds,
1036 					         (int)call_stats[SCCP_CALLSTATISTIC_LAST].severely_concealed_seconds);
1037 				}
1038 			} else if (!strncasecmp(token, "codec[", 6)) {
1039 				char * codecnum = NULL;
1040 
1041 				codecnum      = token + 6;                                                     // move past the '['
1042 				codecnum      = strsep(&codecnum, "]");                                        // trim trailing ']' if any
1043 				int codec_int = sccp_atoi(codecnum, strlen(codecnum));
1044 				if (skinny_codecs[codec_int].key) {
1045 					sccp_copy_string(buf, codec2name((skinny_codec_t)codec_int), buf_len);
1046 				} else {
1047 					buf[0] = '\0';
1048 				}
1049 			} else {
1050 				pbx_log(LOG_WARNING, "SCCPChannel(%s): unknown colname: %s\n", data, token);
1051 				buf[0] = '\0';
1052 			}
1053 
1054 			/** copy buf to coldata */
1055 			pbx_str_append_escapecommas(&coldata, 0, buf, buf_len);
1056 			token = strtok_r(NULL, delims, &tokenrest);
1057 			if (token != NULL) {
1058 				pbx_str_append(&coldata, 0, ",");
1059 			}
1060 			buf[0] = '\0';
1061 			/** */
1062 		}
1063 
1064 		pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", pbx_str_buffer(colnames)); /* setvar ODBCFIELDS so that results can be used by HASH() and ARRAY() */
1065 		sccp_copy_string(output, pbx_str_buffer(coldata), len);
1066 	}
1067 	return 0;
1068 }
1069 
1070 /*! \brief Stucture to declare a dialplan function: SCCPChannel */
1071 static struct pbx_custom_function sccpchannel_function = {
1072 	.name = "SCCPChannel",
1073 	.read = sccp_func_sccpchannel,
1074 };
1075 
1076 /*!
1077  * \brief       Set the Preferred Codec for a SCCP channel via the dialplan
1078  * \param       chan Asterisk Channel
1079  * \param       data single codec name
1080  * \return      Success as int
1081  *
1082  * \called_from_asterisk
1083  * \deprecated
1084  */
1085 #if ASTERISK_VERSION_NUMBER >= 10800
sccp_app_prefcodec(PBX_CHANNEL_TYPE * chan,const char * data)1086 static int sccp_app_prefcodec(PBX_CHANNEL_TYPE * chan, const char * data)
1087 #else
1088 static int sccp_app_prefcodec(PBX_CHANNEL_TYPE * chan, void * data)
1089 #endif
1090 {
1091 	AUTO_RELEASE(sccp_channel_t, c, get_sccp_channel_from_pbx_channel(chan));
1092 	int res = 0;
1093 
1094 	if (!c) {
1095 		pbx_log(LOG_WARNING, "SCCPSetCodec: Not an SCCP channel\n");
1096 		return -1;
1097 	}
1098 
1099 	res = sccp_channel_setPreferredCodec(c, data);
1100 	pbx_log(LOG_WARNING, "SCCPSetCodec: Is now deprecated. Please use 'Set(CHANNEL(codec)=%s)' insteadl.\n", (char *)data);
1101 	return res ? 0 : -1;
1102 }
1103 static char * prefcodec_name = "SCCPSetCodec";
1104 
1105 /*!
1106  * \brief       Set the Name and Number of the Called Party to the Calling Phone
1107  * \param       chan Asterisk Channel
1108  * \param       data CallerId in format "Name" \<number\>
1109  * \return      Success as int
1110  *
1111  * \called_from_asterisk
1112  * \deprecated
1113  */
1114 #if ASTERISK_VERSION_NUMBER >= 10800
sccp_app_calledparty(PBX_CHANNEL_TYPE * chan,const char * data)1115 static int sccp_app_calledparty(PBX_CHANNEL_TYPE * chan, const char * data)
1116 #else
1117 static int sccp_app_calledparty(PBX_CHANNEL_TYPE * chan, void * data)
1118 #endif
1119 {
1120 	char * text = (char *)data;
1121 	char * num  = NULL;
1122 
1123 	char * name = NULL;
1124 	AUTO_RELEASE(sccp_channel_t, c, get_sccp_channel_from_pbx_channel(chan));
1125 	if (!c) {
1126 		pbx_log(LOG_WARNING, "SCCPSetCalledParty: Not an SCCP channel\n");
1127 		return 0;
1128 	}
1129 
1130 	if (!text) {
1131 		pbx_log(LOG_WARNING, "SCCPSetCalledParty: No CalledParty Information Provided\n");
1132 		return 0;
1133 	}
1134 
1135 	if (!text || sccp_strlen_zero(text)) {
1136 		pbx_log(LOG_ERROR, "SCCPSetCalledParty: No valid party information provided: '%s'\n", text);
1137 		return 0;
1138 	}
1139 	pbx_callerid_parse(text, &name, &num);
1140 	sccp_channel_set_calledparty(c, name, num);
1141 	sccp_channel_display_callInfo(c);
1142 	pbx_builtin_setvar_helper(c->owner, "SETCALLEDPARTY", text);
1143 
1144 	return 0;
1145 }
1146 static char * calledparty_name = "SCCPSetCalledParty";
1147 
1148 /*!
1149  * \brief       It allows you to send a message to the calling device.
1150  * \author      Frank Segtrop <fs@matflow.net>
1151  * \param       chan asterisk channel
1152  * \param       data message to sent - if empty clear display
1153  * \version     20071112_1944
1154  *
1155  * \called_from_asterisk
1156  */
1157 #if ASTERISK_VERSION_NUMBER >= 10800
sccp_app_setmessage(PBX_CHANNEL_TYPE * chan,const char * data)1158 static int sccp_app_setmessage(PBX_CHANNEL_TYPE * chan, const char * data)
1159 #else
1160 static int sccp_app_setmessage(PBX_CHANNEL_TYPE * chan, void * data)
1161 #endif
1162 {
1163 	AUTO_RELEASE(sccp_channel_t, c, get_sccp_channel_from_pbx_channel(chan));
1164 	if (!c) {
1165 		pbx_log(LOG_WARNING, "SCCPSetMessage: Not an SCCP channel\n");
1166 		return 0;
1167 	}
1168 
1169 	int                     timeout  = 0;
1170 	sccp_message_priority_t priority = SCCP_MESSAGE_PRIORITY_SENTINEL;
1171 
1172 	char * parse = pbx_strdupa(data);
1173 	AST_DECLARE_APP_ARGS(args, AST_APP_ARG(text); AST_APP_ARG(timeout); AST_APP_ARG(priority););
1174 	AST_STANDARD_APP_ARGS(args, parse);
1175 
1176 	if (!sccp_strlen_zero(args.timeout)) {
1177 		timeout = sccp_atoi(args.timeout, strlen(args.timeout));
1178 	}
1179 	if (!sccp_strlen_zero(args.priority)) {
1180 		priority = (sccp_message_priority_t)sccp_atoi(args.priority, strlen(args.priority));
1181 	}
1182 
1183 	AUTO_RELEASE(sccp_device_t, d, sccp_channel_getDevice(c));
1184 	if (!d) {
1185 		pbx_log(LOG_WARNING, "SCCPSetMessage: Not an SCCP device provided\n");
1186 		return 0;
1187 	}
1188 
1189 	pbx_log(LOG_WARNING, "SCCPSetMessage: text:'%s', prio:%d, timeout:%d\n", args.text, priority, timeout);
1190 	if (!sccp_strlen_zero(args.text)) {
1191 		if (priority != SCCP_MESSAGE_PRIORITY_SENTINEL) {
1192 			sccp_dev_displayprinotify(d, args.text, priority, timeout);
1193 		} else {
1194 			sccp_dev_set_message(d, args.text, timeout, TRUE, FALSE);
1195 		}
1196 	} else {
1197 		if (priority != SCCP_MESSAGE_PRIORITY_SENTINEL) {
1198 			sccp_dev_cleardisplayprinotify(d, priority);
1199 		} else {
1200 			sccp_dev_clear_message(d, TRUE);
1201 		}
1202 	}
1203 	return 0;
1204 }
1205 static char * setmessage_name = "SCCPSetMessage";
1206 
1207 //#include "pbx_impl/ast113/ast113.h"
sccp_register_dialplan_functions(void)1208 int sccp_register_dialplan_functions(void)
1209 {
1210 	int result = 0;
1211 
1212 	/* Register application functions */
1213 	result = iPbx.register_application(calledparty_name, sccp_app_calledparty);
1214 	result |= iPbx.register_application(setmessage_name, sccp_app_setmessage);
1215 	result |= iPbx.register_application(prefcodec_name, sccp_app_prefcodec);
1216 
1217 	/* Register dialplan functions */
1218 	result |= iPbx.register_function(&sccpdevice_function);
1219 	result |= iPbx.register_function(&sccpline_function);
1220 	result |= iPbx.register_function(&sccpchannel_function);
1221 
1222 	return result;
1223 }
1224 
sccp_unregister_dialplan_functions(void)1225 int sccp_unregister_dialplan_functions(void)
1226 {
1227 	int result = 0;
1228 
1229 	/* Unregister applications functions */
1230 	result = iPbx.unregister_application(calledparty_name);
1231 	result |= iPbx.unregister_application(setmessage_name);
1232 	result |= iPbx.unregister_application(prefcodec_name);
1233 
1234 	/* Unregister dial plan functions */
1235 	result |= iPbx.unregister_function(&sccpdevice_function);
1236 	result |= iPbx.unregister_function(&sccpline_function);
1237 	result |= iPbx.unregister_function(&sccpchannel_function);
1238 
1239 	return result;
1240 }
1241 
1242 // 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;
1243