s3 swat: Create random nonce in CGI mode
[nivanova/samba-autobuild/.git] / source3 / web / cgi.c
1 /* 
2    some simple CGI helper routines
3    Copyright (C) Andrew Tridgell 1997-1998
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20 #include "includes.h"
21 #include "system/passwd.h"
22 #include "system/filesys.h"
23 #include "web/swat_proto.h"
24 #include "intl/lang_tdb.h"
25 #include "auth.h"
26 #include "secrets.h"
27
28 #define MAX_VARIABLES 10000
29
30 /* set the expiry on fixed pages */
31 #define EXPIRY_TIME (60*60*24*7)
32
33 #ifdef DEBUG_COMMENTS
34 extern void print_title(char *fmt, ...);
35 #endif
36
37 struct cgi_var {
38         char *name;
39         char *value;
40 };
41
42 static struct cgi_var variables[MAX_VARIABLES];
43 static int num_variables;
44 static int content_length;
45 static int request_post;
46 static char *query_string;
47 static const char *baseurl;
48 static char *pathinfo;
49 static char *C_user;
50 static char *C_pass;
51 static bool inetd_server;
52 static bool got_request;
53
54 static char *grab_line(FILE *f, int *cl)
55 {
56         char *ret = NULL;
57         int i = 0;
58         int len = 0;
59
60         while ((*cl)) {
61                 int c;
62
63                 if (i == len) {
64                         char *ret2;
65                         if (len == 0) len = 1024;
66                         else len *= 2;
67                         ret2 = (char *)SMB_REALLOC_KEEP_OLD_ON_ERROR(ret, len);
68                         if (!ret2) return ret;
69                         ret = ret2;
70                 }
71
72                 c = fgetc(f);
73                 (*cl)--;
74
75                 if (c == EOF) {
76                         (*cl) = 0;
77                         break;
78                 }
79
80                 if (c == '\r') continue;
81
82                 if (strchr_m("\n&", c)) break;
83
84                 ret[i++] = c;
85
86         }
87
88         if (ret) {
89                 ret[i] = 0;
90         }
91         return ret;
92 }
93
94 /**
95  URL encoded strings can have a '+', which should be replaced with a space
96
97  (This was in rfc1738_unescape(), but that broke the squid helper)
98 **/
99
100 static void plus_to_space_unescape(char *buf)
101 {
102         char *p=buf;
103
104         while ((p=strchr_m(p,'+')))
105                 *p = ' ';
106 }
107
108 /***************************************************************************
109   load all the variables passed to the CGI program. May have multiple variables
110   with the same name and the same or different values. Takes a file parameter
111   for simulating CGI invocation eg loading saved preferences.
112   ***************************************************************************/
113 void cgi_load_variables(void)
114 {
115         static char *line;
116         char *p, *s, *tok;
117         int len, i;
118         FILE *f = stdin;
119
120 #ifdef DEBUG_COMMENTS
121         char dummy[100]="";
122         print_title(dummy);
123         printf("<!== Start dump in cgi_load_variables() %s ==>\n",__FILE__);
124 #endif
125
126         if (!content_length) {
127                 p = getenv("CONTENT_LENGTH");
128                 len = p?atoi(p):0;
129         } else {
130                 len = content_length;
131         }
132
133
134         if (len > 0 && 
135             (request_post ||
136              ((s=getenv("REQUEST_METHOD")) && 
137               strequal(s,"POST")))) {
138                 while (len && (line=grab_line(f, &len))) {
139                         p = strchr_m(line,'=');
140                         if (!p) continue;
141
142                         *p = 0;
143
144                         variables[num_variables].name = SMB_STRDUP(line);
145                         variables[num_variables].value = SMB_STRDUP(p+1);
146
147                         SAFE_FREE(line);
148
149                         if (!variables[num_variables].name || 
150                             !variables[num_variables].value)
151                                 continue;
152
153                         plus_to_space_unescape(variables[num_variables].value);
154                         rfc1738_unescape(variables[num_variables].value);
155                         plus_to_space_unescape(variables[num_variables].name);
156                         rfc1738_unescape(variables[num_variables].name);
157
158 #ifdef DEBUG_COMMENTS
159                         printf("<!== POST var %s has value \"%s\"  ==>\n",
160                                variables[num_variables].name,
161                                variables[num_variables].value);
162 #endif
163
164                         num_variables++;
165                         if (num_variables == MAX_VARIABLES) break;
166                 }
167         }
168
169         fclose(stdin);
170         open("/dev/null", O_RDWR);
171
172         if ((s=query_string) || (s=getenv("QUERY_STRING"))) {
173                 char *saveptr;
174                 for (tok=strtok_r(s, "&;", &saveptr); tok;
175                      tok=strtok_r(NULL, "&;", &saveptr)) {
176                         p = strchr_m(tok,'=');
177                         if (!p) continue;
178
179                         *p = 0;
180
181                         variables[num_variables].name = SMB_STRDUP(tok);
182                         variables[num_variables].value = SMB_STRDUP(p+1);
183
184                         if (!variables[num_variables].name ||
185                             !variables[num_variables].value)
186                                 continue;
187
188                         plus_to_space_unescape(variables[num_variables].value);
189                         rfc1738_unescape(variables[num_variables].value);
190                         plus_to_space_unescape(variables[num_variables].name);
191                         rfc1738_unescape(variables[num_variables].name);
192
193 #ifdef DEBUG_COMMENTS
194                         printf("<!== Commandline var %s has value \"%s\"  ==>\n",
195                                variables[num_variables].name,
196                                variables[num_variables].value);
197 #endif
198                         num_variables++;
199                         if (num_variables == MAX_VARIABLES) break;
200                 }
201
202         }
203 #ifdef DEBUG_COMMENTS
204         printf("<!== End dump in cgi_load_variables() ==>\n");
205 #endif
206
207         /* variables from the client are in UTF-8 - convert them
208            to our internal unix charset before use */
209         for (i=0;i<num_variables;i++) {
210                 TALLOC_CTX *frame = talloc_stackframe();
211                 char *dest = NULL;
212                 size_t dest_len;
213
214                 convert_string_talloc(frame, CH_UTF8, CH_UNIX,
215                                variables[i].name, strlen(variables[i].name),
216                                &dest, &dest_len);
217                 SAFE_FREE(variables[i].name);
218                 variables[i].name = SMB_STRDUP(dest ? dest : "");
219
220                 dest = NULL;
221                 convert_string_talloc(frame, CH_UTF8, CH_UNIX,
222                                variables[i].value, strlen(variables[i].value),
223                                &dest, &dest_len);
224                 SAFE_FREE(variables[i].value);
225                 variables[i].value = SMB_STRDUP(dest ? dest : "");
226                 TALLOC_FREE(frame);
227         }
228 }
229
230
231 /***************************************************************************
232   find a variable passed via CGI
233   Doesn't quite do what you think in the case of POST text variables, because
234   if they exist they might have a value of "" or even " ", depending on the
235   browser. Also doesn't allow for variables[] containing multiple variables
236   with the same name and the same or different values.
237   ***************************************************************************/
238
239 const char *cgi_variable(const char *name)
240 {
241         int i;
242
243         for (i=0;i<num_variables;i++)
244                 if (strcmp(variables[i].name, name) == 0)
245                         return variables[i].value;
246         return NULL;
247 }
248
249 /***************************************************************************
250  Version of the above that can't return a NULL pointer.
251 ***************************************************************************/
252
253 const char *cgi_variable_nonull(const char *name)
254 {
255         const char *var = cgi_variable(name);
256         if (var) {
257                 return var;
258         } else {
259                 return "";
260         }
261 }
262
263 /***************************************************************************
264 tell a browser about a fatal error in the http processing
265   ***************************************************************************/
266 static void cgi_setup_error(const char *err, const char *header, const char *info)
267 {
268         if (!got_request) {
269                 /* damn browsers don't like getting cut off before they give a request */
270                 char line[1024];
271                 while (fgets(line, sizeof(line)-1, stdin)) {
272                         if (strnequal(line,"GET ", 4) || 
273                             strnequal(line,"POST ", 5) ||
274                             strnequal(line,"PUT ", 4)) {
275                                 break;
276                         }
277                 }
278         }
279
280         printf("HTTP/1.0 %s\r\n%sConnection: close\r\nContent-Type: text/html\r\n\r\n<HTML><HEAD><TITLE>%s</TITLE></HEAD><BODY><H1>%s</H1>%s<p></BODY></HTML>\r\n\r\n", err, header, err, err, info);
281         fclose(stdin);
282         fclose(stdout);
283         exit(0);
284 }
285
286
287 /***************************************************************************
288 tell a browser about a fatal authentication error
289   ***************************************************************************/
290 static void cgi_auth_error(void)
291 {
292         if (inetd_server) {
293                 cgi_setup_error("401 Authorization Required", 
294                                 "WWW-Authenticate: Basic realm=\"SWAT\"\r\n",
295                                 "You must be authenticated to use this service");
296         } else {
297                 printf("Content-Type: text/html\r\n");
298
299                 printf("\r\n<HTML><HEAD><TITLE>SWAT</TITLE></HEAD>\n");
300                 printf("<BODY><H1>Installation Error</H1>\n");
301                 printf("SWAT must be installed via inetd. It cannot be run as a CGI script<p>\n");
302                 printf("</BODY></HTML>\r\n");
303         }
304         exit(0);
305 }
306
307 /***************************************************************************
308 authenticate when we are running as a CGI
309   ***************************************************************************/
310 static void cgi_web_auth(void)
311 {
312         const char *user = getenv("REMOTE_USER");
313         struct passwd *pwd;
314         const char *head = "Content-Type: text/html\r\n\r\n<HTML><BODY><H1>SWAT installation Error</H1>\n";
315         const char *tail = "</BODY></HTML>\r\n";
316
317         if (!user) {
318                 printf("%sREMOTE_USER not set. Not authenticated by web server.<br>%s\n",
319                        head, tail);
320                 exit(0);
321         }
322
323         pwd = Get_Pwnam_alloc(talloc_tos(), user);
324         if (!pwd) {
325                 printf("%sCannot find user %s<br>%s\n", head, user, tail);
326                 exit(0);
327         }
328
329         C_user = SMB_STRDUP(user);
330
331         if (!setuid(0)) {
332                 C_pass = secrets_fetch_generic("root", "SWAT");
333                 if (C_pass == NULL) {
334                         char *tmp_pass = NULL;
335                         tmp_pass = generate_random_password(talloc_tos(),
336                                                             16, 16);
337                         if (tmp_pass == NULL) {
338                                 printf("%sFailed to create random nonce for "
339                                        "SWAT session\n<br>%s\n", head, tail);
340                                 exit(0);
341                         }
342                         secrets_store_generic("root", "SWAT", tmp_pass);
343                         C_pass = SMB_STRDUP(tmp_pass);
344                         TALLOC_FREE(tmp_pass);
345                 }
346         }
347         setuid(pwd->pw_uid);
348         if (geteuid() != pwd->pw_uid || getuid() != pwd->pw_uid) {
349                 printf("%sFailed to become user %s - uid=%d/%d<br>%s\n", 
350                        head, user, (int)geteuid(), (int)getuid(), tail);
351                 exit(0);
352         }
353         TALLOC_FREE(pwd);
354 }
355
356
357 /***************************************************************************
358 handle a http authentication line
359   ***************************************************************************/
360 static bool cgi_handle_authorization(char *line)
361 {
362         char *p;
363         fstring user, user_pass;
364         struct passwd *pass = NULL;
365         const char *rhost;
366         char addr[INET6_ADDRSTRLEN];
367         size_t size = 0;
368
369         if (!strnequal(line,"Basic ", 6)) {
370                 goto err;
371         }
372         line += 6;
373         while (line[0] == ' ') line++;
374         base64_decode_inplace(line);
375         if (!(p=strchr_m(line,':'))) {
376                 /*
377                  * Always give the same error so a cracker
378                  * cannot tell why we fail.
379                  */
380                 goto err;
381         }
382         *p = 0;
383
384         if (!convert_string(CH_UTF8, CH_UNIX,
385                        line, -1, 
386                        user, sizeof(user), &size)) {
387                 goto err;
388         }
389
390         if (!convert_string(CH_UTF8, CH_UNIX,
391                        p+1, -1, 
392                        user_pass, sizeof(user_pass), &size)) {
393                 goto err;
394         }
395
396         /*
397          * Try and get the user from the UNIX password file.
398          */
399
400         pass = Get_Pwnam_alloc(talloc_tos(), user);
401
402         rhost = client_name(1);
403         if (strequal(rhost,"UNKNOWN"))
404                 rhost = client_addr(1, addr, sizeof(addr));
405
406         /*
407          * Validate the password they have given.
408          */
409
410         if NT_STATUS_IS_OK(pass_check(pass, user, rhost, user_pass, false)) {
411                 if (pass) {
412                         /*
413                          * Password was ok.
414                          */
415
416                         if ( initgroups(pass->pw_name, pass->pw_gid) != 0 )
417                                 goto err;
418
419                         become_user_permanently(pass->pw_uid, pass->pw_gid);
420
421                         /* Save the users name */
422                         C_user = SMB_STRDUP(user);
423                         C_pass = SMB_STRDUP(user_pass);
424                         TALLOC_FREE(pass);
425                         return True;
426                 }
427         }
428
429 err:
430         cgi_setup_error("401 Bad Authorization", 
431                         "WWW-Authenticate: Basic realm=\"SWAT\"\r\n",
432                         "username or password incorrect");
433
434         TALLOC_FREE(pass);
435         return False;
436 }
437
438 /***************************************************************************
439 is this root?
440   ***************************************************************************/
441 bool am_root(void)
442 {
443         if (geteuid() == 0) {
444                 return( True);
445         } else {
446                 return( False);
447         }
448 }
449
450 /***************************************************************************
451 return a ptr to the users name
452   ***************************************************************************/
453 char *cgi_user_name(void)
454 {
455         return(C_user);
456 }
457
458 /***************************************************************************
459 return a ptr to the users password
460   ***************************************************************************/
461 char *cgi_user_pass(void)
462 {
463         return(C_pass);
464 }
465
466 /***************************************************************************
467 handle a file download
468   ***************************************************************************/
469 static void cgi_download(char *file)
470 {
471         SMB_STRUCT_STAT st;
472         char buf[1024];
473         int fd, l, i;
474         char *p;
475         char *lang;
476
477         /* sanitise the filename */
478         for (i=0;file[i];i++) {
479                 if (!isalnum((int)file[i]) && !strchr_m("/.-_", file[i])) {
480                         cgi_setup_error("404 File Not Found","",
481                                         "Illegal character in filename");
482                 }
483         }
484
485         if (sys_stat(file, &st, false) != 0)    {
486                 cgi_setup_error("404 File Not Found","",
487                                 "The requested file was not found");
488         }
489
490         if (S_ISDIR(st.st_ex_mode))
491         {
492                 snprintf(buf, sizeof(buf), "%s/index.html", file);
493                 if (!file_exist_stat(buf, &st, false)
494                     || !S_ISREG(st.st_ex_mode))
495                 {
496                         cgi_setup_error("404 File Not Found","",
497                                         "The requested file was not found");
498                 }
499         }
500         else if (S_ISREG(st.st_ex_mode))
501         {
502                 snprintf(buf, sizeof(buf), "%s", file);
503         }
504         else
505         {
506                 cgi_setup_error("404 File Not Found","",
507                                 "The requested file was not found");
508         }
509
510         fd = web_open(buf,O_RDONLY,0);
511         if (fd == -1) {
512                 cgi_setup_error("404 File Not Found","",
513                                 "The requested file was not found");
514         }
515         printf("HTTP/1.0 200 OK\r\n");
516         if ((p=strrchr_m(buf, '.'))) {
517                 if (strcmp(p,".gif")==0) {
518                         printf("Content-Type: image/gif\r\n");
519                 } else if (strcmp(p,".jpg")==0) {
520                         printf("Content-Type: image/jpeg\r\n");
521                 } else if (strcmp(p,".png")==0) {
522                         printf("Content-Type: image/png\r\n");
523                 } else if (strcmp(p,".css")==0) {
524                         printf("Content-Type: text/css\r\n");
525                 } else if (strcmp(p,".txt")==0) {
526                         printf("Content-Type: text/plain\r\n");
527                 } else {
528                         printf("Content-Type: text/html\r\n");
529                 }
530         }
531         printf("Expires: %s\r\n", 
532                    http_timestring(talloc_tos(), time(NULL)+EXPIRY_TIME));
533
534         lang = lang_tdb_current();
535         if (lang) {
536                 printf("Content-Language: %s\r\n", lang);
537         }
538
539         printf("Content-Length: %d\r\n\r\n", (int)st.st_ex_size);
540         while ((l=read(fd,buf,sizeof(buf)))>0) {
541                 if (fwrite(buf, 1, l, stdout) != l) {
542                         break;
543                 }
544         }
545         close(fd);
546         exit(0);
547 }
548
549
550
551 /* return true if the char* contains ip addrs only.  Used to avoid
552 name lookup calls */
553
554 static bool only_ipaddrs_in_list(const char **list)
555 {
556         bool only_ip = true;
557
558         if (!list) {
559                 return true;
560         }
561
562         for (; *list ; list++) {
563                 /* factor out the special strings */
564                 if (strequal(*list, "ALL") || strequal(*list, "FAIL") ||
565                     strequal(*list, "EXCEPT")) {
566                         continue;
567                 }
568
569                 if (!is_ipaddress(*list)) {
570                         /*
571                          * If we failed, make sure that it was not because
572                          * the token was a network/netmask pair. Only
573                          * network/netmask pairs have a '/' in them.
574                          */
575                         if ((strchr_m(*list, '/')) == NULL) {
576                                 only_ip = false;
577                                 DEBUG(3,("only_ipaddrs_in_list: list has "
578                                         "non-ip address (%s)\n",
579                                         *list));
580                                 break;
581                         }
582                 }
583         }
584
585         return only_ip;
586 }
587
588 /* return true if access should be allowed to a service for a socket */
589 static bool check_access(int sock, const char **allow_list,
590                          const char **deny_list)
591 {
592         bool ret = false;
593         bool only_ip = false;
594         char addr[INET6_ADDRSTRLEN];
595
596         if ((!deny_list || *deny_list==0) && (!allow_list || *allow_list==0)) {
597                 return true;
598         }
599
600         /* Bypass name resolution calls if the lists
601          * only contain IP addrs */
602         if (only_ipaddrs_in_list(allow_list) &&
603             only_ipaddrs_in_list(deny_list)) {
604                 only_ip = true;
605                 DEBUG (3, ("check_access: no hostnames "
606                            "in host allow/deny list.\n"));
607                 ret = allow_access(deny_list,
608                                    allow_list,
609                                    "",
610                                    get_peer_addr(sock,addr,sizeof(addr)));
611         } else {
612                 DEBUG (3, ("check_access: hostnames in "
613                            "host allow/deny list.\n"));
614                 ret = allow_access(deny_list,
615                                    allow_list,
616                                    get_peer_name(sock,true),
617                                    get_peer_addr(sock,addr,sizeof(addr)));
618         }
619
620         if (ret) {
621                 DEBUG(2,("Allowed connection from %s (%s)\n",
622                          only_ip ? "" : get_peer_name(sock,true),
623                          get_peer_addr(sock,addr,sizeof(addr))));
624         } else {
625                 DEBUG(0,("Denied connection from %s (%s)\n",
626                          only_ip ? "" : get_peer_name(sock,true),
627                          get_peer_addr(sock,addr,sizeof(addr))));
628         }
629
630         return(ret);
631 }
632
633 /**
634  * @brief Setup the CGI framework.
635  *
636  * Setup the cgi framework, handling the possibility that this program
637  * is either run as a true CGI program with a gateway to a web server, or
638  * is itself a mini web server.
639  **/
640 void cgi_setup(const char *rootdir, int auth_required)
641 {
642         bool authenticated = False;
643         char line[1024];
644         char *url=NULL;
645         char *p;
646         char *lang;
647
648         if (chdir(rootdir)) {
649                 cgi_setup_error("500 Server Error", "",
650                                 "chdir failed - the server is not configured correctly");
651         }
652
653         /* Handle the possibility we might be running as non-root */
654         sec_init();
655
656         if ((lang=getenv("HTTP_ACCEPT_LANGUAGE"))) {
657                 /* if running as a cgi program */
658                 web_set_lang(lang);
659         }
660
661         /* maybe we are running under a web server */
662         if (getenv("CONTENT_LENGTH") || getenv("REQUEST_METHOD")) {
663                 if (auth_required) {
664                         cgi_web_auth();
665                 }
666                 return;
667         }
668
669         inetd_server = True;
670
671         if (!check_access(1, lp_hostsallow(-1), lp_hostsdeny(-1))) {
672                 cgi_setup_error("403 Forbidden", "",
673                                 "Samba is configured to deny access from this client\n<br>Check your \"hosts allow\" and \"hosts deny\" options in smb.conf ");
674         }
675
676         /* we are a mini-web server. We need to read the request from stdin
677            and handle authentication etc */
678         while (fgets(line, sizeof(line)-1, stdin)) {
679                 if (line[0] == '\r' || line[0] == '\n') break;
680                 if (strnequal(line,"GET ", 4)) {
681                         got_request = True;
682                         url = SMB_STRDUP(&line[4]);
683                 } else if (strnequal(line,"POST ", 5)) {
684                         got_request = True;
685                         request_post = 1;
686                         url = SMB_STRDUP(&line[5]);
687                 } else if (strnequal(line,"PUT ", 4)) {
688                         got_request = True;
689                         cgi_setup_error("400 Bad Request", "",
690                                         "This server does not accept PUT requests");
691                 } else if (strnequal(line,"Authorization: ", 15)) {
692                         authenticated = cgi_handle_authorization(&line[15]);
693                 } else if (strnequal(line,"Content-Length: ", 16)) {
694                         content_length = atoi(&line[16]);
695                 } else if (strnequal(line,"Accept-Language: ", 17)) {
696                         web_set_lang(&line[17]);
697                 }
698                 /* ignore all other requests! */
699         }
700
701         if (auth_required && !authenticated) {
702                 cgi_auth_error();
703         }
704
705         if (!url) {
706                 cgi_setup_error("400 Bad Request", "",
707                                 "You must specify a GET or POST request");
708         }
709
710         /* trim the URL */
711         if ((p = strchr_m(url,' ')) || (p=strchr_m(url,'\t'))) {
712                 *p = 0;
713         }
714         while (*url && strchr_m("\r\n",url[strlen(url)-1])) {
715                 url[strlen(url)-1] = 0;
716         }
717
718         /* anything following a ? in the URL is part of the query string */
719         if ((p=strchr_m(url,'?'))) {
720                 query_string = p+1;
721                 *p = 0;
722         }
723
724         string_sub(url, "/swat/", "", 0);
725
726         if (url[0] != '/' && strstr(url,"..")==0) {
727                 cgi_download(url);
728         }
729
730         printf("HTTP/1.0 200 OK\r\nConnection: close\r\n");
731         printf("Date: %s\r\n", http_timestring(talloc_tos(), time(NULL)));
732         baseurl = "";
733         pathinfo = url+1;
734 }
735
736
737 /***************************************************************************
738 return the current pages URL
739   ***************************************************************************/
740 const char *cgi_baseurl(void)
741 {
742         if (inetd_server) {
743                 return baseurl;
744         }
745         return getenv("SCRIPT_NAME");
746 }
747
748 /***************************************************************************
749 return the current pages path info
750   ***************************************************************************/
751 const char *cgi_pathinfo(void)
752 {
753         char *r;
754         if (inetd_server) {
755                 return pathinfo;
756         }
757         r = getenv("PATH_INFO");
758         if (!r) return "";
759         if (*r == '/') r++;
760         return r;
761 }
762
763 /***************************************************************************
764 return the hostname of the client
765   ***************************************************************************/
766 const char *cgi_remote_host(void)
767 {
768         if (inetd_server) {
769                 return get_peer_name(1,False);
770         }
771         return getenv("REMOTE_HOST");
772 }
773
774 /***************************************************************************
775 return the hostname of the client
776   ***************************************************************************/
777 const char *cgi_remote_addr(void)
778 {
779         if (inetd_server) {
780                 char addr[INET6_ADDRSTRLEN];
781                 get_peer_addr(1,addr,sizeof(addr));
782                 return talloc_strdup(talloc_tos(), addr);
783         }
784         return getenv("REMOTE_ADDR");
785 }
786
787
788 /***************************************************************************
789 return True if the request was a POST
790   ***************************************************************************/
791 bool cgi_waspost(void)
792 {
793         if (inetd_server) {
794                 return request_post;
795         }
796         return strequal(getenv("REQUEST_METHOD"), "POST");
797 }