+ log_error(LOG_LEVEL_ERROR, "Marking suspicious CONNECT request from %s for blocking.",
+ csp->ip_addr_str);
+ csp->action->flags |= ACTION_BLOCK;
+ http->ssl = 0;
+ }
+ else
+ {
+ write_socket(csp->cfd, CFORBIDDEN, strlen(CFORBIDDEN));
+ log_error(LOG_LEVEL_CONNECT, "Denying suspicious CONNECT request from %s", csp->ip_addr_str);
+ log_error(LOG_LEVEL_CLF, "%s - - [%T] \" \" 403 0", csp->ip_addr_str);
+
+ list_remove_all(csp->headers);
+
+ return;
+ }
+ }
+ }
+
+ if (http->ssl == 0)
+ {
+ freez(csp->headers->first->str);
+ build_request_line(csp, fwd, &csp->headers->first->str);
+ }
+
+ hdr = list_to_text(csp->headers);
+ if (hdr == NULL)
+ {
+ /* FIXME Should handle error properly */
+ log_error(LOG_LEVEL_FATAL, "Out of memory parsing client header");
+ }
+
+ /*
+ * We have a request. Check if one of the crunchers wants it.
+ */
+ if (crunch_response_triggered(csp, crunchers_all))
+ {
+ /*
+ * Yes. The client got the crunch response
+ * and we are done here after cleaning up.
+ */
+ freez(hdr);
+ list_remove_all(csp->headers);
+
+ return;
+ }
+
+ /*
+ * The headers can't be removed earlier because
+ * they were still needed for the referrer check
+ * in case of CGI crunches.
+ *
+ * XXX: Would it be worth to move the referrer check
+ * into client_referrer() and set a flag if it's trusted?
+ */
+ list_remove_all(csp->headers);
+
+ log_error(LOG_LEVEL_GPC, "%s%s", http->hostport, http->path);
+
+ if (fwd->forward_host)
+ {
+ log_error(LOG_LEVEL_CONNECT, "via %s:%d to: %s",
+ fwd->forward_host, fwd->forward_port, http->hostport);
+ }
+ else
+ {
+ log_error(LOG_LEVEL_CONNECT, "to %s", http->hostport);
+ }
+
+ /* here we connect to the server, gateway, or the forwarder */
+
+ while ( (csp->sfd = forwarded_connect(fwd, http, csp))
+ && (errno == EINVAL) && (forwarded_connect_retries++ < max_forwarded_connect_retries))
+ {
+ log_error(LOG_LEVEL_ERROR, "failed request #%u to connect to %s. Trying again.",
+ forwarded_connect_retries, http->hostport);
+ }
+
+ if (csp->sfd == JB_INVALID_SOCKET)
+ {
+ if (fwd->type != SOCKS_NONE)
+ {
+ /* Socks error. */
+ rsp = error_response(csp, "forwarding-failed", errno);
+ }
+ else if (errno == EINVAL)
+ {
+ rsp = error_response(csp, "no-such-domain", errno);
+ }
+ else
+ {
+ rsp = error_response(csp, "connect-failed", errno);
+ log_error(LOG_LEVEL_CONNECT, "connect to: %s failed: %E",
+ http->hostport);
+ }
+
+
+ /* Write the answer to the client */
+ if (rsp != NULL)
+ {
+ send_crunch_response(csp, rsp);
+ }
+
+ freez(hdr);
+ return;
+ }
+
+ if (fwd->forward_host || (http->ssl == 0))
+ {
+ /* write the client's (modified) header to the server
+ * (along with anything else that may be in the buffer)
+ */
+
+ if (write_socket(csp->sfd, hdr, strlen(hdr))
+ || (flush_socket(csp->sfd, csp) < 0))
+ {
+ log_error(LOG_LEVEL_CONNECT, "write header to: %s failed: %E",
+ http->hostport);
+
+ rsp = error_response(csp, "connect-failed", errno);
+
+ if(rsp)
+ {
+ send_crunch_response(csp, rsp);
+ }
+
+ freez(hdr);
+ return;
+ }
+ }
+ else
+ {
+ /*
+ * We're running an SSL tunnel and we're not forwarding,
+ * so just send the "connect succeeded" message to the
+ * client, flush the rest, and get out of the way.
+ */
+ if (write_socket(csp->cfd, CSUCCEED, strlen(CSUCCEED)))
+ {
+ freez(hdr);
+ return;
+ }
+ IOB_RESET(csp);
+ }
+
+ log_error(LOG_LEVEL_CONNECT, "to %s successful", http->hostport);
+
+ /* we're finished with the client's header */
+ freez(hdr);
+
+ maxfd = ( csp->cfd > csp->sfd ) ? csp->cfd : csp->sfd;
+
+ /* pass data between the client and server
+ * until one or the other shuts down the connection.
+ */
+
+ server_body = 0;
+
+ for (;;)
+ {
+#ifdef __OS2__
+ /*
+ * FD_ZERO here seems to point to an errant macro which crashes.
+ * So do this by hand for now...
+ */
+ memset(&rfds,0x00,sizeof(fd_set));
+#else
+ FD_ZERO(&rfds);
+#endif
+ FD_SET(csp->cfd, &rfds);
+ FD_SET(csp->sfd, &rfds);
+
+ n = select((int)maxfd+1, &rfds, NULL, NULL, NULL);
+
+ if (n < 0)
+ {
+ log_error(LOG_LEVEL_ERROR, "select() failed!: %E");
+ return;
+ }
+
+ /* this is the body of the browser's request
+ * just read it and write it.
+ */
+
+ if (FD_ISSET(csp->cfd, &rfds))
+ {
+ len = read_socket(csp->cfd, buf, sizeof(buf) - 1);
+
+ if (len <= 0)
+ {
+ break; /* "game over, man" */
+ }
+
+ if (write_socket(csp->sfd, buf, (size_t)len))
+ {
+ log_error(LOG_LEVEL_ERROR, "write to: %s failed: %E", http->host);
+ return;
+ }
+ continue;
+ }
+
+ /*
+ * The server wants to talk. It could be the header or the body.
+ * If `hdr' is null, then it's the header otherwise it's the body.
+ * FIXME: Does `hdr' really mean `host'? No.
+ */
+
+
+ if (FD_ISSET(csp->sfd, &rfds))
+ {
+ fflush( 0 );
+ len = read_socket(csp->sfd, buf, sizeof(buf) - 1);
+
+ if (len < 0)
+ {
+ log_error(LOG_LEVEL_ERROR, "read from: %s failed: %E", http->host);
+
+ if (http->ssl && (fwd->forward_host == NULL))
+ {
+ /*
+ * Just hang up. We already confirmed the client's CONNECT
+ * request with status code 200 and unencrypted content is
+ * no longer welcome.
+ */
+ log_error(LOG_LEVEL_ERROR,
+ "CONNECT already confirmed. Unable to tell the client about the problem.");
+ return;
+ }
+
+ rsp = error_response(csp, "connect-failed", errno);
+
+ if(rsp)
+ {
+ send_crunch_response(csp, rsp);
+ }
+
+ return;
+ }
+
+ /* Add a trailing zero. This lets filter_popups
+ * use string operations.
+ */
+ buf[len] = '\0';
+
+#ifdef FEATURE_KILL_POPUPS
+ /* Filter the popups on this read. */
+ if (block_popups_now)
+ {
+ filter_popups(buf, csp);
+ }
+#endif /* def FEATURE_KILL_POPUPS */
+
+ /* Normally, this would indicate that we've read
+ * as much as the server has sent us and we can
+ * close the client connection. However, Microsoft
+ * in its wisdom has released IIS/5 with a bug that
+ * prevents it from sending the trailing \r\n in
+ * a 302 redirect header (and possibly other headers).
+ * To work around this if we've haven't parsed
+ * a full header we'll append a trailing \r\n
+ * and see if this now generates a valid one.
+ *
+ * This hack shouldn't have any impacts. If we've
+ * already transmitted the header or if this is a
+ * SSL connection, then we won't bother with this
+ * hack. So we only work on partially received
+ * headers. If we append a \r\n and this still
+ * doesn't generate a valid header, then we won't
+ * transmit anything to the client.
+ */
+ if (len == 0)
+ {
+
+ if (server_body || http->ssl)
+ {
+ /*
+ * If we have been buffering up the document,
+ * now is the time to apply content modification
+ * and send the result to the client.
+ */
+ if (content_filter)
+ {
+ /*
+ * If the content filter fails, use the original
+ * buffer and length.
+ * (see p != NULL ? p : csp->iob->cur below)
+ */
+ if (NULL == (p = (*content_filter)(csp)))
+ {
+ csp->content_length = (size_t)(csp->iob->eod - csp->iob->cur);
+ }
+
+ if (JB_ERR_OK != sed(server_patterns_light, NULL, csp))
+ {
+ log_error(LOG_LEVEL_FATAL, "Failed to parse server headers.");
+ }
+
+ hdr = list_to_text(csp->headers);
+ if (hdr == NULL)
+ {
+ /* FIXME Should handle error properly */
+ log_error(LOG_LEVEL_FATAL, "Out of memory parsing server header");
+ }
+
+ /*
+ * Shouldn't happen because this was the second sed run
+ * and tags are only created for the first one.
+ */
+ assert(!crunch_response_triggered(csp, crunchers_all));
+
+ if (write_socket(csp->cfd, hdr, strlen(hdr))
+ || write_socket(csp->cfd, p != NULL ? p : csp->iob->cur, csp->content_length))
+ {
+ log_error(LOG_LEVEL_ERROR, "write modified content to client failed: %E");
+ freez(hdr);
+ freez(p);
+ return;
+ }
+
+ freez(hdr);
+ freez(p);
+ }
+
+ break; /* "game over, man" */
+ }
+
+ /*
+ * This is NOT the body, so
+ * Let's pretend the server just sent us a blank line.
+ */
+ snprintf(buf, sizeof(buf), "\r\n");
+ len = (int)strlen(buf);
+
+ /*
+ * Now, let the normal header parsing algorithm below do its
+ * job. If it fails, we'll exit instead of continuing.
+ */
+
+ ms_iis5_hack = 1;
+ }
+
+ /*
+ * If this is an SSL connection or we're in the body
+ * of the server document, just write it to the client,
+ * unless we need to buffer the body for later content-filtering
+ */
+
+ if (server_body || http->ssl)
+ {
+ if (content_filter)
+ {
+ /*
+ * If there is no memory left for buffering the content, or the buffer limit
+ * has been reached, switch to non-filtering mode, i.e. make & write the
+ * header, flush the iob and buf, and get out of the way.
+ */
+ if (add_to_iob(csp, buf, len))
+ {
+ size_t hdrlen;
+ int flushed;
+
+ log_error(LOG_LEVEL_ERROR, "Flushing header and buffers. Stepping back from filtering.");
+ if (JB_ERR_OK != sed(server_patterns, add_server_headers, csp))
+ {
+ log_error(LOG_LEVEL_FATAL, "Failed to parse server headers.");
+ }
+ hdr = list_to_text(csp->headers);
+ if (hdr == NULL)
+ {
+ /*
+ * Memory is too tight to even generate the header.
+ * Send our static "Out-of-memory" page.
+ */
+ log_error(LOG_LEVEL_ERROR, "Out of memory while trying to flush.");
+ rsp = cgi_error_memory();
+ send_crunch_response(csp, rsp);
+
+ return;
+ }
+
+ if (crunch_response_triggered(csp, crunchers_light))
+ {
+ /*
+ * One of the tags created by a server-header
+ * tagger triggered a crunch. We already
+ * delivered the crunch response to the client
+ * and are done here after cleaning up.
+ */
+ freez(hdr);
+ return;
+ }
+
+ hdrlen = strlen(hdr);
+
+ if (write_socket(csp->cfd, hdr, hdrlen)
+ || ((flushed = flush_socket(csp->cfd, csp)) < 0)
+ || (write_socket(csp->cfd, buf, (size_t)len)))
+ {
+ log_error(LOG_LEVEL_CONNECT, "Flush header and buffers to client failed: %E");
+
+ freez(hdr);
+ return;
+ }
+
+ byte_count += hdrlen + (size_t)flushed + (size_t)len;
+ freez(hdr);
+ content_filter = NULL;
+ server_body = 1;
+
+ }
+ }
+ else
+ {
+ if (write_socket(csp->cfd, buf, (size_t)len))
+ {
+ log_error(LOG_LEVEL_ERROR, "write to client failed: %E");
+ return;
+ }
+ }
+ byte_count += (size_t)len;
+ continue;
+ }
+ else
+ {
+ /* we're still looking for the end of the
+ * server's header ... (does that make header
+ * parsing an "out of body experience" ?
+ */
+
+ /*
+ * buffer up the data we just read. If that fails,
+ * there's little we can do but send our static
+ * out-of-memory page.
+ */
+ if (add_to_iob(csp, buf, len))
+ {
+ log_error(LOG_LEVEL_ERROR, "Out of memory while looking for end of server headers.");
+ rsp = cgi_error_memory();
+ send_crunch_response(csp, rsp);
+
+ return;
+ }
+
+ /* Convert iob into something sed() can digest */
+ if (JB_ERR_PARSE == get_server_headers(csp))
+ {
+ if (ms_iis5_hack)
+ {
+ /* Well, we tried our MS IIS/5
+ * hack and it didn't work.
+ * The header is incomplete
+ * and there isn't anything
+ * we can do about it.
+ */
+ break;
+ }
+ else
+ {
+ /* Since we have to wait for
+ * more from the server before
+ * we can parse the headers
+ * we just continue here.
+ */
+ continue;
+ }
+ }
+
+ /* Did we actually get anything? */
+ if (NULL == csp->headers->first)
+ {
+ log_error(LOG_LEVEL_ERROR, "Empty server or forwarder response.");
+ log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 502 0", csp->ip_addr_str, http->cmd);
+ write_socket(csp->cfd, NO_SERVER_DATA_RESPONSE, strlen(NO_SERVER_DATA_RESPONSE));
+ free_http_request(http);
+ return;
+ }
+
+ /* we have now received the entire header.
+ * filter it and send the result to the client
+ */
+ if (JB_ERR_OK != sed(server_patterns, add_server_headers, csp))
+ {
+ log_error(LOG_LEVEL_FATAL, "Failed to parse server headers.");
+ }
+ hdr = list_to_text(csp->headers);
+ if (hdr == NULL)
+ {
+ /* FIXME Should handle error properly */
+ log_error(LOG_LEVEL_FATAL, "Out of memory parsing server header");
+ }
+
+ if (crunch_response_triggered(csp, crunchers_light))
+ {
+ /*
+ * One of the tags created by a server-header
+ * tagger triggered a crunch. We already
+ * delivered the crunch response to the client
+ * and are done here after cleaning up.
+ */
+ freez(hdr);
+ return;
+ }
+ /* Buffer and pcrs filter this if appropriate. */
+
+ if (!http->ssl) /* We talk plaintext */
+ {
+
+#ifdef FEATURE_KILL_POPUPS
+ /* Start blocking popups if appropriate. */
+ if ((csp->content_type & CT_TEXT) && /* It's a text / * MIME-Type */
+ (csp->action->flags & ACTION_NO_POPUPS) != 0) /* Policy allows */
+ {
+ block_popups_now = 1;
+ /*
+ * Filter the part of the body that came in the same read
+ * as the last headers:
+ */
+ filter_popups(csp->iob->cur, csp);
+ }
+#endif /* def FEATURE_KILL_POPUPS */
+ content_filter = get_filter_function(csp);
+ }
+ /*
+ * Only write if we're not buffering for content modification
+ */
+ if (!content_filter)
+ {
+ /* write the server's (modified) header to
+ * the client (along with anything else that
+ * may be in the buffer)
+ */
+
+ if (write_socket(csp->cfd, hdr, strlen(hdr))
+ || ((len = flush_socket(csp->cfd, csp)) < 0))
+ {
+ log_error(LOG_LEVEL_CONNECT, "write header to client failed: %E");
+
+ /* the write failed, so don't bother
+ * mentioning it to the client...
+ * it probably can't hear us anyway.
+ */
+ freez(hdr);
+ return;
+ }
+
+ byte_count += (size_t)len;
+ }
+
+ /* we're finished with the server's header */
+
+ freez(hdr);
+ server_body = 1;
+
+ /* If this was a MS IIS/5 hack then it means
+ * the server has already closed the
+ * connection. Nothing more to read. Time
+ * to bail.
+ */
+ if (ms_iis5_hack)
+ {
+ break;
+ }
+ }
+ continue;
+ }
+
+ return; /* huh? we should never get here */
+ }
+
+ if (csp->content_length == 0)
+ {
+ /*
+ * If Privoxy didn't recalculate the
+ * Content-Lenght, byte_count is still
+ * correct.
+ */
+ csp->content_length = byte_count;
+ }
+
+ log_error(LOG_LEVEL_CLF, "%s - - [%T] \"%s\" 200 %d",
+ csp->ip_addr_str, http->ocmd, csp->content_length);
+}
+
+
+/*********************************************************************
+ *
+ * Function : serve
+ *
+ * Description : This is little more than chat. We only "serve" to
+ * to close any socket that chat may have opened.
+ *
+ * Parameters :
+ * 1 : csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns : N/A
+ *
+ *********************************************************************/
+#ifdef AMIGA
+void serve(struct client_state *csp)
+#else /* ifndef AMIGA */
+static void serve(struct client_state *csp)
+#endif /* def AMIGA */
+{
+ chat(csp);
+ close_socket(csp->cfd);
+
+ if (csp->sfd != JB_INVALID_SOCKET)
+ {
+ close_socket(csp->sfd);
+ }
+
+ csp->flags &= ~CSP_FLAG_ACTIVE;
+
+}
+
+
+#ifdef __BEOS__
+/*********************************************************************
+ *
+ * Function : server_thread
+ *
+ * Description : We only exist to call `serve' in a threaded environment.
+ *
+ * Parameters :
+ * 1 : data = Current client state (buffers, headers, etc...)
+ *
+ * Returns : Always 0.
+ *
+ *********************************************************************/
+static int32 server_thread(void *data)
+{
+ serve((struct client_state *) data);
+ return 0;
+
+}
+#endif
+
+
+/*********************************************************************
+ *
+ * Function : usage
+ *
+ * Description : Print usage info & exit.
+ *
+ * Parameters : Pointer to argv[0] for identifying ourselves
+ *
+ * Returns : No. ,-)
+ *
+ *********************************************************************/
+void usage(const char *myname)
+{
+ printf("Privoxy version " VERSION " (" HOME_PAGE_URL ")\n"
+ "Usage: %s "
+#if defined(unix)
+ "[--chroot] "
+#endif /* defined(unix) */
+ "[--help] "
+#if defined(unix)
+ "[--no-daemon] [--pidfile pidfile] [--user user[.group]] "
+#endif /* defined(unix) */
+ "[--version] [configfile]\n"
+ "Aborting\n", myname);
+
+ exit(2);
+
+}
+
+
+/*********************************************************************
+ *
+ * Function : initialize_mutexes
+ *
+ * Description : Prepares mutexes if mutex support is available.
+ *
+ * Parameters : None
+ *
+ * Returns : Void, exits in case of errors.
+ *
+ *********************************************************************/
+void initialize_mutexes()
+{
+ int err = 0;
+
+#ifdef FEATURE_PTHREAD
+ /*
+ * Prepare global mutex semaphores
+ */
+ err = pthread_mutex_init(&log_mutex, 0);
+
+ if (!err) err = pthread_mutex_init(&log_init_mutex, 0);
+
+ /*
+ * XXX: The assumptions below are a bit naive
+ * and can cause locks that aren't necessary.
+ *
+ * For example older FreeBSD versions (< 6.x?)
+ * have no gethostbyname_r, but gethostbyname is
+ * thread safe.
+ */
+#if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_GETHOSTBYNAME_R)
+ if (!err) err = pthread_mutex_init(&resolver_mutex, 0);
+#endif /* !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_GETHOSTBYNAME_R) */
+ /*
+ * XXX: should we use a single mutex for
+ * localtime() and gmtime() as well?
+ */
+#ifndef HAVE_GMTIME_R
+ if (!err) err = pthread_mutex_init(&gmtime_mutex, 0);
+#endif /* ndef HAVE_GMTIME_R */
+
+#ifndef HAVE_LOCALTIME_R
+ if (!err) err = pthread_mutex_init(&localtime_mutex, 0);
+#endif /* ndef HAVE_GMTIME_R */
+
+#ifndef HAVE_RANDOM
+ if (!err) err = pthread_mutex_init(&rand_mutex, 0);
+#endif /* ndef HAVE_RANDOM */
+#endif /* FEATURE_PTHREAD */
+
+ /*
+ * TODO: mutex support for mingw32 would be swell.
+ */
+
+ if (err)
+ {
+ printf("Fatal error. Mutex initialization failed: %s.\n",
+ strerror(err));
+ exit(1);
+ }
+
+ return;
+}
+
+
+/*********************************************************************
+ *
+ * Function : main
+ *
+ * Description : Load the config file and start the listen loop.
+ * This function is a lot more *sane* with the `load_config'
+ * and `listen_loop' functions; although it stills does
+ * a *little* too much for my taste.
+ *
+ * Parameters :
+ * 1 : argc = Number of parameters (including $0).
+ * 2 : argv = Array of (char *)'s to the parameters.
+ *
+ * Returns : 1 if : can't open config file, unrecognized directive,
+ * stats requested in multi-thread mode, can't open the
+ * log file, can't open the jar file, listen port is invalid,
+ * any load fails, and can't bind port.
+ *
+ * Else main never returns, the process must be signaled
+ * to terminate execution. Or, on Windows, use the
+ * "File", "Exit" menu option.
+ *
+ *********************************************************************/
+#ifdef __MINGW32__
+int real_main(int argc, const char *argv[])
+#else
+int main(int argc, const char *argv[])
+#endif
+{
+ int argc_pos = 0;
+ unsigned int random_seed;
+#ifdef unix
+ struct passwd *pw = NULL;
+ struct group *grp = NULL;
+ char *p;
+ int do_chroot = 0;
+#endif
+
+ Argc = argc;
+ Argv = argv;
+
+ configfile =
+#if !defined(_WIN32)
+ "config"
+#else
+ "config.txt"
+#endif
+ ;
+
+ /*
+ * Parse the command line arguments
+ *
+ * XXX: simply printing usage information in case of
+ * invalid arguments isn't particular user friendly.
+ */
+ while (++argc_pos < argc)
+ {
+#ifdef _WIN32
+ /* Check to see if the service must be installed or uninstalled */
+ if (strncmp(argv[argc_pos], "--install", 9) == 0)
+ {
+ const char *pName = argv[argc_pos] + 9;
+ if (*pName == ':')
+ pName++;
+ exit( (install_service(pName)) ? 0 : 1 );
+ }
+ else if (strncmp(argv[argc_pos], "--uninstall", + 11) == 0)
+ {
+ const char *pName = argv[argc_pos] + 11;
+ if (*pName == ':')
+ pName++;
+ exit((uninstall_service(pName)) ? 0 : 1);
+ }
+ else if (strcmp(argv[argc_pos], "--service" ) == 0)
+ {
+ bRunAsService = TRUE;
+ w32_set_service_cwd();
+ atexit(w32_service_exit_notify);
+ }
+ else
+#endif /* defined(_WIN32) */
+
+
+#if !defined(_WIN32) || defined(_WIN_CONSOLE)
+
+ if (strcmp(argv[argc_pos], "--help") == 0)
+ {
+ usage(argv[0]);
+ }
+
+ else if(strcmp(argv[argc_pos], "--version") == 0)
+ {
+ printf("Privoxy version " VERSION " (" HOME_PAGE_URL ")\n");
+ exit(0);
+ }
+
+#if defined(unix)
+
+ else if (strcmp(argv[argc_pos], "--no-daemon" ) == 0)
+ {
+ no_daemon = 1;
+ }
+
+ else if (strcmp(argv[argc_pos], "--pidfile" ) == 0)
+ {
+ if (++argc_pos == argc) usage(argv[0]);
+ pidfile = strdup(argv[argc_pos]);
+ }
+
+ else if (strcmp(argv[argc_pos], "--user" ) == 0)
+ {
+ if (++argc_pos == argc) usage(argv[argc_pos]);
+
+ if ((NULL != (p = strchr(argv[argc_pos], '.'))) && *(p + 1) != '0')
+ {
+ *p++ = '\0';
+ if (NULL == (grp = getgrnam(p)))
+ {
+ log_error(LOG_LEVEL_FATAL, "Group %s not found.", p);
+ }
+ }
+
+ if (NULL == (pw = getpwnam(argv[argc_pos])))
+ {
+ log_error(LOG_LEVEL_FATAL, "User %s not found.", argv[argc_pos]);
+ }
+
+ if (p != NULL) *--p = '\0';
+ }
+
+ else if (strcmp(argv[argc_pos], "--chroot" ) == 0)
+ {
+ do_chroot = 1;
+ }
+#endif /* defined(unix) */
+
+ else if (argc_pos + 1 != argc)
+ {
+ /*
+ * This is neither the last command line
+ * option, nor was it recognized before,
+ * therefore it must be invalid.
+ */
+ usage(argv[0]);
+ }
+ else
+
+#endif /* defined(_WIN32) && !defined(_WIN_CONSOLE) */
+ {
+ configfile = argv[argc_pos];
+ }
+
+ } /* -END- while (more arguments) */
+
+#if defined(unix)
+ if ( *configfile != '/' )
+ {
+ char cwd[BUFFER_SIZE];
+ char *abs_file;
+ size_t abs_file_size;
+
+ /* make config-filename absolute here */
+ if (NULL == getcwd(cwd, sizeof(cwd)))
+ {
+ perror("failed to get current working directory");
+ exit( 1 );
+ }
+
+ /* XXX: why + 5? */
+ abs_file_size = strlen(cwd) + strlen(configfile) + 5;
+ basedir = strdup(cwd);
+
+ if (NULL == basedir ||
+ NULL == (abs_file = malloc(abs_file_size)))
+ {
+ perror("malloc failed");
+ exit( 1 );
+ }
+ strlcpy(abs_file, basedir, abs_file_size);
+ strlcat(abs_file, "/", abs_file_size );
+ strlcat(abs_file, configfile, abs_file_size);
+ configfile = abs_file;
+ }
+#endif /* defined unix */
+
+
+ files->next = NULL;
+ clients->next = NULL;
+
+#ifdef AMIGA
+ InitAmiga();
+#elif defined(_WIN32)
+ InitWin32();
+#endif
+
+ /* Prepare mutexes if supported and necessary. */
+ initialize_mutexes();
+
+ random_seed = (unsigned int)time(NULL);
+#ifdef HAVE_RANDOM
+ srandom(random_seed);
+#else
+ srand(random_seed);
+#endif /* ifdef HAVE_RANDOM */
+
+ /*
+ * Unix signal handling
+ *
+ * Catch the abort, interrupt and terminate signals for a graceful exit
+ * Catch the hangup signal so the errlog can be reopened.
+ * Ignore the broken pipe signals (FIXME: Why?)
+ */
+#if !defined(_WIN32) && !defined(__OS2__) && !defined(AMIGA)
+{
+ int idx;
+ const int catched_signals[] = { SIGTERM, SIGINT, SIGHUP, 0 };
+ const int ignored_signals[] = { SIGPIPE, 0 };
+
+ for (idx = 0; catched_signals[idx] != 0; idx++)
+ {
+#ifdef sun /* FIXME: Is it safe to check for HAVE_SIGSET instead? */
+ if (sigset(catched_signals[idx], sig_handler) == SIG_ERR)
+#else
+ if (signal(catched_signals[idx], sig_handler) == SIG_ERR)
+#endif /* ifdef sun */
+ {
+ log_error(LOG_LEVEL_FATAL, "Can't set signal-handler for signal %d: %E", catched_signals[idx]);
+ }
+ }
+
+ for (idx = 0; ignored_signals[idx] != 0; idx++)
+ {
+ if (signal(ignored_signals[idx], SIG_IGN) == SIG_ERR)
+ {
+ log_error(LOG_LEVEL_FATAL, "Can't set ignore-handler for signal %d: %E", ignored_signals[idx]);
+ }
+ }
+
+}
+#else /* ifdef _WIN32 */
+# ifdef _WIN_CONSOLE
+ /*
+ * We *are* in a windows console app.
+ * Print a verbose messages about FAQ's and such
+ */
+ printf("%s", win32_blurb);
+# endif /* def _WIN_CONSOLE */
+#endif /* def _WIN32 */