source: main/trunk/greenstone2/runtime-src/src/recpt/sqlqueryaction.cpp@ 28888

Last change on this file since 28888 was 28888, checked in by ak19, 10 years ago

First security commit. 1. Introducing the new securitools.h and .cpp files, which port the functions necessary to implement security in Greenstone from OWASP-ESAPI for Java, since OWASP's C++ version is largely not yet implemented, even though their code compiles. The newly added runtime-src/packages/security which contains OWASP ESAPI for C++ will therefore be removed again shortly. 2. receptionist.cpp now sets various web-encoded variants for each cgiarg macro, such as HTML entity encoded, attr encoded, javascript encoded (and css encoded variants). These are now used in the macro files based on which variant is suited to the context. 3. This commit further contains the minimum changes to protect the c, d, and p cgi variables.

File size: 13.5 KB
Line 
1/**********************************************************************
2 *
3 * sqlqueryaction.cpp --
4 * Copyright (C) 2010 The New Zealand Digital Library Project
5 *
6 * A component of the Greenstone digital library software
7 * from the New Zealand Digital Library Project at the
8 * University of Waikato, New Zealand.
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 of the License, or
13 * (at your option) 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 this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 *********************************************************************/
25
26#include "sqlqueryaction.h"
27#include "querytools.h"
28#include "formattools.h"
29#include "cgiutils.h"
30#include "OIDtools.h"
31#include "fileutil.h"
32#include "text_t.h"
33#include "historydb.h"
34#include "htmlutils.h" // for html_safe in do_action
35#include "gsdltools.h"
36#include <stdlib.h> // for strtol
37#include <assert.h>
38
39
40sqlqueryaction::sqlqueryaction ()
41 : basequeryaction()
42{
43 cgiarginfo arg_ainfo;
44
45 // this action uses cgi variable "a"
46 arg_ainfo.shortname = "a";
47 arg_ainfo.longname = "action";
48 arg_ainfo.multiplechar = true;
49 arg_ainfo.multiplevalue = false;
50 arg_ainfo.defaultstatus = cgiarginfo::weak;
51 arg_ainfo.argdefault = "sqlq";
52 arg_ainfo.savedarginfo = cgiarginfo::must;
53 argsinfo.addarginfo (NULL, arg_ainfo);
54
55 // "sqlqto" - 0 = not available, 1 = available
56 arg_ainfo.shortname = "sqlqto";
57 arg_ainfo.longname = "sqlquery type options";
58 arg_ainfo.multiplechar = true; // can be empty or single char
59 arg_ainfo.multiplevalue = false;
60 arg_ainfo.defaultstatus = cgiarginfo::weak;
61 arg_ainfo.argdefault = g_EmptyText;
62 arg_ainfo.savedarginfo = cgiarginfo::must;
63 argsinfo.addarginfo (NULL, arg_ainfo);
64
65 // "sqlfqn" - number of fields in the query form
66 arg_ainfo.shortname = "sqlfqn";
67 arg_ainfo.longname = "sql form query num fields";
68 arg_ainfo.multiplechar = true;
69 arg_ainfo.multiplevalue = false;
70 arg_ainfo.defaultstatus = cgiarginfo::weak;
71 arg_ainfo.argdefault = "4";
72 arg_ainfo.savedarginfo = cgiarginfo::must;
73 argsinfo.addarginfo (NULL, arg_ainfo);
74
75 // "sqlfqf" - the list of field names in the form query
76 // - a comma separated list
77 arg_ainfo.shortname = "sqlfqf";
78 arg_ainfo.longname = "sql form query fields";
79 arg_ainfo.multiplechar = true;
80 arg_ainfo.multiplevalue = false;
81 arg_ainfo.defaultstatus = cgiarginfo::weak;
82 arg_ainfo.argdefault = g_EmptyText;
83 arg_ainfo.savedarginfo = cgiarginfo::must;
84 argsinfo.addarginfo (NULL, arg_ainfo);
85
86 // "sqlfqv" - the list of values in the form query
87 // - a comma separated list
88 arg_ainfo.shortname = "sqlfqv";
89 arg_ainfo.longname = "sql form query values";
90 arg_ainfo.multiplechar = true;
91 arg_ainfo.multiplevalue = false;
92 arg_ainfo.defaultstatus = cgiarginfo::weak;
93 arg_ainfo.argdefault = g_EmptyText;
94 arg_ainfo.savedarginfo = cgiarginfo::must;
95 argsinfo.addarginfo (NULL, arg_ainfo);
96
97 // "sqlfqc" - the list of boolean operators in the form query
98 // - a comma separated list
99 arg_ainfo.shortname = "sqlfqc";
100 arg_ainfo.longname = "sql form query combines";
101 arg_ainfo.multiplechar = true;
102 arg_ainfo.multiplevalue = false;
103 arg_ainfo.defaultstatus = cgiarginfo::weak;
104 arg_ainfo.argdefault = g_EmptyText;
105 arg_ainfo.savedarginfo = cgiarginfo::must;
106 argsinfo.addarginfo (NULL, arg_ainfo);
107
108 // ****
109 // Looks like 'multiplevalue' is left undefined (e.g. not set in
110 // constructor). This looks like a fairly major problem
111 // in terms of leaving it undefine
112 // => in the case of "sqlsf" leads to conflict over value it has
113
114 // "sqlsf" - Sort field. Set to field to be used for sorting search reult
115 // set
116 arg_ainfo.shortname = "sqlsf";
117 arg_ainfo.longname = "sql sort field";
118 arg_ainfo.multiplechar = true;
119 arg_ainfo.multiplevalue = false;
120 arg_ainfo.defaultstatus = cgiarginfo::weak;
121 arg_ainfo.argdefault = g_EmptyText;
122 arg_ainfo.savedarginfo = cgiarginfo::must;
123 argsinfo.addarginfo (NULL, arg_ainfo);
124
125 // "sqlfilter" - boolean flag to control whether "delete all" or
126 // "keep all" functionality is available
127 arg_ainfo.shortname = "sqlfilter";
128 arg_ainfo.longname = "whether sql filtering is active or not";
129 arg_ainfo.multiplechar = false;
130 arg_ainfo.multiplevalue = false;
131 arg_ainfo.defaultstatus = cgiarginfo::weak;
132 arg_ainfo.argdefault = "0";
133 arg_ainfo.savedarginfo = cgiarginfo::must;
134 argsinfo.addarginfo (NULL, arg_ainfo);
135}
136
137sqlqueryaction::~sqlqueryaction ()
138{
139}
140
141void sqlqueryaction::configure (const text_t &key, const text_tarray &cfgline) {
142 action::configure (key, cfgline);
143}
144
145bool sqlqueryaction::init (ostream &logout)
146{
147 return basequeryaction::init (logout);
148}
149
150
151bool sqlqueryaction::check_cgiargs (cgiargsinfoclass &argsinfo,
152 cgiargsclass &args,
153 recptprotolistclass* protos,
154 ostream &logout)
155{
156
157 // check sqlfqn argument
158 int arg_sqlfqn = args.getintarg("sqlfqn");
159 if (arg_sqlfqn < -1) {
160 logout << "Warning: \"sqlfqn\" argument less than -1 (" << arg_sqlfqn << ")\n";
161 cgiarginfo *sqlfqninfo = argsinfo.getarginfo ("sqlfqn");
162 if (sqlfqninfo != NULL) args["sqlfqn"] = sqlfqninfo->argdefault;
163 }
164
165 return basequeryaction::check_cgiargs(argsinfo,args,protos,logout);
166}
167
168
169
170void sqlqueryaction::define_external_macros (displayclass &disp,
171 cgiargsclass &args,
172 recptprotolistclass *protos,
173 ostream &logout)
174{
175 recptproto *collectproto = protos->getrecptproto (args["c"], logout);
176 if (collectproto == NULL) return;
177
178 ColInfoResponse_t *colinfo = recpt->get_collectinfo_ptr(collectproto,
179 args["c"],
180 logout);
181 comerror_t err;
182 InfoFilterOptionsResponse_t response;
183 InfoFilterOptionsRequest_t request;
184 request.filterName = "SQLQueryFilter";
185
186 collectproto->get_filteroptions (args["c"], request, response, err, logout);
187 if (err == noError) {
188 FilterOption_tmap::const_iterator it_dom;
189 FilterOption_tmap::const_iterator it_ran;
190 FilterOption_tmap::const_iterator end = response.filterOptions.end();
191
192 // _sqlfqfselection_ field list
193 it_dom = response.filterOptions.find("IndexFieldDomain");
194 it_ran = response.filterOptions.find("IndexFieldRange");
195 if ((it_dom!=end) && (it_ran!=end)) {
196
197 set_option_macro ("sqlfqf",args["sqlfqf"], true,true,
198 (*it_dom).second, (*it_ran).second, disp);
199
200 if (args["b"] == "1") {
201 // set the sort field macro
202 set_sfselection_macro(args["sqlsf"], (*it_dom).second,(*it_ran).second,
203 disp);
204 }
205 }
206 }
207
208
209}
210
211
212void sqlqueryaction::set_sfselection_macro(text_t current_value,
213 const FilterOption_t &option_domain,
214 const FilterOption_t &option_range,
215 displayclass &disp)
216{
217
218 // we need at least one option here to continue
219 if (option_domain.validValues.size() < 1) { return; }
220 if (option_range.validValues.size() < 1) { return; }
221
222 text_t macrovalue = "<select name=\"sqlsf\">\n";
223
224 if (current_value.empty()) current_value = "";
225
226 text_tarray::const_iterator dom_thisvalue = option_domain.validValues.begin();
227 text_tarray::const_iterator dom_endvalue = option_domain.validValues.end();
228
229 text_tarray::const_iterator ran_thisvalue = option_range.validValues.begin();
230 text_tarray::const_iterator ran_endvalue = option_range.validValues.end();
231
232 int valid_count = 0;
233 while ((dom_thisvalue != dom_endvalue) && (ran_thisvalue != ran_endvalue)) {
234
235 if (*ran_thisvalue != "ZZ" && *ran_thisvalue != "TX") {
236 ++valid_count;
237
238 text_t option_val = *dom_thisvalue;
239 option_val.replace(",","/");
240
241 macrovalue += "<option value=\"" + option_val + "\"";
242 if (current_value == *dom_thisvalue)
243 macrovalue += " selected";
244 macrovalue += ">_" + *ran_thisvalue + "_\n";
245 }
246 ++dom_thisvalue;
247 ++ran_thisvalue;
248 }
249 macrovalue += "</select>";
250 if (valid_count > 0) {
251 disp.setmacro ("sqlsfselection", displayclass::defaultpackage, macrovalue);
252 }
253}
254
255
256void sqlqueryaction::get_formatted_query_string (text_t& formattedstring,
257 bool segment,
258 cgiargsclass& args,
259 displayclass& disp,
260 ostream& logout)
261{
262 // A great many characters have meanings in SQL queries, including > and %,
263 // where % stands for a multi-char wildcard
264 // http://docs.oracle.com/cd/B10501_01/text.920/a96518/cqspcl.htm
265 // Further, Greenstone's Advanced SQLite Search allows <, >, %, ' (rounded brackets and more)
266 // So it's best to url-decode all encoded cgi-args
267 // We do so here if normal text search or explicit query, and in the
268 // parse_sql_query_form functions if dealing with forms.
269
270 if (args["qt"]=="0" && args["sqlqto"] != "1") { // normal text search
271 unsafe_cgi_arg("ALL", args["q"]);
272 formattedstring = "SELECT DISTINCT docOID FROM document_metadata WHERE " + args["q"];
273 }
274 else if (args["qt"]=="1" || args["sqlqto"]=="1"){ // form search
275
276 if (args["b"]=="1" && args["fqa"]=="1") { // explicit query
277 formattedstring = args["q"];
278 unsafe_cgi_arg("ALL", formattedstring);
279 }
280 else { // form search
281 // *****
282 // Consider making this its own method in basequeryaction
283 // then make parse_.*reg_query_form virtual method in class
284
285 if (args["b"]=="0") { // regular form
286 parse_sqlreg_query_form(formattedstring, args, segment);
287 }
288 else { // advanced form
289 parse_sqladv_query_form(formattedstring, args, segment);
290 }
291 args["q"] = formattedstring;
292
293 // reset the cgiargfqv macro - need to escape any quotes in it
294 disp.setmacro("cgiargfqv", "query", escape_quotes(args["fqv"]));
295
296 // also reset the _cgiargq_ macro as it has changed now
297 disp.setmacro("cgiargq", displayclass::defaultpackage, html_safe(args["q"]));
298
299 // reset the compressed options to include the q arg
300 text_t compressedoptions = recpt->get_compressed_arg(args, logout);
301 if (!compressedoptions.empty()) {
302 disp.setmacro ("compressedoptions", displayclass::defaultpackage, dm_safe(compressedoptions));
303 // need a decoded version of compressedoptions for use within forms
304 // as browsers encode values from forms before sending to server
305 // (e.g. %25 becomes %2525)
306 decode_cgi_arg (compressedoptions);
307 if (args["w"] == "utf-8") { // if the encoding was utf-8, then compressed options was utf-8, and we need unicode.
308 // if encoding wasn't utf-8, then compressed opotions may be screwed up, but seems to work for 8 bit encodings?
309 compressedoptions = to_uni(compressedoptions);
310 }
311
312 text_t dmacrovalue = dm_safe(compressedoptions);
313 disp.setmacro ("decodedcompressedoptions", displayclass::defaultpackage, dmacrovalue);
314 disp.setmacro ("decodedcompressedoptionsAttrsafe", displayclass::defaultpackage, encodeForHTMLAttr(dmacrovalue));
315 }
316 } // form search
317 } // args["qt"]=1
318 else {
319 logout << "ERROR (sqlqueryaction::get_formatted_query_string): querytype not defined\n";
320 }
321
322}
323
324
325// request.filterResultOptions and request.fields (if required) should
326// be set from the calling code
327void sqlqueryaction::set_queryfilter_options (FilterRequest_t &request,
328 const text_t &querystring,
329 cgiargsclass &args)
330{
331 request.filterName = "SQLQueryFilter";
332
333 OptionValue_t option;
334
335 option.name = "SQLWhere";
336 option.value = querystring;
337 request.filterOptions.push_back (option);
338
339 set_sql_queryfilter_options(request, args);
340}
341
342
343
344// should this change for cross coll search??
345bool sqlqueryaction::save_search_history (cgiargsclass &args, int numdocs,
346 isapprox isApprox)
347{
348
349
350 if (args["q"]=="") return true; // null query, dont save
351 if (args["hs"]=="0") return true; // only save when submit query pressed
352
353 // get userid
354 text_t userid = args["z"];
355
356 // the number of docs goes on the front of the query string
357 text_t query = text_t(numdocs);
358 if (isApprox==MoreThan) { // there were more docs found
359 query.push_back('+');
360 }
361 query += "c="+args["c"];
362
363 text_t qstring = args["q"];
364 //text_t formattedquery =cgi_safe(qstring);
365 //query += "&amp;q="+formattedquery;
366 query += ";q="+qstring;
367 bool display=false;
368 int hd = args.getintarg("hd");
369 if (hd > 0) display=true;
370 if (set_history_info(userid, query, dbhome, display)) return true;
371 else return false;
372
373}
374
375void sqlqueryaction::define_form_macros (displayclass &disp,
376 cgiargsclass &args,
377 recptprotolistclass *protos,
378 ostream &logout)
379{
380 // defines the following macros
381 // _sqlregformlist_
382 // _sqladvformlist_
383
384 text_t form = "";
385 int argsqlfqn = args.getintarg("sqlfqn");
386
387 if (args["b"] == "1") { // advanced form
388 form += "_firstsqladvformelement_\n";
389 for (int i=1; i<argsqlfqn; ++i) {
390 form += "_sqladvformelement_\n";
391 }
392 disp.setmacro("sqladvformlist", "query", form);
393 }
394 else { // simple form
395 for (int i=0; i<argsqlfqn; ++i) {
396 form += "_sqlregformelement_\n";
397 }
398 disp.setmacro("sqlregformlist", "query", form);
399 }
400
401}
402
403
404bool sqlqueryaction::do_action (cgiargsclass &args,
405 recptprotolistclass *protos,
406 browsermapclass *browsers, displayclass &disp,
407 outconvertclass &outconvert, ostream &textout,
408 ostream &logout)
409{
410 return search_single_collection (args, args["c"], protos, browsers, disp,
411 outconvert, textout, logout);
412}
413
414
415
Note: See TracBrowser for help on using the repository browser.