Web Wispers 1.2.2 C++ Unit Test Coverage
Current view: top level - ssrc/wispers/fcgi - parse_query.cc (source / functions) Hit Total Coverage
Test: Web Wispers 1.2.2 C++ Unit Tests Lines: 57 93 61.3 %
Date: 2012-04-09 Functions: 3 5 60.0 %
Branches: 72 198 36.4 %

           Branch data     Line data    Source code
       1                 :            : /* Copyright 2006-2011 Savarese Software Research Corporation
       2                 :            :  *
       3                 :            :  * Licensed under the Apache License, Version 2.0 (the "License");
       4                 :            :  * you may not use this file except in compliance with the License.
       5                 :            :  * You may obtain a copy of the License at
       6                 :            :  *
       7                 :            :  *     https://www.savarese.com/software/ApacheLicense-2.0
       8                 :            :  *
       9                 :            :  * Unless required by applicable law or agreed to in writing, software
      10                 :            :  * distributed under the License is distributed on an "AS IS" BASIS,
      11                 :            :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12                 :            :  * See the License for the specific language governing permissions and
      13                 :            :  * limitations under the License.
      14                 :            :  */
      15                 :            : 
      16                 :            : #include <ssrc/wispers/fcgi/parse_query.h>
      17                 :            : #include <ssrc/wispers/fcgi/URI.h>
      18                 :            : #include <ssrc/wispers/utility/WebStrings.h>
      19                 :            : 
      20                 :            : #include <boost/algorithm/string/trim.hpp>
      21                 :            : 
      22                 :            : __BEGIN_NS_SSRC_WSPR_FCGI
      23                 :            : 
      24                 :            : // Note, this is an approximation because a header value could contain:
      25                 :            : //   Foo-Header: foobar; bar=" foo=value"; foo=value
      26                 :            : // In practice, that will not be the case for well-behaved programs.
      27                 :         12 : string parse_header_attribute(const string & header_value,
      28                 :            :                               const string & attribute,
      29                 :            :                               string::size_type pos,
      30                 :            :                               string::size_type *end)
      31                 :            : {
      32   [ +  -  +  -  :         24 :   string attr = std::move(string(" ").append(attribute).append("="));
          +  -  +  -  +  
                -  +  - ]
      33                 :         12 :   string::size_type end_pos = 0;
      34                 :            : 
      35         [ +  - ]:         12 :   pos = header_value.find(attr, pos);
      36                 :            : 
      37         [ +  + ]:         12 :   if(pos != string::npos) {
      38                 :            : 
      39         [ +  - ]:         10 :     pos+=attr.size();
      40                 :            : 
      41   [ +  -  +  - ]:         10 :     if(pos < header_value.size()) {
      42   [ +  -  +  + ]:         10 :       if(header_value[pos] == '"') {
      43                 :          8 :         ++pos;
      44         [ +  - ]:          8 :         end_pos = header_value.find('"', pos);
      45         [ -  + ]:          8 :         if(end_pos == string::npos) {
      46                 :          0 :           end_pos = pos;
      47                 :            :         }
      48                 :            :       } else {
      49         [ +  - ]:          2 :         end_pos = header_value.find_first_of("; \r\n", pos + 1);
      50                 :            : 
      51         [ +  + ]:          2 :         if(end_pos == string::npos) {
      52         [ +  - ]:          1 :           end_pos = header_value.size();
      53                 :            :         }
      54                 :            :       }
      55                 :            :     } else {
      56                 :          0 :       pos = 0;
      57                 :            :     }
      58                 :            :   } else {
      59                 :          2 :     pos = 0;
      60                 :            :   }
      61                 :            : 
      62         [ +  + ]:         12 :   if(end) {
      63                 :          3 :     *end = end_pos;
      64                 :            :   }
      65                 :            : 
      66         [ +  - ]:         24 :   return header_value.substr(pos, end_pos - pos);
      67                 :            : }
      68                 :            : 
      69                 :          5 : void parse_query_string(const string & query_str, parameter_map & parameters) {
      70                 :            :   using NS_SSRC_WSPR_UTILITY::unescape_url;
      71                 :            : 
      72         [ -  + ]:          5 :   if(query_str.empty())
      73                 :          5 :     return;
      74                 :            : 
      75                 :          5 :   string::size_type and_pos = query_str.find('&');
      76                 :          5 :   string::size_type eq_pos = query_str.find('=');
      77                 :          5 :   string::size_type pos = 0;
      78   [ +  -  +  - ]:         10 :   string name, value;
      79                 :            : 
      80         [ +  + ]:         18 :   while(eq_pos != string::npos) {
      81   [ +  +  +  +  :         28 :     while(and_pos < eq_pos || and_pos == string::npos) {
                   +  + ]
      82         [ +  + ]:         12 :       if(and_pos == string::npos) {
      83         [ +  - ]:          5 :         and_pos = query_str.size();
      84                 :            :       } else {
      85                 :          7 :         pos = and_pos + 1;
      86         [ +  - ]:          7 :         and_pos = query_str.find('&', pos);
      87                 :            :       }
      88                 :            :     }
      89                 :            : 
      90                 :          8 :     const string::size_type key_size = eq_pos - pos;
      91                 :          8 :     const string::size_type value_size = and_pos - eq_pos - 1;
      92                 :            : 
      93         [ +  + ]:          8 :     if(key_size > 0) {
      94         [ +  - ]:          7 :       name.assign(query_str, pos, key_size);
      95         [ +  - ]:          7 :       value.assign(query_str, eq_pos + 1, value_size);
      96         [ +  - ]:          7 :       unescape_url(name);
      97         [ +  - ]:          7 :       unescape_url(value);
      98                 :            : 
      99                 :            :       // We do not allow leading or trailing whitespace in input values.
     100         [ +  - ]:          7 :       boost::algorithm::trim(value);
     101                 :            : 
     102   [ +  -  +  -  :          7 :       parameters.insert(parameter_map::value_type(name, value));
                   +  - ]
     103                 :            :     }
     104                 :            : 
     105         [ +  - ]:          8 :     eq_pos = query_str.find('=', and_pos);
     106                 :            :   }
     107                 :            : }
     108                 :            : 
     109                 :          1 : void parse_multipart_data(const string & data, const string & boundary,
     110                 :            :                           parameter_map & parameters)
     111                 :            : {
     112                 :          1 :   const string::size_type step = boundary.size();
     113                 :          1 :   const string::size_type max_pos = data.size();
     114                 :          1 :   string::size_type pos = data.find(boundary), start;
     115                 :            : 
     116         [ +  - ]:          1 :   if(pos != string::npos) {
     117                 :          1 :     pos+=step;   
     118                 :            : 
     119   [ +  -  +  +  :          5 :     while(pos < max_pos && data[pos] != '-') {
                   +  + ]
     120                 :            :       // Since we don't handle file data, we don't bother looking
     121                 :            :       // for specific header fields.  Instead, we assume only the
     122                 :            :       // Content-Disposition header contains a name attribute, thereby
     123                 :            :       // allowing us to search only for the next name attribute.
     124   [ +  -  +  -  :          6 :       const string && name = parse_header_attribute(data, "name", pos, &pos);
                   +  - ]
     125                 :            : 
     126   [ +  -  -  + ]:          3 :       if(name.empty()) {
     127                 :            :         break;
     128                 :            :       }
     129                 :            : 
     130                 :            :       // Look for start of data.
     131   [ +  -  -  + ]:          3 :       if((pos = data.find("\r\n\r\n", pos)) == string::npos) {
     132                 :            :         break;
     133                 :            :       }
     134                 :            : 
     135                 :          3 :       start = pos + 4;
     136                 :            : 
     137                 :            :       // Look for end of data.
     138   [ +  -  -  + ]:          3 :       if((pos = data.find(boundary, start)) == string::npos) {
     139                 :            :         break;
     140                 :            :       }
     141                 :            : 
     142         [ +  - ]:          3 :       if(pos - 2 >= start) {
     143   [ +  -  +  -  :          3 :         parameters.insert(parameter_map::value_type(name, data.substr(start, pos - start - 2)));
          +  -  +  -  +  
                      - ]
     144                 :            :       }
     145                 :            : 
     146         [ +  - ]:          6 :       pos+=step;
     147                 :            :     }
     148                 :            :   }
     149                 :          1 : }
     150                 :            : 
     151                 :          0 : void parse_get_parameters(const FCGIRequest & request,
     152                 :            :                           parameter_map & parameters)
     153                 :            : {
     154                 :          0 :   const char *query_str = request.fcgx_get_param("QUERY_STRING");
     155                 :            : 
     156   [ #  #  #  # ]:          0 :   if(!query_str || *query_str == 0) {
     157                 :          0 :     const char *request_uri = request.fcgx_get_param("REQUEST_URI");
     158         [ #  # ]:          0 :     if(request_uri) {
     159   [ #  #  #  #  :          0 :       URI uri(request_uri);
                   #  # ]
     160   [ #  #  #  #  :          0 :       parse_query_string(uri.query(), parameters);
                   #  # ]
     161                 :          0 :     }
     162                 :            :   } else
     163   [ #  #  #  #  :          0 :     parse_query_string(query_str, parameters);
                   #  # ]
     164                 :          0 : }
     165                 :            : 
     166                 :          0 : void parse_post_parameters(const FCGIRequest & request,
     167                 :            :                            parameter_map & parameters)
     168                 :            : {
     169                 :          0 :   const string && content_type = request.content_type();
     170                 :            :   const bool is_urlencoded =
     171         [ #  # ]:          0 :     (content_type.find("application/x-www-form-urlencoded") == 0);
     172                 :            :   const bool is_multipart =
     173   [ #  #  #  # ]:          0 :     (is_urlencoded ? false : (content_type.find( "multipart/form-data") == 0));
     174                 :            : 
     175                 :            :   // TODO: Check that content length is less than some maximum value first.
     176   [ #  #  #  # ]:          0 :   if(is_urlencoded || is_multipart) {
     177         [ #  # ]:          0 :     long length = request.content_length();
     178                 :            : 
     179         [ #  # ]:          0 :     if(length > 0) {
     180   [ #  #  #  # ]:          0 :       string data(length, ' ');
     181                 :          0 :       int bytes_read = 0, total = 0;
     182                 :            : 
     183                 :            :       // Using &data[i] as a contiguous buffer may not work with some
     184                 :            :       // STL implementations, but C++ 0x requires the behavior.
     185         [ #  # ]:          0 :       while(length > 0) {
     186   [ #  #  #  # ]:          0 :         bytes_read = request.fcgx_get_str(&data[total], length);
     187         [ #  # ]:          0 :         if(bytes_read <= 0)
     188                 :          0 :           break;
     189                 :          0 :         total+=bytes_read;
     190                 :          0 :         length-=bytes_read;
     191                 :            :       }
     192                 :            : 
     193                 :            :       // Ignore short reads.  The error will be handled elsewhere.
     194         [ #  # ]:          0 :       if(length != 0)
     195         [ #  # ]:          0 :         data.resize(total);
     196                 :            : 
     197         [ #  # ]:          0 :       if(is_urlencoded) {
     198         [ #  # ]:          0 :         parse_query_string(data, parameters);
     199                 :            :       } else {
     200   [ #  #  #  # ]:          0 :         string boundary("--");
     201   [ #  #  #  #  :          0 :         boundary.append(parse_header_attribute(content_type, "boundary"));
          #  #  #  #  #  
                      # ]
     202   [ #  #  #  # ]:          0 :         if(boundary.size() > 2) {
     203         [ #  # ]:          0 :           parse_multipart_data(data, boundary, parameters);
     204                 :            :         }
     205                 :            :       }
     206                 :            :     }
     207                 :            :   }
     208                 :          0 : }
     209                 :            : 
     210                 :            : __END_NS_SSRC_WSPR_FCGI