1/*
2* Conditions Of Use
3*
4* This software was developed by employees of the National Institute of
5* Standards and Technology (NIST), an agency of the Federal Government.
6* Pursuant to title 15 Untied States Code Section 105, works of NIST
7* employees are not subject to copyright protection in the United States
8* and are considered to be in the public domain.  As a result, a formal
9* license is not needed to use the software.
10*
11* This software is provided by NIST as a service and is expressly
12* provided "AS IS."  NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
13* OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
14* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
15* AND DATA ACCURACY.  NIST does not warrant or make any representations
16* regarding the use of the software or the results thereof, including but
17* not limited to the correctness, accuracy, reliability or usefulness of
18* the software.
19*
20* Permission to use this software is contingent upon your acceptance
21* of the terms of this agreement
22*
23* .
24*
25*/
26package gov.nist.javax.sip.parser;
27
28import gov.nist.javax.sip.header.*;
29import gov.nist.core.*;
30import java.text.ParseException;
31
32/**
33 * Parser for via headers.
34 *
35 * @version 1.2 $Revision: 1.12 $ $Date: 2009/07/17 18:58:07 $
36 * @since 1.1
37 *
38 * @author Olivier Deruelle
39 * @author M. Ranganathan
40 */
41public class ViaParser extends HeaderParser {
42
43    public ViaParser(String via) {
44        super(via);
45    }
46
47    public ViaParser(Lexer lexer) {
48        super(lexer);
49    }
50
51    /**
52     * a parser for the essential part of the via header.
53     */
54    private void parseVia(Via v) throws ParseException {
55        // The protocol
56        lexer.match(TokenTypes.ID);
57        Token protocolName = lexer.getNextToken();
58
59        this.lexer.SPorHT();
60        // consume the "/"
61        lexer.match('/');
62        this.lexer.SPorHT();
63        lexer.match(TokenTypes.ID);
64        this.lexer.SPorHT();
65        Token protocolVersion = lexer.getNextToken();
66
67        this.lexer.SPorHT();
68
69        // We consume the "/"
70        lexer.match('/');
71        this.lexer.SPorHT();
72        lexer.match(TokenTypes.ID);
73        this.lexer.SPorHT();
74
75        Token transport = lexer.getNextToken();
76        this.lexer.SPorHT();
77
78        Protocol protocol = new Protocol();
79        protocol.setProtocolName(protocolName.getTokenValue());
80        protocol.setProtocolVersion(protocolVersion.getTokenValue());
81        protocol.setTransport(transport.getTokenValue());
82        v.setSentProtocol(protocol);
83
84        // sent-By
85        HostNameParser hnp = new HostNameParser(this.getLexer());
86        HostPort hostPort = hnp.hostPort( true );
87        v.setSentBy(hostPort);
88
89        // Ignore blanks
90        this.lexer.SPorHT();
91
92        // parameters
93        while (lexer.lookAhead(0) == ';') {
94            this.lexer.consume(1);
95            this.lexer.SPorHT();
96            NameValue nameValue = this.nameValue();
97            String name = nameValue.getName();
98            if (name.equals(Via.BRANCH)) {
99                String branchId = (String) nameValue.getValueAsObject();
100                if (branchId == null)
101                    throw new ParseException("null branch Id", lexer.getPtr());
102
103            }
104            v.setParameter(nameValue);
105            this.lexer.SPorHT();
106        }
107
108        //
109        // JvB Note: RFC3261 does not allow a comment in Via headers anymore
110        //
111        if (lexer.lookAhead(0) == '(') {
112            this.lexer.selectLexer("charLexer");
113            lexer.consume(1);
114            StringBuffer comment = new StringBuffer();
115            while (true) {
116                char ch = lexer.lookAhead(0);
117                if (ch == ')') {
118                    lexer.consume(1);
119                    break;
120                } else if (ch == '\\') {
121                    // Escaped character
122                    Token tok = lexer.getNextToken();
123                    comment.append(tok.getTokenValue());
124                    lexer.consume(1);
125                    tok = lexer.getNextToken();
126                    comment.append(tok.getTokenValue());
127                    lexer.consume(1);
128                } else if (ch == '\n') {
129                    break;
130                } else {
131                    comment.append(ch);
132                    lexer.consume(1);
133                }
134            }
135            v.setComment(comment.toString());
136        }
137
138    }
139
140    /**
141     * Overrides the superclass nameValue parser because we have to tolerate
142     * IPV6 addresses in the received parameter.
143     */
144    protected NameValue nameValue() throws ParseException {
145        if (debug)
146            dbg_enter("nameValue");
147        try {
148
149            lexer.match(LexerCore.ID);
150            Token name = lexer.getNextToken();
151            // eat white space.
152            lexer.SPorHT();
153            try {
154
155                boolean quoted = false;
156
157                char la = lexer.lookAhead(0);
158
159                if (la == '=') {
160                    lexer.consume(1);
161                    lexer.SPorHT();
162                    String str = null;
163                    if (name.getTokenValue().compareToIgnoreCase(Via.RECEIVED) == 0) {
164                        // Allow for IPV6 Addresses.
165                        // these could have : in them!
166                        str = lexer.byteStringNoSemicolon();
167                    } else {
168                        if (lexer.lookAhead(0) == '\"') {
169                            str = lexer.quotedString();
170                            quoted = true;
171                        } else {
172                            lexer.match(LexerCore.ID);
173                            Token value = lexer.getNextToken();
174                            str = value.getTokenValue();
175                        }
176                    }
177                    NameValue nv = new NameValue(name.getTokenValue()
178                            .toLowerCase(), str);
179                    if (quoted)
180                        nv.setQuotedValue();
181                    return nv;
182                } else {
183                    return new NameValue(name.getTokenValue().toLowerCase(),
184                            null);
185                }
186            } catch (ParseException ex) {
187                return new NameValue(name.getTokenValue(), null);
188            }
189
190        } finally {
191            if (debug)
192                dbg_leave("nameValue");
193        }
194
195    }
196
197    public SIPHeader parse() throws ParseException {
198        if (debug)
199            dbg_enter("parse");
200        try {
201            ViaList viaList = new ViaList();
202            // The first via header.
203            this.lexer.match(TokenTypes.VIA);
204            this.lexer.SPorHT(); // ignore blanks
205            this.lexer.match(':'); // expect a colon.
206            this.lexer.SPorHT(); // ingore blanks.
207
208            while (true) {
209                Via v = new Via();
210                parseVia(v);
211                viaList.add(v);
212                this.lexer.SPorHT(); // eat whitespace.
213                if (this.lexer.lookAhead(0) == ',') {
214                    this.lexer.consume(1); // Consume the comma
215                    this.lexer.SPorHT(); // Ignore space after.
216                }
217                if (this.lexer.lookAhead(0) == '\n')
218                    break;
219            }
220            this.lexer.match('\n');
221            return viaList;
222        } finally {
223            if (debug)
224                dbg_leave("parse");
225        }
226
227    }
228
229    /**
230     *
231     * public static void main(String args[]) throws ParseException { String
232     * via[] = { "Via: SIP/2.0/UDP 135.180.130.133;branch=-12345\n", "Via:
233     * SIP/2.0/UDP 166.34.120.100;branch=0000045d-00000001"+ ",SIP/2.0/UDP
234     * 166.35.224.216:5000\n", "Via: SIP/2.0/UDP sip33.example.com,"+ "
235     * SIP/2.0/UDP sip32.example.com (oli),"+ "SIP/2.0/UDP sip31.example.com\n",
236     * "Via: SIP/2.0/UDP host.example.com;received=::133;"+ "
237     * branch=C1C3344E2710000000E299E568E7potato10potato0potato0\n", "Via:
238     * SIP/2.0/UDP host.example.com;received=135.180.130.133;"+ "
239     * branch=C1C3344E2710000000E299E568E7potato10potato0potato0\n", "Via:
240     * SIP/2.0/UDP company.com:5604 ( Hello )"+ ", SIP / 2.0 / UDP
241     * 135.180.130.133\n", "Via: SIP/2.0/UDP
242     * 129.6.55.9:7060;received=stinkbug.antd.nist.gov\n",
243     *
244     * "Via: SIP/2.0/UDP ss2.wcom.com:5060;branch=721e418c4.1"+ ", SIP/2.0/UDP
245     * ss1.wcom.com:5060;branch=2d4790.1"+ " , SIP/2.0/UDP here.com:5060( Hello
246     * the big world) \n" ,"Via: SIP/2.0/UDP
247     * ss1.wcom.com:5060;branch=2d4790.1\n", "Via: SIP/2.0/UDP
248     * first.example.com:4000;ttl=16"+ ";maddr=224.2.0.1 ;branch=a7c6a8dlze.1
249     * (Acme server)\n" };
250     *
251     * for (int i = 0; i < via.length; i++ ) { ViaParser vp = new
252     * ViaParser(via[i]); System.out.println("toParse = " + via[i]); ViaList vl =
253     * (ViaList) vp.parse(); System.out.println("encoded = " + vl.encode()); }
254     *  }
255     *
256     */
257
258}
259