package org.asteriskjava.config.dialplan; import java.io.BufferedReader; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import org.asteriskjava.config.Category; import org.asteriskjava.config.ConfigElement; import org.asteriskjava.config.ConfigFile; import org.asteriskjava.config.ConfigFileImpl; import org.asteriskjava.config.ConfigFileReader; import org.asteriskjava.config.ConfigParseException; import org.asteriskjava.config.ConfigVariable; /* * Interprets extensions.conf as a special kind of config file, the dialplan. * - Line numbers correspond with pbx_config.c, tags/1.4.19 revision 96024 */ public class ExtensionsConfigFileReader extends ConfigFileReader { /* * This method corresponds to an iteration of the loop at line 2212 Notes: * 1. [general] and [globals] are allowed to be a context here if they contain only configvariables * 2. switch and ignorepat are treated like regular ConfigVariable. */ protected ConfigElement processTextLine(String configfile, int lineno, String line) throws ConfigParseException { ConfigElement configElement; if( (line.trim().startsWith("exten") || line.trim().startsWith("include")) && currentCategory != null && (currentCategory.getName().equals("general") || currentCategory.getName().equals("globals")) ) throw new ConfigParseException(configfile, lineno, "cannot have 'exten' or 'include' in global or general sections"); /* * Goal here is to break out anything unique that we might want to * look at and parse separately. For now, only exten and include fit * that criteria. Eventually, I could see parsing sections for things * from macros, contexts to differentiate them from categories, switch * for realtime, and more. */ if (line.trim().startsWith("exten")) { configElement = parseExtension(configfile, lineno, line); currentCategory.addElement(configElement); return configElement; } else if(line.trim().startsWith("include")) { // use parseVariable since we have access to it ConfigVariable configvar = parseVariable(configfile, lineno, line); configElement = new ConfigInclude(configfile, lineno, configvar.getValue()); currentCategory.addElement(configElement); return configElement; } // leave everything else the same configElement = super.processTextLine(configfile, lineno, line); return configElement; } /* Roughly corresponds to pbx_config.c:2222 */ protected ConfigExtension parseExtension(String configfile, int lineno, String line) throws ConfigParseException { String dupline = new String(line); ConfigVariable initialVariable = parseVariable(configfile, lineno, dupline); if(!initialVariable.getName().equals("exten")) throw new ConfigParseException(configfile, lineno, "missing 'exten' near " + dupline); dupline = initialVariable.getValue().trim(); int nameIndex = dupline.indexOf(",", 0); if(nameIndex == -1) throw new ConfigParseException(configfile, lineno, "missing extension name near " + dupline); String name = dupline.substring(0, nameIndex); dupline = dupline.substring(name.length()+1, dupline.length()).trim(); int priorityDelimiter = dupline.indexOf(",", 0); if(priorityDelimiter == -1) throw new ConfigParseException(configfile, lineno, "missing extension priority near " + dupline); String priority = dupline.substring(0, priorityDelimiter); dupline = dupline.substring(priority.length()+1, dupline.length()).trim(); String [] application = harvestApplicationWithArguments(dupline); return new ConfigExtension(configfile,lineno,name,priority,application); } /* Roughly corresponds to pbx_config.c:2276 */ private static String [] harvestApplicationWithArguments(String arg) { List<String> args = new ArrayList<String>(); if(args != null && arg.trim().length() >= 0) { String appl = "", data = ""; /* Find the first occurrence of either '(' or ',' */ int firstc = arg.indexOf(','); int firstp = arg.indexOf('('); if (firstc != -1 && (firstp == -1 || firstc < firstp)) { /* comma found, no parenthesis */ /* or both found, but comma found first */ String [] split = arg.split(","); appl = split[0]; for(int i = 1; i < split.length; i++) data += split[i] + (i+1<split.length?",":""); } else if (firstc == -1 && firstp == -1) { /* Neither found */ data = ""; } else { /* Final remaining case is parenthesis found first */ String [] split = arg.split("\\("); appl = split[0]; for(int i = 1; i < split.length; i++) data += split[i] + (i+1<split.length?"(":""); int end = data.lastIndexOf(')'); if (end == -1) { //ast_log(LOG_WARNING, "No closing parenthesis found? '%s(%s'\n", appl, data); } else if(end == data.length()-1) { data = data.substring(0, end); } data = processQuotesAndSlashes(data, ',', '|'); } if(!appl.trim().equals("")) { args.add(appl.trim()); if(!data.trim().equals("")) { String [] dataSplit = data.split("\\|"); for(int i = 0; i < dataSplit.length; i++) args.add(dataSplit[i].trim()); } } } return args.toArray(new String[0]); } public ExtensionsConfigFile readExtensionsFile(String configfile) throws IOException, ConfigParseException { super.readFile(configfile); /* at some point, we may want to resolve back references */ /* that include or goto from one context to another */ return new ExtensionsConfigFile(configfile, categories); } /* ast_process_quotes_and_slashes rewritten to be java friendly */ private static String processQuotesAndSlashes(String start, char find, char replace_with) { String dataPut = ""; int inEscape = 0; int inQuotes = 0; char [] startChars = start.toCharArray(); for (int i = 0; i < startChars.length; i++) { if (inEscape != 0) { dataPut += startChars[i]; /* Always goes verbatim */ inEscape = 0; } else { if (startChars[i] == '\\') { inEscape = 1; /* Do not copy \ into the data */ } else if (startChars[i] == '\'') { inQuotes = 1 - inQuotes; /* Do not copy ' into the data */ } else { /* Replace , with |, unless in quotes */ dataPut += inQuotes != 0 ? startChars[i] : ((startChars[i] == find) ? replace_with : startChars[i]); } } } return dataPut; } }