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

Last change on this file since 2426 was 2426, checked in by sjboddie, 23 years ago

added an ugly hack to prevent problems when multiple value arguments
contain commas - this should be fixed properly some day but for now
it'll do the trick

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 14.8 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
112 // if arg occurs multiple times (as is the case with multiple
113 // checkboxes using the same name) we'll create a comma separated
114 // list of all the values (this uses a hack that encodes naturally
115 // occurring commas as %2C - values will therefore need to be decoded
116 // again before use) - it should use an array instead
117 const cgiarginfo *info = argsinfo.getarginfo (key);
118 if (info != NULL && info->multiplevalue) {
119 text_t newvalue = args[key];
120 if (args.lookupcgiarg(key).source == cgiarg_t::cgi_arg) newvalue += ",";
121 newvalue += encode_commas(value);
122 args.setarg (key, newvalue, cgiarg_t::cgi_arg);
123
124 } else {
125 args.setarg (key, value, cgiarg_t::cgi_arg);
126 }
127 }
128 }
129}
130
131text_t encode_commas (const text_t &intext) {
132
133 text_t outtext;
134
135 text_t::const_iterator here = intext.begin ();
136 text_t::const_iterator end = intext.end ();
137
138 while (here != end) {
139 if (*here == ',') outtext += "%2C";
140 else outtext.push_back (*here);
141 here ++;
142 }
143 return outtext;
144}
145
146text_t decode_commas (const text_t &intext) {
147
148 text_t outtext;
149
150 text_t::const_iterator here = intext.begin ();
151 text_t::const_iterator end = intext.end ();
152
153 while (here != end) {
154 if ((here+2<end) && *here == '%' && *(here+1) == '2' &&
155 (*(here+2) == 'C' || *(here+2) == 'c')) {
156 here += 2;
157 outtext.push_back(',');
158
159 }else outtext.push_back (*here);
160 here ++;
161 }
162 return outtext;
163}
164
165text_t minus_safe (const text_t &intext) {
166
167 text_t outtext;
168
169 text_t::const_iterator here = intext.begin ();
170 text_t::const_iterator end = intext.end ();
171
172 while (here != end) {
173 if (*here == '-') outtext += "Zz-";
174 else outtext.push_back (*here);
175 here ++;
176 }
177 outtext = cgi_safe (outtext);
178 return outtext;
179}
180
181text_t cgi_safe (const text_t &intext) {
182 text_t outtext;
183
184 text_t::const_iterator here = intext.begin ();
185 text_t::const_iterator end = intext.end ();
186 unsigned short c;
187 text_t ttmp;
188
189 while (here != end) {
190 c = *here;
191 if (((c >= 'a') && (c <= 'z')) ||
192 ((c >= 'A') && (c <= 'Z')) ||
193 ((c >= '0') && (c <= '9')) ||
194 (c == '+') || (c == '%') || (c == '-')) {
195 // alphanumeric character
196 outtext.push_back(c);
197 } else if (c == ' ') {
198 // space
199 outtext.push_back('+');
200 } else {
201 // everything else
202 outtext.push_back('%');
203 c2hex(c, ttmp);
204 outtext += ttmp;
205 }
206
207 here++;
208 }
209
210 return outtext;
211}
212
213
214
215
216static text_t::const_iterator get_next_save_arg (text_t::const_iterator first,
217 text_t::const_iterator last,
218 text_t &argname) {
219 first = getdelimitstr (first, last, '-', argname);
220 return first;
221}
222
223
224// check_save_conf_str checks the configuration string for
225// the saved args and makes sure it does not conflict with
226// the information about the arguments. If an error is encountered
227// it will return false and the program should not produce any
228// output.
229bool check_save_conf_str (const text_t &saveconf,
230 const cgiargsinfoclass &argsinfo,
231 ostream &logout) {
232 outconvertclass text_t2ascii;
233
234 text_tset argsset;
235 text_t::const_iterator saveconfhere = saveconf.begin ();
236 text_t::const_iterator saveconfend = saveconf.end ();
237 text_t argname;
238 const cgiarginfo *info;
239
240 // first check to make sure all saved arguments can be saved
241
242 while (saveconfhere != saveconfend) {
243 saveconfhere = get_next_save_arg (saveconfhere, saveconfend, argname);
244
245 if (!argname.empty()) {
246 // save the argument name for later
247 argsset.insert (argname);
248
249 // check the argument
250 info = argsinfo.getarginfo (argname);
251 if (info == NULL) {
252 logout << text_t2ascii << "Error: the cgi argument \"" << argname
253 << "\" is used in the configuration string for the\n"
254 << "saved arguments but does not exist as a valid argument.\n\n";
255 return false;
256 }
257 if (info->savedarginfo == cgiarginfo::mustnot) {
258 logout << text_t2ascii << "Error: the cgi argument \"" << argname
259 << "\" is used in the configuration string for the\n"
260 << "saved arguments but has been specified as an argument whose\n"
261 << "state must not be saved.\n\n";
262 return false;
263 }
264 }
265 }
266
267
268 // next check that all saved arguments that should be saved
269 // are saved
270 cgiargsinfoclass::const_iterator argsinfohere = argsinfo.begin ();
271 cgiargsinfoclass::const_iterator argsinfoend = argsinfo.end ();
272
273 while (argsinfohere != argsinfoend) {
274 if (((*argsinfohere).second.savedarginfo == cgiarginfo::must) &&
275 (argsset.find((*argsinfohere).second.shortname) == argsset.end())) {
276 logout << text_t2ascii << "Error: the cgi argument \""
277 << (*argsinfohere).second.shortname << "\" was specified as needing to\n"
278 << "be save but was not listed in the saved arguments.\n\n";
279 return false;
280 }
281
282 argsinfohere++;
283 }
284
285 return true; // made it, no clashes
286}
287
288
289// create_save_conf_str will create a configuration string
290// based on the information in argsinfo. This method of configuration
291// is not recomended as small changes can produce large changes in
292// the resulting configuration string (for instance a totally different
293// ordering). Only arguments which "must" be saved are included in
294// the resulting string.
295text_t create_save_conf_str (const cgiargsinfoclass &argsinfo,
296 ostream &/*logout*/) {
297 cgiargsinfoclass::const_iterator argsinfohere = argsinfo.begin ();
298 cgiargsinfoclass::const_iterator argsinfoend = argsinfo.end ();
299 text_t saveconf;
300 bool first = true;
301
302 while (argsinfohere != argsinfoend) {
303 // save this argument if it must be saved
304 if ((*argsinfohere).second.savedarginfo == cgiarginfo::must) {
305 if (!first) saveconf.push_back ('-');
306 else first = false;
307 saveconf += (*argsinfohere).second.shortname;
308 }
309
310 argsinfohere++;
311 }
312
313 return saveconf;
314}
315
316
317// expand_save_args will expand the saved arguments based
318// on saveconf placing the results in args if they are not
319// already defined. If it encounters an error it will return false
320// and output more information to logout.
321bool expand_save_args (const cgiargsinfoclass &argsinfo,
322 const text_t &saveconf,
323 cgiargsclass &args,
324 ostream &logout) {
325 outconvertclass text_t2ascii;
326
327 text_t *arg_e = args.getarg("e");
328 if (arg_e == NULL) return true; // no compressed arguments
329 if (arg_e->empty()) return true; // no compressed arguments
330
331 text_t argname, argvalue;
332 const cgiarginfo *argnameinfo;
333
334 text_t::const_iterator saveconfhere = saveconf.begin();
335 text_t::const_iterator saveconfend = saveconf.end();
336
337 text_t::iterator arg_ehere = arg_e->begin();
338 text_t::iterator arg_eend = arg_e->end();
339 while (saveconfhere != saveconfend && arg_ehere != arg_eend) {
340 saveconfhere = get_next_save_arg (saveconfhere, saveconfend, argname);
341
342 if (!argname.empty()) {
343 // found another entry
344 argnameinfo = argsinfo.getarginfo (argname);
345
346 if (argnameinfo == NULL) {
347 // no information about the argument could be found
348 // we can't keep going because we don't know whether
349 // this argument is a single or multiple character value
350 logout << text_t2ascii << "Error: the cgi argument \"" << argname
351 << "\" was specified as being a compressed argument\n"
352 << "but no information about it could be found within the "
353 << "cgiargsinfoclass.\n";
354 return false;
355
356 } else {
357
358 // found the argument information
359 if (argnameinfo->multiplechar) {
360 text_t::const_iterator sav = arg_ehere;
361 arg_ehere = getdelimitstr (arg_ehere, arg_eend, '-', argvalue);
362 // replace any '-' chars escaped with 'Zz'
363 bool first = true;
364 while ((*(arg_ehere-3) == 'Z') && (*(arg_ehere-2) == 'z')) {
365 if (first) argvalue.clear();
366 arg_ehere = (findchar (arg_ehere, arg_eend, '-')) + 1;
367 while (sav != (arg_ehere-1)) {
368 if (!((*sav == 'Z') && (*(sav+1) == 'z') && (*(sav+2) == '-')) &&
369 !((*(sav-1) == 'Z') && (*sav == 'z') && (*(sav+1) == '-'))) argvalue.push_back (*sav);
370 sav ++;
371 }
372 first = false;
373 }
374
375 argvalue.setencoding(1); // other encoding
376 if (!argvalue.empty()) args.setdefaultarg (argname, argvalue, cgiarg_t::compressed_arg);
377 } else {
378 args.setdefaultcarg (argname,*arg_ehere, cgiarg_t::compressed_arg);
379 arg_ehere++;
380 }
381 }
382 }
383 }
384
385 return true;
386}
387
388
389// adds the default values for those arguments which have not
390// been specified
391void add_default_args (const cgiargsinfoclass &argsinfo,
392 cgiargsclass &args,
393 ostream &/*logout*/) {
394 cgiargsinfoclass::const_iterator argsinfohere = argsinfo.begin ();
395 cgiargsinfoclass::const_iterator argsinfoend = argsinfo.end ();
396
397 while (argsinfohere != argsinfoend) {
398 if ((*argsinfohere).second.defaultstatus != cgiarginfo::none) {
399 args.setdefaultarg ((*argsinfohere).second.shortname,
400 (*argsinfohere).second.argdefault, cgiarg_t::default_arg);
401 }
402 argsinfohere++;
403 }
404}
405
406
407// compress_save_args will compress the arguments and return
408// them in compressed_args. If an error was encountered
409// compressed_args will be set to to "", an error will be
410// written to logout, and the function will return false.
411bool compress_save_args (const cgiargsinfoclass &argsinfo,
412 const text_t &saveconf,
413 cgiargsclass &args,
414 text_t &compressed_args,
415 outconvertclass &outconvert,
416 ostream &logout) {
417 outconvertclass text_t2ascii;
418
419 compressed_args.clear();
420
421 text_t argname, argvalue;
422 const cgiarginfo *argnameinfo;
423
424 text_t::const_iterator saveconfhere = saveconf.begin();
425 text_t::const_iterator saveconfend = saveconf.end();
426
427 while (saveconfhere != saveconfend) {
428 saveconfhere = get_next_save_arg (saveconfhere, saveconfend, argname);
429
430 if (!argname.empty()) {
431 // found another entry
432 argnameinfo = argsinfo.getarginfo (argname);
433
434 if (argnameinfo == NULL) {
435 // no information about the argument could be found
436 // we can't keep going because we don't know whether
437 // this argument is a single or multiple character value
438 logout << text_t2ascii << "Error: the cgi argument \"" << argname
439 << "\" was specified as being a compressed argument\n"
440 << "but no information about it could be found within the "
441 << "cgiargsinfoclass.\n";
442 compressed_args.clear();
443 return false;
444
445 } else {
446 // found the argument information
447 if (argnameinfo->multiplechar) {
448 // multiple character argument -- sort out any '-' chars
449 compressed_args += minus_safe (outconvert.convert(args[argname]));
450 if (saveconfhere != saveconfend) compressed_args.push_back ('-');
451
452 } else {
453 // single character argument
454 if (args[argname].size() == 0) {
455 logout << text_t2ascii << "Error: the cgi argument \"" << argname
456 << "\" was specified as being a compressed argument which\n"
457 << "should have a one character value but it was empty.\n\n";
458 compressed_args.clear ();
459 return false;
460
461 } else if (args[argname].size() > 1) {
462 logout << text_t2ascii << "Error: the cgi argument \"" << argname
463 << "\" was specified as being a compressed argument which\n"
464 << "should have a one character value but it had multiple characters.\n\n";
465 compressed_args.clear ();
466 return false;
467 }
468
469 // everything is ok
470 compressed_args += args[argname];
471 }
472 }
473 }
474 }
475
476 return true;
477}
478
479
480// args_tounicode converts any arguments which are not in unicode
481// to unicode using inconvert
482void args_tounicode (cgiargsclass &args, inconvertclass &inconvert) {
483 cgiargsclass::iterator here = args.begin();
484 cgiargsclass::iterator end = args.end();
485
486 while (here != end) {
487 if ((*here).second.value.getencoding() > 0) {
488 (*here).second.value = inconvert.convert((*here).second.value);
489 }
490
491 here++;
492 }
493}
494
495// fcgienv will be loaded with environment name-value pairs
496// if using fastcgi (had to do this as getenv doesn't work
497// with our implementation of fastcgi). if fcgienv is empty
498// we'll simply use getenv
499text_t gsdl_getenv (const text_t &name, text_tmap &fcgienv) {
500 if (fcgienv.empty()) {
501 char *n = name.getcstr();
502 char *v = getenv(n);
503 delete n;
504 if (v != NULL) return v;
505 return "";
506
507 } else return fcgienv[name];
508}
Note: See TracBrowser for help on using the repository browser.