Support quoted spaces in configuration files.
[cubemap] / parse.cpp
1 #include <ctype.h>
2 #include <string.h>
3 #include <string>
4 #include <vector>
5
6 #include "log.h"
7 #include "parse.h"
8
9 using namespace std;
10
11 vector<string> split_tokens(const string &line)
12 {
13         vector<string> ret;
14         string current_token;
15
16         bool in_quote = false;
17
18         for (size_t i = 0; i < line.size(); ++i) {
19                 // Handle all escaped characters.
20                 if (line[i] == '\\' && i < line.size() - 1) {
21                         current_token.push_back(line[++i]);
22                         continue;
23                 }
24
25                 // Handle start and end quote.
26                 if (line[i] == '"') {
27                         in_quote = !in_quote;
28                         continue;
29                 }
30
31                 // Handle break.
32                 if (isspace(line[i]) && !in_quote) {
33                         if (!current_token.empty()) {
34                                 ret.push_back(current_token);
35                         }
36                         current_token.clear();
37                 } else {
38                         current_token.push_back(line[i]);
39                 }
40         }
41         if (!current_token.empty()) {
42                 ret.push_back(current_token);
43         }
44         return ret;
45 }
46
47 vector<string> split_lines(const string &str)
48 {
49         vector<string> ret;
50         string current_line;
51
52         for (size_t i = 0; i < str.size(); ++i) {
53                 // Skip \r if followed by an \n.
54                 if (str[i] == '\r' && i < str.size() - 1 && str[i + 1] == '\n') {
55                         continue;
56                 }
57
58                 // End of the current line?
59                 if (str[i] == '\n') {
60                         if (!current_line.empty()) {
61                                 ret.push_back(current_line);
62                         }
63                         current_line.clear();
64                 } else {
65                         current_line.push_back(str[i]);
66                 }
67         }
68         if (!current_line.empty()) {
69                 ret.push_back(current_line);
70         }
71         return ret;
72 }
73
74 HTTPHeaderMultimap extract_headers(const vector<string> &lines, const string &log_context)
75 {
76         HTTPHeaderMultimap parameters;
77         for (size_t i = 1; i < lines.size(); ++i) {
78                 size_t split = lines[i].find(":");
79                 if (split == string::npos) {
80                         log(WARNING, "[%s] Ignoring malformed HTTP response line '%s'",
81                                 log_context.c_str(), lines[i].c_str());
82                         continue;
83                 }
84
85                 string key(lines[i].begin(), lines[i].begin() + split);
86
87                 // Skip any spaces after the colon.
88                 do {
89                         ++split;
90                 } while (split < lines[i].size() && (lines[i][split] == ' ' || lines[i][split] == '\t'));
91
92                 string value(lines[i].begin() + split, lines[i].end());
93
94                 parameters.insert(make_pair(key, value));
95         }
96
97         return parameters;
98 }
99
100 #define MAX_REQUEST_SIZE 16384  /* 16 kB. */
101
102 RequestParseStatus wait_for_double_newline(string *existing_data, const char *new_data, size_t new_data_size)
103 {
104         // Guard against overlong requests gobbling up all of our space.
105         if (existing_data->size() + new_data_size > MAX_REQUEST_SIZE) {
106                 return RP_OUT_OF_SPACE;
107         }       
108
109         // See if we have \r\n\r\n anywhere in the request. We start three bytes
110         // before what we just appended, in case we just got the final character.
111         size_t existing_data_bytes = existing_data->size();
112         existing_data->append(string(new_data, new_data + new_data_size));
113         
114         const size_t start_at = (existing_data_bytes >= 3 ? existing_data_bytes - 3 : 0);
115         const char *ptr = reinterpret_cast<char *>(
116                 memmem(existing_data->data() + start_at, existing_data->size() - start_at,
117                        "\r\n\r\n", 4));
118         if (ptr == nullptr) {
119                 return RP_NOT_FINISHED_YET;
120         }
121         if (ptr != existing_data->data() + existing_data->size() - 4) {
122                 return RP_EXTRA_DATA;
123         }
124         return RP_FINISHED;
125 }