Merge pull request #4676 from jmarshallnz/dont_set_scraper_on_tvshow_on_nfo
[vuplus_xbmc] / xbmc / utils / HttpParser.cpp
1 /*
2  * This code implements parsing of HTTP requests.
3  * This code was written by Steve Hanov in 2009, no copyright is claimed.
4  * This code is in the public domain.
5  * Code was taken from http://refactormycode.com/codes/778-an-efficient-http-parser
6  *
7  *      Copyright (C) 2011-2013 Team XBMC
8  *      http://xbmc.org
9  *
10  *  This Program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2, or (at your option)
13  *  any later version.
14  *
15  *  This Program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with XBMC; see the file COPYING.  If not, see
22  *  <http://www.gnu.org/licenses/>.
23  *
24  */
25
26 #include "HttpParser.h"
27
28 HttpParser::HttpParser() :
29     _headerStart(0),
30     _bodyStart(0),
31     _parsedTo( 0 ),
32     _state( 0 ),
33     _keyIndex(0),
34     _valueIndex(0),
35     _contentLength(0),
36     _contentStart(0),
37     _uriIndex(0),
38     _status( Incomplete )
39 {
40
41 }
42
43 HttpParser::~HttpParser()
44 {
45
46 }
47
48 void
49 HttpParser::parseHeader()
50 {
51     // run the fsm.
52     const int  CR = 13;
53     const int  LF = 10;
54     const int  ANY = 256;
55
56     enum Action {
57         // make lower case
58         LOWER = 0x1,
59
60         // convert current character to null.
61         NULLIFY = 0x2,
62
63         // set the header index to the current position
64         SET_HEADER_START = 0x4,
65
66         // set the key index to the current position
67         SET_KEY = 0x8,
68
69         // set value index to the current position.
70         SET_VALUE = 0x10,
71
72         // store current key/value pair.
73         STORE_KEY_VALUE = 0x20,
74
75         // sets content start to current position + 1
76         SET_CONTENT_START = 0x40
77     };
78
79     static const struct FSM {
80         State curState;
81         int c;
82         State nextState;
83         unsigned actions;
84     } fsm[] = {
85         { p_request_line,         CR, p_request_line_cr,     NULLIFY                            },
86         { p_request_line,        ANY, p_request_line,        0                                  },
87         { p_request_line_cr,      LF, p_request_line_crlf,   0                                  },
88         { p_request_line_crlf,    CR, p_request_line_crlfcr, 0                                  },
89         { p_request_line_crlf,   ANY, p_key,                 SET_HEADER_START | SET_KEY | LOWER },
90         { p_request_line_crlfcr,  LF, p_content,             SET_CONTENT_START                  },
91         { p_key,                 ':', p_key_colon,           NULLIFY                            },
92         { p_key,                 ANY, p_key,                 LOWER                              },
93         { p_key_colon,           ' ', p_key_colon_sp,        0                                  },
94         { p_key_colon_sp,        ANY, p_value,               SET_VALUE                          },
95         { p_value,                CR, p_value_cr,            NULLIFY | STORE_KEY_VALUE          },
96         { p_value,               ANY, p_value,               0                                  },
97         { p_value_cr,             LF, p_value_crlf,          0                                  },
98         { p_value_crlf,           CR, p_value_crlfcr,        0                                  },
99         { p_value_crlf,          ANY, p_key,                 SET_KEY | LOWER                    },
100         { p_value_crlfcr,         LF, p_content,             SET_CONTENT_START                  },
101         { p_error,               ANY, p_error,               0                                  }
102     };
103
104     for( unsigned i = _parsedTo; i < _data.length(); ++i) {
105         char c = _data[i];
106         State nextState = p_error;
107
108         for ( unsigned d = 0; d < sizeof(fsm) / sizeof(FSM); ++d ) {
109             if ( fsm[d].curState == _state && 
110                     ( c == fsm[d].c || fsm[d].c == ANY ) ) {
111
112                 nextState = fsm[d].nextState;
113
114                 if ( fsm[d].actions & LOWER ) {
115                     _data[i] = tolower( _data[i] );
116                 }
117
118                 if ( fsm[d].actions & NULLIFY ) {
119                     _data[i] = 0;
120                 }
121
122                 if ( fsm[d].actions & SET_HEADER_START ) {
123                     _headerStart = i;
124                 }
125
126                 if ( fsm[d].actions & SET_KEY ) {
127                     _keyIndex = i;
128                 }
129
130                 if ( fsm[d].actions & SET_VALUE ) {
131                     _valueIndex = i;
132                 }
133
134                 if ( fsm[d].actions & SET_CONTENT_START ) {
135                     _contentStart = i + 1;
136                 }
137
138                 if ( fsm[d].actions & STORE_KEY_VALUE ) {
139                     // store position of first character of key.
140                     _keys.push_back( _keyIndex );
141                 }
142
143                 break;
144             }
145         }
146
147         _state = nextState;
148
149         if ( _state == p_content ) {
150             const char* str = getValue("content-length");
151             if ( str ) {
152                 _contentLength = atoi( str );
153             }
154             break;
155         }
156     }
157
158     _parsedTo = _data.length();
159
160 }
161
162 bool
163 HttpParser::parseRequestLine()
164 {
165     size_t sp1;
166     size_t sp2;
167
168     sp1 = _data.find( ' ', 0 );
169     if ( sp1 == std::string::npos ) return false;
170     sp2 = _data.find( ' ', sp1 + 1 );
171     if ( sp2 == std::string::npos ) return false;
172
173     _data[sp1] = 0;
174     _data[sp2] = 0;
175     _uriIndex = sp1 + 1;
176     return true;
177 }
178
179 HttpParser::status_t
180 HttpParser::addBytes( const char* bytes, unsigned len )
181 {
182     if ( _status != Incomplete ) {
183         return _status;
184     }
185
186     // append the bytes to data.
187     _data.append( bytes, len );
188
189     if ( _state < p_content ) {
190         parseHeader();
191     }
192
193     if ( _state == p_error ) {
194         _status = Error;
195     } else if ( _state == p_content ) {
196         if ( _contentLength == 0 || _data.length() - _contentStart >= _contentLength ) {
197             if ( parseRequestLine() ) {
198                 _status = Done;
199             } else {
200                 _status = Error;
201             }
202         }
203     }
204
205     return _status;
206 }
207
208 const char*
209 HttpParser::getMethod()
210 {
211     return &_data[0];
212 }
213
214 const char*
215 HttpParser::getUri()
216 {
217     return &_data[_uriIndex];
218 }
219
220 const char*
221 HttpParser::getQueryString()
222 {
223     const char* pos = getUri();
224     while( *pos ) {
225         if ( *pos == '?' ) {
226             pos++;
227             break;
228         }
229         pos++;
230     }
231     return pos;
232 }
233
234 const char* 
235 HttpParser::getBody()
236 {
237     if ( _contentLength > 0 ) {
238         return &_data[_contentStart];
239     } else  {
240         return NULL;
241     }
242 }
243
244 // key should be in lower case.
245 const char* 
246 HttpParser::getValue( const char* key )
247 {
248     for( IntArray::iterator iter = _keys.begin();
249             iter != _keys.end(); ++iter  )
250     {
251         unsigned index = *iter;
252         if ( strcmp( &_data[index], key ) == 0 ) {
253             return &_data[index + strlen(key) + 2];
254         }
255
256     }
257
258     return NULL;
259 }
260
261 unsigned
262 HttpParser::getContentLength()
263 {
264     return _contentLength;
265 }
266