source: trunk/gsdl/src/recpt/cgiutils.cpp@ 776

Last change on this file since 776 was 776, checked in by sjboddie, 24 years ago

added multiplevalue option to cgiarginfo

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