root/main/trunk/greenstone2/runtime-src/src/recpt/basequeryaction.cpp @ 28888

Revision 28888, 27.4 KB (checked in by ak19, 4 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.

Line 
1/**********************************************************************
2 *
3 * basequeryaction.cpp --
4 * Copyright (C) 1999  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 "basequeryaction.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 "phrases.h" // for get_phrases
37#include <stdlib.h> // for strtol
38#include <assert.h>
39
40void colinfo_t::clear () {
41  formatlistptr = NULL;
42  browserptr = NULL;
43}
44
45void QueryResult_t::clear() {
46  doc.clear();
47  collection.clear();
48}
49
50basequeryaction::basequeryaction () {
51
52  recpt = NULL;
53
54  cgiarginfo arg_ainfo;
55
56  // "q"
57  arg_ainfo.shortname = "q";
58  arg_ainfo.longname = "query string";
59  arg_ainfo.multiplechar = true;
60  arg_ainfo.multiplevalue = false;
61  arg_ainfo.defaultstatus = cgiarginfo::weak;
62  arg_ainfo.argdefault = g_EmptyText;
63  arg_ainfo.savedarginfo = cgiarginfo::must;
64  argsinfo.addarginfo (NULL, arg_ainfo);
65
66  // "q2"
67  arg_ainfo.shortname = "q2";
68  arg_ainfo.longname = "query string for second query";
69  arg_ainfo.multiplechar = true;
70  arg_ainfo.multiplevalue = false;
71  arg_ainfo.defaultstatus = cgiarginfo::weak;
72  arg_ainfo.argdefault = g_EmptyText;
73  arg_ainfo.savedarginfo = cgiarginfo::must;
74  argsinfo.addarginfo (NULL, arg_ainfo);
75
76  // "cq2" ""=don't combine, "and", "or", "not"
77  arg_ainfo.shortname = "cq2";
78  arg_ainfo.longname = "combine queries";
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 
87  // "m"
88  arg_ainfo.shortname = "m";
89  arg_ainfo.longname = "maximum number of documents";
90  arg_ainfo.multiplechar = true;
91  arg_ainfo.multiplevalue = false;
92  arg_ainfo.defaultstatus = cgiarginfo::weak;
93  arg_ainfo.argdefault = "50";
94  arg_ainfo.savedarginfo = cgiarginfo::must;
95  argsinfo.addarginfo (NULL, arg_ainfo);
96
97  // "o"
98  arg_ainfo.shortname = "o";
99  arg_ainfo.longname = "hits per page";
100  arg_ainfo.multiplechar = true;
101  arg_ainfo.multiplevalue = false;
102  arg_ainfo.defaultstatus = cgiarginfo::weak;
103  arg_ainfo.argdefault = "20";
104  arg_ainfo.savedarginfo = cgiarginfo::must;
105  argsinfo.addarginfo (NULL, arg_ainfo);
106
107  // "r"
108  arg_ainfo.shortname = "r";
109  arg_ainfo.longname = "start results from";
110  arg_ainfo.multiplechar = true;
111  arg_ainfo.multiplevalue = false;
112  arg_ainfo.defaultstatus = cgiarginfo::weak;
113  arg_ainfo.argdefault = "1";
114  arg_ainfo.savedarginfo = cgiarginfo::must;
115  argsinfo.addarginfo (NULL, arg_ainfo);
116
117
118  // "ifl" - I'm feeling lucky! (Go directly to a matching document)
119  arg_ainfo.shortname = "ifl";
120  arg_ainfo.longname = "i'm feeling lucky";
121  arg_ainfo.multiplechar = false;
122  arg_ainfo.multiplevalue = false;
123  arg_ainfo.defaultstatus = cgiarginfo::weak;
124  arg_ainfo.argdefault = g_EmptyText;
125  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
126  argsinfo.addarginfo (NULL, arg_ainfo);
127
128  // "ifln" - I'm feeling lucky number (Go directly to the nth matching document)
129  arg_ainfo.shortname = "ifln";
130  arg_ainfo.longname = "i'm feeling lucky number";
131  arg_ainfo.multiplechar = true;
132  arg_ainfo.multiplevalue = false;
133  arg_ainfo.defaultstatus = cgiarginfo::weak;
134  arg_ainfo.argdefault = "1";
135  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
136  argsinfo.addarginfo (NULL, arg_ainfo);
137
138  // "srn" - the next search result
139  arg_ainfo.shortname = "srn";
140  arg_ainfo.longname = "the next search result number";
141  arg_ainfo.multiplechar = true;
142  arg_ainfo.multiplevalue = false;
143  arg_ainfo.defaultstatus = cgiarginfo::weak;
144  arg_ainfo.argdefault = "0";
145  arg_ainfo.savedarginfo = cgiarginfo::must;
146  argsinfo.addarginfo (NULL, arg_ainfo);
147
148  // "srp" - the previous search result
149  arg_ainfo.shortname = "srp";
150  arg_ainfo.longname = "the previous search result number";
151  arg_ainfo.multiplechar = true;
152  arg_ainfo.multiplevalue = false;
153  arg_ainfo.defaultstatus = cgiarginfo::weak;
154  arg_ainfo.argdefault = "0";
155  arg_ainfo.savedarginfo = cgiarginfo::must;
156  argsinfo.addarginfo (NULL, arg_ainfo);
157
158  // "sf" - Sort field. Set to field to be used for sorting search reult
159  // set (only implemented for lucene collections at present).
160  arg_ainfo.shortname = "sf";
161  arg_ainfo.longname = "sort field";
162  arg_ainfo.multiplechar = true;
163  arg_ainfo.multiplevalue = false;
164  arg_ainfo.defaultstatus = cgiarginfo::weak;
165  arg_ainfo.argdefault = g_EmptyText;
166  arg_ainfo.savedarginfo = cgiarginfo::must;
167  argsinfo.addarginfo (NULL, arg_ainfo);
168
169  // "so" - sort order. 0 = ascending (natural order),
170  //                    1 = descending (reverse order)
171  arg_ainfo.shortname = "so";
172  arg_ainfo.longname = "sort order";
173  arg_ainfo.multiplechar = false;
174  arg_ainfo.multiplevalue = false;
175  arg_ainfo.defaultstatus = cgiarginfo::weak;
176  arg_ainfo.argdefault = "0";
177  arg_ainfo.savedarginfo = cgiarginfo::must;
178  argsinfo.addarginfo (NULL, arg_ainfo);
179
180  // "fqn" - number of fields in the query form
181  arg_ainfo.shortname = "fqn";
182  arg_ainfo.longname = "form query num fields";
183  arg_ainfo.multiplechar = true;
184  arg_ainfo.multiplevalue = false;
185  arg_ainfo.defaultstatus = cgiarginfo::weak;
186  arg_ainfo.argdefault = "4";
187  arg_ainfo.savedarginfo = cgiarginfo::must;
188  argsinfo.addarginfo (NULL, arg_ainfo);
189
190  // "fqf" - the list of field names in the form query
191  // - a comma separated list
192  arg_ainfo.shortname = "fqf";
193  arg_ainfo.longname = "form query fields";
194  arg_ainfo.multiplechar = true;
195  arg_ainfo.multiplevalue = false;
196  arg_ainfo.defaultstatus = cgiarginfo::weak;
197  arg_ainfo.argdefault = g_EmptyText;
198  arg_ainfo.savedarginfo = cgiarginfo::must;
199  argsinfo.addarginfo (NULL, arg_ainfo);
200 
201  // "fqv" - the list of values in the form query
202  // - a comma separated list
203  arg_ainfo.shortname = "fqv";
204  arg_ainfo.longname = "form query values";
205  arg_ainfo.multiplechar = true;
206  arg_ainfo.multiplevalue = false;
207  arg_ainfo.defaultstatus = cgiarginfo::weak;
208  arg_ainfo.argdefault = g_EmptyText;
209  arg_ainfo.savedarginfo = cgiarginfo::must;
210  argsinfo.addarginfo (NULL, arg_ainfo);
211 
212
213  // "fqc" - the list of boolean operators in the form query
214  // - a comma separated list
215  arg_ainfo.shortname = "fqc";
216  arg_ainfo.longname = "form query combines";
217  arg_ainfo.multiplechar = true;
218  arg_ainfo.multiplevalue = false;
219  arg_ainfo.defaultstatus = cgiarginfo::weak;
220  arg_ainfo.argdefault = g_EmptyText;
221  arg_ainfo.savedarginfo = cgiarginfo::must;
222  argsinfo.addarginfo (NULL, arg_ainfo);
223
224  // "fqa" - form query advanced - for "run query"
225  arg_ainfo.shortname = "fqa";
226  arg_ainfo.longname = "form query advanced query";
227  arg_ainfo.multiplechar = false;
228  arg_ainfo.multiplevalue = false;
229  arg_ainfo.defaultstatus = cgiarginfo::weak;
230  arg_ainfo.argdefault = "0";
231  arg_ainfo.savedarginfo = cgiarginfo::must;
232  argsinfo.addarginfo (NULL, arg_ainfo);
233
234  // "fuzziness" controls how closely the search terms must match
235  // 100 = exact match, 0 = very inexact match (only implemented for Lucene)
236  arg_ainfo.shortname = "fuzziness";
237  arg_ainfo.longname = "Lucene fuzziness value";
238  arg_ainfo.multiplechar = true;
239  arg_ainfo.multiplevalue = false;
240  arg_ainfo.defaultstatus = cgiarginfo::weak;
241  arg_ainfo.argdefault = g_EmptyText;
242  arg_ainfo.savedarginfo = cgiarginfo::must;
243  argsinfo.addarginfo (NULL, arg_ainfo);
244
245  // "hd" history display  - search history only displayed when
246  // this var set to something other than 0
247  // this number of records is displayed
248  arg_ainfo.shortname = "hd";
249  arg_ainfo.longname = "history display";
250  arg_ainfo.multiplechar = true;
251  arg_ainfo.multiplevalue = false;
252  arg_ainfo.defaultstatus = cgiarginfo::weak;
253  arg_ainfo.argdefault = "0";
254  arg_ainfo.savedarginfo = cgiarginfo::must;
255  argsinfo.addarginfo (NULL, arg_ainfo);
256       
257  // "hs"  save - set to 1 in query form, so only save when submit
258  // query
259  // 0 = no save 1 = save
260  arg_ainfo.shortname = "hs";
261  arg_ainfo.longname = "history save";
262  arg_ainfo.multiplechar = false;
263  arg_ainfo.multiplevalue = false;
264  arg_ainfo.defaultstatus = cgiarginfo::weak;
265  arg_ainfo.argdefault = "0";
266  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
267  argsinfo.addarginfo (NULL, arg_ainfo);
268}
269
270void basequeryaction::configure (const text_t &key, const text_tarray &cfgline) {
271  action::configure (key, cfgline);
272}
273
274bool basequeryaction::init (ostream &logout) {
275  return action::init (logout);
276}
277
278bool basequeryaction::check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args,
279                 recptprotolistclass * /*protos*/, ostream &logout) {
280
281
282  // check m argument
283  int arg_m = args.getintarg("m");
284  if (arg_m < -1) {
285    logout << "Warning: \"m\" argument less than -1 (" << arg_m << ")\n";
286    cgiarginfo *minfo = argsinfo.getarginfo ("m");
287    if (minfo != NULL) args["m"] = minfo->argdefault;
288  }
289
290  // check o argument
291  int arg_o = args.getintarg("o");
292  if (arg_o < -1) {
293    logout << "Warning: \"o\" argument less than -1 (" << arg_o << ")\n";
294    cgiarginfo *oinfo = argsinfo.getarginfo ("o");
295    if (oinfo != NULL) args["o"] = oinfo->argdefault;
296  }
297
298  // check r argument
299  int arg_r = args.getintarg("r");
300  if (arg_r < 1) {
301    logout << "Warning: \"r\" argument less than 1 (" << arg_r << ")\n";
302    cgiarginfo *rinfo = argsinfo.getarginfo ("r");
303    if (rinfo != NULL) args["r"] = rinfo->argdefault;
304  }
305
306  //check hd argument
307  int arg_hd = args.getintarg("hd");
308  if (arg_hd <0 ) {
309    logout << "Warning: \"hd\" argument less than 0 (" << arg_hd << ")\n";
310    cgiarginfo *hdinfo = argsinfo.getarginfo ("hd");
311    if (hdinfo != NULL) args["hd"] = hdinfo->argdefault;
312  } 
313 
314  //check hs argument
315  int arg_hs = args.getintarg("hs");
316  if (arg_hs !=0 && arg_hs !=1) {
317    logout << "Warning: \"hs\" argument out of range (" << arg_hs << ")\n";
318    cgiarginfo *hsinfo = argsinfo.getarginfo ("hs");
319    if (hsinfo != NULL) args["hs"] = hsinfo->argdefault;
320  }
321
322  return true;
323}
324
325void basequeryaction::get_cgihead_info (cgiargsclass &args, recptprotolistclass * /*protos*/,
326                    response_t &response, text_t &response_data,
327                    ostream &/*logout*/) {
328  // If this is an "I'm feeling lucky" request, we don't know the target location until later
329  if (!args["ifl"].empty()) {
330    response = undecided_location;
331    return;
332  }
333
334  response = content;
335  response_data = "text/html";
336}
337
338
339
340
341void basequeryaction::define_internal_macros (displayclass &disp, cgiargsclass &args,
342                          recptprotolistclass * protos,
343                          ostream &logout)
344{
345  // The following macros are set later (in define_query_macros) as they can't be set until
346  // the query has been done.
347  // _quotedquery_   the part of the query string that was quoted for post-processing
348  // _freqmsg_      the term frequency string
349
350  // _resultline_   the "x documents matched the query" string
351
352  // _prevfirst_    these are used when setting up the links to previous/next
353  // _prevlast_     pages of results (_thisfirst_ and _thislast_ are used to set
354  // _nextfirst_    the 'results x-x for query: xxxx' string in the title bar)
355  // _nextlast_
356  // _thisfirst_
357  // _thislast_
358
359 
360  define_form_macros(disp, args, protos, logout);
361}
362
363
364
365
366
367// define_query_macros sets the macros that couldn't be set until the
368// query had been done. Those macros are
369// _resultline_, _nextfirst_, _nextlast_, _prevfirst_, _prevlast_,
370// _thisfirst_, and _thislast_ and _quotedquery_
371// this has been simplified so it can be used with both search_single_coll
372// and search_multiple_coll
373void basequeryaction::define_query_macros (cgiargsclass &args,
374                       displayclass &disp,
375                       int numdocs, isapprox isApprox) {
376 
377  // set up _resultline_ macro
378  text_t resline;
379  int maxdocs = args.getintarg("m");
380  if (maxdocs == -1) maxdocs = numdocs;
381  else if (numdocs > maxdocs) {
382    numdocs = maxdocs;
383    isApprox = MoreThan;
384  }
385
386  if (isApprox == Approximate) resline = "_textapprox_";
387  else if (isApprox == MoreThan) resline = "_textmorethan_";
388 
389  if (numdocs == 0) resline = "_textnodocs_";
390  else if (numdocs == 1) resline += "_text1doc_";
391  else resline += text_t(numdocs) + " _textlotsdocs_";
392 
393  disp.setmacro("resultline", "query", resline);
394
395  int firstdoc = args.getintarg("r");
396  int hitsperpage = args.getintarg("o");
397  if (hitsperpage == -1) hitsperpage = numdocs;
398
399  // set up _thisfirst_ and _thislast_ macros
400  disp.setmacro ("thisfirst", "query", firstdoc);
401  int thislast = firstdoc + (hitsperpage - 1);
402  if (thislast > numdocs) thislast = numdocs;
403  disp.setmacro ("thislast", "query", thislast);
404
405  // set up _prevfirst_ and _prevlast_ macros
406  if (firstdoc > 1) {
407    disp.setmacro ("prevlast", "query", firstdoc - 1);
408    int prevfirst = firstdoc - hitsperpage;
409    if (prevfirst < 1) prevfirst = 1;
410    disp.setmacro ("prevfirst", "query", prevfirst);
411  }
412
413  // set up _nextfirst_ and _nextlast_ macros
414  if (thislast < numdocs) {
415    disp.setmacro ("nextfirst", "query", thislast + 1);
416    int nextlast = thislast + hitsperpage;
417    if (nextlast > numdocs) nextlast = numdocs;
418    disp.setmacro ("nextlast", "query", nextlast);
419  }
420}
421
422
423// sets the selection box macros such as:
424//   _hselection_, _jselection_, _nselection_ _gselection_, _fqfselection_
425
426void basequeryaction::set_option_macro (const text_t &macroname,
427                    text_t current_value,
428                    bool display_single,
429                    bool add_js_update,
430                    const FilterOption_t &option,
431                    displayclass &disp)
432{
433  if (option.validValues.empty()) return;
434  if (option.validValues.size() == 1) {
435    if (display_single) {
436      disp.setmacro (macroname + "selection", displayclass::defaultpackage, "_" + option.defaultValue + "_");
437    }
438    return;   
439  }
440  if (option.validValues.size() < 2) return;
441 
442  text_t macrovalue = "<select name=\"" + macroname + "\"";
443  if (add_js_update) {
444    macrovalue += " onChange=\"update"+macroname+"();\"";
445  }
446  macrovalue += ">\n";
447 
448  if (current_value.empty()) current_value = option.defaultValue;
449 
450  text_tarray::const_iterator thisvalue = option.validValues.begin();
451  text_tarray::const_iterator endvalue = option.validValues.end();
452
453  while (thisvalue != endvalue) {
454    macrovalue += "<option value=\"" + *thisvalue + "\"";
455    if (*thisvalue == current_value)
456      macrovalue += " selected";
457    macrovalue += ">_" + *thisvalue + "_\n";
458    ++thisvalue;
459  }
460  macrovalue += "</select>\n";
461  disp.setmacro (macroname + "selection", displayclass::defaultpackage, macrovalue);
462}
463
464
465
466// sets the selection box macros such as:
467//   _sqlfqfselection_
468
469void basequeryaction::set_option_macro (const text_t &macroname,
470                    text_t current_value,
471                    bool display_single,
472                    bool add_js_update,
473                    const FilterOption_t &option_domain,
474                    const FilterOption_t &option_range,
475                    displayclass &disp)
476{
477  // this should probably be moved to sqlqueryaction.cpp // *****
478
479  if (option_domain.validValues.empty()) return;
480  if (option_range.validValues.empty()) return;
481
482  if (option_range.validValues.size() == 1) {
483    if (display_single) {
484      disp.setmacro (macroname + "selection",
485             displayclass::defaultpackage, "_" + option_range.defaultValue + "_");
486    }
487    return;   
488  }
489  if (option_domain.validValues.size() < 2) return;
490  if (option_range.validValues.size() < 2) return;
491 
492  text_t macrovalue = "<select name=\"" + macroname + "\"";
493  if (add_js_update) {
494    macrovalue += " onChange=\"update"+macroname+"();\"";
495  }
496  macrovalue += ">\n";
497 
498  if (current_value.empty()) current_value = option_domain.defaultValue;
499
500  text_tarray::const_iterator dom_thisvalue = option_domain.validValues.begin();
501  text_tarray::const_iterator dom_endvalue = option_domain.validValues.end();
502 
503  text_tarray::const_iterator ran_thisvalue = option_range.validValues.begin();
504  text_tarray::const_iterator ran_endvalue = option_range.validValues.end();
505
506
507  while ((dom_thisvalue != dom_endvalue) && (ran_thisvalue != ran_endvalue)) {
508    if (*ran_thisvalue != "ZZ" && *ran_thisvalue != "TX") {
509
510      text_t option_val = *dom_thisvalue;
511      option_val.replace(",","/");
512      macrovalue += "<option value=\"" + option_val + "\"";
513
514      if (*dom_thisvalue == current_value)
515    macrovalue += " selected";
516      macrovalue += ">_" + *ran_thisvalue + "_\n";
517    }
518
519    ++dom_thisvalue;
520    ++ran_thisvalue;
521  }
522  macrovalue += "</select>\n";
523  disp.setmacro (macroname + "selection", displayclass::defaultpackage, macrovalue);
524}
525
526
527
528
529// define_single_query_macros sets the extra macros for search_single_coll
530// that couldn't be set until the query had been done. Those macros are
531// _freqmsg_ and _stopwordsmsg_
532void basequeryaction::define_single_query_macros (cgiargsclass &args,
533                          displayclass &disp,
534                          const FilterResponse_t &response) {
535  // set up _freqmsg_ and _stopwordsmsg_ macros
536
537  text_t freqmsg = "";
538  freqmsg = "_textfreqmsg1_";
539  TermInfo_tarray::const_iterator this_term = response.termInfo.begin();
540  TermInfo_tarray::const_iterator end_term = response.termInfo.end();
541  while (this_term != end_term) {
542    freqmsg += (*this_term).term + ": " + (*this_term).freq;
543    if ((this_term + 1) != end_term)
544      freqmsg += ", ";
545    ++this_term;
546  }
547  disp.setmacro ("freqmsg", "query", freqmsg);
548
549  text_tset::const_iterator this_stopword = response.stopwords.begin();
550  text_tset::const_iterator end_stopword = response.stopwords.end();
551  if (this_stopword != end_stopword) {
552    text_t stopwordsmsg = "_textstopwordsmsg_ ";
553    while (this_stopword != end_stopword) {
554      if (stopwordsmsg != "_textstopwordsmsg_ ") {
555    stopwordsmsg += ", ";
556      }
557      stopwordsmsg += (*this_stopword);
558      ++this_stopword;
559    }
560    disp.setmacro("stopwordsmsg", "query", stopwordsmsg);
561  }
562}
563
564
565
566void basequeryaction::define_history_macros (displayclass &disp,
567                         cgiargsclass &args,
568                         recptprotolistclass *protos,
569                         ostream &logout)
570{
571
572  // defines the following macros
573  // _searchhistorylist_
574
575  text_t historylist;
576  int arghd = args.getintarg("hd");
577  if (arghd == 0) {
578    historylist="";
579  }
580  else {
581    historylist = "<!-- Search History List -->\n";
582   
583    text_t userid = args["z"];
584    text_tarray entries;
585    if (get_history_info (userid, entries, dbhome, logout)) {
586      int count = 1;
587      text_tarray::iterator here = entries.begin();
588      text_tarray::iterator end = entries.end();
589      int numrecords=(int)entries.size();
590      if (numrecords>arghd) { // only display some of them
591    numrecords = arghd;
592      }
593      historylist += "<form action=\"_gwcgi_\" name=\"HistoryForm\"><table width=\"537\">\n";
594
595      for (int i=0; i<numrecords;++i) {
596    text_t query;
597    text_t numdocs;
598    text_t cgiargs;
599    text_t userinfo;
600    text_t escquery;
601    split_saved_query(entries[i],numdocs,cgiargs);
602    parse_saved_args(cgiargs, "q", query); // get query string out
603    decode_cgi_arg(query); // un cgisafe it
604    escquery = escape_quotes(query); // escape the quotes and newlines
605    text_t histvalue = "histvalue";
606    histvalue += i;
607    disp.setmacro(histvalue, "query", escquery);
608    format_user_info(cgiargs, userinfo, args, protos, logout);
609   
610    historylist += "<tr><td align=\"right\">_imagehistbutton_(";
611    historylist += i;
612    historylist += ")</td>\n";
613    historylist += "<td><table border=\"1\" cellspacing=\"0\" ";
614    historylist += "cellpadding=\"0\"><tr><td width=\"365\" align=\"left\">"
615      + query
616      + "</td></tr></table></td><td width=\"110\" align=\"center\"><small>"
617      + numdocs;
618    if (numdocs == 1) historylist += " _texthresult_";
619    else historylist += " _texthresults_";
620    if (!userinfo.empty()) {
621      historylist += "<br>( "+userinfo+" )";
622    }
623    historylist += "</small></td>\n";
624      }
625      historylist+="</table></form>\n\n";
626     
627    } // if get history info
628    else {
629      historylist += "_textnohistory_";
630    }
631    historylist += "<! ---- end of history list ----->\n";
632  } // else display list
633  disp.setmacro("searchhistorylist", "query", historylist);
634 
635} // define history macros
636
637
638bool basequeryaction::search_single_collection (cgiargsclass& args,
639                        const text_t& collection,
640                        recptprotolistclass *protos,
641                        browsermapclass* browsers,
642                        displayclass& disp,
643                        outconvertclass& outconvert,
644                        ostream& textout,
645                        ostream& logout)
646{
647  recptproto *collectproto = protos->getrecptproto (collection, logout);
648  if (collectproto == NULL) {
649    logout << outconvert << "basequeryaction::search_single_collection: " << collection
650       << " collection has a NULL collectproto\n";
651
652    // Display the "this collection is not installed on this system" page
653    disp.setmacro("cvariable", displayclass::defaultpackage, encodeForHTML(collection));
654    disp.setmacro("content", "query", "<p>_textbadcollection_<p>");
655
656    textout << outconvert << disp << "_query:header_\n"
657        << "_query:content_\n" << "_query:footer_\n";
658    return true;
659  }
660
661  comerror_t err;
662  ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr (collectproto, collection, logout);
663 
664  if (cinfo == NULL) {
665    logout << "ERROR (query_action::search_single_collection): get_collectinfo_ptr returned NULL\n";
666    return false;
667  }
668   
669  bool segment = cinfo->isSegmented;
670
671  // get the formatted query string
672  text_t formattedstring = "";
673  get_formatted_query_string(formattedstring, segment, args, disp, logout);
674
675  if (formattedstring.empty()) {
676    // no point carrying on if we have no query
677    define_history_macros (disp, args, protos, logout);
678    textout << outconvert << disp << "_query:header_\n"
679      << "_query:content_";
680    textout << outconvert << disp << "_query:footer_";
681
682    return true;
683  }
684   
685  // (.*)queryaction uses "VList" browser to display results,
686  // a queries clasification is "Search"
687  text_t browsertype = "VList";
688  text_t classification = "Search";
689
690  browserclass *bptr = browsers->getbrowser (browsertype);
691
692  // get the formatstring if there is one
693  text_t formatstring;
694  if (!get_formatstring (classification, browsertype,
695             cinfo->format, formatstring)) {
696    formatstring = bptr->get_default_formatstring();
697  }
698  FilterRequest_t request;
699  FilterResponse_t response;
700
701  text_t hits_per_page_old = args["o"];
702  text_t start_results_from_old = args["r"]; 
703
704  // if the "ifl" argument is set to 1, we only want to get one document
705  // this may be the first search result (from "I feel lucky") or maybe a
706  // specified search result (from next/prev search result link)
707  if (args["ifl"] == "1") {
708    args["r"] = args["ifln"]; // the document number we want
709    args["o"] = "1";
710  }
711
712  bptr->set_filter_options (request, args);
713  bptr->load_metadata_defaults (request.fields);
714
715  format_t *formatlistptr = new format_t();
716  parse_formatstring (formatstring, formatlistptr, request.fields, request.getParents);
717   
718  request.filterResultOptions = FROID | FRmetadata | FRtermFreq;
719  // note! formattedstring is in unicode! mg and mgpp must convert!
720  set_queryfilter_options (request, formattedstring, args);
721 
722  // do the query
723  collectproto->filter (collection, request, response, err, logout);
724
725  // we had an error
726  if (err != noError) {
727    outconvertclass text_t2ascii;
728    logout << text_t2ascii
729       << "basequeryaction::search_single_collections: call to "
730       << query_filter_name() << " failed "
731       << "for " << collection << " collection (" << get_comerror_string (err) << ")\n";
732   
733    disp.setmacro("resultline", "query", "_textnodocs_");
734    if (err == syntaxError) {
735      disp.setmacro ("freqmsg", "query", "_textinvalidquery_");
736    } else {
737      disp.setmacro ("freqmsg", "query", "");
738    }
739    // restore r and o args in case they have changed
740    args["r"] = start_results_from_old;
741    args["o"] = hits_per_page_old;
742   
743    define_history_macros (disp, args, protos, logout);
744   
745    textout << outconvert << disp << "_query:header_\n"
746        << "_query:content_";
747    textout << outconvert << disp << "_query:footer_";
748    delete (formatlistptr);
749    return true;
750  }
751 
752  // ok, so no error, lets output the results
753 
754  // Perform the "I'm feeling lucky" trick if the "ifl" argument is set
755  if (!args["ifl"].empty()) {
756    //Restore the "r" and "o" arg
757    args["r"] = start_results_from_old;
758    args["o"] = hits_per_page_old;
759   
760    //Find whether DocumentSearchResultLinks is enabled
761    bool show_links = false;
762    text_tmap::const_iterator format_here = cinfo->format.begin();
763    text_tmap::const_iterator format_end = cinfo->format.end();
764   
765    while (format_here != format_end) {
766      if (((*format_here).first == "DocumentSearchResultLinks") &&
767      ((*format_here).second == "true")){
768    show_links = true;
769    break;
770      }
771      ++format_here;
772    }
773   
774    if (args["ifl"] == 1 || (args["ifl"] == 2 && response.numDocs == 1)) {
775     
776      // The first search result is the one we want
777      if (response.docInfo.begin() != response.docInfo.end()) {
778   
779    ResultDocInfo_tarray::iterator section = response.docInfo.begin();
780   
781    // We still need to set "srn" and "srp" values (next and prev search result numbers) if we are showing them
782    int srn = 0;
783    int srp = 0;
784    if (show_links) {
785      int ifln = args["ifln"].getint();
786      srn = ifln + 1;
787      if (srn > response.numDocs) {
788        srn = 0;
789      }
790      srp = ifln - 1;
791      if (srp < 0) {
792        srp = 0;
793      }
794    }
795   
796    textout << outconvert << disp
797        << "Location: _gwcgi_?e=_compressedoptions_&a=d&c="
798        << collection << "&cl=search&d=" << (*section).OID
799        << "&srn=" << srn << "&srp=" << srp << "\n\n";
800    textout << flush;
801   
802    return true;
803      }
804    }
805   
806    // There weren't enough (or any) matching documents
807    // We'll just carry on as if ifl wasn't set. The only catch is that get_cgihead_info won't have
808    // done the right thing (because ifl was set), so we need to make sure the output is html
809    textout << "Content-type: text/html\n\n";
810   
811  }
812
813  define_query_macros (args, disp, response.numDocs, response.isApprox);
814  define_single_query_macros(args, disp, response);
815  // save the query if appropriate
816  save_search_history(args, response.numDocs, response.isApprox);
817 
818 
819  // would this result in an error  in err???
820  // If Lucene threw a TooManyClauses exception, tell the user about it
821  if (args["ct"] == 2 && response.error_message == "TOO_MANY_CLAUSES") {
822    cerr << "too many clauses\n";
823    disp.setmacro ("freqmsg", "query", "_textlucenetoomanyclauses_");
824  }
825 
826
827  //Restore the "r" and "o" arg in case they have been changed and we still get here
828  args["r"] = start_results_from_old;
829  args["o"] = hits_per_page_old;
830
831  define_history_macros (disp, args, protos, logout);
832
833  textout << outconvert << disp << "_query:header_\n"
834      << "_query:content_";
835 
836  // output the results
837  text_t numdocs_t = response.numDocs; 
838  args["nmd"] = numdocs_t;
839  bool use_table = is_table_content (formatlistptr);
840  bptr->output_section_group (response, args, collection, 0, formatlistptr,
841                  use_table, request.fields, request.getParents,
842                  collectproto, disp, outconvert, textout, logout);
843 
844 
845  textout << outconvert << disp << "_query:footer_";
846 
847  delete (formatlistptr);
848 
849  return true;
850}
851 
Note: See TracBrowser for help on using the browser.