1 /*****************************************************************************\
5 **---------------------------------------------------------------------------**
6 ** Copyright: Andreas Eversberg **
8 ** match processing of routing configuration **
10 \*****************************************************************************/
15 struct route_ruleset *ruleset_first; /* first entry */
16 struct route_ruleset *ruleset_main; /* pointer to main ruleset */
18 struct cond_defs cond_defs[] = {
19 { "extern", MATCH_EXTERN, COND_TYPE_NULL,
20 "extern", "Matches if call is from external port (no extension)."},
21 { "intern", MATCH_INTERN,COND_TYPE_NULL,
22 "intern", "Matches if call is from an extension."},
23 { "port", MATCH_PORT, COND_TYPE_INTEGER,
24 "port=<number>[-<number>][,...]", "Matches if call is received from given port(s). NOT INTERFACE!"},
25 { "interface", MATCH_INTERFACE,COND_TYPE_STRING,
26 "interface=<interface>[,...]", "Matches if call is received from given interface(s). NOT PORTS!"},
27 { "callerid", MATCH_CALLERID, COND_TYPE_STRING,
28 "callerid=<digits>[-<digits>][,...]", "Matches if caller ID matches or begins with the given (range(s) of) prefixes(s)."},
29 { "callerid2", MATCH_CALLERID2,COND_TYPE_STRING,
30 "callerid2=<digits>[-<digits>][,...]", "Matches the second caller ID (network provided)."},
31 { "extension", MATCH_EXTENSION,COND_TYPE_STRING,
32 "extension=<digits>[-<digits>][,...]", "Matches if caller calls from given (range(s) of) extension(s)."},
33 { "dialing", MATCH_DIALING, COND_TYPE_STRING,
34 "dialing=<digits>[-<digits>][,...]", "Matches if caller has dialed the given (range(s) of) digits at least."},
35 { "enblock", MATCH_ENBLOCK, COND_TYPE_NULL,
36 "enblock", "Matches if caller dialed en block. (Dial the number before pick up.)"},
37 { "overlap", MATCH_OVERLAP, COND_TYPE_NULL,
38 "overlap", "Matches if caller dialed digit by digit. (Dial the number after pick up.)"},
39 { "anonymous", MATCH_ANONYMOUS,COND_TYPE_NULL,
40 "anonymous", "Matches if caller uses restricted caller ID or if not available."},
41 { "visible", MATCH_VISIBLE, COND_TYPE_NULL,
42 "visible", "Matches if caller ID is presented and if available."},
43 { "unknown", MATCH_UNKNOWN, COND_TYPE_NULL,
44 "unknown", "Matches if no ID is available from caller."},
45 { "available", MATCH_AVAILABLE,COND_TYPE_NULL,
46 "available", "Matches if ID is available from caller."},
47 { "fake", MATCH_FAKE, COND_TYPE_NULL,
48 "fake", "Matches if caller ID is not screened and may be faked by caller."},
49 { "real", MATCH_REAL, COND_TYPE_NULL,
50 "real", "Matches if caller ID is screend and so it is the real caller's ID."},
51 { "redirected", MATCH_REDIRECTED,COND_TYPE_NULL,
52 "redirected", "Matches if caller has been redirected."},
53 { "direct", MATCH_DIRECT ,COND_TYPE_NULL,
54 "direct", "Matches if caller did not come from an redirection."},
55 { "redirid", MATCH_REDIRID ,COND_TYPE_STRING,
56 "redirid=<digits>[-<digits>][,...]", "Matches if the caller has been redirected by the given (range(s) of) ID(s) or prefix(es))"},
57 { "time", MATCH_TIME, COND_TYPE_TIME,
58 "time=<minutes>[-<minutes>][,...]", "Matches if the caller calls within the given (range(s) of) time(s). (e.g. 0700-1900)"},
59 { "mday", MATCH_MDAY, COND_TYPE_MDAY,
60 "mday=<day>[-<day>][,...]", "Matches if the caller calls within the given (range(s) of) day(s) of the month. (1..31)"},
61 { "month", MATCH_MONTH, COND_TYPE_MONTH,
62 "month=<month>[-<month>][,...]", "Matches if the caller calls within the given (range(s) of) month(s). (1=January..12=December)"},
63 { "year", MATCH_YEAR, COND_TYPE_YEAR,
64 "year=<year>[-<year>][,...]", "Matches if the caller calls within the given (range(s) of) year(s). (1970..2106)"},
65 { "wday", MATCH_WDAY, COND_TYPE_WDAY,
66 "wday=<day>[-<day>][,...]", "Matches if the caller calls within the given (range(s) of) weekday(s). (1=Monday..7=Sunday)"},
67 { "capability", MATCH_CAPABILITY, COND_TYPE_CAPABILITY,
68 "capability=speech|audio|video|digital-restricted|digital-unrestricted|digital-unrestricted-tones[,...]", "Matches the given bearer capability(s)."},
69 { "infolayer1", MATCH_INFOLAYER1, COND_TYPE_INTEGER,
70 "infolayer1=<value>[,...]", "Matches the given information layer 1. (2=u-Law, 3=a-law, see info layer 1 in bearer capability.)"},
71 { "hlc", MATCH_HLC, COND_TYPE_HLC,
72 "hlc=telephony|faxg2g3|faxg4|teletex1|teletex2|teletex3|videotex1|videotex2|telex|mhs|osi|maintenance|management|audiovisual[,...]", "Matches the high layer capability(s)."},
73 { "file", MATCH_FILE, COND_TYPE_STRING,
74 "file=<path>[,...]", "Mathes is the given file exists and if the first character is '1'."},
75 { "execute", MATCH_EXECUTE, COND_TYPE_STRING,
76 "execute=<command>[,...]","Matches if the return value of the given command is greater 0."},
77 { "default", MATCH_DEFAULT, COND_TYPE_NULL,
78 "default","Matches if no further dialing could match."},
79 { "timeout", MATCH_TIMEOUT, COND_TYPE_INTEGER,
80 "timeout=<seconds>","Matches if the ruleset was entered AFTER given seconds."},
81 { "free", MATCH_FREE, COND_TYPE_IFATTR,
82 "free=<interface>:<channel>","Matches if the given minimum of channels are free."},
83 { "notfree", MATCH_NOTFREE, COND_TYPE_IFATTR,
84 "notfree=<interface>:<channel>","Matches if NOT the given minimum of channels are free."},
85 { "blocked", MATCH_DOWN, COND_TYPE_STRING,
86 "blocked=<interfaces>[,...]","Matches if all of the given interfaces are blocked."},
87 { "idle", MATCH_UP, COND_TYPE_STRING,
88 "idle=<interface>[,...]","Matches if any of the given interfaces is idle."},
89 { "busy", MATCH_BUSY, COND_TYPE_STRING,
90 "busy=<extension>[,...]","Matches if any of the given extension is busy."},
91 { "notbusy", MATCH_IDLE, COND_TYPE_STRING,
92 "notbusy=<extension>[,...]","Matches if any of the given extension is not busy."},
93 { "remote", MATCH_REMOTE, COND_TYPE_STRING,
94 "remote=<application name>","Matches if remote application is running."},
95 { "notremote", MATCH_NOTREMOTE,COND_TYPE_STRING,
96 "notremote=<application name>","Matches if remote application is not running."},
97 { "pots-flash", MATCH_POTS_FLASH,COND_TYPE_NULL,
98 "pots-flash","When using POTS: Matches if call was invoked by flash/earth button."},
99 { "pots-cw", MATCH_POTS_CW, COND_TYPE_NULL,
100 "pots-cw","When using POTS: Matches if a call is waiting."},
101 { "pots-calls", MATCH_POTS_CALLS,COND_TYPE_INTEGER,
102 "pots-calls=<total number>","When using POTS: Matches if given number of calls are held."},
103 { "pots-last", MATCH_POTS_LAST,COND_TYPE_INTEGER,
104 "pots-last=<call number>","When using POTS: Matches if given call number (1=oldest) was the last active call."},
108 struct param_defs param_defs[] = {
110 "proceeding", PARAM_TYPE_NULL,
111 "proceeding", "Will set the call into 'proceeding' state to prevent dial timeout."},
113 "alerting", PARAM_TYPE_NULL,
114 "alerting", "Will set the call into 'altering' state."},
116 "connect", PARAM_TYPE_NULL,
117 "connect", "Will complete the call before processing the action. Audio path for external calls will be established."},
119 "extension", PARAM_TYPE_STRING,
120 "extension=<digits>", "Give extension name (digits) to relate this action to."},
122 "extensions", PARAM_TYPE_STRING,
123 "extensions=<extension>[,<extension>[,...]]", "One or more extensions may be given."},
125 "prefix", PARAM_TYPE_STRING,
126 "prefix=<digits>", "Add prefix in front of the dialed number."},
128 "capability", PARAM_TYPE_CAPABILITY,
129 "capability=speech|audio|video|digital-restricted|digital-unrestricted|digital-unrestricted-tones", "Alter the service type of the call."},
131 "bmode", PARAM_TYPE_BMODE,
132 "bmode=transparent|hdlc", "Alter the bchannel mode of the call. Use hdlc for data calls."},
134 "infolayer1", PARAM_TYPE_INTEGER,
135 "infolayer1=<value>", "Alter the layer 1 information of a call. Use 3 for ALAW or 2 for uLAW."},
137 "hlc", PARAM_TYPE_HLC,
138 "hlc=telephony|faxg2g3|faxg4|teletex1|teletex2|teletex3|videotex1|videotex2|telex|mhs|osi|maintenance|management|audiovisual", "Alter the HLC identification."},
140 "exthlc", PARAM_TYPE_HLC,
141 "exthlc=<value>", "Alter extended HLC value, see hlc. (Mainenance only, don't use it.)"},
143 "present", PARAM_TYPE_YESNO,
144 "present=yes|no", "Allow or restrict caller ID regardless what the caller wants."},
146 "diversion", PARAM_TYPE_DIVERSION,
147 "diversion=cfu|cfnr|cfb|cfp", "Set diversion type."},
149 "dest", PARAM_TYPE_DESTIN,
150 "dest=<string>", "Destination number to divert to. Use 'vbox' to divert to vbox. (cfu,cfnr,cfb only)"},
152 "select", PARAM_TYPE_NULL,
153 "select", "Lets the caller select the history of calls using keys '1'/'3' or '*'/'#'."},
155 "delay", PARAM_TYPE_INTEGER,
156 "delay=<seconds>", "Number of seconds to delay."},
158 "limit", PARAM_TYPE_INTEGER,
159 "limit=<retries>", "Number of maximum retries."},
161 "host", PARAM_TYPE_STRING,
162 "host=<string>", "Name of remote VoIP host."},
164 "port", PARAM_TYPE_STRING,
165 "port=<value>", "Alternate port to use if 'host' is given."},
167 "interfaces", PARAM_TYPE_STRING,
168 "interfaces=<interface>[,<interface>[,...]]", "Give one or a list of Interfaces to select a free channel from."},
170 "address", PARAM_TYPE_STRING,
171 "address=<string>", "Complete VoIP address. ( [user@]host[:port] )"},
173 "sample", PARAM_TYPE_STRING,
174 "sample=<file prefix>", "Filename of sample (current tone's dir) or full path to sample. ('.wav'/'.wave'/'.isdn' is added automatically."},
175 { PARAM_ANNOUNCEMENT,
176 "announcement",PARAM_TYPE_STRING,
177 "announcement=<file prefix>", "Filename of announcement (inside vbox recording dir) or full path to sample. ('.wav'/'.wave'/'.isdn' is added automatically."},
179 "ruleset", PARAM_TYPE_STRING,
180 "ruleset=<name>", "Ruleset to go to."},
182 "cause", PARAM_TYPE_INTEGER,
183 "cause=<cause value>", "Cause value when disconnecting. (21=reject 1=unassigned 63=service not available)"},
185 "location", PARAM_TYPE_INTEGER,
186 "location=<location value>", "Location of cause value when disconnecting. (0=user 1=private network sering local user)"},
188 "display", PARAM_TYPE_STRING,
189 "display=<text>", "Message to display on the caller's telephone. (internal only)"},
191 "ports", PARAM_TYPE_INTEGER,
192 "ports=<port>[,<port>[,...]]", "ISDN port[s] to use."},
194 "tpreset", PARAM_TYPE_INTEGER,
195 "tpreset=<seconds>", "Preset of countdown timer."},
197 "file", PARAM_TYPE_STRING,
198 "file=<full path>", "Full path to file name."},
200 "content", PARAM_TYPE_STRING,
201 "content=<string>", "Content to write into file."},
203 "append", PARAM_TYPE_NULL,
204 "append", "Will append to given file, rather than overwriting it."},
206 "execute", PARAM_TYPE_STRING,
207 "execute=<full path>", "Full path to script/command name. (Dialed digits are the argument 1.)"},
209 "param", PARAM_TYPE_STRING,
210 "param=<string>", "Optionally this parameter can be inserted as argument 1, others are shifted."},
212 "type", PARAM_TYPE_TYPE,
213 "type=unknown|subscriber|national|international", "Type of number to dial, default is 'unknown'."},
215 "complete", PARAM_TYPE_NULL,
216 "complete", "Indicates complete number as given by prefix. Proceeding of long distance calls may be faster."},
218 "callerid", PARAM_TYPE_STRING,
219 "callerid=<digits>", "Change caller ID to given string."},
220 { PARAM_CALLERIDTYPE,
221 "calleridtype",PARAM_TYPE_CALLERIDTYPE,
222 "calleridtype=[unknown|subscriber|national|international]", "Type of caller ID. For normal MSN use 'unknown'"},
224 "callto", PARAM_TYPE_STRING,
225 "callto=<digits>", "Where to call back. By default the caller ID is used."},
227 "room", PARAM_TYPE_INTEGER,
228 "room=<digits>", "Conference room number, must be greater 0, as in real life."},
230 "jingle", PARAM_TYPE_NULL,
231 "jingle", "Conference members will hear a jingle if a member joins."},
233 "timeout", PARAM_TYPE_INTEGER,
234 "timeout=<seconds>", "Timeout before continue with next action."},
236 "nopassword", PARAM_TYPE_NULL,
237 "nopassword", "Don't ask for password. Be sure to authenticate right via real caller ID."},
239 "strip", PARAM_TYPE_NULL,
240 "strip", "Remove digits that were required to match this rule."},
242 "application",PARAM_TYPE_STRING,
243 "application=<name>", "Name of remote application to make call to."},
245 "context", PARAM_TYPE_STRING,
246 "context=<context>", "Give context parameter to the remote application."},
248 "exten", PARAM_TYPE_STRING,
249 "exten=<extension>", "Give exten parameter to the remote application. (overrides dialed number)"},
251 "on", PARAM_TYPE_STRING,
252 "on=[init|hangup]", "Defines if the action is executed on call init or on hangup."},
254 "keypad", PARAM_TYPE_NULL,
255 "keypad", "Use 'keypad facility' for dialing, instead of 'called number'."},
257 "pots-call", PARAM_TYPE_INTEGER,
258 "pots-call=<call #>", "Select call number. The oldest call is number 1."},
259 { 0, NULL, 0, NULL, NULL}
262 struct action_defs action_defs[] = {
264 "extern", &EndpointAppPBX::action_init_call, &EndpointAppPBX::action_dialing_external, &EndpointAppPBX::action_hangup_call,
265 PARAM_CONNECT | PARAM_PREFIX | PARAM_COMPLETE | PARAM_TYPE | PARAM_CAPA | PARAM_BMODE | PARAM_INFO1 | PARAM_HLC | PARAM_EXTHLC | PARAM_PRESENT | PARAM_INTERFACES | PARAM_CALLERID | PARAM_CALLERIDTYPE | PARAM_KEYPAD | PARAM_CONTEXT | PARAM_TIMEOUT,
266 "Call is routed to extern number as dialed."},
268 "intern", &EndpointAppPBX::action_init_call, &EndpointAppPBX::action_dialing_internal, &EndpointAppPBX::action_hangup_call,
269 PARAM_CONNECT | PARAM_EXTENSION | PARAM_TYPE | PARAM_CAPA | PARAM_BMODE | PARAM_INFO1 | PARAM_HLC | PARAM_EXTHLC | PARAM_PRESENT | PARAM_TIMEOUT,
270 "Call is routed to intern extension as given by the dialed number or specified by option."},
272 "outdial", &EndpointAppPBX::action_init_call, &EndpointAppPBX::action_dialing_external, &EndpointAppPBX::action_hangup_call,
273 PARAM_CONNECT | PARAM_PREFIX | PARAM_COMPLETE | PARAM_TYPE | PARAM_CAPA | PARAM_BMODE | PARAM_INFO1 | PARAM_HLC | PARAM_EXTHLC | PARAM_PRESENT | PARAM_INTERFACES | PARAM_CALLERID | PARAM_CALLERIDTYPE | PARAM_KEYPAD | PARAM_TIMEOUT,
275 { ACTION_VBOX_RECORD,
276 "vbox-record",&EndpointAppPBX::action_init_call, &EndpointAppPBX::action_dialing_vbox_record, &EndpointAppPBX::action_hangup_call,
277 PARAM_CONNECT | PARAM_EXTENSION | PARAM_ANNOUNCEMENT | PARAM_TIMEOUT,
278 "Caller is routed to the voice box of given extension."},
280 "partyline",&EndpointAppPBX::action_init_partyline, NULL, &EndpointAppPBX::action_hangup_call,
281 PARAM_ROOM | PARAM_JINGLE,
282 "Caller is participating the conference with the given room number."},
284 "login", NULL, &EndpointAppPBX::action_dialing_login, NULL,
285 PARAM_CONNECT | PARAM_EXTENSION | PARAM_NOPASSWORD,
286 "Log into the given extension. Password required."},
288 "callerid", &EndpointAppPBX::action_init_change_callerid, &EndpointAppPBX::action_dialing_callerid, NULL,
289 PARAM_CONNECT | PARAM_CALLERID | PARAM_CALLERIDTYPE | PARAM_PRESENT,
290 "Caller changes the caller ID for all calls."},
291 { ACTION_CALLERIDNEXT,
292 "calleridnext",&EndpointAppPBX::action_init_change_callerid, &EndpointAppPBX::action_dialing_calleridnext, NULL,
293 PARAM_CONNECT | PARAM_CALLERID | PARAM_CALLERIDTYPE | PARAM_PRESENT,
294 "Caller changes the caller ID for the next call."},
296 "forward", &EndpointAppPBX::action_init_change_forward, &EndpointAppPBX::action_dialing_forward, NULL,
297 PARAM_CONNECT | PARAM_DIVERSION | PARAM_DEST | PARAM_DELAY,
298 "Caller changes the diversion of given type to the given destination or voice box."},
300 "redial", &EndpointAppPBX::action_init_redial_reply, &EndpointAppPBX::action_dialing_redial, NULL,
301 PARAM_CONNECT | PARAM_SELECT,
302 "Caller redials. (last outgoing call(s))"},
304 "reply", &EndpointAppPBX::action_init_redial_reply, &EndpointAppPBX::action_dialing_reply, NULL,
305 PARAM_CONNECT | PARAM_SELECT,
306 "Caller replies. (last incoming call(s))"},
308 "powerdial", NULL, &EndpointAppPBX::action_dialing_powerdial, NULL,
309 PARAM_CONNECT | PARAM_DELAY | PARAM_LIMIT | PARAM_TIMEOUT,
310 "Caller redials using powerdialing."},
312 "callback", NULL, &EndpointAppPBX::action_dialing_callback, &EndpointAppPBX::action_hangup_callback,
313 PARAM_PROCEEDING | PARAM_ALERTING | PARAM_CONNECT | PARAM_EXTENSION | PARAM_DELAY | PARAM_CALLTO | PARAM_PREFIX,
314 "Caller will use the callback service. After disconnecting, the callback is triggered."},
316 "abbrev", NULL, &EndpointAppPBX::action_dialing_abbrev, NULL,
318 "Caller dials abbreviation."},
320 "test", NULL, &EndpointAppPBX::action_dialing_test, NULL,
321 PARAM_CONNECT | PARAM_PREFIX | PARAM_TIMEOUT,
322 "Caller dials test mode."},
324 "play", &EndpointAppPBX::action_init_play, NULL, NULL,
325 PARAM_PROCEEDING | PARAM_ALERTING | PARAM_CONNECT | PARAM_SAMPLE | PARAM_TIMEOUT,
326 "Plays the given sample."},
328 "vbox-play", &EndpointAppPBX::action_init_vbox_play, &EndpointAppPBX::action_dialing_vbox_play, NULL,
330 "Caller listens to her voice box or to given extension."},
332 "calculator", NULL, &EndpointAppPBX::action_dialing_calculator, NULL,
334 "Caller calls the calculator."},
336 "timer", NULL, &EndpointAppPBX::action_dialing_timer, NULL,
337 PARAM_CONNECT | PARAM_TPRESET | PARAM_TIMEOUT,
339 // "Caller calls the timer."},
341 "goto", NULL, &EndpointAppPBX::action_dialing_goto, NULL,
342 PARAM_PROCEEDING | PARAM_ALERTING | PARAM_CONNECT | PARAM_RULESET | PARAM_STRIP | PARAM_SAMPLE,
343 "Jump to given ruleset and optionally play sample. Dialed digits are not flushed."},
345 "menu", NULL, &EndpointAppPBX::action_dialing_menu, NULL,
346 PARAM_CONNECT | PARAM_RULESET | PARAM_SAMPLE,
347 "Same as 'goto', but flushes all digits dialed so far."},
349 "disconnect", NULL, &EndpointAppPBX::action_dialing_disconnect, NULL,
350 PARAM_CONNECT | PARAM_CAUSE | PARAM_LOCATION | PARAM_SAMPLE | PARAM_DISPLAY,
351 "Caller gets disconnected optionally with given cause and given sample and given display text."},
353 "release", NULL, &EndpointAppPBX::action_dialing_release, NULL,
354 PARAM_CONNECT | PARAM_CAUSE | PARAM_LOCATION | PARAM_DISPLAY,
355 "Same as 'disconnect', but using RELEASE message on ISDN."},
357 "deflect", NULL, &EndpointAppPBX::action_dialing_deflect, NULL,
360 // "External call is deflected to the given destination within the telephone network."},
362 "setforward", NULL, &EndpointAppPBX::action_dialing_setforward, NULL,
363 PARAM_CONNECT | PARAM_DIVERSION | PARAM_DEST | PARAM_PORT,
365 // "The call forward is set within the telephone network of the external line."},
367 "execute", &EndpointAppPBX::action_init_execute, NULL, &EndpointAppPBX::action_hangup_execute,
368 PARAM_CONNECT | PARAM_EXECUTE | PARAM_PARAM | PARAM_ON | PARAM_TIMEOUT,
369 "Executes the given script file. The file must terminate quickly, because it will halt the PBX."},
371 "file", NULL, NULL, &EndpointAppPBX::action_hangup_file,
372 PARAM_CONNECT | PARAM_FILE | PARAM_CONTENT | PARAM_APPEND,
373 "Writes givent content to given file. If content is not given, the dialed digits are written."},
375 "pick", &EndpointAppPBX::action_init_pick, NULL, NULL,
377 "Pick up a call that is ringing on any phone. Extensions may be given to limit the picking ability."},
379 "password", NULL, &EndpointAppPBX::action_dialing_password, NULL,
382 { ACTION_PASSWORD_WRITE,
383 "password_wr",NULL, &EndpointAppPBX::action_dialing_password_wr, NULL,
387 "nothing", NULL, NULL, NULL,
388 PARAM_PROCEEDING | PARAM_ALERTING | PARAM_CONNECT | PARAM_TIMEOUT,
389 "does nothing. Usefull to wait for calls to be released completely, by giving timeout value."},
391 "efi", &EndpointAppPBX::action_init_efi, NULL, NULL,
392 PARAM_PROCEEDING | PARAM_ALERTING | PARAM_CONNECT,
393 "Elektronische Fernsprecher Identifikation - announces caller ID."},
394 { ACTION_POTS_RETRIEVE,
395 "pots-retrieve", &EndpointAppPBX::action_init_pots_retrieve, NULL, NULL,
397 "When using POTS: Select call on hold to retrieve."},
398 { ACTION_POTS_RELEASE,
399 "pots-release", &EndpointAppPBX::action_init_pots_release, NULL, NULL,
401 "When using POTS: Select call on hold to release."},
402 { ACTION_POTS_REJECT,
403 "pots-reject", &EndpointAppPBX::action_init_pots_reject, NULL, NULL,
405 "When using POTS: Reject incomming waiting call."},
406 { ACTION_POTS_ANSWER,
407 "pots-answer", &EndpointAppPBX::action_init_pots_answer, NULL, NULL,
409 "When using POTS: Answer incomming waiting call."},
411 "pots-3pty", &EndpointAppPBX::action_init_pots_3pty, NULL, NULL,
413 "When using POTS: Invoke 3PTY call of two calls on hold"},
414 { ACTION_POTS_TRANSFER,
415 "pots-transfer", &EndpointAppPBX::action_init_pots_transfer, NULL, NULL,
417 "When using POTS: Interconnect two calls on hold"},
419 NULL, NULL, NULL, NULL, 0, NULL}
423 /* display documentation of rules */
425 void doc_rules(const char *name)
431 while(action_defs[i].name) {
432 if (!strcasecmp(action_defs[i].name, name))
436 if (!action_defs[i].name) {
437 fprintf(stderr, "Given action '%s' unknown.\n", name);
440 name = action_defs[i].name;
443 printf("Syntax overview:\n");
444 printf("----------------\n\n");
445 printf("[ruleset]\n");
446 printf("<condition> ... : <action> [parameter ...] [timeout=X : <action> ...]\n");
448 printf("Please refer to the documentation for description on rule format.\n\n");
451 printf("Available conditions to match:\n");
452 printf("------------------------------\n\n");
454 while(cond_defs[i].name) {
455 printf("Usage: %s\n", cond_defs[i].doc);
456 printf("%s\n\n", cond_defs[i].help);
460 printf("Available actions with their parameters:\n");
461 printf("----------------------------------------\n\n");
463 printf("Detailes parameter description of action:\n");
464 printf("-----------------------------------------\n\n");
467 while(action_defs[i].name) {
468 if (name && !!strcmp(action_defs[i].name,name)) { /* not selected */
472 if (!action_defs[i].help) { /* not internal actions */
476 printf("Usage: %s", action_defs[i].name);
479 if ((1LL<<j) & action_defs[i].params)
480 printf(" [%s]", param_defs[j].doc);
483 printf("\n%s\n\n", action_defs[i].help);
484 if (name) { /* only show parameter help for specific action */
487 if ((1LL<<j) & action_defs[i].params)
488 printf("%s:\n\t%s\n", param_defs[j].doc, param_defs[j].help);
497 void ruleset_free(struct route_ruleset *ruleset_start)
499 struct route_ruleset *ruleset;
500 struct route_rule *rule;
501 struct route_cond *cond;
502 struct route_action *action;
503 struct route_param *param;
505 while(ruleset_start) {
506 ruleset = ruleset_start;
507 ruleset_start = ruleset->next;
508 while(ruleset->rule_first) {
509 rule = ruleset->rule_first;
510 ruleset->rule_first = rule->next;
511 while(rule->cond_first) {
512 cond = rule->cond_first;
513 if (cond->string_value) {
514 FREE(cond->string_value, 0);
517 if (cond->string_value_to) {
518 FREE(cond->string_value_to, 0);
521 rule->cond_first = cond->next;
522 FREE(cond, sizeof(struct route_cond));
525 while(rule->action_first) {
526 action = rule->action_first;
527 rule->action_first = action->next;
528 while(action->param_first) {
529 param = action->param_first;
530 action->param_first = param->next;
531 if (param->string_value) {
532 FREE(param->string_value, 0);
535 FREE(param, sizeof(struct route_param));
538 FREE(action, sizeof(struct route_action));
541 FREE(rule, sizeof(struct route_rule));
544 FREE(ruleset, sizeof(struct route_ruleset));
549 void ruleset_debug(struct route_ruleset *ruleset_start)
551 struct route_ruleset *ruleset;
552 struct route_rule *rule;
553 struct route_cond *cond;
554 struct route_action *action;
555 struct route_param *param;
558 ruleset = ruleset_start;
560 printf("Ruleset: '%s'\n", ruleset->name);
561 rule = ruleset->rule_first;
565 cond = rule->cond_first;
568 printf(" Condition:");
572 printf(" %s", cond_defs[cond->index].name);
573 if (cond->value_type != VALUE_TYPE_NULL)
576 switch(cond->value_type) {
577 case VALUE_TYPE_NULL:
580 case VALUE_TYPE_INTEGER:
581 printf("%d", cond->integer_value);
584 case VALUE_TYPE_INTEGER_RANGE:
585 printf("%d-%d", cond->integer_value, cond->integer_value_to);
588 case VALUE_TYPE_STRING:
589 printf("'%s'", cond->string_value);
592 case VALUE_TYPE_STRING_RANGE:
593 printf("'%s'-'%s'", cond->string_value, cond->string_value_to);
597 printf("Software error: VALUE_TYPE_* %d not known in function '%s' line=%d", cond->value_type, __FUNCTION__, __LINE__);
599 if (cond->value_extension && cond->next) {
602 goto next_cond_value;
610 action = rule->action_first;
612 printf(" Action: %s\n", action_defs[action->index].name);
615 param = action->param_first;
622 printf(" %s", param_defs[param->index].name);
623 if (param->value_type != VALUE_TYPE_NULL)
625 switch(param->value_type) {
626 case VALUE_TYPE_NULL:
629 case VALUE_TYPE_INTEGER:
630 if (param_defs[param->index].type == PARAM_TYPE_CALLERIDTYPE) {
631 switch(param->integer_value) {
632 case INFO_NTYPE_UNKNOWN:
635 case INFO_NTYPE_SUBSCRIBER:
636 printf("subscriber");
638 case INFO_NTYPE_NATIONAL:
641 case INFO_NTYPE_INTERNATIONAL:
642 printf("international");
645 printf("unknown(%d)", param->integer_value);
649 if (param_defs[param->index].type == PARAM_TYPE_CAPABILITY) {
650 switch(param->integer_value) {
660 case INFO_BC_DATARESTRICTED:
661 printf("digital-restricted");
663 case INFO_BC_DATAUNRESTRICTED:
664 printf("digital-unrestricted");
666 case INFO_BC_DATAUNRESTRICTED_TONES:
667 printf("digital-unrestricted-tones");
670 printf("unknown(%d)", param->integer_value);
674 if (param_defs[param->index].type == PARAM_TYPE_DIVERSION) {
675 switch(param->integer_value) {
676 case INFO_DIVERSION_CFU:
679 case INFO_DIVERSION_CFNR:
682 case INFO_DIVERSION_CFB:
685 case INFO_DIVERSION_CFP:
689 printf("unknown(%d)", param->integer_value);
693 if (param_defs[param->index].type == PARAM_TYPE_TYPE) {
694 switch(param->integer_value) {
695 case INFO_NTYPE_UNKNOWN:
698 case INFO_NTYPE_SUBSCRIBER:
699 printf("subscriber");
701 case INFO_NTYPE_NATIONAL:
704 case INFO_NTYPE_INTERNATIONAL:
705 printf("international");
708 printf("unknown(%d)", param->integer_value);
712 if (param_defs[param->index].type == PARAM_TYPE_YESNO) {
713 switch(param->integer_value) {
721 printf("unknown(%d)", param->integer_value);
725 if (param_defs[param->index].type == PARAM_TYPE_NULL) {
728 printf("%d", param->integer_value);
731 case VALUE_TYPE_STRING:
732 printf("'%s'", param->string_value);
736 printf("Software error: VALUE_TYPE_* %d not known in function '%s' line=%d", param->value_type, __FUNCTION__, __LINE__);
743 printf(" Timeout: %d\n", action->timeout);
744 action = action->next;
750 ruleset = ruleset->next;
758 static char *read_string(char *p, char *key, int key_size, const char *special)
777 if (--key_size == 0) {
778 UPRINT(key, "\001String too long.");
783 UPRINT(key, "\001Unexpected end of line inside quotes.");
789 if (strchr(special, *p)) {
796 UPRINT(key, "\001Unexpected end of line.");
800 if (--key_size == 0) {
801 UPRINT(key, "\001String too long.");
809 char ruleset_error[256];
810 struct route_ruleset *ruleset_parse(void)
815 unsigned long long j;
819 FILE *fp[MAXNESTING];
820 char filename[MAXNESTING][256];
821 int line[MAXNESTING];
828 int expecting = 1; /* 1 = expecting ruleset */
832 integer_to; /* condition index, .. */
833 struct route_ruleset *ruleset_start = NULL, *ruleset;
834 struct route_ruleset **ruleset_pointer = &ruleset_start;
835 struct route_rule *rule;
836 struct route_rule **rule_pointer = NULL;
837 struct route_cond *cond;
838 struct route_cond **cond_pointer = NULL;
839 struct route_action *action;
840 struct route_action **action_pointer = NULL;
841 struct route_param *param;
842 struct route_param **param_pointer = NULL;
844 unsigned long long allowed_params;
846 /* check the integrity of IDs for ACTION_* and PARAM_* */
848 while(action_defs[i].name) {
849 if (action_defs[i].id != i) {
850 PERROR("Software Error action '%s' must have id of %d, but has %d.\n",
851 action_defs[i].name, i, action_defs[i].id);
857 while(param_defs[i].name) {
858 if (param_defs[i].id != j) {
859 PERROR("Software Error param '%s' must have id of 0x%llx, but has 0x%llx.\n",
860 param_defs[i].name, j, param_defs[i].id);
867 SPRINT(filename[0], "%s/routing.conf", CONFIG_DATA);
869 if (!(fp[0]=fopen(filename[0],"r")))
871 PERROR("Cannot open %s\n",filename[0]);
880 while((GETLINE(buffer, fp[nesting])))
893 /* skip spaces, if any */
904 /* don't skip "define" */
905 if (!!strncmp(p, "define", 6))
917 p = read_string(p, key, sizeof(key), " ");
918 if (key[0] == 1) { /* error */
919 SPRINT(failure, "Parsing Filename failed: %s", key+1);
922 if (nesting == MAXNESTING-1) {
923 SPRINT(failure, "'include' is nesting too deep.\n");
927 SCPY(filename[nesting+1], key);
929 SPRINT(filename[nesting+1], "%s/%s", CONFIG_DATA, key);
930 if (!(fp[nesting+1]=fopen(filename[nesting+1],"r"))) {
931 PERROR("Cannot open %s\n", filename[nesting+1]);
938 if (*p == '/') if (p[1] == '/')
941 /* skip empty lines */
945 /* expecting ruleset */
950 SPRINT(failure, "Expecting ruleset name starting with '['.");
955 /* reading ruleset name text */
957 while(*p>' ' && *p<127 && *p!=']') {
958 if (*p>='A' && *p<='Z') *p = *p-'A'+'a'; /* lower case */
960 if (i == sizeof(key)) i--; /* limit */
963 if (key[0] == '\0') {
964 SPRINT(failure, "Missing ruleset name after '['.");
968 /* expecting ] and nothing more */
970 SPRINT(failure, "Expecting ']' after ruleset name.");
975 SPRINT(failure, "Unexpected character after ruleset name.");
979 /* check for duplicate rulesets */
980 ruleset = ruleset_start;
982 if (!strcmp(ruleset->name, key)) {
983 SPRINT(failure, "Duplicate ruleset '%s', already defined in file '%s' line %d.", key, ruleset->file, ruleset->line);
986 ruleset = ruleset->next;
990 ruleset = (struct route_ruleset *)MALLOC(sizeof(struct route_ruleset));
992 *ruleset_pointer = ruleset;
993 ruleset_pointer = &(ruleset->next);
994 SCPY(ruleset->name, key);
995 SCPY(ruleset->file, filename[nesting]);
996 ruleset->line = line[nesting];
997 rule_pointer = &(ruleset->rule_first);
1002 /* for new ruleset [ */
1007 /* Alloc memory for rule */
1008 rule = (struct route_rule *)MALLOC(sizeof(struct route_rule));
1010 *rule_pointer = rule;
1011 rule_pointer = &(rule->next);
1012 cond_pointer = &(rule->cond_first);
1013 action_pointer = &(rule->action_first);
1014 SCPY(rule->file, filename[nesting]);
1015 rule->line = line[nesting];
1017 /* loop CONDITIONS */
1018 while(*p!=':' && *p!='\0') {
1019 /* read item text */
1021 while((*p>='a' && *p<='z') || (*p>='A' && *p<='Z') || (*p>='0' && *p<='9') || *p == '-') {
1022 if (*p>='A' && *p<='Z') *p = *p-'A'+'a'; /* lower case */
1024 if (i == sizeof(key)) i--; /* limit */
1027 if (key[0] == '\0') {
1028 SPRINT(failure, "Expecting condition item name or ':' for end of condition list.");
1031 if (*p!=' ' && *p!='=') {
1032 SPRINT(failure, "Illegal character '%c' after condition name '%s'. Expecting '=' for equation or ' ' to seperate condition items.", *p, key);
1036 /* check if condition exists */
1038 while(cond_defs[index].name) {
1039 if (!strcmp(cond_defs[index].name, key))
1043 if (cond_defs[index].name == NULL) {
1044 SPRINT(failure, "Unknown condition item name '%s'.", key);
1048 /* items without values must not have any parameter */
1049 if (cond_defs[index].type == COND_TYPE_NULL) {
1051 SPRINT(failure, "Condition item '%s' must not have any value. Don't use '=' for this type of condition.", key);
1055 SPRINT(failure, "Condition item '%s' must not have any value. Expecting ' ' or tab after item name.", key);
1061 SPRINT(failure, "Condition item '%s' must have at least one value, '=' expected, and not a space.", key);
1065 SPRINT(failure, "Condition item '%s' must have at least one value, '=' expected.", key);
1071 /* check for duplicate condition */
1072 cond = rule->cond_first;
1074 if (cond->index == index) {
1075 SPRINT(failure, "Duplicate condition '%s', use ',' to give multiple values.", key);
1082 /* Alloc memory for item */
1083 cond = (struct route_cond *)MALLOC(sizeof(struct route_cond));
1085 *cond_pointer = cond;
1086 cond_pointer = &(cond->next);
1087 cond->index = index;
1088 cond->match = cond_defs[index].match;
1089 switch(cond_defs[index].type) {
1090 case COND_TYPE_NULL:
1092 SPRINT(failure, "Expecting no value.");
1095 value_type = VALUE_TYPE_NULL;
1098 /* parse all integer values/ranges */
1099 case COND_TYPE_INTEGER:
1100 case COND_TYPE_TIME:
1101 case COND_TYPE_MDAY:
1102 case COND_TYPE_MONTH:
1103 case COND_TYPE_WDAY:
1104 case COND_TYPE_YEAR:
1105 integer = integer_to = 0;
1106 if (*p==',' || *p==' ' || *p=='\0') {
1107 SPRINT(failure, "Missing integer value.");
1110 while(*p>='0' && *p<='9') {
1111 integer = integer*10 + *p-'0';
1114 value_type = VALUE_TYPE_INTEGER;
1117 if (*p==',' || *p==' ' || *p=='\0') {
1118 SPRINT(failure, "Missing integer value.");
1121 while(*p>='0' && *p<='9') {
1122 integer_to = integer_to*10 + *p-'0';
1125 value_type = VALUE_TYPE_INTEGER_RANGE;
1127 if (cond_defs[index].type == COND_TYPE_TIME) {
1128 // Simon: i store the time as decimal, later i compare it correctly:
1129 // hours * 100 + minutes
1130 if (integer == 2400)
1132 if (integer >= 2400) {
1134 SPRINT(failure, "Given time '%d' not in range 0000..2359 (or 2400 for 0000)", integer);
1137 if (integer%100 >= 60)
1138 goto timeoutofrange1;
1139 if (value_type == VALUE_TYPE_INTEGER)
1141 if (integer_to == 2400)
1143 if (integer_to >= 2400) {
1145 SPRINT(failure, "Given time '%d' not in range 0000..2359 (or 2400 for 0000)", integer_to);
1148 if (integer_to%100 >= 60)
1149 goto timeoutofrange2;
1151 if (cond_defs[index].type == COND_TYPE_MDAY) {
1152 if (integer<1 || integer>31) {
1153 SPRINT(failure, "Given day-of-month '%d' not in range 1..31", integer);
1156 if (value_type == VALUE_TYPE_INTEGER)
1158 if (integer_to<1 || integer_to>31) {
1159 SPRINT(failure, "Given day-of-month '%d' not in range 1..31", integer_to);
1163 if (cond_defs[index].type == COND_TYPE_WDAY) {
1164 if (integer<1 || integer>7) {
1165 SPRINT(failure, "Given day-of-week '%d' not in range 1..7", integer);
1168 if (value_type == VALUE_TYPE_INTEGER)
1170 if (integer_to<1 || integer_to>7) {
1171 SPRINT(failure, "Given day-of-week '%d' not in range 1..7", integer_to);
1175 if (cond_defs[index].type == COND_TYPE_MONTH) {
1176 if (integer<1 || integer>12) {
1177 SPRINT(failure, "Given month '%d' not in range 1..12", integer);
1180 if (value_type == VALUE_TYPE_INTEGER)
1182 if (integer_to<1 || integer_to>12) {
1183 SPRINT(failure, "Given month '%d' not in range 1..12", integer_to);
1187 if (cond_defs[index].type == COND_TYPE_YEAR) {
1188 if (integer<1970 || integer>2106) {
1189 SPRINT(failure, "Given year '%d' not in range 1970..2106", integer);
1192 if (value_type == VALUE_TYPE_INTEGER)
1194 if (integer_to<1970 || integer_to>2106) {
1195 SPRINT(failure, "Given year '%d' not in range 1970..2106", integer_to);
1200 cond->integer_value = integer;
1201 cond->integer_value_to = integer_to;
1202 cond->value_type = value_type;
1205 /* parse all string values/ranges */
1206 case COND_TYPE_STRING:
1207 key[0] = key_to[0] = '\0';
1208 if (*p==',' || *p==' ' || *p=='\0') {
1209 SPRINT(failure, "Missing string value, use \"\" for empty string.");
1212 p = read_string(p, key, sizeof(key), "-, ");
1213 if (key[0] == 1) { /* error */
1214 SPRINT(failure, "Parsing String failed: %s", key+1);
1217 value_type = VALUE_TYPE_STRING;
1220 if (*p==',' || *p==' ' || *p=='\0') {
1221 SPRINT(failure, "Missing string value, use \"\" for empty string.");
1224 p = read_string(p, key_to, sizeof(key_to), "-, ");
1225 if (key_to[0] == 1) { /* error */
1226 SPRINT(failure, "Parsing string failed: %s", key_to+1);
1229 value_type = VALUE_TYPE_STRING_RANGE;
1230 if (strlen(key) != strlen(key_to)) {
1231 SPRINT(failure, "Given range of strings \"%s\"-\"%s\" have unequal length.", key, key_to);
1234 if (key[0] == '\0') {
1235 SPRINT(failure, "Given range has no length.");
1240 cond->string_value = (char *)MALLOC(strlen(key)+1);
1242 UCPY(cond->string_value, key);
1243 if (value_type == VALUE_TYPE_STRING_RANGE) {
1244 cond->string_value_to = (char *)MALLOC(strlen(key_to)+1);
1246 UCPY(cond->string_value_to, key_to);
1247 cond->comp_string = strcmp(key, key_to);
1249 cond->value_type = value_type;
1252 /* parse service value */
1253 case COND_TYPE_CAPABILITY:
1254 if (!strncasecmp("speech", p, 6)) {
1255 cond->integer_value = INFO_BC_SPEECH;
1257 } else if (!strncasecmp("audio", p, 5)) {
1258 cond->integer_value = INFO_BC_AUDIO;
1260 } else if (!strncasecmp("video", p, 5)) {
1261 cond->integer_value = INFO_BC_VIDEO;
1263 } else if (!strncasecmp("digital-restricted", p, 18)) {
1264 cond->integer_value = INFO_BC_DATARESTRICTED;
1266 } else if (!strncasecmp("digital-unrestricted", p, 20)) {
1267 cond->integer_value = INFO_BC_DATAUNRESTRICTED;
1269 } else if (!strncasecmp("digital-unrestricted-tones", p, 26)) {
1270 cond->integer_value = INFO_BC_DATAUNRESTRICTED_TONES;
1273 SPRINT(failure, "Given service type is invalid or misspelled.");
1276 cond->value_type = VALUE_TYPE_INTEGER;
1279 /* parse bmode value */
1280 case COND_TYPE_BMODE:
1281 if (!strncasecmp("transparent", p, 11)) {
1282 cond->integer_value = INFO_BMODE_CIRCUIT;
1284 } else if (!strncasecmp("hdlc", p, 4)) {
1285 cond->integer_value = INFO_BMODE_PACKET;
1288 SPRINT(failure, "Given bchannel mode is invalid or misspelled.");
1291 cond->value_type = VALUE_TYPE_INTEGER;
1294 /* parse service value */
1296 if (!strncasecmp("telephony", p, 9)) {
1297 cond->integer_value = INFO_HLC_TELEPHONY;
1299 } else if (!strncasecmp("faxg2g3", p, 7)) {
1300 cond->integer_value = INFO_HLC_FAXG2G3;
1302 } else if (!strncasecmp("faxg4", p, 5)) {
1303 cond->integer_value = INFO_HLC_FAXG4;
1305 } else if (!strncasecmp("teletex1", p, 8)) {
1306 cond->integer_value = INFO_HLC_TELETEX1;
1308 } else if (!strncasecmp("teletex2", p, 8)) {
1309 cond->integer_value = INFO_HLC_TELETEX2;
1311 } else if (!strncasecmp("teletex3", p, 8)) {
1312 cond->integer_value = INFO_HLC_TELETEX3;
1314 } else if (!strncasecmp("videotex1", p, 9)) {
1315 cond->integer_value = INFO_HLC_VIDEOTEX1;
1317 } else if (!strncasecmp("videotex2", p, 9)) {
1318 cond->integer_value = INFO_HLC_VIDEOTEX2;
1320 } else if (!strncasecmp("telex", p, 5)) {
1321 cond->integer_value = INFO_HLC_TELEX;
1323 } else if (!strncasecmp("mhs", p, 3)) {
1324 cond->integer_value = INFO_HLC_MHS;
1326 } else if (!strncasecmp("osi", p, 3)) {
1327 cond->integer_value = INFO_HLC_OSI;
1329 } else if (!strncasecmp("maintenance", p, 11)) {
1330 cond->integer_value = INFO_HLC_MAINTENANCE;
1332 } else if (!strncasecmp("management", p, 10)) {
1333 cond->integer_value = INFO_HLC_MANAGEMENT;
1335 } else if (!strncasecmp("audiovisual", p, 11)) {
1336 cond->integer_value = INFO_HLC_AUDIOVISUAL;
1339 SPRINT(failure, "Given HLC type is invalid or misspelled.");
1342 cond->value_type = VALUE_TYPE_INTEGER;
1345 /* parse interface attribute <if>:<value> */
1346 case COND_TYPE_IFATTR:
1347 key[0] = key_to[0] = '\0';
1348 if (*p==':' || *p==',' || *p==' ' || *p=='\0') {
1349 SPRINT(failure, "Missing interface name.");
1352 p = read_string(p, key, sizeof(key), ":-, ");
1353 if (key[0] == 1) { /* error */
1354 SPRINT(failure, "Parsing interface failed: %s", key+1);
1358 SPRINT(failure, "Expeciting kolon to seperate value behind interface name.");
1362 while(*p>='0' && *p<='9') {
1365 if (*p!=',' && *p!=' ' && *p!='\0') {
1366 SPRINT(failure, "Invalid characters behind value.");
1369 value_type = VALUE_TYPE_STRING;
1374 SPRINT(failure, "Software error: COND_TYPE_* %d not parsed in function '%s'", cond_defs[index].type, __FUNCTION__);
1377 /* if we have another value for that item, we attach it */
1381 cond->value_extension = 1;
1384 /* to seperate the items, a space is required */
1386 SPRINT(failure, "Character '%c' not expected here. Use ',' to seperate multiple possible values.", *p);
1398 /* we are done with CONDITIONS, so we expect the ACTION */
1400 SPRINT(failure, "Expecting ':' after condition item(s).");
1406 /* skip spaces, if any */
1414 /* read action name */
1416 while((*p>='a' && *p<='z') || (*p>='A' && *p<='Z') || (*p>='0' && *p<='9') || *p == '-') {
1417 if (*p>='A' && *p<='Z') *p = *p-'A'+'a'; /* lower case */
1419 if (i == sizeof(key)) i--; /* limit */
1422 if (key[0] == '\0') {
1423 SPRINT(failure, "Expecting action name.");
1427 /* check if item exists */
1429 while(action_defs[index].name) {
1430 if (!action_defs[index].help) { /* not internal actions */
1434 if (!strcmp(action_defs[index].name, key))
1438 if (action_defs[index].name == NULL) {
1439 SPRINT(failure, "Unknown action name '%s'.", key);
1442 allowed_params = action_defs[index].params;
1444 /* alloc memory for action */
1445 action = (struct route_action *)MALLOC(sizeof(struct route_action));
1447 *action_pointer = action;
1448 action_pointer = &(action->next);
1449 param_pointer = &(action->param_first);
1450 action->index = index;
1451 action->line = line[nesting];
1453 /* skip spaces after param name */
1463 /* read param text */
1465 while((*p>='a' && *p<='z') || (*p>='A' && *p<='Z') || (*p>='0' && *p<='9') || *p == '-') {
1466 if (*p>='A' && *p<='Z') *p = *p-'A'+'a'; /* lower case */
1468 if (i == sizeof(key)) i--; /* limit */
1475 if (key[0] == '\0') {
1476 SPRINT(failure, "Expecting parameter name.");
1480 /* check if item exists */
1482 while(param_defs[index].name) {
1483 if (!strcmp(param_defs[index].name, key))
1487 if (param_defs[index].name == NULL) {
1488 SPRINT(failure, "Unknown param name '%s'.", key);
1492 /* check if item is allowed for the action */
1493 if (!(param_defs[index].id & allowed_params)) {
1494 SPRINT(failure, "Param name '%s' exists, but not for this action.", key);
1498 /* params without values must not have any parameter */
1499 if (param_defs[index].type == PARAM_TYPE_NULL) {
1500 if (*p!=' ' && *p!='\0') {
1501 SPRINT(failure, "Parameter '%s' must not have any value.", key);
1506 SPRINT(failure, "Parameter '%s' must have at least one value, '=' expected and not a space.", key);
1510 SPRINT(failure, "Parameter '%s' must have at least one value, '=' expected.", key);
1516 /* special timeout value */
1517 if (!strcmp("timeout", key)) {
1518 if (action->timeout) {
1519 SPRINT(failure, "Duplicate timeout value.");
1522 if (*p==',' || *p==' ' || *p=='\0') {
1523 SPRINT(failure, "Missing integer value.");
1527 while(*p>='0' && *p<='9') {
1528 integer = integer*10 + *p-'0';
1532 SPRINT(failure, "Expecting timeout value greater 0.");
1535 if (*p!=' ' && *p!='\0') {
1536 SPRINT(failure, "Character '%c' not expected here. Use ' ' to seperate parameters.", *p);
1545 action->timeout = integer;
1546 /* check for next ACTION */
1554 /* check for duplicate parameters */
1555 param = action->param_first;
1557 if (param->index == index) {
1558 SPRINT(failure, "Duplicate parameter '%s', use ',' to give multiple values.", key);
1561 param = param->next;
1565 /* Alloc memory for param */
1566 param = (struct route_param *)MALLOC(sizeof(struct route_param));
1568 *param_pointer = param;
1569 param_pointer = &(param->next);
1570 param->index = index;
1571 param->id = param_defs[index].id;
1573 switch(param_defs[index].type) {
1574 /* parse null value */
1575 case PARAM_TYPE_NULL:
1576 param->value_type = VALUE_TYPE_NULL;
1579 /* parse integer value */
1580 case PARAM_TYPE_INTEGER:
1582 if (*p==',' || *p==' ' || *p=='\0') {
1583 SPRINT(failure, "Missing integer value.");
1586 while(*p>='0' && *p<='9') {
1587 integer = integer*10 + *p-'0';
1590 param->integer_value = integer;
1591 param->value_type = VALUE_TYPE_INTEGER;
1595 /* parse ports value */
1596 case PARAM_TYPE_PORTS:
1598 if (*p==',' || *p==' ' || *p=='\0') {
1599 SPRINT(failure, "Missing port number, omit parameter or give port number.");
1605 while(*p>='0' && *p<='9') {
1606 if (i < (int)sizeof(key)-1) {
1610 integer = integer*10 + *p-'0';
1613 if (integer > 255) {
1614 SPRINT(failure, "Port number too high.");
1618 if (i < (int)sizeof(key)-1) {
1628 /* parse string value */
1629 case PARAM_TYPE_STRING:
1630 case PARAM_TYPE_CALLERIDTYPE:
1631 case PARAM_TYPE_CAPABILITY:
1632 case PARAM_TYPE_BMODE:
1633 case PARAM_TYPE_HLC:
1634 case PARAM_TYPE_DIVERSION:
1635 case PARAM_TYPE_DESTIN:
1636 case PARAM_TYPE_TYPE:
1637 case PARAM_TYPE_YESNO:
1640 if (*p==',' || *p==' ' || *p=='\0') {
1641 SPRINT(failure, "Missing string value, use \"\" for empty string.");
1644 p = read_string(p, key, sizeof(key), " ");
1645 if (key[0] == 1) { /* error */
1646 SPRINT(failure, "Parsing string failed: %s", key+1);
1649 if (param_defs[index].type == PARAM_TYPE_CALLERIDTYPE) {
1650 param->value_type = VALUE_TYPE_INTEGER;
1651 if (!strcasecmp(key, "unknown")) {
1652 param->integer_value = INFO_NTYPE_UNKNOWN;
1655 if (!strcasecmp(key, "subscriber")) {
1656 param->integer_value = INFO_NTYPE_SUBSCRIBER;
1659 if (!strcasecmp(key, "national")) {
1660 param->integer_value = INFO_NTYPE_NATIONAL;
1663 if (!strcasecmp(key, "international")) {
1664 param->integer_value = INFO_NTYPE_INTERNATIONAL;
1667 SPRINT(failure, "Caller ID type '%s' unknown.", key);
1670 if (param_defs[index].type == PARAM_TYPE_ON) {
1671 param->value_type = VALUE_TYPE_INTEGER;
1672 if (!strcasecmp(key, "init")) {
1673 param->integer_value = INFO_ON_INIT;
1676 if (!strcasecmp(key, "hangup")) {
1677 param->integer_value = INFO_ON_HANGUP;
1680 SPRINT(failure, "Execute on '%s' unknown.", key);
1683 if (param_defs[index].type == PARAM_TYPE_CAPABILITY) {
1684 param->value_type = VALUE_TYPE_INTEGER;
1685 if (!strcasecmp(key, "speech")) {
1686 param->integer_value = INFO_BC_SPEECH;
1689 if (!strcasecmp(key, "audio")) {
1690 param->integer_value = INFO_BC_AUDIO;
1693 if (!strcasecmp(key, "video")) {
1694 param->integer_value = INFO_BC_VIDEO;
1697 if (!strcasecmp(key, "digital-restricted")) {
1698 param->integer_value = INFO_BC_DATARESTRICTED;
1701 if (!strcasecmp(key, "digital-unrestricted")) {
1702 param->integer_value = INFO_BC_DATAUNRESTRICTED;
1705 if (!strcasecmp(key, "digital-unrestricted-tones")) {
1706 param->integer_value = INFO_BC_DATAUNRESTRICTED_TONES;
1709 SPRINT(failure, "Service type '%s' unknown.", key);
1712 if (param_defs[index].type == PARAM_TYPE_BMODE) {
1713 param->value_type = VALUE_TYPE_INTEGER;
1714 if (!strcasecmp(key, "transparent")) {
1715 param->integer_value = INFO_BMODE_CIRCUIT;
1718 if (!strcasecmp(key, "hdlc")) {
1719 param->integer_value = INFO_BMODE_PACKET;
1722 SPRINT(failure, "Bchannel mode '%s' unknown.", key);
1725 if (param_defs[index].type == PARAM_TYPE_HLC) {
1726 param->value_type = VALUE_TYPE_INTEGER;
1727 if (!strcasecmp(key, "telephony")) {
1728 param->integer_value = INFO_HLC_TELEPHONY;
1731 if (!strcasecmp(key, "faxg2g3")) {
1732 param->integer_value = INFO_HLC_FAXG2G3;
1735 if (!strcasecmp(key, "faxg4")) {
1736 param->integer_value = INFO_HLC_FAXG4;
1739 if (!strcasecmp(key, "teletex1")) {
1740 param->integer_value = INFO_HLC_TELETEX1;
1743 if (!strcasecmp(key, "teletex2")) {
1744 param->integer_value = INFO_HLC_TELETEX2;
1747 if (!strcasecmp(key, "teletex3")) {
1748 param->integer_value = INFO_HLC_TELETEX3;
1751 if (!strcasecmp(key, "videotex1")) {
1752 param->integer_value = INFO_HLC_VIDEOTEX1;
1755 if (!strcasecmp(key, "videotex2")) {
1756 param->integer_value = INFO_HLC_VIDEOTEX2;
1759 if (!strcasecmp(key, "telex")) {
1760 param->integer_value = INFO_HLC_TELEX;
1763 if (!strcasecmp(key, "mhs")) {
1764 param->integer_value = INFO_HLC_MHS;
1767 if (!strcasecmp(key, "osi")) {
1768 param->integer_value = INFO_HLC_OSI;
1771 if (!strcasecmp(key, "maintenance")) {
1772 param->integer_value = INFO_HLC_MAINTENANCE;
1775 if (!strcasecmp(key, "management")) {
1776 param->integer_value = INFO_HLC_MANAGEMENT;
1779 if (!strcasecmp(key, "audiovisual")) {
1780 param->integer_value = INFO_HLC_AUDIOVISUAL;
1783 SPRINT(failure, "HLC type '%s' unknown.", key);
1786 if (param_defs[index].type == PARAM_TYPE_DIVERSION) {
1787 param->value_type = VALUE_TYPE_INTEGER;
1788 if (!strcasecmp(key, "cfu")) {
1789 param->integer_value = INFO_DIVERSION_CFU;
1792 if (!strcasecmp(key, "cfb")) {
1793 param->integer_value = INFO_DIVERSION_CFB;
1796 if (!strcasecmp(key, "cfnr")) {
1797 param->integer_value = INFO_DIVERSION_CFNR;
1800 if (!strcasecmp(key, "cfp")) {
1801 param->integer_value = INFO_DIVERSION_CFP;
1804 SPRINT(failure, "Diversion type '%s' unknown.", key);
1807 if (param_defs[index].type == PARAM_TYPE_TYPE) {
1808 param->value_type = VALUE_TYPE_INTEGER;
1809 if (!strcasecmp(key, "unknown")) {
1810 param->integer_value = INFO_NTYPE_UNKNOWN;
1813 if (!strcasecmp(key, "subscriber")) {
1814 param->integer_value = INFO_NTYPE_SUBSCRIBER;
1817 if (!strcasecmp(key, "national")) {
1818 param->integer_value = INFO_NTYPE_NATIONAL;
1821 if (!strcasecmp(key, "international")) {
1822 param->integer_value = INFO_NTYPE_INTERNATIONAL;
1825 SPRINT(failure, "Number type '%s' unknown.", key);
1828 if (param_defs[index].type == PARAM_TYPE_YESNO) {
1829 param->value_type = VALUE_TYPE_INTEGER;
1830 if (!strcasecmp(key, "yes")) {
1831 param->integer_value = 1;
1834 if (!strcasecmp(key, "no")) {
1835 param->integer_value = 0;
1838 SPRINT(failure, "Value '%s' unknown. ('yes' or 'no')", key);
1841 param->string_value = (char *)MALLOC(strlen(key)+1);
1843 UCPY(param->string_value, key);
1844 param->value_type = VALUE_TYPE_STRING;
1848 SPRINT(failure, "Software error: PARAM_TYPE_* %d not parsed in function '%s'", param_defs[index].type, __FUNCTION__);
1855 param->value_extension = 1;
1856 goto nextparamvalue;
1863 /* to seperate the items, a space is required */
1865 SPRINT(failure, "Character '%c' not expected here. Use ' ' to seperate parameters, or ',' for multiple values.", *p);
1876 /* check for next ACTION */
1884 fclose(fp[nesting--]);
1890 if (!ruleset_start) {
1891 SPRINT(failure, "No ruleset defined.");
1893 return(ruleset_start);
1896 printf("While parsing %s, an error occurred in line %d:\n", filename[nesting], line[nesting]);
1897 printf("-> %s\n", buffer);
1898 memset(pointer, ' ', sizeof(pointer));
1899 pointer[p-buffer] = '^';
1900 pointer[p-buffer+1] = '\0';
1901 printf(" %s\n", pointer);
1902 printf("%s\n", failure);
1903 SPRINT(ruleset_error, "Error in file %s, line %d: %s", filename[nesting], line[nesting], failure);
1906 while(nesting >= 0) {
1907 fclose(fp[nesting--]);
1911 ruleset_free(ruleset_start);
1916 * return ruleset by name
1918 struct route_ruleset *getrulesetbyname(const char *name)
1920 struct route_ruleset *ruleset = ruleset_first;
1923 if (!strcasecmp(name, ruleset->name)) {
1926 ruleset = ruleset->next;
1928 PDEBUG(DEBUG_ROUTE, "ruleset %s %s.\n", name, ruleset?"found":"not found");
1933 * parses the current ruleset and returns action
1935 struct route_action *EndpointAppPBX::route(struct route_ruleset *ruleset)
1938 couldmatch = 0, /* any rule could match */
1943 struct route_rule *rule = ruleset->rule_first;
1944 struct route_cond *cond;
1945 struct route_action *action = NULL;
1946 unsigned long comp_len;
1949 char *argv[11]; /* check also number of args below */
1950 char callerid[64], callerid2[64], redirid[64];
1954 long long timeout, now_ll = 0, match_timeout = 0;
1955 struct timeval current_time;
1957 struct mISDNport *mISDNport;
1962 class Pfxs *ourfxs, *fxs;
1966 struct admin_list *admin;
1971 /* reset timeout action */
1972 e_match_to_action = NULL;
1974 SCPY(callerid, numberrize_callerinfo(e_callerinfo.id, e_callerinfo.ntype, options.national, options.international));
1975 SCPY(callerid2, numberrize_callerinfo(e_callerinfo.id2, e_callerinfo.ntype2, options.national, options.international));
1976 SCPY(redirid, numberrize_callerinfo(e_redirinfo.id, e_redirinfo.ntype, options.national, options.international));
1978 PDEBUG(DEBUG_ROUTE, "parsing ruleset '%s'\n", ruleset->name);
1980 PDEBUG(DEBUG_ROUTE, "checking rule in line %d\n", rule->line);
1981 match = 1; /* this rule matches */
1982 dialing_required = 0;
1983 timeout = 0; /* timeout time */
1984 cond = rule->cond_first;
1986 condition = 0; /* any condition element is true (1) or could be true (2) */
1988 istrue = 0; /* this condition-element is true */
1989 couldbetrue = 0; /* this conditions-element could be true */
1990 switch(cond->match) {
1992 if (!e_ext.number[0])
1997 if (e_ext.number[0])
2003 if (ea_endpoint->ep_portlist)
2004 if ((ea_endpoint->ep_portlist->port_type & PORT_CLASS_MASK) != PORT_CLASS_mISDN)
2006 integer = e_callerinfo.isdn_port;
2011 case MATCH_INTERFACE:
2012 if (!e_callerinfo.interface[0])
2014 string = e_callerinfo.interface;
2017 case MATCH_CALLERID:
2019 goto match_string_prefix;
2021 case MATCH_CALLERID2:
2023 goto match_string_prefix;
2025 case MATCH_EXTENSION:
2026 string = e_ext.number;
2030 string = e_dialinginfo.id;
2031 goto match_string_prefix;
2043 case MATCH_ANONYMOUS:
2044 if (e_callerinfo.present != INFO_PRESENT_ALLOWED)
2049 if (e_callerinfo.present == INFO_PRESENT_ALLOWED)
2054 if (e_callerinfo.present == INFO_PRESENT_NOTAVAIL)
2058 case MATCH_AVAILABLE:
2059 if (e_callerinfo.present != INFO_PRESENT_NOTAVAIL)
2064 if (e_callerinfo.screen == INFO_SCREEN_USER)
2069 if (e_callerinfo.screen != INFO_SCREEN_USER)
2073 case MATCH_REDIRECTED:
2074 if (e_redirinfo.ntype != INFO_NTYPE_NOTPRESENT)
2079 if (e_redirinfo.ntype == INFO_NTYPE_NOTPRESENT)
2085 goto match_string_prefix;
2089 now_tm = localtime(&now);
2090 integer = now_tm->tm_hour*100 + now_tm->tm_min;
2095 now_tm = localtime(&now);
2096 integer = now_tm->tm_mday;
2101 now_tm = localtime(&now);
2102 integer = now_tm->tm_mon+1;
2107 now_tm = localtime(&now);
2108 integer = now_tm->tm_year + 1900;
2113 now_tm = localtime(&now);
2114 integer = now_tm->tm_wday;
2115 integer = integer?integer:7; /* correct sunday */
2118 case MATCH_CAPABILITY:
2119 integer = e_capainfo.bearer_capa;
2122 case MATCH_INFOLAYER1:
2123 integer = e_capainfo.bearer_info1;
2127 integer = e_capainfo.hlc;
2131 tfp = fopen(cond->string_value, "r");
2135 if (fgetc(tfp) == '1')
2143 argv[j++] = (char *)"/bin/sh";
2144 argv[j++] = (char *)"-c";
2145 argv[j++] = cond->string_value;
2147 argv[j++] = cond->string_value;
2148 argv[j++] = e_extdialing;
2149 argv[j++] = (char *)numberrize_callerinfo(e_callerinfo.id, e_callerinfo.ntype, options.national, options.international);
2150 argv[j++] = e_callerinfo.extension;
2151 argv[j++] = e_callerinfo.name;
2152 SPRINT(isdn_port, "%d", e_callerinfo.isdn_port);
2153 argv[j++] = isdn_port;
2154 argv[j++] = e_callerinfo.imsi;
2155 argv[j++] = NULL; /* check also number of args above */
2156 switch ((pid = fork())) {
2158 execve(cond->string_value, argv, environ);
2164 waitpid(pid, &status, 0);
2165 if (0 == WEXITSTATUS(status))
2177 gettimeofday(¤t_time, NULL);
2178 now_ll = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec;
2180 timeout = now_ll + (cond->integer_value * MICRO_SECONDS);
2187 if (!(comp_len = (unsigned long)strchr(cond->string_value, ':')))
2189 comp_len = comp_len-(unsigned long)cond->string_value;
2191 mISDNport = mISDNport_first;
2193 if (mISDNport->ifport)
2194 if (strlen(mISDNport->ifport->interface->name) == comp_len)
2195 if (!strncasecmp(mISDNport->ifport->interface->name, cond->string_value, comp_len))
2196 if (!mISDNport->l2hold || mISDNport->l2link>0) {
2198 jj = mISDNport->b_num;
2201 if (mISDNport->b_state[j])
2206 mISDNport = mISDNport->next;
2208 if (cond->match == MATCH_FREE) {
2209 if (avail >= atoi(cond->string_value + comp_len + 1))
2212 if (avail < atoi(cond->string_value + comp_len + 1))
2221 mISDNport = mISDNport_first;
2223 if (mISDNport->ifport)
2224 if (!strcasecmp(mISDNport->ifport->interface->name, cond->string_value))
2225 if (!mISDNport->l2hold || mISDNport->l2link>0)
2227 mISDNport = mISDNport->next;
2229 if (!mISDNport) /* all down */
2236 mISDNport = mISDNport_first;
2238 if (mISDNport->ifport)
2239 if (!strcasecmp(mISDNport->ifport->interface->name, cond->string_value))
2240 if (!mISDNport->l2hold || mISDNport->l2link>0)
2243 mISDNport = mISDNport->next;
2245 if (mISDNport) /* one link at least */
2254 mISDNport = mISDNport_first;
2256 if (mISDNport->ifport)
2257 if (!strcasecmp(mISDNport->ifport->interface->name, cond->string_value))
2258 if (mISDNport->use) /* break if in use */
2260 mISDNport = mISDNport->next;
2262 if (mISDNport && cond->match==MATCH_BUSY)
2264 if (!mISDNport && cond->match==MATCH_IDLE)
2270 case MATCH_NOTREMOTE:
2271 admin = admin_first;
2273 if (admin->remote_name[0] && !strcmp(cond->string_value, admin->remote_name))
2275 admin = admin->next;
2277 if (admin && cond->match==MATCH_REMOTE)
2279 if (!admin && cond->match==MATCH_NOTREMOTE)
2284 case MATCH_POTS_FLASH:
2285 if (e_dialinginfo.flash)
2290 port = find_port_id(ea_endpoint->ep_portlist->port_id);
2293 if ((port->p_type & PORT_CLASS_POTS_MASK) != PORT_CLASS_POTS_FXS)
2295 ourfxs = (class Pfxs *)port;
2298 if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
2299 fxs = (class Pfxs *)port;
2300 if (fxs->p_m_mISDNport == ourfxs->p_m_mISDNport && fxs != ourfxs) {
2301 if (fxs->p_state == PORT_STATE_OUT_ALERTING) {
2311 case MATCH_POTS_CALLS:
2312 port = find_port_id(ea_endpoint->ep_portlist->port_id);
2315 if ((port->p_type & PORT_CLASS_POTS_MASK) != PORT_CLASS_POTS_FXS)
2317 ourfxs = (class Pfxs *)port;
2321 if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
2322 fxs = (class Pfxs *)port;
2323 if (fxs->p_m_mISDNport == ourfxs->p_m_mISDNport && fxs != ourfxs) {
2324 if (fxs->p_state == PORT_STATE_CONNECT) {
2333 case MATCH_POTS_LAST:
2334 port = find_port_id(ea_endpoint->ep_portlist->port_id);
2337 if ((port->p_type & PORT_CLASS_POTS_MASK) != PORT_CLASS_POTS_FXS)
2339 ourfxs = (class Pfxs *)port;
2340 /* integer gets the call number that has been the least active call on hold */
2346 if ((port->p_type & PORT_CLASS_POTS_MASK) == PORT_CLASS_POTS_FXS) {
2347 fxs = (class Pfxs *)port;
2348 if (fxs->p_m_mISDNport == ourfxs->p_m_mISDNport && fxs != ourfxs) {
2349 if (fxs->p_state == PORT_STATE_CONNECT) {
2351 if (fxs->p_m_fxs_age > fxs_age) {
2352 fxs_age = fxs->p_m_fxs_age;
2353 integer = fxs_count;
2364 PERROR("Software error: MATCH_* %d not parsed in function '%s'", cond->match, __FUNCTION__);
2368 if (cond->value_type == VALUE_TYPE_INTEGER) {
2369 if (integer != cond->integer_value)
2374 if (cond->value_type == VALUE_TYPE_INTEGER_RANGE) {
2375 /* check if negative range (2100 - 700 o'clock) */
2376 if (cond->integer_value > cond->integer_value_to) {
2377 if (integer>=cond->integer_value && integer<=cond->integer_value_to)
2381 /* range is positive */
2382 if (integer>=cond->integer_value && integer<=cond->integer_value_to)
2389 if (strlen(cond->string_value) != strlen(string))
2392 match_string_prefix:
2393 comp_len = strlen(cond->string_value); /* because we must reach value's length */
2394 /* we must have greater or equal length to values */
2395 if ((unsigned long)strlen(string) < comp_len) {
2396 /* special case for unfinished dialing */
2397 if (cond->match == MATCH_DIALING) {
2398 couldbetrue = 1; /* could match */
2399 comp_len = strlen(string);
2404 /* on single string match */
2405 if (cond->value_type == VALUE_TYPE_STRING) {
2406 if (!strncmp(string, cond->string_value, comp_len)) {
2408 /* must be set for changing 'e_extdialing' */
2409 if (cond->match == MATCH_DIALING)
2410 dialing_required = comp_len;
2415 /* on range match */
2416 if (cond->value_type == VALUE_TYPE_STRING_RANGE) {
2417 /* check if negative range ("55"-"22") */
2418 if (cond->comp_string > 0) {
2419 if (strncmp(string, cond->string_value, comp_len) >= 0) {
2421 /* must be set for changing 'e_extdialing' */
2422 if (cond->match == MATCH_DIALING)
2423 dialing_required = comp_len;
2426 if (strncmp(string, cond->string_value_to, comp_len) <= 0) {
2427 /* must be set for changing 'e_extdialing' */
2429 if (cond->match == MATCH_DIALING)
2430 dialing_required = comp_len;
2435 /* range is positive */
2436 if (strncmp(string, cond->string_value, comp_len) < 0)
2438 if (strncmp(string, cond->string_value_to, comp_len) > 0)
2441 if (cond->match == MATCH_DIALING)
2442 dialing_required = comp_len;
2448 /* set current condition */
2449 if (istrue && !couldbetrue)
2450 condition = 1; /* element matches, so condition matches */
2451 if (istrue && couldbetrue && !condition)
2452 condition = 2; /* element could match and other elements don't match, so condition could match */
2454 /* if not matching or could match */
2455 if (condition != 1) {
2456 /* if we have more values to check */
2457 if (cond->value_extension && cond->next) {
2459 goto checkextension;
2465 /* skip exteded values, beacuse we already have one matching value */
2466 while(cond->value_extension && cond->next)
2471 if (timeout>now_ll && match==1) { /* the matching rule with timeout in the future */
2472 if (match_timeout == 0 || timeout < match_timeout) { /* first timeout or lower */
2473 /* set timeout in the furture */
2474 match_timeout = timeout;
2475 e_match_to_action = rule->action_first;
2476 e_match_to_extdialing = e_dialinginfo.id + dialing_required;
2478 match = 0; /* matches in the future */
2481 /* matching, we return first action */
2482 action = rule->action_first;
2483 match_timeout = 0; /* no timeout */
2484 e_match_to_action = NULL;
2485 e_extdialing = e_dialinginfo.id + dialing_required;
2489 /* rule could match if more is dialed */
2494 if (match_timeout == 0)
2495 unsched_timer(&e_match_timeout); /* no timeout */
2497 schedule_timer(&e_match_timeout, (match_timeout-now_ll) / 1000000, (match_timeout-now_ll) % 1000000);
2503 * parses the current action's parameters and return them
2505 struct route_param *EndpointAppPBX::routeparam(struct route_action *action, unsigned long long id)
2507 struct route_param *param = action->param_first;
2510 if (param->id == id)
2512 param = param->next;
2519 * internal rules that are not defined by route.conf
2521 struct route_action action_password = {
2529 struct route_action action_password_write = {
2532 ACTION_PASSWORD_WRITE,
2537 struct route_action action_external = {
2545 struct route_action action_internal = {
2553 struct route_action action_vbox = {
2561 struct route_action action_partyline = {
2569 struct route_action action_disconnect = {