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

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

changes to get fastcgi working properly

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