source: branches/z3950-branch/gsdl/src/recpt/cgiutils.cpp@ 1342

Last change on this file since 1342 was 1342, checked in by johnmcp, 24 years ago

Relatively stable z39.50 implementation now, merged with the mgpp source.
(Still needs a decent interface and query language though...)

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 13.7 KB
Line 
1/**********************************************************************
2 *
3 * cgiutils.cpp -- general cgi utilities
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 "cgiutils.h"
27
28
29static unsigned short hexdigit (unsigned short c) {
30 if (c >= '0' && c <= '9') return (c-'0');
31 if (c >= 'a' && c <= 'f') return (c-'a'+10);
32 if (c >= 'A' && c <= 'F') return (c-'A'+10);
33 return c;
34}
35
36
37static void c2hex (unsigned short c, text_t &t) {
38 t.clear();
39
40 if (c >= 256) {
41 t = "20"; // ' '
42 return;
43 }
44
45 unsigned short o1, o2;
46
47 o1 = (c/16) % 16;
48 o2 = c % 16;
49 if (o1 >= 10) o1 += 'a' - 10;
50 else o1 += '0';
51 if (o2 >= 10) o2 += 'a' - 10;
52 else o2 += '0';
53
54 t.push_back(o1);
55 t.push_back(o2);
56}
57
58// convert %xx and + to their appropriate equivalents
59void decode_cgi_arg (text_t &argstr) {
60 text_t::iterator in = argstr.begin();
61 text_t::iterator out = in;
62 text_t::iterator end = argstr.end();
63
64 while (in != end) {
65 if (*in == '+') *out = ' ';
66
67 else if (*in == '%') {
68 unsigned short c = '%';
69 in++;
70 if (in != end) {
71 c = hexdigit (*in);
72 in++;
73 }
74 if (in != end && c < 16) { // sanity check on the previous character
75 c = c*16 + hexdigit (*in);
76 }
77
78 *out = c;
79 } else *out = *in;
80
81 if (in != end) in++;
82 out++;
83 }
84
85 // remove the excess characters
86 argstr.erase (out, end);
87}
88
89
90// split up the cgi arguments
91void split_cgi_args (const cgiargsinfoclass &argsinfo, text_t argstr,
92 cgiargsclass &args) {
93 args.clear();
94
95 text_t::iterator here = argstr.begin();
96 text_t::iterator end = argstr.end();
97 text_t key, value;
98
99 // extract out the key=value pairs
100 while (here != end) {
101 // get the next key and value pair
102 here = getdelimitstr (here, end, '=', key);
103 here = getdelimitstr (here, end, '&', value);
104
105 // convert %xx and + to their appropriate equivalents
106 decode_cgi_arg (value);
107
108 value.setencoding(1); // other encoding
109 // store this key=value pair
110 if (!key.empty()) {
111 // if arg occurs multiple times (as is the case with
112 // multiple checkboxes using the same name) we'll
113 // create a comma separated list of all the values
114 // (if multiplevalue)
115 if (!args[key].empty()) {
116 const cgiarginfo *info = argsinfo.getarginfo (key);
117 if (info != NULL && info->multiplevalue)
118 args[key] += "," + value;
119 else args.setarg (key, value, cgiarg_t::cgi_arg);
120 } else
121 args.setarg (key, value, cgiarg_t::cgi_arg);
122 }
123 }
124}
125
126text_t minus_safe (const text_t &intext) {
127
128 text_t outtext;
129
130 text_t::const_iterator here = intext.begin ();
131 text_t::const_iterator end = intext.end ();
132
133 while (here != end) {
134 if (*here == '-') outtext += "Z-";
135 else outtext.push_back (*here);
136 here ++;
137 }
138 outtext = cgi_safe (outtext);
139 return outtext;
140}
141
142text_t cgi_safe (const text_t &intext) {
143 text_t outtext;
144
145 text_t::const_iterator here = intext.begin ();
146 text_t::const_iterator end = intext.end ();
147 unsigned short c;
148 text_t ttmp;
149
150 while (here != end) {
151 c = *here;
152 if (((c >= 'a') && (c <= 'z')) ||
153 ((c >= 'A') && (c <= 'Z')) ||
154 ((c >= '0') && (c <= '9')) ||
155 (c == '+') || (c == '%') || (c == '-')) {
156 // alphanumeric character
157 outtext.push_back(c);
158 } else if (c == ' ') {
159 // space
160 outtext.push_back('+');
161 } else {
162 // everything else
163 outtext.push_back('%');
164 c2hex(c, ttmp);
165 outtext += ttmp;
166 }
167
168 here++;
169 }
170
171 return outtext;
172}
173
174
175
176
177static text_t::const_iterator get_next_save_arg (text_t::const_iterator first,
178 text_t::const_iterator last,
179 text_t &argname) {
180 first = getdelimitstr (first, last, '-', argname);
181 return first;
182}
183
184
185// check_save_conf_str checks the configuration string for
186// the saved args and makes sure it does not conflict with
187// the information about the arguments. If an error is encountered
188// it will return false and the program should not produce any
189// output.
190bool check_save_conf_str (const text_t &saveconf,
191 const cgiargsinfoclass &argsinfo,
192 ostream &logout) {
193 outconvertclass text_t2ascii;
194
195 text_tset argsset;
196 text_t::const_iterator saveconfhere = saveconf.begin ();
197 text_t::const_iterator saveconfend = saveconf.end ();
198 text_t argname;
199 const cgiarginfo *info;
200
201 // first check to make sure all saved arguments can be saved
202
203 while (saveconfhere != saveconfend) {
204 saveconfhere = get_next_save_arg (saveconfhere, saveconfend, argname);
205
206 if (!argname.empty()) {
207 // save the argument name for later
208 argsset.insert (argname);
209
210 // check the argument
211 info = argsinfo.getarginfo (argname);
212 if (info == NULL) {
213 logout << text_t2ascii << "Error: the cgi argument \"" << argname
214 << "\" is used in the configuration string for the\n"
215 << "saved arguments but does not exist as a valid argument.\n\n";
216 return false;
217 }
218 if (info->savedarginfo == cgiarginfo::mustnot) {
219 logout << text_t2ascii << "Error: the cgi argument \"" << argname
220 << "\" is used in the configuration string for the\n"
221 << "saved arguments but has been specified as an argument whose\n"
222 << "state must not be saved.\n\n";
223 return false;
224 }
225 }
226 }
227
228
229 // next check that all saved arguments that should be saved
230 // are saved
231 cgiargsinfoclass::const_iterator argsinfohere = argsinfo.begin ();
232 cgiargsinfoclass::const_iterator argsinfoend = argsinfo.end ();
233
234 while (argsinfohere != argsinfoend) {
235 if (((*argsinfohere).second.savedarginfo == cgiarginfo::must) &&
236 (argsset.find((*argsinfohere).second.shortname) == argsset.end())) {
237 logout << text_t2ascii << "Error: the cgi argument \""
238 << (*argsinfohere).second.shortname << "\" was specified as needing to\n"
239 << "be save but was not listed in the saved arguments.\n\n";
240 return false;
241 }
242
243 argsinfohere++;
244 }
245
246 return true; // made it, no clashes
247}
248
249
250// create_save_conf_str will create a configuration string
251// based on the information in argsinfo. This method of configuration
252// is not recomended as small changes can produce large changes in
253// the resulting configuration string (for instance a totally different
254// ordering). Only arguments which "must" be saved are included in
255// the resulting string.
256text_t create_save_conf_str (const cgiargsinfoclass &argsinfo,
257 ostream &/*logout*/) {
258 cgiargsinfoclass::const_iterator argsinfohere = argsinfo.begin ();
259 cgiargsinfoclass::const_iterator argsinfoend = argsinfo.end ();
260 text_t saveconf;
261 bool first = true;
262
263 while (argsinfohere != argsinfoend) {
264 // save this argument if it must be saved
265 if ((*argsinfohere).second.savedarginfo == cgiarginfo::must) {
266 if (!first) saveconf.push_back ('-');
267 else first = false;
268 saveconf += (*argsinfohere).second.shortname;
269 }
270
271 argsinfohere++;
272 }
273
274 return saveconf;
275}
276
277
278// expand_save_args will expand the saved arguments based
279// on saveconf placing the results in args if they are not
280// already defined. If it encounters an error it will return false
281// and output more information to logout.
282bool expand_save_args (const cgiargsinfoclass &argsinfo,
283 const text_t &saveconf,
284 cgiargsclass &args,
285 ostream &logout) {
286 outconvertclass text_t2ascii;
287
288 text_t *arg_e = args.getarg("e");
289 if (arg_e == NULL) return true; // no compressed arguments
290 if (arg_e->empty()) return true; // no compressed arguments
291
292 text_t argname, argvalue;
293 const cgiarginfo *argnameinfo;
294
295 text_t::const_iterator saveconfhere = saveconf.begin();
296 text_t::const_iterator saveconfend = saveconf.end();
297
298 text_t::iterator arg_ehere = arg_e->begin();
299 text_t::iterator arg_eend = arg_e->end();
300 while (saveconfhere != saveconfend && arg_ehere != arg_eend) {
301 saveconfhere = get_next_save_arg (saveconfhere, saveconfend, argname);
302
303 if (!argname.empty()) {
304 // found another entry
305 argnameinfo = argsinfo.getarginfo (argname);
306
307 if (argnameinfo == NULL) {
308 // no information about the argument could be found
309 // we can't keep going because we don't know whether
310 // this argument is a single or multiple character value
311 logout << text_t2ascii << "Error: the cgi argument \"" << argname
312 << "\" was specified as being a compressed argument\n"
313 << "but no information about it could be found within the "
314 << "cgiargsinfoclass.\n";
315 return false;
316
317 } else {
318
319 // found the argument information
320 if (argnameinfo->multiplechar) {
321 text_t::const_iterator sav = arg_ehere;
322 arg_ehere = getdelimitstr (arg_ehere, arg_eend, '-', argvalue);
323 // replace any '-' chars escaped with 'Z'
324 if (*(arg_ehere-2) == 'Z') {
325 argvalue.clear();
326 arg_ehere = (findchar (arg_ehere, arg_eend, '-')) + 1;
327 while (sav != (arg_ehere-1)) {
328 if (!((*sav == 'Z') && (*(sav+1) == '-'))) argvalue.push_back (*sav);
329 sav ++;
330 }
331 }
332
333 argvalue.setencoding(1); // other encoding
334 if (!argvalue.empty()) args.setdefaultarg (argname, argvalue, cgiarg_t::compressed_arg);
335 } else {
336 args.setdefaultcarg (argname,*arg_ehere, cgiarg_t::compressed_arg);
337 arg_ehere++;
338 }
339 }
340 }
341 }
342
343 return true;
344}
345
346
347// adds the default values for those arguments which have not
348// been specified
349void add_default_args (const cgiargsinfoclass &argsinfo,
350 cgiargsclass &args,
351 ostream &/*logout*/) {
352 cgiargsinfoclass::const_iterator argsinfohere = argsinfo.begin ();
353 cgiargsinfoclass::const_iterator argsinfoend = argsinfo.end ();
354
355 while (argsinfohere != argsinfoend) {
356 if ((*argsinfohere).second.defaultstatus != cgiarginfo::none) {
357 args.setdefaultarg ((*argsinfohere).second.shortname,
358 (*argsinfohere).second.argdefault, cgiarg_t::default_arg);
359 }
360 argsinfohere++;
361 }
362}
363
364
365// compress_save_args will compress the arguments and return
366// them in compressed_args. If an error was encountered
367// compressed_args will be set to to "", an error will be
368// written to logout, and the function will return false.
369bool compress_save_args (const cgiargsinfoclass &argsinfo,
370 const text_t &saveconf,
371 cgiargsclass &args,
372 text_t &compressed_args,
373 outconvertclass &outconvert,
374 ostream &logout) {
375 outconvertclass text_t2ascii;
376
377 compressed_args.clear();
378
379 text_t argname, argvalue;
380 const cgiarginfo *argnameinfo;
381
382 text_t::const_iterator saveconfhere = saveconf.begin();
383 text_t::const_iterator saveconfend = saveconf.end();
384
385 while (saveconfhere != saveconfend) {
386 saveconfhere = get_next_save_arg (saveconfhere, saveconfend, argname);
387
388 if (!argname.empty()) {
389 // found another entry
390 argnameinfo = argsinfo.getarginfo (argname);
391
392 if (argnameinfo == NULL) {
393 // no information about the argument could be found
394 // we can't keep going because we don't know whether
395 // this argument is a single or multiple character value
396 logout << text_t2ascii << "Error: the cgi argument \"" << argname
397 << "\" was specified as being a compressed argument\n"
398 << "but no information about it could be found within the "
399 << "cgiargsinfoclass.\n";
400 compressed_args.clear();
401 return false;
402
403 } else {
404 // found the argument information
405 if (argnameinfo->multiplechar) {
406 // multiple character argument -- sort out any '-' chars
407 compressed_args += minus_safe (outconvert.convert(args[argname]));
408 if (saveconfhere != saveconfend) compressed_args.push_back ('-');
409
410 } else {
411 // single character argument
412 if (args[argname].size() == 0) {
413 logout << text_t2ascii << "Error: the cgi argument \"" << argname
414 << "\" was specified as being a compressed argument which\n"
415 << "should have a one character value but it was empty.\n\n";
416 compressed_args.clear ();
417 return false;
418
419 } else if (args[argname].size() > 1) {
420 logout << text_t2ascii << "Error: the cgi argument \"" << argname
421 << "\" was specified as being a compressed argument which\n"
422 << "should have a one character value but it had multiple characters.\n\n";
423 compressed_args.clear ();
424 return false;
425 }
426
427 // everything is ok
428 compressed_args += args[argname];
429 }
430 }
431 }
432 }
433
434 return true;
435}
436
437
438// args_tounicode converts any arguments which are not in unicode
439// to unicode using inconvert
440void args_tounicode (cgiargsclass &args, inconvertclass &inconvert) {
441 cgiargsclass::iterator here = args.begin();
442 cgiargsclass::iterator end = args.end();
443
444 while (here != end) {
445 if ((*here).second.value.getencoding() > 0) {
446 (*here).second.value = inconvert.convert((*here).second.value);
447 }
448
449 here++;
450 }
451}
452
453// fcgienv will be loaded with environment name-value pairs
454// if using fastcgi (had to do this as getenv doesn't work
455// with our implementation of fastcgi). if fcgienv is empty
456// we'll simply use getenv
457text_t gsdl_getenv (const text_t &name, text_tmap &fcgienv) {
458 if (fcgienv.empty()) {
459 char *n = name.getcstr();
460 char *v = getenv(n);
461 delete n;
462 if (v != NULL) return v;
463 return "";
464
465 } else return fcgienv[name];
466}
Note: See TracBrowser for help on using the repository browser.