1 // -*- mode: c++; c-basic-offset:4 -*-
2 
3 // This file is part of libdap, A C++ implementation of the OPeNDAP Data
4 // Access Protocol.
5 
6 // Copyright (c) 2011 OPeNDAP, Inc.
7 // Author: James Gallagher <jgallagher@opendap.org>
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 //
23 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
24 #include "config.h"
25 
26 #include <iostream>
27 #include <vector>
28 
29 //#define DODS_DEBUG
30 
31 #include "Keywords2.h"
32 #include "Error.h"
33 #include "escaping.h"
34 #include "debug.h"
35 
36 using namespace std;
37 
38 namespace libdap {
39 
Keywords()40 Keywords::Keywords()
41 {
42     // Load known keywords and their allowed values
43     vector<string> v1(7);
44     v1[0] = "2"; v1[1] = "2.0"; v1[2] = "3.2"; v1[3] = "3.3"; v1[4] = "3.4";
45     v1[5] = "4"; v1[6] = "4.0";
46     value_set_t vs = value_set_t(v1.begin(), v1.end());
47     d_known_keywords["dap"] = vs;
48 
49     vector<string> v2(4);
50     v2[0] = "md5"; v2[1] = "MD5"; v2[2] = "sha1"; v2[3] = "SHA1";
51     value_set_t vs2 = value_set_t(v2.begin(), v2.end());
52     d_known_keywords["checksum"] = vs2;
53 }
54 
~Keywords()55 Keywords::~Keywords()
56 {
57 }
58 
59 /** Static function to parse the keyword notation.
60  * @param kw the keyword clause '<word> ( <value> )'
61  * @param word (result) the word
62  * @param value (result) the value
63  * @return True if the parse was successful (word and value contain useful
64  * results) and False otherwise.
65  */
f_parse_keyword(const string & kw,string & word,string & value)66 static bool f_parse_keyword(const string &kw, string &word, string &value)
67 {
68     word = "";
69     value = "";
70     string::size_type i = kw.find('(');
71     if (i == string::npos)
72 	return false;
73     word = kw.substr(0, i);
74     string::size_type j = kw.find(')');
75     if (j == string::npos)
76 	return false;
77     ++i; // Move past the opening paren
78     value = kw.substr(i, j-i);
79 
80     return (!word.empty() && !value.empty());
81 }
82 
83 /**
84  * Add the keyword to the set of keywords that apply to this request.
85  * @note Should call m_is_valid_keyword first.
86  * @param word The keyword/function name
87  * @param value The keyword/function value
88  */
m_add_keyword(const keyword & word,const keyword_value & value)89 void Keywords::m_add_keyword(const keyword &word, const keyword_value &value)
90 {
91     d_parsed_keywords[word] = value;
92 }
93 
94 /** Is the string a valid keyword clause? Assumption: the word and value have
95  * already been successfully parsed.
96  * @param word The keyword/function name
97  * @param value The keyword/function value
98  * @return True if the string is valid keyword and the value is one of the
99  * allowed values.
100  */
m_is_valid_keyword(const keyword & word,const keyword_value & value) const101 bool Keywords::m_is_valid_keyword(const keyword &word, const keyword_value &value) const
102 {
103     map<keyword, value_set_t>::const_iterator ci = d_known_keywords.find(word);
104     if (ci == d_known_keywords.end())
105 	return false;
106     else {
107 	value_set_t vs = ci->second;
108 
109 	if (vs.find(value) == vs.end())
110 	    throw Error("Bad value passed to the keyword/function: " + word);
111     }
112 
113     return true;
114 }
115 
116 /**
117  * Is the word one of the known keywords for this version of libdap?
118  * @param s As a string, including the value
119  * @return true if the keyword is known
120  */
is_known_keyword(const string & word) const121 bool Keywords::is_known_keyword(const string &word) const
122 {
123 	return d_known_keywords.count(word) == 1;
124 }
125 
126 /**
127  * Get a list of the strings that make up the set of current keywords for
128  * this request.
129  * @return The list of keywords as a list of string objects.
130  */
get_keywords() const131 list<Keywords::keyword> Keywords::get_keywords() const
132 {
133     list<keyword> kws;
134     map<keyword, keyword_value>::const_iterator i;
135     for (i = d_parsed_keywords.begin(); i != d_parsed_keywords.end(); ++i)
136     	kws.push_front((*i).first);
137 
138     return kws;
139 }
140 
141 
142 /**
143  * Lookup a keyword_kind and return true if it has been set for this request,
144  * otherwise return false.
145  * @param kw Keyword
146  * @return true if the keyword is set.
147  */
has_keyword(const keyword & kw) const148 bool Keywords::has_keyword(const keyword &kw) const
149 {
150     return d_parsed_keywords.count(kw) == 1;
151 }
152 
153 /** Look at the parsed keywords for the value associated with a given keyword.
154  *
155  * @param k
156  * @return The value
157  */
get_keyword_value(const keyword & kw) const158 Keywords::keyword_value Keywords::get_keyword_value(const keyword &kw) const
159 {
160     if (d_known_keywords.find(kw) == d_known_keywords.end())
161     	throw Error("Keyword not known (" + kw + ")");
162 
163     return d_parsed_keywords.find(kw)->second;
164 }
165 
166 /** Parse the constraint expression, removing all keywords. As a side effect,
167  * return the remaining CE.
168  * @param ce
169  * @return The CE stripped of all recognized keywords.
170  */
parse_keywords(const string & ce)171 string Keywords::parse_keywords(const string &ce)
172 {
173     // Get the whole CE
174     string projection = www2id(ce, "%", "%20");
175     string selection = "";
176 
177     // Separate the selection part (which follows/includes the first '&')
178     string::size_type amp = projection.find('&');
179     if (amp != string::npos) {
180 	selection = projection.substr(amp);
181 	projection = projection.substr(0, amp);
182     }
183 
184     // Extract keywords; add to the Keywords keywords. For this, scan for
185     // a known set of keywords and assume that anything else is part of the
186     // projection and should be left alone. Keywords must come before variables
187     // The 'projection' string will look like: '' or 'dap4.0' or 'dap4.0,u,v'
188     while (!projection.empty()) {
189 	string::size_type i = projection.find(',');
190 	string next_word = projection.substr(0, i);
191 	string word, value;
192 	if (f_parse_keyword(next_word, word, value)
193 	    && m_is_valid_keyword(word, value)) {
194 	    m_add_keyword(word, value);
195 	    if (i != string::npos)
196 		projection = projection.substr(i + 1);
197 	    else
198 		projection = "";
199 	}
200 	else {
201 	    break; // exit on first non-keyword
202 	}
203     }
204 
205     // The CE is whatever is left after removing the keywords
206     return projection + selection;
207 }
208 
209 }
210