View Javadoc

1   /*
2    *  Copyright 2004-2006 Stefan Reuter
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   *
16   */
17  package org.asteriskjava.fastagi.internal;
18  
19  import org.asteriskjava.fastagi.AgiRequest;
20  import org.asteriskjava.util.AstUtil;
21  import org.asteriskjava.util.Log;
22  import org.asteriskjava.util.LogFactory;
23  
24  import java.io.UnsupportedEncodingException;
25  import java.net.InetAddress;
26  import java.net.URLDecoder;
27  import java.util.*;
28  import java.util.regex.Matcher;
29  import java.util.regex.Pattern;
30  
31  
32  /**
33   * Default implementation of the AGIRequest interface.
34   * 
35   * @author srt
36   * @version $Id$
37   */
38  public class AgiRequestImpl implements AgiRequest
39  {
40      private final Log logger = LogFactory.getLog(getClass());
41      private static final Pattern SCRIPT_PATTERN = Pattern.compile("^([^\\?]*)\\?(.*)$");
42      private static final Pattern PARAMETER_PATTERN = Pattern.compile("^(.*)=(.*)$");
43  
44      private String rawCallerId;
45  
46      private Map<String, String> request;
47  
48      /**
49       * A map assigning the values of a parameter (an array of Strings) to the
50       * name of the parameter.
51       */
52      private Map<String, String[]> parameterMap;
53  
54      private String[] arguments;
55  
56      private String parameters;
57      private String script;
58      private boolean callerIdCreated;
59      private InetAddress localAddress;
60      private int localPort;
61      private InetAddress remoteAddress;
62      private int remotePort;
63  
64      /**
65       * Creates a new AGIRequestImpl.
66       * 
67       * @param environment the first lines as received from Asterisk containing
68       *            the environment.
69       */
70      AgiRequestImpl(final List<String> environment)
71      {
72          this(buildMap(environment));
73      }
74  
75      /**
76       * Creates a new AgiRequestImpl based on a preparsed map of parameters.
77       *
78       * @param request a map representing the AGI request. Keys must not contain the "agi_" or "ogi_" prefix.
79       * @since 1.0.0
80       */
81      private AgiRequestImpl(final Map<String, String> request)
82      {
83          this.request = request;
84  
85          script = request.get("network_script");
86          if (script != null)
87          {
88              Matcher scriptMatcher = SCRIPT_PATTERN.matcher(script);
89              if (scriptMatcher.matches())
90              {
91                  script = scriptMatcher.group(1);
92                  parameters = scriptMatcher.group(2);
93              }
94          }
95      }
96  
97      /**
98       * Builds a map containing variable names as key (with the "agi_" or "ogi_" prefix
99       * stripped) and the corresponding values.<p>
100      * Syntactically invalid and empty variables are skipped.
101      * 
102      * @param lines the environment to transform.
103      * @return a map with the variables set corresponding to the given environment.
104      * @throws IllegalArgumentException if lines is <code>null</code>
105      */
106     private static Map<String, String> buildMap(final Collection<String> lines) throws IllegalArgumentException
107     {
108         final Map<String, String> map;
109 
110         if (lines == null)
111         {
112             throw new IllegalArgumentException("Environment must not be null.");
113         }
114 
115         map = new HashMap<String, String>();
116 
117         for (String line : lines)
118         {
119             int colonPosition;
120             String key;
121             String value;
122 
123             colonPosition = line.indexOf(':');
124 
125             // no colon on the line?
126             if (colonPosition < 0)
127             {
128                 continue;
129             }
130 
131             // key doesn't start with agi_ or ogi_?
132             if (!line.startsWith("agi_") && !line.startsWith("ogi_"))
133             {
134                 continue;
135             }
136 
137             // first colon in line is last character -> no value present?
138             if (line.length() < colonPosition + 2)
139             {
140                 continue;
141             }
142 
143             key = line.substring(4, colonPosition).toLowerCase(Locale.ENGLISH);
144             value = line.substring(colonPosition + 2);
145 
146             if (value.length() != 0)
147             {
148                 map.put(key, value);
149             }
150         }
151 
152         return map;
153     }
154 
155     public Map<String, String> getRequest()
156     {
157         return request;
158     }
159 
160     /**
161      * Returns the name of the script to execute.
162      * 
163      * @return the name of the script to execute.
164      */
165     public synchronized String getScript()
166     {
167         return script;
168     }
169 
170     /**
171      * Returns the full URL of the request in the form
172      * agi://host[:port][/script].
173      * 
174      * @return the full URL of the request in the form
175      *         agi://host[:port][/script].
176      */
177     public String getRequestURL()
178     {
179         return request.get("request");
180     }
181 
182     /**
183      * Returns the name of the channel.
184      * 
185      * @return the name of the channel.
186      */
187     public String getChannel()
188     {
189         return request.get("channel");
190     }
191 
192     /**
193      * Returns the unqiue id of the channel.
194      * 
195      * @return the unqiue id of the channel.
196      */
197     public String getUniqueId()
198     {
199         return request.get("uniqueid");
200     }
201 
202     public String getType()
203     {
204         return request.get("type");
205     }
206 
207     public String getLanguage()
208     {
209         return request.get("language");
210     }
211 
212     @Deprecated public String getCallerId()
213     {
214         return getCallerIdNumber();
215     }
216 
217     public String getCallerIdNumber()
218     {
219         String callerIdName;
220         String callerId;
221 
222         callerIdName = request.get("calleridname");
223         callerId = request.get("callerid");
224         if (callerIdName != null)
225         {
226             // Asterisk 1.2
227             if (callerId == null || "unknown".equals(callerId))
228             {
229                 return null;
230             }
231 
232             return callerId;
233         }
234         else
235         {
236             // Asterisk 1.0
237             return getCallerId10();
238         }
239     }
240 
241     public String getCallerIdName()
242     {
243         String callerIdName;
244 
245         callerIdName = request.get("calleridname");
246         if (callerIdName != null)
247         {
248             // Asterisk 1.2
249             if ("unknown".equals(callerIdName))
250             {
251                 return null;
252             }
253 
254             return callerIdName;
255         }
256         else
257         {
258             // Asterisk 1.0
259             return getCallerIdName10();
260         }
261     }
262 
263     /**
264      * Returns the Caller*ID number using Asterisk 1.0 logic.
265      * 
266      * @return the Caller*ID number
267      */
268     private synchronized String getCallerId10()
269     {
270         final String[] parsedCallerId;
271 
272         if (!callerIdCreated)
273         {
274             rawCallerId = request.get("callerid");
275             callerIdCreated = true;
276         }
277 
278         parsedCallerId = AstUtil.parseCallerId(rawCallerId);
279         if (parsedCallerId[1] == null)
280         {
281             return parsedCallerId[0];
282         }
283         else
284         {
285             return parsedCallerId[1];
286         }
287     }
288 
289     /**
290      * Returns the Caller*ID name using Asterisk 1.0 logic.
291      * 
292      * @return the Caller*ID name
293      */
294     private synchronized String getCallerIdName10()
295     {
296         if (!callerIdCreated)
297         {
298             rawCallerId = request.get("callerid");
299             callerIdCreated = true;
300         }
301 
302         return AstUtil.parseCallerId(rawCallerId)[0];
303     }
304 
305     public String getDnid()
306     {
307         String dnid;
308         
309         dnid = request.get("dnid");
310         
311         if (dnid == null || "unknown".equals(dnid))
312         {
313             return null;
314         }
315         
316         return dnid; 
317     }
318 
319     public String getRdnis()
320     {
321         String rdnis;
322         
323         rdnis = request.get("rdnis");
324         
325         if (rdnis == null || "unknown".equals(rdnis))
326         {
327             return null;
328         }
329         
330         return rdnis; 
331     }
332 
333     public String getContext()
334     {
335         return request.get("context");
336     }
337 
338     public String getExtension()
339     {
340         return request.get("extension");
341     }
342 
343     public Integer getPriority()
344     {
345         if (request.get("priority") != null)
346         {
347             return Integer.valueOf(request.get("priority"));
348         }
349         return null;
350     }
351 
352     public Boolean getEnhanced()
353     {
354         if (request.get("enhanced") != null)
355         {
356             if ("1.0".equals(request.get("enhanced")))
357             {
358                 return Boolean.TRUE;
359             }
360             else
361             {
362                 return Boolean.FALSE;
363             }
364         }
365         return null;
366     }
367 
368     public String getAccountCode()
369     {
370         return request.get("accountcode");
371     }
372 
373     public Integer getCallingAni2()
374     {
375         if (request.get("callingani2") == null)
376         {
377             return null;
378         }
379 
380         try
381         {
382             return Integer.valueOf(request.get("callingani2"));
383         }
384         catch (NumberFormatException e)
385         {
386             return null;
387         }
388     }
389 
390     public Integer getCallingPres()
391     {
392         if (request.get("callingpres") == null)
393         {
394             return null;
395         }
396         
397         try
398         {
399             return Integer.valueOf(request.get("callingpres"));
400         }
401         catch (NumberFormatException e)
402         {
403             return null;
404         }
405     }
406 
407     public Integer getCallingTns()
408     {
409         if (request.get("callingtns") == null)
410         {
411             return null;
412         }
413         
414         try
415         {
416             return Integer.valueOf(request.get("callingtns"));
417         }
418         catch (NumberFormatException e)
419         {
420             return null;
421         }
422     }
423 
424     public Integer getCallingTon()
425     {
426         if (request.get("callington") == null)
427         {
428             return null;
429         }
430         
431         try
432         {
433             return Integer.valueOf(request.get("callington"));
434         }
435         catch (NumberFormatException e)
436         {
437             return null;
438         }
439     }
440 
441     public String getParameter(String name)
442     {
443         String[] values;
444 
445         values = getParameterValues(name);
446 
447         if (values == null || values.length == 0)
448         {
449             return null;
450         }
451 
452         return values[0];
453     }
454 
455     public synchronized String[] getParameterValues(String name)
456     {
457         if (getParameterMap().isEmpty())
458         {
459             return new String[0];
460         }
461 
462         final String[] values = parameterMap.get(name);
463         return values == null ? new String[0] : values;
464     }
465 
466     public synchronized Map<String, String[]> getParameterMap()
467     {
468         if (parameterMap == null)
469         {
470             parameterMap = parseParameters(parameters);
471         }
472         return parameterMap;
473     }
474 
475     /**
476      * Parses the given parameter string and caches the result.
477      * 
478      * @param s the parameter string to parse
479      * @return a Map made up of parameter names their values
480      */
481     private synchronized Map<String, String[]> parseParameters(String s)
482     {
483         Map<String, List<String>> parameterMap;
484         Map<String, String[]> result;
485         StringTokenizer st;
486 
487         parameterMap = new HashMap<String, List<String>>();
488         result = new HashMap<String, String[]>();
489 
490         if (s == null)
491         {
492             return result;
493         }
494 
495         st = new StringTokenizer(s, "&");
496         while (st.hasMoreTokens())
497         {
498             String parameter;
499             Matcher parameterMatcher;
500             String name;
501             String value;
502             List<String> values;
503 
504             parameter = st.nextToken();
505             parameterMatcher = PARAMETER_PATTERN.matcher(parameter);
506             if (parameterMatcher.matches())
507             {
508                 try
509                 {
510                     name = URLDecoder.decode(parameterMatcher.group(1), "UTF-8");
511                     value = URLDecoder.decode(parameterMatcher.group(2), "UTF-8");
512                 }
513                 catch (UnsupportedEncodingException e)
514                 {
515                     logger.error("Unable to decode parameter '" + parameter + "'", e);
516                     continue;
517                 }
518             }
519             else
520             {
521                 try
522                 {
523                     name = URLDecoder.decode(parameter, "UTF-8");
524                     value = "";
525                 }
526                 catch (UnsupportedEncodingException e)
527                 {
528                     logger.error("Unable to decode parameter '" + parameter + "'", e);
529                     continue;
530                 }
531             }
532 
533             if (parameterMap.get(name) == null)
534             {
535                 values = new ArrayList<String>();
536                 values.add(value);
537                 parameterMap.put(name, values);
538             }
539             else
540             {
541                 values = parameterMap.get(name);
542                 values.add(value);
543             }
544         }
545 
546         for (Map.Entry<String, List<String>> entry : parameterMap.entrySet())
547         {
548             String[] valueArray;
549 
550             valueArray = new String[entry.getValue().size()];
551             result.put(entry.getKey(), entry.getValue().toArray(valueArray));
552         }
553 
554         return result;
555     }
556 
557     public synchronized String[] getArguments()
558     {
559         if (arguments != null)
560         {
561             return arguments.clone();
562         }
563 
564         final Map<Integer, String> map = new HashMap<Integer, String>();
565         int maxIndex = 0;
566         for (Map.Entry<String, String> entry : request.entrySet())
567         {
568             if (! entry.getKey().startsWith("arg_"))
569             {
570                 continue;
571             }
572 
573             int index = Integer.valueOf(entry.getKey().substring(4));
574             if (index > maxIndex)
575             {
576                 maxIndex = index;
577             }
578             map.put(index, entry.getValue());
579         }
580 
581         arguments = new String[maxIndex];
582         for (int i = 0; i < maxIndex; i++)
583         {
584             arguments[i] = map.get(i + 1);
585         }
586         
587         return arguments.clone();
588     }
589 
590     public InetAddress getLocalAddress()
591     {
592         return localAddress;
593     }
594 
595     void setLocalAddress(InetAddress localAddress)
596     {
597         this.localAddress = localAddress;
598     }
599 
600     public int getLocalPort()
601     {
602         return localPort;
603     }
604 
605     void setLocalPort(int localPort)
606     {
607         this.localPort = localPort;
608     }
609 
610     public InetAddress getRemoteAddress()
611     {
612         return remoteAddress;
613     }
614 
615     void setRemoteAddress(InetAddress remoteAddress)
616     {
617         this.remoteAddress = remoteAddress;
618     }
619 
620     public int getRemotePort()
621     {
622         return remotePort;
623     }
624 
625     void setRemotePort(int remotePort)
626     {
627         this.remotePort = remotePort;
628     }
629 
630     @Override
631    public String toString()
632     {
633         StringBuffer sb;
634 
635         sb = new StringBuffer("AgiRequest[");
636         sb.append("script='").append(getScript()).append("',");
637         sb.append("requestURL='").append(getRequestURL()).append("',");
638         sb.append("channel='").append(getChannel()).append("',");
639         sb.append("uniqueId='").append(getUniqueId()).append("',");
640         sb.append("type='").append(getType()).append("',");
641         sb.append("language='").append(getLanguage()).append("',");
642         sb.append("callerIdNumber='").append(getCallerIdNumber()).append("',");
643         sb.append("callerIdName='").append(getCallerIdName()).append("',");
644         sb.append("dnid='").append(getDnid()).append("',");
645         sb.append("rdnis='").append(getRdnis()).append("',");
646         sb.append("context='").append(getContext()).append("',");
647         sb.append("extension='").append(getExtension()).append("',");
648         sb.append("priority='").append(getPriority()).append("',");
649         sb.append("enhanced='").append(getEnhanced()).append("',");
650         sb.append("accountCode='").append(getAccountCode()).append("',");
651         sb.append("systemHashcode=").append(System.identityHashCode(this));
652         sb.append("]");
653 
654         return sb.toString();
655     }
656 }