/* hcalproxy -- personal HTTP proxy to convert HTML+hcalendar to icalendar * * Works as an HTTP server. When presented with a request for * http://thismachine:thisport/http://othermachine/path/to/document, * it will issue a request to othermachine, checks that the result is * (X)HTML, parse it and return an icalendar (text/calendar) formed * from all the hcalendar entries in that docoument. * * Options: * * -d * * Run as daemon (i.e., in background). By default, the program * runs in the foreground. * * -p port-or-service * * The port number (or the name of a service from /etc/services) * to listen on. Default is 8000. * * -i * Read a single request from stdin and then exit. * * -c category[,category...] * * Add these categories to every event (in addition to any * categories found in the HTML source). * * To do: * * Handle HEAD. * * Handle hatom and hcard with same code? Maybe use different URL * prefixes to distinguish different functions. * * Add some form of (optional) logging. Use syslog? * * Created: 4 Aug 2008 * Author: Bert Bos #if HAVE_SYS_TYPES_H # include #endif #if HAVE_SYS_SOCKET_H # include #endif #include #include #if HAVE_NETINET_IN_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_UNISTD_H # include #endif #include #include #include #if HAVE_STRINGS_H # include #endif #if HAVE_STRING_H # include #endif #include #include #include #include "heap.e" #include "types.e" #include "errexit.e" #include "scan.e" #include "html.e" #include "dict.e" #include "connectsock.e" #include "openurl.e" #include "tree.e" #include "class.e" #include "unent.e" #include "headers.e" #include "proxy.e" #define QLEN 5 /* Maximum connection queue length */ /* reaper -- clean up zombie children */ static RETSIGTYPE reaper(int unused) { while (waitpid(-1, NULL, WNOHANG) >= 0) ; } /* usage -- print usage message and exit */ static void usage(const conststring progname) { errexit("Usage: %s [-d [-p port-or-service-name] | -i]\n", progname); } int main(int argc, char *argv[]) { string port = "8000"; /* Service name or port number to listen on */ struct sockaddr_in fsin; /* The address of a client */ bool daemon = false; /* Whether to run in the background */ bool use_stdin = false; /* Whether to read stdin instead of socket */ socklen_t alen; /* Length of client's address */ int msock; /* Master server socket */ int ssock; /* Slave server socket */ string categories = NULL; /* Categories to add */ char c; while ((c = getopt(argc, argv, "dp:ic:")) != -1) switch (c) { case 'd': if (!use_stdin) daemon = true; else usage(argv[0]); break; case 'p': if (!use_stdin) port = optarg; else usage(argv[0]); break; case 'i': if (!daemon) use_stdin = true; else usage(argv[0]); break; case 'c': categories = optarg; break; default: usage(argv[0]); } /* If reading stdin, read one request and then exit. */ if (use_stdin) exit(proxy(0, 1, categories)); /* Otherwise, open a socket for listening and never exit */ msock = passiveTCP(port, QLEN); if (msock < 0) errexit("%s: Could not open port %s for listening: %s\n", argv[0], port, strerror(errno)); if (daemon) { switch (fork()) { case -1: /* Error */ errexit("%s: fork(): %s\n", argv[0], strerror(errno)); case 0: /* Child */ printf("Going into background (pid = %d) listening on port %s\n", getpid(), port); close(0); close(1); /* close(2); */ break; default: /* Parent */ exit(0); } } (void) signal(SIGCHLD, reaper); while (1) { alen = sizeof(fsin); ssock = accept(msock, (struct sockaddr *)&fsin, &alen); if (ssock < 0) { if (errno == EINTR) continue; errexit("%s: accept(): %s\n", argv[0], strerror(errno)); } switch (fork()) { case 0: /* Child */ (void) close(msock); exit(proxy(ssock, ssock, categories)); default: /* Parent */ (void) close(ssock); break; case -1: errexit("%s: fork(): %s\n", argv[0], strerror(errno)); } } }