source: main/tags/2.32/gsdl/src/recpt/cgiutils.cpp@ 25600

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

'-'s in the compressed args are now escaped with 'Zz' (instead of the old 'z')
I know this is an ugly hack... It works though.

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 13.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 *********************************************************************/
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 += "Zz-";
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 'Zz'
324 bool first = true;
325 while ((*(arg_ehere-3) == 'Z') && (*(arg_ehere-2) == 'z')) {
326 if (first) argvalue.clear();
327 arg_ehere = (findchar (arg_ehere, arg_eend, '-')) + 1;
328 while (sav != (arg_ehere-1)) {
329 if (!((*sav == 'Z') && (*(sav+1) == 'z') && (*(sav+2) == '-')) &&
330 !((*(sav-1) == 'Z') && (*sav == 'z') && (*(sav+1) == '-'))) argvalue.push_back (*sav);
331 sav ++;
332 }
333 first = false;
334 }
335
336 argvalue.setencoding(1); // other encoding
337 if (!argvalue.empty()) args.setdefaultarg (argname, argvalue, cgiarg_t::compressed_arg);
338 } else {
339 args.setdefaultcarg (argname,*arg_ehere, cgiarg_t::compressed_arg);
340 arg_ehere++;
341 }
342 }
343 }
344 }
345
346 return true;
347}
348
349
350// adds the default values for those arguments which have not
351// been specified
352void add_default_args (const cgiargsinfoclass &argsinfo,
353 cgiargsclass &args,
354 ostream &/*logout*/) {
355 cgiargsinfoclass::const_iterator argsinfohere = argsinfo.begin ();
356 cgiargsinfoclass::const_iterator argsinfoend = argsinfo.end ();
357
358 while (argsinfohere != argsinfoend) {
359 if ((*argsinfohere).second.defaultstatus != cgiarginfo::none) {
360 args.setdefaultarg ((*argsinfohere).second.shortname,
361 (*argsinfohere).second.argdefault, cgiarg_t::default_arg);
362 }
363 argsinfohere++;
364 }
365}
366
367
368// compress_save_args will compress the arguments and return
369// them in compressed_args. If an error was encountered
370// compressed_args will be set to to "", an error will be
371// written to logout, and the function will return false.
372bool compress_save_args (const cgiargsinfoclass &argsinfo,
373 const text_t &saveconf,
374 cgiargsclass &args,
375 text_t &compressed_args,
376 outconvertclass &outconvert,
377 ostream &logout) {
378 outconvertclass text_t2ascii;
379
380 compressed_args.clear();
381
382 text_t argname, argvalue;
383 const cgiarginfo *argnameinfo;
384
385 text_t::const_iterator saveconfhere = saveconf.begin();
386 text_t::const_iterator saveconfend = saveconf.end();
387
388 while (saveconfhere != saveconfend) {
389 saveconfhere = get_next_save_arg (saveconfhere, saveconfend, argname);
390
391 if (!argname.empty()) {
392 // found another entry
393 argnameinfo = argsinfo.getarginfo (argname);
394
395 if (argnameinfo == NULL) {
396 // no information about the argument could be found
397 // we can't keep going because we don't know whether
398 // this argument is a single or multiple character value
399 logout << text_t2ascii << "Error: the cgi argument \"" << argname
400 << "\" was specified as being a compressed argument\n"
401 << "but no information about it could be found within the "
402 << "cgiargsinfoclass.\n";
403 compressed_args.clear();
404 return false;
405
406 } else {
407 // found the argument information
408 if (argnameinfo->multiplechar) {
409 // multiple character argument -- sort out any '-' chars
410 compressed_args += minus_safe (outconvert.convert(args[argname]));
411 if (saveconfhere != saveconfend) compressed_args.push_back ('-');
412
413 } else {
414 // single character argument
415 if (args[argname].size() == 0) {
416 logout << text_t2ascii << "Error: the cgi argument \"" << argname
417 << "\" was specified as being a compressed argument which\n"
418 << "should have a one character value but it was empty.\n\n";
419 compressed_args.clear ();
420 return false;
421
422 } else if (args[argname].size() > 1) {
423 logout << text_t2ascii << "Error: the cgi argument \"" << argname
424 << "\" was specified as being a compressed argument which\n"
425 << "should have a one character value but it had multiple characters.\n\n";
426 compressed_args.clear ();
427 return false;
428 }
429
430 // everything is ok
431 compressed_args += args[argname];
432 }
433 }
434 }
435 }
436
437 return true;
438}
439
440
441// args_tounicode converts any arguments which are not in unicode
442// to unicode using inconvert
443void args_tounicode (cgiargsclass &args, inconvertclass &inconvert) {
444 cgiargsclass::iterator here = args.begin();
445 cgiargsclass::iterator end = args.end();
446
447 while (here != end) {
448 if ((*here).second.value.getencoding() > 0) {
449 (*here).second.value = inconvert.convert((*here).second.value);
450 }
451
452 here++;
453 }
454}
455
456// fcgienv will be loaded with environment name-value pairs
457// if using fastcgi (had to do this as getenv doesn't work
458// with our implementation of fastcgi). if fcgienv is empty
459// we'll simply use getenv
460text_t gsdl_getenv (const text_t &name, text_tmap &fcgienv) {
461 if (fcgienv.empty()) {
462 char *n = name.getcstr();
463 char *v = getenv(n);
464 delete n;
465 if (v != NULL) return v;
466 return "";
467
468 } else return fcgienv[name];
469}
Note: See TracBrowser for help on using the repository browser.