aboutsummaryrefslogtreecommitdiffstats
path: root/vbistuff
diff options
context:
space:
mode:
authorGerd Hoffmann <kraxel@redhat.com>2010-04-01 09:24:38 (GMT)
committerGerd Hoffmann <kraxel@redhat.com>2010-04-01 09:24:38 (GMT)
commita4a3e6b21da7d11e66364ab9ab67795a3f78020a (patch)
tree4ab98e8339361b8a4c89bedbe8888055292746b6 /vbistuff
parentf53a049ec82b6e1d0877a93387ed4d15797749a2 (diff)
v3.74
Diffstat (limited to 'vbistuff')
-rw-r--r--vbistuff/Makefile2
-rw-r--r--vbistuff/Subdir.mk42
-rw-r--r--vbistuff/about.html.in30
-rw-r--r--vbistuff/alevt.css.in159
-rw-r--r--vbistuff/bottom.html.in20
-rw-r--r--vbistuff/httpd.h85
-rw-r--r--vbistuff/main.c772
-rw-r--r--vbistuff/ntsc-cc.c713
-rw-r--r--vbistuff/page.c392
-rw-r--r--vbistuff/request.c195
-rw-r--r--vbistuff/response.c190
-rw-r--r--vbistuff/top.html.in7
12 files changed, 2607 insertions, 0 deletions
diff --git a/vbistuff/Makefile b/vbistuff/Makefile
new file mode 100644
index 0000000..4e33e30
--- /dev/null
+++ b/vbistuff/Makefile
@@ -0,0 +1,2 @@
+default:
+ cd ..; $(MAKE)
diff --git a/vbistuff/Subdir.mk b/vbistuff/Subdir.mk
new file mode 100644
index 0000000..d65684f
--- /dev/null
+++ b/vbistuff/Subdir.mk
@@ -0,0 +1,42 @@
+
+# variables
+TARGETS-vbistuff := vbistuff/alevtd vbistuff/ntsc-cc
+
+HTML-alevtd := \
+ vbistuff/alevt.css.h \
+ vbistuff/top.html.h \
+ vbistuff/bottom.html.h \
+ vbistuff/about.html.h \
+
+OBJS-alevtd := \
+ vbistuff/main.o \
+ vbistuff/request.o \
+ vbistuff/response.o \
+ vbistuff/page.o \
+ libng/devices.o \
+ libvbi/libvbi.a
+OBJS-ntsc-cc := \
+ vbistuff/ntsc-cc.o
+
+# local targets
+vbistuff/alevtd: $(OBJS-alevtd)
+ $(CC) $(CFLAGS) -o $@ $(OBJS-alevtd)
+
+vbistuff/ntsc-cc: $(OBJS-ntsc-cc)
+ $(CC) $(CFLAGS) -o $@ $(OBJS-ntsc-cc) $(ATHENA_LIBS)
+
+
+# global targets
+all:: $(TARGETS-vbistuff)
+
+install::
+ $(INSTALL_PROGRAM) -s $(TARGETS-vbistuff) $(bindir)
+
+clean::
+ rm -f $(HTML-alevtd)
+
+distclean::
+ rm -f $(TARGETS-vbistuff)
+
+# special dependences
+vbistuff/main.o:: vbistuff/main.c $(HTML-alevtd)
diff --git a/vbistuff/about.html.in b/vbistuff/about.html.in
new file mode 100644
index 0000000..e09683d
--- /dev/null
+++ b/vbistuff/about.html.in
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<html><head>
+<title>about alevtd</title>
+<link rel="stylesheet" type="text/css" href="/alevt.css">
+</head>
+<body>
+<b>about the alevt http daemon</b>
+<p>
+This daemon serves videotext pages as HTML.&nbsp; The vbi decoder is
+taken from <a href="http://lecker.essen.de/~froese/">alevt
+1.6.0</a>.&nbsp; Source code of this deamon comes with <a
+href="http://bytesex.org/xawtv/">xawtv</a>.
+<p>
+&copy; 1999-2002 Gerd Knorr &lt;kraxel@goldbach.in-berlin.de&gt; [http server]
+<br>
+&copy; 1998-2000 Edgar Toernig &lt;froese@gmx.de&gt; [vbi decoding, alevt]
+<br>
+misc patches by several other hackers.
+<hr noshade size=1>
+<a href="/100/">100</a> &nbsp;
+<a href="/200/">200</a> &nbsp;
+<a href="/300/">300</a> &nbsp;
+<a href="/400/">400</a> &nbsp;
+<a href="/500/">500</a> &nbsp;
+<a href="/600/">600</a> &nbsp;
+<a href="/700/">700</a> &nbsp;
+<a href="/800/">800</a> &nbsp;
+<a href="/about.html">about</a>
+</body>
+</html>
diff --git a/vbistuff/alevt.css.in b/vbistuff/alevt.css.in
new file mode 100644
index 0000000..08648b4
--- /dev/null
+++ b/vbistuff/alevt.css.in
@@ -0,0 +1,159 @@
+body { background-color: white; color: black }
+
+pre.vt { border: 1pt solid black; padding: 12pt }
+pre.vt { background-color: #c0c0c0; width: auto; float: left }
+pre.vt { white-space: pre }
+
+div.quick { clear: both }
+
+/* please don't re-color my links (underline is fine) */
+/* it would be that simple in theory,
+ but Mozilla/4 does not understand "inherit" *grumble*
+span a { color: inherit; background-color: inherit }
+*/
+
+/* videotext colors */
+span.c00 { color: #000000; background-color: #000000 }
+span.c10 { color: #ff0000; background-color: #000000 }
+span.c20 { color: #00ff00; background-color: #000000 }
+span.c30 { color: #ffff00; background-color: #000000 }
+span.c40 { color: #0000ff; background-color: #000000 }
+span.c50 { color: #ff00ff; background-color: #000000 }
+span.c60 { color: #00ffff; background-color: #000000 }
+span.c70 { color: #ffffff; background-color: #000000 }
+
+span.c01 { color: #000000; background-color: #ff0000 }
+span.c11 { color: #ff0000; background-color: #ff0000 }
+span.c21 { color: #00ff00; background-color: #ff0000 }
+span.c31 { color: #ffff00; background-color: #ff0000 }
+span.c41 { color: #0000ff; background-color: #ff0000 }
+span.c51 { color: #ff00ff; background-color: #ff0000 }
+span.c61 { color: #00ffff; background-color: #ff0000 }
+span.c71 { color: #ffffff; background-color: #ff0000 }
+
+span.c02 { color: #000000; background-color: #00ff00 }
+span.c12 { color: #ff0000; background-color: #00ff00 }
+span.c22 { color: #00ff00; background-color: #00ff00 }
+span.c32 { color: #ffff00; background-color: #00ff00 }
+span.c42 { color: #0000ff; background-color: #00ff00 }
+span.c52 { color: #ff00ff; background-color: #00ff00 }
+span.c62 { color: #00ffff; background-color: #00ff00 }
+span.c72 { color: #ffffff; background-color: #00ff00 }
+
+span.c03 { color: #000000; background-color: #ffff00 }
+span.c13 { color: #ff0000; background-color: #ffff00 }
+span.c23 { color: #00ff00; background-color: #ffff00 }
+span.c33 { color: #ffff00; background-color: #ffff00 }
+span.c43 { color: #0000ff; background-color: #ffff00 }
+span.c53 { color: #ff00ff; background-color: #ffff00 }
+span.c63 { color: #00ffff; background-color: #ffff00 }
+span.c73 { color: #ffffff; background-color: #ffff00 }
+
+span.c04 { color: #000000; background-color: #0000ff }
+span.c14 { color: #ff0000; background-color: #0000ff }
+span.c24 { color: #00ff00; background-color: #0000ff }
+span.c34 { color: #ffff00; background-color: #0000ff }
+span.c44 { color: #0000ff; background-color: #0000ff }
+span.c54 { color: #ff00ff; background-color: #0000ff }
+span.c64 { color: #00ffff; background-color: #0000ff }
+span.c74 { color: #ffffff; background-color: #0000ff }
+
+span.c05 { color: #000000; background-color: #ff00ff }
+span.c15 { color: #ff0000; background-color: #ff00ff }
+span.c25 { color: #00ff00; background-color: #ff00ff }
+span.c35 { color: #ffff00; background-color: #ff00ff }
+span.c45 { color: #0000ff; background-color: #ff00ff }
+span.c55 { color: #ff00ff; background-color: #ff00ff }
+span.c65 { color: #00ffff; background-color: #ff00ff }
+span.c75 { color: #ffffff; background-color: #ff00ff }
+
+span.c06 { color: #000000; background-color: #00ffff }
+span.c16 { color: #ff0000; background-color: #00ffff }
+span.c26 { color: #00ff00; background-color: #00ffff }
+span.c36 { color: #ffff00; background-color: #00ffff }
+span.c46 { color: #0000ff; background-color: #00ffff }
+span.c56 { color: #ff00ff; background-color: #00ffff }
+span.c66 { color: #00ffff; background-color: #00ffff }
+span.c76 { color: #ffffff; background-color: #00ffff }
+
+span.c07 { color: #000000; background-color: #ffffff }
+span.c17 { color: #ff0000; background-color: #ffffff }
+span.c27 { color: #00ff00; background-color: #ffffff }
+span.c37 { color: #ffff00; background-color: #ffffff }
+span.c47 { color: #0000ff; background-color: #ffffff }
+span.c57 { color: #ff00ff; background-color: #ffffff }
+span.c67 { color: #00ffff; background-color: #ffffff }
+span.c77 { color: #ffffff; background-color: #ffffff }
+
+/* The same again for all the links... */
+span.c00 a { color: #000000; background-color: #000000 }
+span.c10 a { color: #ff0000; background-color: #000000 }
+span.c20 a { color: #00ff00; background-color: #000000 }
+span.c30 a { color: #ffff00; background-color: #000000 }
+span.c40 a { color: #0000ff; background-color: #000000 }
+span.c50 a { color: #ff00ff; background-color: #000000 }
+span.c60 a { color: #00ffff; background-color: #000000 }
+span.c70 a { color: #ffffff; background-color: #000000 }
+
+span.c01 a { color: #000000; background-color: #ff0000 }
+span.c11 a { color: #ff0000; background-color: #ff0000 }
+span.c21 a { color: #00ff00; background-color: #ff0000 }
+span.c31 a { color: #ffff00; background-color: #ff0000 }
+span.c41 a { color: #0000ff; background-color: #ff0000 }
+span.c51 a { color: #ff00ff; background-color: #ff0000 }
+span.c61 a { color: #00ffff; background-color: #ff0000 }
+span.c71 a { color: #ffffff; background-color: #ff0000 }
+
+span.c02 a { color: #000000; background-color: #00ff00 }
+span.c12 a { color: #ff0000; background-color: #00ff00 }
+span.c22 a { color: #00ff00; background-color: #00ff00 }
+span.c32 a { color: #ffff00; background-color: #00ff00 }
+span.c42 a { color: #0000ff; background-color: #00ff00 }
+span.c52 a { color: #ff00ff; background-color: #00ff00 }
+span.c62 a { color: #00ffff; background-color: #00ff00 }
+span.c72 a { color: #ffffff; background-color: #00ff00 }
+
+span.c03 a { color: #000000; background-color: #ffff00 }
+span.c13 a { color: #ff0000; background-color: #ffff00 }
+span.c23 a { color: #00ff00; background-color: #ffff00 }
+span.c33 a { color: #ffff00; background-color: #ffff00 }
+span.c43 a { color: #0000ff; background-color: #ffff00 }
+span.c53 a { color: #ff00ff; background-color: #ffff00 }
+span.c63 a { color: #00ffff; background-color: #ffff00 }
+span.c73 a { color: #ffffff; background-color: #ffff00 }
+
+span.c04 a { color: #000000; background-color: #0000ff }
+span.c14 a { color: #ff0000; background-color: #0000ff }
+span.c24 a { color: #00ff00; background-color: #0000ff }
+span.c34 a { color: #ffff00; background-color: #0000ff }
+span.c44 a { color: #0000ff; background-color: #0000ff }
+span.c54 a { color: #ff00ff; background-color: #0000ff }
+span.c64 a { color: #00ffff; background-color: #0000ff }
+span.c74 a { color: #ffffff; background-color: #0000ff }
+
+span.c05 a { color: #000000; background-color: #ff00ff }
+span.c15 a { color: #ff0000; background-color: #ff00ff }
+span.c25 a { color: #00ff00; background-color: #ff00ff }
+span.c35 a { color: #ffff00; background-color: #ff00ff }
+span.c45 a { color: #0000ff; background-color: #ff00ff }
+span.c55 a { color: #ff00ff; background-color: #ff00ff }
+span.c65 a { color: #00ffff; background-color: #ff00ff }
+span.c75 a { color: #ffffff; background-color: #ff00ff }
+
+span.c06 a { color: #000000; background-color: #00ffff }
+span.c16 a { color: #ff0000; background-color: #00ffff }
+span.c26 a { color: #00ff00; background-color: #00ffff }
+span.c36 a { color: #ffff00; background-color: #00ffff }
+span.c46 a { color: #0000ff; background-color: #00ffff }
+span.c56 a { color: #ff00ff; background-color: #00ffff }
+span.c66 a { color: #00ffff; background-color: #00ffff }
+span.c76 a { color: #ffffff; background-color: #00ffff }
+
+span.c07 a { color: #000000; background-color: #ffffff }
+span.c17 a { color: #ff0000; background-color: #ffffff }
+span.c27 a { color: #00ff00; background-color: #ffffff }
+span.c37 a { color: #ffff00; background-color: #ffffff }
+span.c47 a { color: #0000ff; background-color: #ffffff }
+span.c57 a { color: #ff00ff; background-color: #ffffff }
+span.c67 a { color: #00ffff; background-color: #ffffff }
+span.c77 a { color: #ffffff; background-color: #ffffff }
diff --git a/vbistuff/bottom.html.in b/vbistuff/bottom.html.in
new file mode 100644
index 0000000..aed8480
--- /dev/null
+++ b/vbistuff/bottom.html.in
@@ -0,0 +1,20 @@
+<a href="/100/">100</a> &nbsp;
+<a href="/200/">200</a> &nbsp;
+<a href="/300/">300</a> &nbsp;
+<a href="/400/">400</a> &nbsp;
+<a href="/500/">500</a> &nbsp;
+<a href="/600/">600</a> &nbsp;
+<a href="/700/">700</a> &nbsp;
+<a href="/800/">800</a> &nbsp;
+<a href="/about.html">about</a>
+</div>
+<form name="goto" action="/goto/">
+Jump to page
+<input type="text" name="p" size="4" maxlength="3">
+<input type="submit" value="ok">
+</form>
+<script language="JavaScript">
+document.forms["goto"].elements["p"].focus();
+</script>
+</body>
+</html>
diff --git a/vbistuff/httpd.h b/vbistuff/httpd.h
new file mode 100644
index 0000000..9d4dbf2
--- /dev/null
+++ b/vbistuff/httpd.h
@@ -0,0 +1,85 @@
+#include <sys/stat.h>
+
+#define STATE_READ_HEADER 1
+#define STATE_PARSE_HEADER 2
+#define STATE_WRITE_HEADER 3
+#define STATE_WRITE_BODY 4
+#define STATE_FINISHED 7
+
+#define STATE_KEEPALIVE 8
+#define STATE_CLOSE 9
+
+#define MAX_HEADER 4096
+#define BR_HEADER 512
+
+struct REQUEST {
+ int fd; /* socket handle */
+ int state; /* what to to ??? */
+ time_t ping; /* last read/write (for timeouts) */
+ int keep_alive;
+
+#ifdef HAVE_SOCKADDR_STORAGE
+ struct sockaddr_storage peer; /* client (log) */
+#else
+ struct sockaddr peer;
+#endif
+ char peerhost[65];
+ char peerserv[9];
+
+ /* request */
+ char hreq[MAX_HEADER+1]; /* request header */
+ int lreq; /* request length */
+ int hdata; /* data in hreq */
+ char type[16]; /* req type */
+ char uri[1024]; /* req uri */
+ char hostname[65]; /* hostname */
+ char path[1024]; /* file path */
+ int major,minor; /* http version */
+
+ /* response */
+ int status; /* status code (log) */
+ int bc; /* byte counter (log) */
+ char hres[MAX_HEADER+1]; /* response header */
+ int lres; /* header length */
+ char *mime; /* mime type */
+ char *body;
+ int lbody;
+ int free_the_mallocs;
+ int head_only;
+ off_t written;
+
+ /* linked list */
+ struct REQUEST *next;
+};
+
+/* --- main.c --------------------------------------------------- */
+
+extern int debug;
+extern int tcp_port;
+extern int ascii_art;
+extern char *server_name;
+extern int canonicalhost;
+extern char server_host[];
+extern time_t now,start;
+
+extern struct vbi *vbi;
+extern struct export *fmt;
+
+void xperror(int loglevel, char *txt, char *peerhost);
+void xerror(int loglevel, char *txt, char *peerhost);
+
+/* --- request.c ------------------------------------------------ */
+
+void read_request(struct REQUEST *req, int pipelined);
+void parse_request(struct REQUEST *req);
+
+/* --- response.c ----------------------------------------------- */
+
+void mkerror(struct REQUEST *req, int status, int ka);
+void mkredirect(struct REQUEST *req);
+void mkheader(struct REQUEST *req, int status, time_t mtime);
+void write_request(struct REQUEST *req);
+
+/* --- page.c ----------------------------------------------- */
+
+void buildpage(struct REQUEST *req);
diff --git a/vbistuff/main.c b/vbistuff/main.c
new file mode 100644
index 0000000..160bb1c
--- /dev/null
+++ b/vbistuff/main.c
@@ -0,0 +1,772 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <grp.h>
+#include <time.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/signal.h>
+#include <sys/utsname.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#ifdef __linux__
+# include <linux/videodev.h>
+#endif
+
+#include "httpd.h"
+#include "devices.h"
+
+/* libvbi */
+#include "vt.h"
+#include "misc.h"
+#include "fdset.h"
+#include "vbi.h"
+#include "lang.h"
+#include "dllist.h"
+#include "export.h"
+
+/* ---------------------------------------------------------------------- */
+/* public variables - server configuration */
+
+char *server_name = "alevt/1.6.0";
+
+int debug = 0;
+int dontdetach = 0;
+int timeout = 60;
+int keepalive_time = 5;
+int tcp_port = 0;
+int ascii_art = 0;
+char *listen_ip = NULL;
+char *listen_port = "5654";
+int canonicalhost = 0;
+char server_host[256];
+char user[17];
+char group[17];
+char *logfile = NULL;
+FILE *log = NULL;
+int flushlog = 0;
+int usesyslog = 0;
+int have_tty = 1;
+int max_conn = 32;
+int cachereset = 0;
+
+time_t now,start;
+int slisten;
+
+struct vbi *vbi;
+struct export *fmt;
+void vbi_handler(struct vbi *vbi, int fd);
+
+static void
+dummy_client(struct dl_head *reqs, struct vt_event *ev)
+{
+ struct vt_page *vtp;
+
+ if (!debug)
+ return;
+
+ switch (ev->type) {
+ case EV_PAGE:
+ vtp = ev->p1;
+ fprintf(stderr,"page %x.%02x \r", vtp->pgno, vtp->subno);
+ break;
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int termsig,got_sighup,got_sigusr1;
+
+static void catchsig(int sig)
+{
+ if (SIGTERM == sig || SIGINT == sig)
+ termsig = sig;
+ if (SIGHUP == sig)
+ got_sighup = 1;
+ if (SIGUSR1 == sig)
+ got_sigusr1 = 1;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void
+usage(char *name)
+{
+ char *h;
+ struct passwd *pw;
+ struct group *gr;
+
+ h = strrchr(name,'/');
+ fprintf(stderr,
+ "alevt http daemon.\n"
+ "\n"
+ "usage: %s [ options ]\n"
+ "\n"
+ "Options:\n"
+ " -h print this text\n"
+ " -v dev vbi device [%s]\n"
+ " -6 16 vbi lines (depending on the bttv version\n"
+ " -9 19 vbi lines one of these two should work)\n"
+ " -d enable debug output [%s]\n"
+ " -F do not fork into background [%s]\n"
+ " -s enable syslog (start/stop/errors) [%s]\n"
+ " -t sec set network timeout [%i]\n"
+ " -c n set max. allowed connections [%i]\n"
+ " -p port use tcp-port >port< [%s]\n"
+ " -n host server hostname is >host< [%s]\n"
+ " -N host same as above + UseCanonicalName\n"
+ " -i ip bind to IP-address >ip< [%s]\n"
+ " -l log write access log to file >log< [%s]\n"
+ " -L log same as above + flush every line\n"
+ " -r poll tv frequency and clear cache\n"
+ " on station changes [%s]\n"
+ " -a use ascii art for block graphics [%s]\n",
+ h ? h+1 : name,
+ ng_dev.vbi,
+ debug ? "on" : "off",
+ dontdetach ? "on" : "off",
+ usesyslog ? "on" : "off",
+ timeout, max_conn,
+ listen_port,
+ server_host,
+ listen_ip ? listen_ip : "any",
+ logfile ? logfile : "none",
+ cachereset ? "on" : "off",
+ ascii_art ? "on" : "off");
+ if (getuid() == 0) {
+ pw = getpwuid(0);
+ gr = getgrgid(getgid());
+ fprintf(stderr,
+ " -u user run as user >user< [%s]\n"
+ " -g group run as group >group< [%s]\n",
+ pw ? pw->pw_name : "???",
+ gr ? gr->gr_name : "???");
+ }
+ exit(1);
+}
+
+static void
+fix_ug(void)
+{
+ struct passwd *pw = NULL;
+ struct group *gr = NULL;
+
+ /* root is allowed to use any uid/gid,
+ * others will get their real uid/gid */
+ if (0 == getuid() && strlen(user) > 0) {
+ if (NULL == (pw = getpwnam(user)))
+ pw = getpwuid(atoi(user));
+ } else {
+ pw = getpwuid(getuid());
+ }
+ if (0 == getuid() && strlen(group) > 0) {
+ if (NULL == (gr = getgrnam(group)))
+ gr = getgrgid(atoi(group));
+ } else {
+ gr = getgrgid(getgid());
+ }
+
+ if (NULL == pw) {
+ xerror(LOG_ERR,"user unknown",NULL);
+ exit(1);
+ }
+ if (NULL == gr) {
+ xerror(LOG_ERR,"group unknown",NULL);
+ exit(1);
+ }
+
+ /* set group */
+ if (getegid() != gr->gr_gid || getgid() != gr->gr_gid)
+ setgid(gr->gr_gid);
+ if (getegid() != gr->gr_gid || getgid() != gr->gr_gid) {
+ xerror(LOG_ERR,"setgid failed",NULL);
+ exit(1);
+ }
+ strncpy(group,gr->gr_name,16);
+
+ /* set user */
+ if (geteuid() != pw->pw_uid || getuid() != pw->pw_uid)
+ setuid(pw->pw_uid);
+ if (geteuid() != pw->pw_uid || getuid() != pw->pw_uid) {
+ xerror(LOG_ERR,"setuid failed",NULL);
+ exit(1);
+ }
+ strncpy(user,pw->pw_name,16);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void
+access_log(struct REQUEST *req, time_t now)
+{
+ char timestamp[32];
+
+ /* common log format: host ident authuser date request status bytes */
+ strftime(timestamp,31,"[%d/%b/%Y:%H:%M:%S +0000]",gmtime(&now));
+ if (0 == req->status)
+ req->status = 400; /* bad request */
+ if (400 == req->status) {
+ fprintf(log,"%s - - %s \"-\" 400 %d\n",
+ req->peerhost,
+ timestamp,
+ req->bc);
+ } else {
+ fprintf(log,"%s - - %s \"%s %s HTTP/%d.%d\" %d %d\n",
+ req->peerhost,
+ timestamp,
+ req->type,
+ req->uri,
+ req->major,
+ req->minor,
+ req->status,
+ req->bc);
+ }
+ if (flushlog)
+ fflush(log);
+}
+
+/*
+ * loglevel usage
+ * ERR : fatal errors (which are followed by exit(1))
+ * WARNING: this should'nt happen error (oom, ...)
+ * NOTICE : start/stop of the daemon
+ * INFO : "normal" errors (canceled downloads, timeouts,
+ * stuff what happens all the time)
+ */
+
+static void
+syslog_init(void)
+{
+ openlog("alevtd",LOG_PID, LOG_DAEMON);
+}
+
+static void
+syslog_start(void)
+{
+ syslog(LOG_NOTICE,
+ "started (listen on %s:%d, user=%s, group=%s)\n",
+ listen_ip,tcp_port,user,group);
+}
+
+static void
+syslog_stop(void)
+{
+ if (termsig)
+ syslog(LOG_NOTICE,"stopped on signal %d\n",termsig);
+ else
+ syslog(LOG_NOTICE,"stopped\n");
+ closelog();
+}
+
+void
+xperror(int loglevel, char *txt, char *peerhost)
+{
+ if (LOG_INFO == loglevel && usesyslog < 2 && !debug)
+ return;
+ if (have_tty) {
+ if (NULL == peerhost)
+ perror(txt);
+ else
+ fprintf(stderr,"%s: %s (peer=%s)\n",txt,strerror(errno),
+ peerhost);
+ }
+ if (usesyslog) {
+ if (NULL == peerhost)
+ syslog(loglevel,"%s: %s\n",txt,strerror(errno));
+ else
+ syslog(loglevel,"%s: %s (peer=%s)\n",txt,strerror(errno),
+ peerhost);
+ }
+}
+
+void
+xerror(int loglevel, char *txt, char *peerhost)
+{
+ if (LOG_INFO == loglevel && usesyslog < 2 && !debug)
+ return;
+ if (have_tty) {
+ if (NULL == peerhost)
+ fprintf(stderr,"%s\n",txt);
+ else
+ fprintf(stderr,"%s (peer=%s)\n",txt,peerhost);
+ }
+ if (usesyslog) {
+ if (NULL == peerhost)
+ syslog(loglevel,"%s\n",txt);
+ else
+ syslog(loglevel,"%s (peer=%s)\n",txt,peerhost);
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+/* main loop */
+
+static void*
+mainloop(void)
+{
+ struct REQUEST *conns = NULL;
+ int curr_conn = 0;
+
+ struct REQUEST *req,*prev,*tmp;
+ struct timeval tv;
+ int max,length,freq,lastfreq = 0;
+ fd_set rd,wr;
+
+ for (;!termsig;) {
+ if (got_sighup) {
+ if (NULL != logfile && 0 != strcmp(logfile,"-")) {
+ if (debug)
+ fprintf(stderr,"got SIGHUP, reopen logfile %s\n",logfile);
+ if (log) fclose(log);
+ if (NULL == (log = fopen(logfile,"a")))
+ xperror(LOG_WARNING,"reopen access log",NULL);
+ }
+ got_sighup = 0;
+ }
+ if (got_sigusr1) {
+ if (debug)
+ fprintf(stderr,"got SIGUSR1, reset cached vbi pages\n");
+ vbi->cache->op->reset(vbi->cache);
+ got_sigusr1 = 0;
+ }
+
+ FD_ZERO(&rd);
+ FD_ZERO(&wr);
+ FD_SET(vbi->fd,&rd);
+ max = vbi->fd;
+ /* add listening socket */
+ if (curr_conn < max_conn) {
+ FD_SET(slisten,&rd);
+ max = slisten;
+ }
+ /* add connection sockets */
+ for (req = conns; req != NULL; req = req->next) {
+ switch (req->state) {
+ case STATE_KEEPALIVE:
+ case STATE_READ_HEADER:
+ FD_SET(req->fd,&rd);
+ if (req->fd > max)
+ max = req->fd;
+ break;
+ case STATE_WRITE_HEADER:
+ case STATE_WRITE_BODY:
+ FD_SET(req->fd,&wr);
+ if (req->fd > max)
+ max = req->fd;
+ break;
+ }
+ }
+ /* go! */
+ tv.tv_sec = keepalive_time;
+ tv.tv_usec = 0;
+ if (-1 == select(max+1,&rd,&wr,NULL,(curr_conn > 0) ? &tv : NULL)) {
+ if (debug)
+ perror("select");
+ continue;
+ }
+ now = time(NULL);
+
+ /* vbi data? */
+ if (FD_ISSET(vbi->fd,&rd)) {
+#ifdef __linux__
+ if (cachereset) {
+ ioctl(vbi->fd, VIDIOCGFREQ, &freq);
+ if (lastfreq != freq) {
+ lastfreq = freq;
+ vbi->cache->op->reset( vbi->cache) ;
+ if (debug)
+ fprintf(stderr, "frequency change: cache cleared.\n");
+ }
+ }
+#endif
+ vbi_handler(vbi,vbi->fd);
+ }
+
+ /* new connection ? */
+ if (FD_ISSET(slisten,&rd)) {
+ req = malloc(sizeof(struct REQUEST));
+ if (NULL == req) {
+ /* oom: let the request sit in the listen queue */
+ if (debug)
+ fprintf(stderr,"oom\n");
+ } else {
+ memset(req,0,sizeof(struct REQUEST));
+ if (-1 == (req->fd = accept(slisten,NULL,NULL))) {
+ if (EAGAIN != errno)
+ xperror(LOG_WARNING,"accept",NULL);
+ free(req);
+ } else {
+ fcntl(req->fd,F_SETFL,O_NONBLOCK);
+ req->state = STATE_READ_HEADER;
+ req->ping = now;
+ req->next = conns;
+ conns = req;
+ curr_conn++;
+ if (debug)
+ fprintf(stderr,"%03d: new request (%d)\n",req->fd,curr_conn);
+ length = sizeof(req->peer);
+ if (-1 == getpeername(req->fd,(struct sockaddr*)&(req->peer),&length)) {
+ xperror(LOG_WARNING,"getpeername",NULL);
+ req->state = STATE_CLOSE;
+ }
+ getnameinfo((struct sockaddr*)&req->peer,length,
+ req->peerhost,64,req->peerserv,8,
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if (debug)
+ fprintf(stderr,"%03d: connect from (%s)\n",
+ req->fd,req->peerhost);
+ }
+ }
+ }
+
+ /* check active connections */
+ for (req = conns, prev = NULL; req != NULL;) {
+ /* I/O */
+ if (FD_ISSET(req->fd,&rd)) {
+ if (req->state == STATE_KEEPALIVE)
+ req->state = STATE_READ_HEADER;
+ read_request(req,0);
+ req->ping = now;
+ }
+ if (FD_ISSET(req->fd,&wr)) {
+ write_request(req);
+ req->ping = now;
+ }
+
+ /* check timeouts */
+ if (req->state == STATE_KEEPALIVE) {
+ if (now > req->ping + keepalive_time) {
+ if (debug)
+ fprintf(stderr,"%03d: keepalive timeout\n",req->fd);
+ req->state = STATE_CLOSE;
+ }
+ } else {
+ if (now > req->ping + timeout) {
+ if (req->state == STATE_READ_HEADER) {
+ mkerror(req,408,0);
+ } else {
+ xerror(LOG_INFO,"network timeout",req->peerhost);
+ req->state = STATE_CLOSE;
+ }
+ }
+ }
+
+ /* header parsing */
+header_parsing:
+ if (req->state == STATE_PARSE_HEADER) {
+ parse_request(req);
+ if (req->state == STATE_WRITE_HEADER)
+ write_request(req);
+ }
+
+ /* handle finished requests */
+ if (req->state == STATE_FINISHED && !req->keep_alive)
+ req->state = STATE_CLOSE;
+ if (req->state == STATE_FINISHED) {
+ if (log)
+ access_log(req,now);
+ /* cleanup */
+ if (req->free_the_mallocs)
+ free(req->body);
+ req->free_the_mallocs = 0;
+ req->body = NULL;
+ req->written = 0;
+ req->head_only = 0;
+
+ if (req->hdata == req->lreq) {
+ /* ok, wait for the next one ... */
+ if (debug)
+ fprintf(stderr,"%03d: keepalive wait\n",req->fd);
+ req->state = STATE_KEEPALIVE;
+ req->hdata = 0;
+ req->lreq = 0;
+ } else {
+ /* there is a pipelined request in the queue ... */
+ if (debug)
+ fprintf(stderr,"%03d: keepalive pipeline\n",req->fd);
+ req->state = STATE_READ_HEADER;
+ memmove(req->hreq,req->hreq+req->lreq,
+ req->hdata-req->lreq);
+ req->hdata -= req->lreq;
+ req->lreq = 0;
+ read_request(req,1);
+ goto header_parsing;
+ }
+ }
+
+ /* connections to close */
+ if (req->state == STATE_CLOSE) {
+ if (log)
+ access_log(req,now);
+ /* cleanup */
+ close(req->fd);
+ curr_conn--;
+ if (debug)
+ fprintf(stderr,"%03d: done (%d)\n",req->fd,curr_conn);
+ /* unlink from list */
+ tmp = req;
+ if (prev == NULL) {
+ conns = req->next;
+ req = conns;
+ } else {
+ prev->next = req->next;
+ req = req->next;
+ }
+ /* free memory */
+ if (tmp->free_the_mallocs)
+ free(tmp->body);
+ free(tmp);
+ } else {
+ prev = req;
+ req = req->next;
+ }
+ }
+ }
+ return NULL;
+}
+
+/* ---------------------------------------------------------------------- */
+
+int
+main(int argc, char *argv[])
+{
+ struct sigaction act,old;
+ struct addrinfo ask,*res;
+#ifdef HAVE_SOCKADDR_STORAGE
+ struct sockaddr_storage ss;
+#else
+ struct sockaddr ss;
+#endif
+ int c, opt, rc, ss_len;
+ int bttv = -1;
+ char host[INET6_ADDRSTRLEN+1];
+ char serv[16];
+
+ ng_device_init();
+ gethostname(server_host,255);
+ memset(&ask,0,sizeof(ask));
+ ask.ai_flags = AI_CANONNAME;
+ if (0 == (rc = getaddrinfo(server_host, NULL, &ask, &res))) {
+ if (res->ai_canonname)
+ strcpy(server_host,res->ai_canonname);
+ }
+
+ /* parse options */
+ for (;;) {
+ if (-1 == (c = getopt(argc,argv,"69hasdFr"
+ "p:N:n:i:t:c:u:g:l:L:v:")))
+ break;
+ switch (c) {
+ case 'h':
+ usage(argv[0]);
+ break;
+ case '6':
+ bttv = 0;
+ break;
+ case '9':
+ bttv = 1;
+ break;
+ case 'a':
+ ascii_art++;
+ break;
+ case 's':
+ usesyslog++;
+ break;
+ case 'd':
+ debug++;
+ break;
+ case 'F':
+ dontdetach++;
+ break;
+ case 'r':
+ cachereset++;
+ break;
+ case 'N':
+ canonicalhost = 1;
+ /* fall through */
+ case 'n':
+ strcpy(server_host,optarg);
+ break;
+ case 'i':
+ listen_ip = optarg;
+ break;
+ case 'p':
+ listen_port = optarg;
+ break;
+ case 't':
+ timeout = atoi(optarg);
+ break;
+ case 'c':
+ max_conn = atoi(optarg);
+ break;
+ case 'u':
+ strncpy(user,optarg,16);
+ break;
+ case 'g':
+ strncpy(group,optarg,16);
+ break;
+ case 'L':
+ flushlog = 1;
+ /* fall through */
+ case 'l':
+ logfile = optarg;
+ break;
+ case 'v':
+ ng_dev.vbi = optarg;
+ break;
+ default:
+ exit(1);
+ }
+ }
+ if (usesyslog)
+ syslog_init();
+
+ /* open vbi device */
+ fdset_init(fds);
+ vbi = vbi_open(ng_dev.vbi, cache_open(), 0, bttv);
+ if (vbi == 0) {
+ xperror(LOG_ERR,"cannot open vbi device",NULL);
+ exit(1);
+ }
+ fmt = export_open("ascii");
+ vbi_add_handler(vbi, dummy_client, NULL);
+#if 0
+ if (vbi->cache)
+ vbi->cache->op->mode(vbi->cache, CACHE_MODE_ERC, erc);
+#endif
+
+ /* bind to socket */
+ slisten = -1;
+ memset(&ask,0,sizeof(ask));
+ ask.ai_flags = AI_PASSIVE;
+ if (listen_ip)
+ ask.ai_flags |= AI_CANONNAME;
+ ask.ai_socktype = SOCK_STREAM;
+
+ /* try ipv6 first ... */
+ ask.ai_family = PF_INET6;
+ if (0 != (rc = getaddrinfo(listen_ip, listen_port, &ask, &res))) {
+ if (debug)
+ fprintf(stderr,"getaddrinfo (ipv6): %s\n",gai_strerror(rc));
+ } else {
+ if (-1 == (slisten = socket(res->ai_family, res->ai_socktype,
+ res->ai_protocol)) && debug)
+ xperror(LOG_ERR,"socket (ipv6)",NULL);
+ }
+
+ /* ... failing that try ipv4 */
+ if (-1 == slisten) {
+ ask.ai_family = PF_INET;
+ if (0 != (rc = getaddrinfo(listen_ip, listen_port, &ask, &res))) {
+ fprintf(stderr,"getaddrinfo (ipv4): %s\n",gai_strerror(rc));
+ exit(1);
+ }
+ if (-1 == (slisten = socket(res->ai_family, res->ai_socktype,
+ res->ai_protocol))) {
+ xperror(LOG_ERR,"socket (ipv4)",NULL);
+ exit(1);
+ }
+ }
+
+ memcpy(&ss,res->ai_addr,res->ai_addrlen);
+ ss_len = res->ai_addrlen;
+ if (res->ai_canonname)
+ strcpy(server_host,res->ai_canonname);
+ if (0 != (rc = getnameinfo((struct sockaddr*)&ss,ss_len,
+ host,INET6_ADDRSTRLEN,serv,15,
+ NI_NUMERICHOST | NI_NUMERICSERV))) {
+ fprintf(stderr,"getnameinfo: %s\n",gai_strerror(rc));
+ exit(1);
+ }
+
+ tcp_port = atoi(serv);
+ opt = 1;
+ setsockopt(slisten,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
+ fcntl(slisten,F_SETFL,O_NONBLOCK);
+
+ if (-1 == bind(slisten, (struct sockaddr*) &ss, ss_len)) {
+ xperror(LOG_ERR,"bind",NULL);
+ exit(1);
+ }
+ if (-1 == listen(slisten, 2*max_conn)) {
+ xperror(LOG_ERR,"listen",NULL);
+ exit(1);
+ }
+
+ /* change user/group - also does chroot */
+ fix_ug();
+
+ if (logfile) {
+ if (0 == strcmp(logfile,"-")) {
+ log = stdout;
+ } else {
+ if (NULL == (log = fopen(logfile,"a")))
+ xperror(LOG_WARNING,"open access log",NULL);
+ }
+ }
+
+ if (debug) {
+ fprintf(stderr,
+ "alevt http server started\n"
+ " ipv6 : %s\n"
+ " node : %s\n"
+ " ipaddr: %s\n"
+ " port : %d\n"
+ " user : %s\n"
+ " group : %s\n",
+ res->ai_family == PF_INET6 ? "yes" : "no",
+ server_host,host,tcp_port,user,group);
+ }
+
+ /* run as daemon - detach from terminal */
+ if ((!debug) && (!dontdetach)) {
+ switch (fork()) {
+ case -1:
+ xperror(LOG_ERR,"fork",NULL);
+ exit(1);
+ case 0:
+ close(0); close(1); close(2); setsid();
+ have_tty = 0;
+ break;
+ default:
+ exit(0);
+ }
+ }
+ if (usesyslog) {
+ syslog_start();
+ atexit(syslog_stop);
+ }
+
+ /* setup signal handler */
+ memset(&act,0,sizeof(act));
+ sigemptyset(&act.sa_mask);
+ act.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE,&act,&old);
+ act.sa_handler = catchsig;
+ sigaction(SIGHUP,&act,&old);
+ sigaction(SIGUSR1,&act,&old);
+ sigaction(SIGTERM,&act,&old);
+ if (debug)
+ sigaction(SIGINT,&act,&old);
+
+ /* go! */
+ start = time(NULL);
+ mainloop();
+
+ if (log)
+ fclose(log);
+ if (debug)
+ fprintf(stderr,"bye...\n");
+ exit(0);
+}
diff --git a/vbistuff/ntsc-cc.c b/vbistuff/ntsc-cc.c
new file mode 100644
index 0000000..78b6cc9
--- /dev/null
+++ b/vbistuff/ntsc-cc.c
@@ -0,0 +1,713 @@
+/* cc.c -- closed caption decoder
+ * Mike Baker (mbm@linux.com)
+ * (based on code by timecop@japan.co.jp)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#ifdef HAVE_GETOPT_H
+# include <getopt.h>
+#endif
+
+#ifndef X_DISPLAY_MISSING
+# include <X11/X.h>
+# include <X11/Xlib.h>
+# include <X11/Xutil.h>
+# include <X11/Xproto.h>
+Display *dpy;
+Window Win,Root;
+char dpyname[256] = "";
+GC WinGC;
+GC WinGC0;
+GC WinGC1;
+int x;
+#endif
+
+
+//XDSdecode
+char info[8][25][256];
+char newinfo[8][25][256];
+char *infoptr=newinfo[0][0];
+int mode,type;
+char infochecksum;
+
+//ccdecode
+char *ratings[] = {"(NOT RATED)","TV-Y","TV-Y7","TV-G","TV-PG","TV-14","TV-MA","(NOT RATED)"};
+int rowdata[] = {11,-1,1,2,3,4,12,13,14,15,5,6,7,8,9,10};
+char *specialchar[] = {"","","","","(TM)","","","o/~ ",""," ","","","","","",""};
+char *modes[]={"current","future","channel","miscellaneous","public service","reserved","invalid","invalid","invalid","invalid"};
+int lastcode;
+int ccmode=1; //cc1 or cc2
+char ccbuf[3][256]; //cc is 32 columns per row, this allows for extra characters
+int keywords=0;
+char *keyword[32];
+
+
+//args (this should probably be put into a structure later)
+char useraw=0;
+char semirawdata=0;
+char usexds=0;
+char usecc=0;
+char plain=0;
+char usesen=0;
+char debugwin=0;
+
+char rawline=-1;
+
+int sen;
+int inval;
+
+static int parityok(int n) /* check parity for 2 bytes packed in n */
+{
+ int mask=0;
+ int j, k;
+ for (k = 1, j = 0; j < 7; j++) {
+ if (n & (1<<j))
+ k++;
+ }
+ if ((k & 1) == ((n>>7)&1))
+ mask|=0x00FF;
+ for (k = 1, j = 8; j < 15; j++) {
+ if (n & (1<<j))
+ k++;
+ }
+ if ((k & 1) == ((n>>15)&1))
+ mask|=0xFF00;
+ return mask;
+}
+
+static int decodebit(unsigned char *data, int threshold)
+{
+ int i, sum = 0;
+ for (i = 0; i < 23; i++)
+ sum += data[i];
+ return (sum > threshold*23);
+}
+
+static int decode(unsigned char *vbiline)
+{
+ int max[7], min[7], val[7], i, clk, tmp, sample, packedbits = 0;
+
+ for (clk=0; clk<7; clk++)
+ max[clk] = min[clk] = val[clk] = -1;
+ clk = tmp = 0;
+ i=30;
+
+ while (i < 600 && clk < 7) { /* find and lock all 7 clocks */
+ sample = vbiline[i];
+ if (max[clk] < 0) { /* find maximum value before drop */
+ if (sample > 85 && sample > val[clk])
+ (val[clk] = sample, tmp = i); /* mark new maximum found */
+ else if (val[clk] - sample > 30) /* far enough */
+ (max[clk] = tmp, i = tmp + 10);
+ } else { /* find minimum value after drop */
+ if (sample < 85 && sample < val[clk])
+ (val[clk] = sample, tmp = i); /* mark new minimum found */
+ else if (sample - val[clk] > 30) /* searched far enough */
+ (min[clk++] = tmp, i = tmp + 10);
+ }
+ i++;
+ }
+
+ i=min[6]=min[5]-max[5]+max[6];
+
+ if (clk != 7 || vbiline[max[3]] - vbiline[min[5]] < 45) /* failure to locate clock lead-in */
+ return -1;
+
+#ifndef X_DISPLAY_MISSING
+ if (debugwin) {
+ for (clk=0;clk<7;clk++)
+ {
+ XDrawLine(dpy,Win,WinGC,min[clk]/2,0,min[clk]/2,128);
+ XDrawLine(dpy,Win,WinGC1,max[clk]/2,0,max[clk]/2,128);
+ }
+ XFlush(dpy);
+ }
+#endif
+
+
+ /* calculate threshold */
+ for (i=0,sample=0;i<7;i++)
+ sample=(sample + vbiline[min[i]] + vbiline[max[i]])/3;
+
+ for(i=min[6];vbiline[i]<sample;i++);
+
+#ifndef X_DISPLAY_MISSING
+ if (debugwin) {
+ for (clk=i;clk<i+57*18;clk+=57)
+ XDrawLine(dpy,Win,WinGC,clk/2,0,clk/2,128);
+ XFlush(dpy);
+ }
+#endif
+
+
+ tmp = i+57;
+ for (i = 0; i < 16; i++)
+ if(decodebit(&vbiline[tmp + i * 57], sample))
+ packedbits |= 1<<i;
+ return packedbits&parityok(packedbits);
+} /* decode */
+
+static int XDSdecode(int data)
+{
+ int b1, b2, length;
+
+ if (data == -1)
+ return -1;
+
+ b1 = data & 0x7F;
+ b2 = (data>>8) & 0x7F;
+
+ if (b1 < 15) // start packet
+ {
+ mode = b1;
+ type = b2;
+ infochecksum = b1 + b2 + 15;
+ if (mode > 8 || type > 20)
+ {
+// printf("%% Unsupported mode %s(%d) [%d]\n",modes[(mode-1)>>1],mode,type);
+ mode=0; type=0;
+ }
+ infoptr = newinfo[mode][type];
+ }
+ else if (b1 == 15) // eof (next byte is checksum)
+ {
+#if 0 //debug
+ if (mode == 0)
+ {
+ length=infoptr - newinfo[0][0];
+ infoptr[1]=0;
+ printf("LEN: %d\n",length);
+ for (y=0;y<length;y++)
+ printf(" %03d",newinfo[0][0][y]);
+ printf(" --- %s\n",newinfo[0][0]);
+ }
+#endif
+ if (mode == 0) return 0;
+ if (b2 != 128-((infochecksum%128)&0x7F)) return 0;
+
+ length = infoptr - newinfo[mode][type];
+
+ //don't bug the user with repeated data
+ //only parse it if it's different
+ if (strncmp(info[mode][type],newinfo[mode][type],length-1))
+ {
+ infoptr = info[mode][type];
+ memcpy(info[mode][type],newinfo[mode][type],length+1);
+ printf("\33[33m%%");
+ switch ((mode<<8) + type)
+ {
+ case 0x0101:
+ printf(" TIMECODE: %d/%02d %d:%02d",
+ infoptr[3]&0x0f,infoptr[2]&0x1f,infoptr[1]&0x1f,infoptr[0]&0x3f);
+ case 0x0102:
+ if ((infoptr[1]&0x3f)>5)
+ break;
+ printf(" LENGTH: %d:%02d:%02d of %d:%02d:00",
+ infoptr[3]&0x3f,infoptr[2]&0x3f,infoptr[4]&0x3f,infoptr[1]&0x3f,infoptr[0]&0x3f);
+ break;
+ case 0x0103:
+ infoptr[length] = 0;
+ printf(" TITLE: %s",infoptr);
+ break;
+ case 0x0105:
+ printf(" RATING: %s (%d)",ratings[infoptr[0]&0x07],infoptr[0]);
+ if ((infoptr[0]&0x07)>0)
+ {
+ if (infoptr[0]&0x20) printf(" VIOLENCE");
+ if (infoptr[0]&0x10) printf(" SEXUAL");
+ if (infoptr[0]&0x08) printf(" LANGUAGE");
+ }
+ break;
+ case 0x0501:
+ infoptr[length] = 0;
+ printf(" NETWORK: %s",infoptr);
+ break;
+ case 0x0502:
+ infoptr[length] = 0;
+ printf(" CALL: %s",infoptr);
+ break;
+ case 0x0701:
+ printf(" CUR.TIME: %d:%02d %d/%02d/%04d UTC",infoptr[1]&0x1F,infoptr[0]&0x3f,infoptr[3]&0x0f,infoptr[2]&0x1f,(infoptr[5]&0x3f)+1990);
+ break;
+ case 0x0704: //timezone
+ printf(" TIMEZONE: UTC-%d",infoptr[0]&0x1f);
+ break;
+ case 0x0104: //program genere
+ break;
+ case 0x0110:
+ case 0x0111:
+ case 0x0112:
+ case 0x0113:
+ case 0x0114:
+ case 0x0115:
+ case 0x0116:
+ case 0x0117:
+ infoptr[length+1] = 0;
+ printf(" DESC: %s",infoptr);
+ break;
+ }
+ printf("\33[0m\n");
+ fflush(stdout);
+ }
+ mode = 0; type = 0;
+ }
+ else if( (infoptr - newinfo[mode][type]) < 250 ) // must be a data packet, check if we're in a supported mode
+ {
+ infoptr[0] = b1; infoptr++;
+ infoptr[0] = b2; infoptr++;
+ infochecksum += b1 + b2;
+ }
+ return 0;
+}
+
+static int webtv_check(char * buf,int len)
+{
+ unsigned long sum;
+ unsigned long nwords;
+ unsigned short csum=0;
+ char temp[9];
+ int nbytes=0;
+
+ while (buf[0]!='<' && len > 6) //search for the start
+ {
+ buf++; len--;
+ }
+
+ if (len == 6) //failure to find start
+ return 0;
+
+
+ while (nbytes+6 <= len)
+ {
+ //look for end of object checksum, it's enclosed in []'s and there shouldn't be any [' after
+ if (buf[nbytes] == '[' && buf[nbytes+5] == ']' && buf[nbytes+6] != '[')
+ break;
+ else
+ nbytes++;
+ }
+ if (nbytes+6>len) //failure to find end
+ return 0;
+
+ nwords = nbytes >> 1; sum = 0;
+
+ //add up all two byte words
+ while (nwords-- > 0) {
+ sum += *buf++ << 8;
+ sum += *buf++;
+ }
+ if (nbytes & 1) {
+ sum += *buf << 8;
+ }
+ csum = (unsigned short)(sum >> 16);
+ while(csum !=0) {
+ sum = csum + (sum & 0xffff);
+ csum = (unsigned short)(sum >> 16);
+ }
+ sprintf(temp,"%04X\n",(int)~sum&0xffff);
+ buf++;
+ if(!strncmp(buf,temp,4))
+ {
+ buf[5]=0;
+ printf("\33[35mWEBTV: %s\33[0m\n",buf-nbytes-1);
+ fflush(stdout);
+ }
+ return 0;
+}
+
+static int CCdecode(int data)
+{
+ int b1, b2, row, len, x,y;
+ if (data == -1) //invalid data. flush buffers to be safe.
+ {
+ memset(ccbuf[1],0,255);
+ memset(ccbuf[2],0,255);
+ return -1;
+ }
+ b1 = data & 0x7f;
+ b2 = (data>>8) & 0x7f;
+ len = strlen(ccbuf[ccmode]);
+
+ if (b1&0x60 && data != lastcode) // text
+ {
+ ccbuf[ccmode][len++]=b1;
+ if (b2&0x60) ccbuf[ccmode][len++]=b2;
+ if (b1 == ']' || b2 == ']')
+ webtv_check(ccbuf[ccmode],len);
+ }
+ else if ((b1&0x10) && (b2>0x1F) && (data != lastcode)) //codes are always transmitted twice (apparently not, ignore the second occurance)
+ {
+ ccmode=((b1>>3)&1)+1;
+ len = strlen(ccbuf[ccmode]);
+
+ if (b2 & 0x40) //preamble address code (row & indent)
+ {
+ row=rowdata[((b1<<1)&14)|((b2>>5)&1)];
+ if (len!=0)
+ ccbuf[ccmode][len++]='\n';
+
+ if (b2&0x10) //row contains indent flag
+ for (x=0;x<(b2&0x0F)<<1;x++)
+ ccbuf[ccmode][len++]=' ';
+ }
+ else
+ {
+ switch (b1 & 0x07)
+ {
+ case 0x00: //attribute
+ printf("<ATTRIBUTE %d %d>\n",b1,b2);
+ fflush(stdout);
+ break;
+ case 0x01: //midrow or char
+ switch (b2&0x70)
+ {
+ case 0x20: //midrow attribute change
+ switch (b2&0x0e)
+ {
+ case 0x00: //italics off
+ if (!plain)
+ strcat(ccbuf[ccmode],"\33[0m ");
+ break;
+ case 0x0e: //italics on
+ if (!plain)
+ strcat(ccbuf[ccmode],"\33[36m ");
+ break;
+ }
+ if (b2&0x01) { //underline
+ if (!plain)
+ strcat(ccbuf[ccmode],"\33[4m");
+ } else {
+ if (!plain)
+ strcat(ccbuf[ccmode],"\33[24m");
+ }
+ break;
+ case 0x30: //special character..
+ strcat(ccbuf[ccmode],specialchar[b2&0x0f]);
+ break;
+ }
+ break;
+ case 0x04: //misc
+ case 0x05: //misc + F
+// printf("ccmode %d cmd %02x\n",ccmode,b2);
+ switch (b2)
+ {
+ case 0x21: //backspace
+ ccbuf[ccmode][len--]=0;
+ break;
+
+ /* these codes are insignifigant if we're ignoring positioning */
+ case 0x25: //2 row caption
+ case 0x26: //3 row caption
+ case 0x27: //4 row caption
+ case 0x29: //resume direct caption
+ case 0x2B: //resume text display
+ case 0x2C: //erase displayed memory
+ break;
+
+ case 0x2D: //carriage return
+ if (ccmode==2)
+ break;
+ case 0x2F: //end caption + swap memory
+ case 0x20: //resume caption (new caption)
+ if (!strlen(ccbuf[ccmode]))
+ break;
+ for (x=0;x<strlen(ccbuf[ccmode]);x++)
+ for (y=0;y<keywords;y++)
+ if (!strncasecmp(keyword[y], ccbuf[ccmode]+x, strlen(keyword[y])))
+ printf("\a");
+ printf("%s\33[m\n",ccbuf[ccmode]);
+ fflush(stdout);
+ /* FALL */
+ case 0x2A: //text restart
+ case 0x2E: //erase non-displayed memory
+ memset(ccbuf[ccmode],0,255);
+ break;
+ }
+ break;
+ case 0x07: //misc (TAB)
+ for(x=0;x<(b2&0x03);x++)
+ ccbuf[ccmode][len++]=' ';
+ break;
+ }
+ }
+ }
+ lastcode=data;
+ return 0;
+}
+
+static int RAW(int data)
+{
+ int b1, b2;
+ if (data == -1)
+ return -1;
+
+ // this is just null data with two parity bits
+ // 100000010000000 = 0x8080
+ if (data == 0x8080)
+ return -1;
+
+ b1 = data & 0x7f;
+ b2 = (data>>8) & 0x7f;
+
+ if (!semirawdata) {
+ fprintf(stderr,"%c%c",b1,b2);
+ return 0;
+ }
+
+ // semi-raw data output begins here...
+
+ // a control code.
+ if ( ( b1 >= 0x10 ) && ( b1 <= 0x1F ) ) {
+ if ( ( b2 >= 0x20 ) && ( b2 <= 0x7F ) )
+ fprintf(stderr,"[%02X-%02X]",b1,b2);
+ return 0;
+ }
+
+ // next two rules:
+ // supposed to be one printable char
+ // and the other char to be discarded
+ if ( ( b1 >= 0x0 ) && ( b1 <= 0xF ) ) {
+ fprintf(stderr,"(%02x)%c",b1,b2);
+ //fprintf(stderr,"%c",b2);
+ //fprintf(stderr,"%c%c",0,b2);
+ return 0;
+ }
+ if ( ( b2 >= 0x0 ) && ( b2 <= 0xF ) ) {
+ fprintf(stderr,"%c{%02x}",b1,b2);
+ //fprintf(stderr,"%c",b1);
+ //fprintf(stderr,"%c%c",b1,1);
+ return 0;
+ }
+
+ // just classic two chars to print.
+ fprintf(stderr,"%c%c",b1,b2);
+
+ return 0;
+}
+
+static int sentence(int data)
+{
+ int b1, b2;
+ if (data == -1)
+ return -1;
+ b1 = data & 0x7f;
+ b2 = (data>>8) & 0x7f;
+ inval++;
+ if (data==lastcode)
+ {
+ if (sen==1)
+ {
+ printf(" ");
+ sen=0;
+ }
+ if (inval>10 && sen)
+ {
+ printf("\n");
+ fflush(stdout);
+ sen=0;
+ }
+ return 0;
+ }
+ lastcode=data;
+
+ if (b1&96)
+ {
+ inval=0;
+ if (sen==2 && b1!='.' && b2!='.' && b1!='!' && b2!='!' && b1!='?' && b2!='?' && b1!=')' && b2!=')')
+ {
+ printf("\n");
+ fflush(stdout);
+ sen=1;
+ }
+ else if (b1=='.' || b2=='.' || b1=='!' || b2=='!' || b1=='?' || b2=='?' || b1==')' || b2==')')
+ sen=2;
+ else
+ sen=1;
+ printf("%c%c",tolower(b1),tolower(b2));
+
+ }
+ return 0;
+}
+
+#ifndef X_DISPLAY_MISSING
+static unsigned long getColor(char *colorName, float dim)
+{
+ XColor Color;
+ XWindowAttributes Attributes;
+
+ XGetWindowAttributes(dpy, Root, &Attributes);
+ Color.pixel = 0;
+
+ XParseColor (dpy, Attributes.colormap, colorName, &Color);
+ Color.red=(unsigned short)(Color.red/dim);
+ Color.blue=(unsigned short)(Color.blue/dim);
+ Color.green=(unsigned short)(Color.green/dim);
+ Color.flags=DoRed | DoGreen | DoBlue;
+ XAllocColor (dpy, Attributes.colormap, &Color);
+
+ return Color.pixel;
+}
+#endif
+
+int main(int argc,char **argv)
+{
+ char *vbifile = "/dev/vbi";
+ unsigned char buf[65536];
+ int arg;
+ int args=0;
+ int vbifd;
+ fd_set rfds;
+
+ int x;
+
+ for (;;) //commandline parsing
+ {
+ if (-1 == (arg = getopt(argc, argv, "?hxsckpwRr:d:")))
+ break;
+ switch (arg)
+ {
+ case '?':
+ case 'h':
+ printf("CCDecoder 0.9.1 (mbm@linux.com)\n"
+ "\tx \t decode XDS info\n"
+ "\ts \t decode by sentences \n"
+ "\tc \t decode Closed Caption (includes webtv)\n"
+ "\tk word \t keywords to break line at [broken???]\n"
+ "\tp \t plain output. do not display underline and italic\n"
+ "\tw \t open debugging window (used with -r option)\n"
+ "\tR \t semi-raw data (used with -r option)\n"
+ "\tr #\t raw dump of data (use 11 or 27 as line number)\n"
+ "\td dev \t file to open (default: /dev/vbi)\n"
+ );
+ exit(0);
+ case 'x':
+ usexds=1; args++;
+ break;
+ case 's':
+ usesen=1; args++;
+ break;
+ case 'c':
+ usecc=1; args++;
+ break;
+ case 'k':
+ //args++;
+ keyword[keywords++]=optarg;
+ break;
+ case 'p':
+ plain=1; args++;
+ break;
+ case 'w':
+ debugwin=1;
+ break;
+ case 'R':
+ semirawdata=1;
+ break;
+ case 'r':
+ useraw=1; args++;
+ rawline=atoi(optarg);
+ break;
+ case 'd':
+ vbifile = optarg;
+ break;
+ }
+ }
+
+ if ((vbifd = open(vbifile, O_RDONLY)) < 0) {
+ perror(vbifile);
+ exit(1);
+ }
+
+ else if (!args)
+ exit(0);
+ for (x=0;x<keywords;x++)
+ printf("Keyword(%d): %s\n",x,keyword[x]);
+
+
+#ifndef X_DISPLAY_MISSING
+ if (debugwin) {
+ dpy=XOpenDisplay(dpyname);
+ Root=DefaultRootWindow(dpy);
+ Win = XCreateSimpleWindow(dpy, Root, 10, 10, 1024, 128,0,0,0);
+ WinGC = XCreateGC(dpy, Win, 0, NULL);
+ WinGC0 = XCreateGC(dpy, Win, 0, NULL);
+ WinGC1 = XCreateGC(dpy, Win, 0, NULL);
+ XSetForeground(dpy, WinGC, getColor("blue",1));
+ XSetForeground(dpy, WinGC0, getColor("green",1));
+ XSetForeground(dpy, WinGC1, getColor("red",1));
+
+ if (useraw)
+ XMapWindow(dpy, Win);
+ }
+#endif
+
+
+ //mainloop
+ while(1){
+ FD_ZERO(&rfds);
+ FD_SET(vbifd, &rfds);
+ select(FD_SETSIZE, &rfds, NULL, NULL, NULL);
+ if (FD_ISSET(vbifd, &rfds)) {
+ if (read(vbifd, buf , 65536)!=65536)
+ printf("read error\n");
+ if (useraw)
+ {
+#ifndef X_DISPLAY_MISSING
+ if (debugwin) {
+ XClearArea(dpy,Win,0,0,1024,128,0);
+ XDrawLine(dpy,Win,WinGC1,0,128-85/2,1024,128-85/2);
+ for (x=0;x<1024;x++)
+ if (buf[2048 * rawline+x*2]/2<128 && buf[2048 * rawline+x*2+2]/2 < 128)
+ XDrawLine(dpy,Win,WinGC0,x,128-buf[2048 * rawline+x*2]/2,
+ x+1,128-buf[2048 * rawline+x*2+2]/2);
+ }
+#endif
+ RAW(decode(&buf[2048 * rawline]));
+#ifndef X_DISPLAY_MISSING
+ if (debugwin) {
+ XFlush(dpy);
+ usleep(100);
+ }
+#endif
+ }
+ if (usexds)
+ XDSdecode(decode(&buf[2048 * 27]));
+ if (usecc)
+ CCdecode(decode(&buf[2048 * 11]));
+ if (usesen)
+ sentence(decode(&buf[2048 * 11]));
+#ifndef X_DISPLAY_MISSING
+ if (debugwin) {
+ XFlush(dpy);
+ usleep(100);
+ }
+#endif
+ }
+ }
+ return 0;
+}
diff --git a/vbistuff/page.c b/vbistuff/page.c
new file mode 100644
index 0000000..a608b96
--- /dev/null
+++ b/vbistuff/page.c
@@ -0,0 +1,392 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "httpd.h"
+
+/* libvbi */
+#include "vt.h"
+#include "misc.h"
+#include "fdset.h"
+#include "vbi.h"
+#include "lang.h"
+#include "dllist.h"
+#include "export.h"
+
+void fmt_page(struct export *e, struct fmt_page *pg, struct vt_page *vtp);
+
+#define BUFSIZE 4096
+
+/* ---------------------------------------------------------------------- */
+
+static char stylesheet[] =
+#include "alevt.css.h"
+;
+
+static char page_about[] =
+#include "about.html.h"
+;
+
+static char page_top[] =
+#include "top.html.h"
+;
+
+static char page_bottom[] =
+#include "bottom.html.h"
+;
+
+/* 01234567890123456789012345678901 */
+static char graphic1[32] = " '-.:::.;;;.}/}./#";
+/* 012345 6789 0123 456789012345678901 */
+static char graphic2[32] = ".:::.\\{{.\\||.\\#_:::.[.||].###";
+
+/* ---------------------------------------------------------------------- */
+
+/* fix bcd issues ... */
+static int calc_page(int pagenr, int offset)
+{
+ int result;
+
+ result = pagenr + offset;
+ if (offset < 0) {
+ while ((result & 0x0f) > 0x09)
+ result -= 0x01;
+ while ((result & 0xf0) > 0x90)
+ result -= 0x10;
+ if (result < 0x100)
+ result = 0x100;
+ }
+ if (offset > 0) {
+ while ((result & 0x0f) > 0x09)
+ result += 0x01;
+ while ((result & 0xf0) > 0x90)
+ result += 0x10;
+ if (result > 0x899)
+ result = 0x899;
+ }
+ return result;
+}
+
+static void vbipage(struct REQUEST *req, struct vt_page *page,
+ int pagenr, int subnr, int html)
+{
+ char *out;
+ int size,len,x,y;
+ int color,lcolor,link;
+ struct fmt_page pg[1];
+
+ struct fmt_char l[W+2];
+#define L (l+1)
+
+ fmt_page(fmt,pg,page);
+ size = 2*BUFSIZE;
+ out = malloc(size);
+ len = 0;
+
+ if (html)
+ len += sprintf(out+len,page_top,page->pgno,page->subno);
+ for (y = 0; y < H; y++) {
+ if (~pg->hid & (1 << y)) { /* !hidden */
+ for (x = 0; x < W; ++x) {
+ struct fmt_char c = pg->data[y][x];
+ switch (c.ch) {
+ case 0x00:
+ case 0xa0:
+ c.ch = ' ';
+ break;
+ case 0x7f:
+ c.ch = '*';
+ break;
+ case BAD_CHAR:
+ c.ch = '?';
+ break;
+ default:
+ if (c.attr & EA_GRAPHIC) {
+ if (ascii_art) {
+ if (!(c.ch & 128))
+ c.ch = graphic1[c.ch];
+ else
+ c.ch = graphic2[c.ch-128];
+ } else {
+ c.ch = '#';
+ }
+ }
+ break;
+ }
+ L[x] = c;
+ }
+
+ /* delay fg and attr changes as far as possible */
+ for (x = 0; x < W; ++x)
+ if (L[x].ch == ' ') {
+ L[x].fg = L[x-1].fg;
+ L[x].attr = L[x-1].attr;
+ }
+
+ /* move fg and attr changes to prev bg change point */
+ for (x = W-1; x >= 0; x--)
+ if (L[x].ch == ' ' && L[x].bg == L[x+1].bg) {
+ L[x].fg = L[x+1].fg;
+ L[x].attr = L[x+1].attr;
+ }
+
+ /* now emit the whole line */
+ lcolor = -1; link = -1;
+ for (x = 0; x < W; ++x) {
+ /* close link tags */
+ if (html && link >= 0) {
+ if (0 == link)
+ len += sprintf(out+len,"</a>");
+ link--;
+ }
+
+ /* color handling */
+ color = (L[x].fg&0x0f) * 10 + (L[x].bg&0x0f);
+ if (html && color != lcolor) {
+ if (-1 != lcolor)
+ len += sprintf(out+len,"</span>");
+ len += sprintf(out+len,"<span class=\"c%02d\">",color);
+ lcolor = color;
+ }
+
+ /* check for references to other pages */
+ if (html && y > 0 && -1 == link && x < W-2 &&
+ isdigit(L[x].ch) &&
+ isdigit(L[x+1].ch) &&
+ isdigit(L[x+2].ch) &&
+ !isdigit(L[x+3].ch) &&
+ !isdigit(L[x-1].ch)) {
+ len += sprintf(out+len,"<a href=\"/%c%c%c/\">",
+ L[x].ch, L[x+1].ch, L[x+2].ch);
+ link = 2;
+ }
+ if (html && y > 0 && -1 == link && x < W-1 &&
+ '>' == L[x].ch &&
+ '>' == L[x+1].ch) {
+ len += sprintf(out+len,"<a href=\"/%03x/\">",
+ page->pgno+1);
+ link = 1;
+ }
+ if (html && y > 0 && -1 == link && x < W-1 &&
+ '<' == L[x].ch &&
+ '<' == L[x+1].ch) {
+ len += sprintf(out+len,"<a href=\"/%03x/\">",
+ page->pgno-1);
+ link = 1;
+ }
+
+ /* check for references to WWW pages */
+ if (html && y > 0 && -1 == link && x < W-9 &&
+ (((tolower(L[x+0].ch) == 'w') &&
+ (tolower(L[x+1].ch) == 'w') &&
+ (tolower(L[x+2].ch) == 'w') &&
+ (L[x+3].ch == '.')) ||
+ ((tolower(L[x+0].ch) == 'h') &&
+ (tolower(L[x+1].ch) == 't') &&
+ (tolower(L[x+2].ch) == 't') &&
+ (tolower(L[x+3].ch) == 'p')))) {
+ int offs = 0;
+
+ len += sprintf(out+len,"<a href=\"");
+ if(tolower(L[x].ch == 'w'))
+ len += sprintf(out+len,"http://");
+ while ((L[x+offs].ch!=' ') && (x+offs < W)) {
+ len += sprintf(out+len,"%c",tolower(L[x+offs].ch));
+ offs++;
+ }
+ while ( (*(out+len-1)<'a') || (*(out+len-1)>'z') ) {
+ len--;
+ offs--;
+ }
+ len += sprintf(out+len,"\">");
+ link = offs - 1;
+ }
+
+ /* check for references to other subpages */
+ if (html && y > 0 && -1 == link && x < W-2 &&
+ page->subno > 0 &&
+ isdigit(L[x].ch) &&
+ '/' == L[x+1].ch &&
+ isdigit(L[x+2].ch) &&
+ !isdigit(L[x+3].ch) &&
+ !isdigit(L[x-1].ch)) {
+ if (L[x].ch == L[x+2].ch) {
+ len += sprintf(out+len,"<a href=\"01.html\">");
+ } else {
+ len += sprintf(out+len,"<a href=\"%02x.html\">",
+ L[x].ch+1-'0');
+ }
+ link = 2;
+ }
+ /* check for FastText links */
+ if (html && page->flof && -1 == link && x<W-2 &&
+ 24 == y &&
+ L[x].fg>0 &&
+ L[x].fg<8 &&
+ x>0 &&
+ !isspace(L[x].ch)) {
+ link=(L[x].fg==6?3:L[x].fg-1);
+ if(page->link[link].subno == ANY_SUB)
+ {
+ len+=sprintf(out+len,"<a href=\"/%03x/\">",
+ page->link[link].pgno);
+ }
+ else
+ {
+ len+=sprintf(out+len,"<a href=\"/%03x/%02x.html\">",
+ page->link[link].pgno,
+ page->link[link].subno);
+ }
+ link=0;
+ while((L[x+link].fg == L[x].fg) && (x+link<W))
+ {
+ link++;
+ }
+ link--;
+ if(link<1)
+ {
+ link=1;
+ }
+ }
+ out[len++] = L[x].ch;
+ }
+ /* close any tags + put newline */
+ if (html && link >= 0)
+ len += sprintf(out+len,"</a>");
+ if (html && -1 != lcolor)
+ len += sprintf(out+len,"</span>");
+ out[len++] = '\n';
+
+ /* check bufsize */
+ if (len + BUFSIZE > size) {
+ size += BUFSIZE;
+ out = realloc(out,size);
+ }
+ }
+ }
+ if (html) {
+#define MAXSUBPAGES 32
+ int subpage;
+
+ /* close preformatted text header */
+ len+=sprintf(out+len,"</pre>\n<div class=quick>\n") ;
+ if (vbi->cache->op->get(vbi->cache,pagenr,1)) {
+ /* link all subpages */
+ len += sprintf(out+len,"Subpages:");
+ for (subpage = 1; subpage <= MAXSUBPAGES; subpage++) {
+ if (NULL == vbi->cache->op->get(vbi->cache,pagenr,subpage))
+ continue;
+ if (subpage != subnr) {
+ len += sprintf(out+len," <a href=\"/%03x/%02x.html\">%02x</a>",
+ pagenr, subpage, subpage);
+ } else {
+ len += sprintf(out+len," %02x", subpage);
+ }
+ }
+ len += sprintf(out+len,"<br>\n") ;
+ }
+ len += sprintf(out+len,"<a href=\"/%03x/\"><<</a> &nbsp;",
+ calc_page(pagenr, -0x10));
+ len += sprintf(out+len,"<a href=\"/%03x/\"><</a> &nbsp;",
+ calc_page(pagenr, -0x01));
+ if (!subnr)
+ len += sprintf(out+len,"<a href=\"/%03x/\">o</a> &nbsp;", pagenr);
+ else
+ len += sprintf(out+len,"<a href=\"/%03x/%02x.html\">o</a> &nbsp;", pagenr, subnr);
+ len += sprintf(out+len,"<a href=\"/%03x/\">></a> &nbsp;",
+ calc_page(pagenr, +0x01));
+ len += sprintf(out+len,"<a href=\"/%03x/\">>></a>",
+ calc_page(pagenr, +0x10));
+ len += sprintf(out+len,"<br>\n") ;
+ len += sprintf(out+len,"%s",page_bottom);
+ req->mime = "text/html; charset=\"iso-8859-1\"";
+ } else {
+ req->mime = "text/plain; charset=\"iso-8859-1\"";
+ }
+
+ req->body = out;
+ req->lbody = len;
+ req->free_the_mallocs = 1;
+ mkheader(req,200,-1);
+}
+
+/* ---------------------------------------------------------------------- */
+
+void buildpage(struct REQUEST *req)
+{
+ int pagenr, subpage;
+ char type;
+ struct vt_page *page;
+
+ /* style sheet */
+ if (0 == strcmp(req->path,"/alevt.css")) {
+ req->mime = "text/css";
+ req->body = stylesheet;
+ req->lbody = sizeof(stylesheet);
+ mkheader(req,200,start);
+ return;
+ }
+
+ /* about */
+ if (0 == strcmp(req->path,"/about.html")) {
+ req->mime = "text/html; charset=\"iso-8859-1\"";
+ req->body = page_about;
+ req->lbody = sizeof(page_about);
+ mkheader(req,200,start);
+ return;
+ }
+
+ /* entry page */
+ if (0 == strcmp(req->path,"/")) {
+ strcpy(req->path,"/100/");
+ mkredirect(req);
+ return;
+ }
+
+ /* page with subpages */
+ if (3 == sscanf(req->path,"/%3x/%2x.%c",&pagenr,&subpage,&type)) {
+ if (debug)
+ fprintf(stderr,"trying %03x/%02x\n",pagenr,subpage);
+ page = vbi->cache->op->get(vbi->cache,pagenr,subpage);
+ if (NULL != page) {
+ vbipage(req,page,pagenr,subpage,type=='h');
+ return;
+ }
+ mkerror(req,404,1);
+ return;
+ }
+
+ /* ... without subpage */
+ if (1 == sscanf(req->path,"/%3x/",&pagenr)) {
+ if (debug)
+ fprintf(stderr,"trying %03x\n",pagenr);
+ page = vbi->cache->op->get(vbi->cache,pagenr,0);
+ if (NULL != page) {
+ vbipage(req,page,pagenr,0,1);
+ return;
+ }
+ page = vbi->cache->op->get(vbi->cache,pagenr,ANY_SUB);
+ if (NULL != page) {
+ sprintf(req->path,"/%03x/%02x.html",pagenr,page->subno);
+ mkredirect(req);
+ return;
+ }
+ mkerror(req,404,1);
+ return;
+ }
+
+ /* goto form */
+ if (1 == sscanf(req->path,"/goto/?p=%d",&pagenr)) {
+ sprintf(req->path,"/%d/",pagenr);
+ mkredirect(req);
+ return;
+ }
+
+ mkerror(req,404,1);
+ return;
+}
diff --git a/vbistuff/request.c b/vbistuff/request.c
new file mode 100644
index 0000000..307095c
--- /dev/null
+++ b/vbistuff/request.c
@@ -0,0 +1,195 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <syslog.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "httpd.h"
+
+/* ---------------------------------------------------------------------- */
+
+void
+read_request(struct REQUEST *req, int pipelined)
+{
+ int rc;
+ char *h;
+
+ restart:
+ rc = read(req->fd, req->hreq + req->hdata, MAX_HEADER - req->hdata);
+ switch (rc) {
+ case -1:
+ if (errno == EAGAIN) {
+ if (pipelined)
+ break; /* check if there is already a full request */
+ else
+ return;
+ }
+ if (errno == EINTR)
+ goto restart;
+ xperror(LOG_INFO,"read",req->peerhost);
+ /* fall through */
+ case 0:
+ req->state = STATE_CLOSE;
+ return;
+ default:
+ req->hdata += rc;
+ req->hreq[req->hdata] = 0;
+ }
+
+ /* check if this looks like a http request after
+ the first few bytes... */
+ if (req->hdata < 5)
+ return;
+ if (strncmp(req->hreq,"GET ",4) != 0 &&
+ strncmp(req->hreq,"PUT ",4) != 0 &&
+ strncmp(req->hreq,"HEAD ",5) != 0 &&
+ strncmp(req->hreq,"POST ",5) != 0) {
+ mkerror(req,400,0);
+ return;
+ }
+
+ /* header complete ?? */
+ if (NULL != (h = strstr(req->hreq,"\r\n\r\n")) ||
+ NULL != (h = strstr(req->hreq,"\n\n"))) {
+ if (*h == '\r') {
+ h += 4;
+ *(h-2) = 0;
+ } else {
+ h += 2;
+ *(h-1) = 0;
+ }
+ req->lreq = h - req->hreq;
+ req->state = STATE_PARSE_HEADER;
+ return;
+ }
+
+ if (req->hdata == MAX_HEADER) {
+ /* oops: buffer full, but found no complete request ... */
+ mkerror(req,400,0);
+ return;
+ }
+ return;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int
+unhex(unsigned char c)
+{
+ if (c < '@')
+ return c - '0';
+ return (c & 0x0f) + 9;
+}
+
+static void
+unquote(unsigned char *dst, unsigned char *src)
+{
+ int i,j,q,n=strlen(src);
+
+ q=0;
+ for (i=0, j=0; i<n; i++, j++) {
+ if (src[i] == '?')
+ q = 1;
+ if (q && src[i] == '+') {
+ dst[j] = ' ';
+ } else if (src[i] == '%') {
+ dst[j] = (unhex(src[i+1]) << 4) | unhex(src[i+2]);
+ i += 2;
+ } else {
+ dst[j] = src[i];
+ }
+ }
+ dst[j] = 0;
+}
+
+void
+parse_request(struct REQUEST *req)
+{
+ char filename[2048], proto[5], *h;
+ int port;
+
+ if (debug > 2)
+ fprintf(stderr,"%s\n",req->hreq);
+
+ /* parse request. Hehe, scanf is powerfull :-) */
+ if (4 != sscanf(req->hreq, "%4[A-Z] %255[^ \t\r\n] HTTP/%d.%d",
+ req->type,filename,&(req->major),&(req->minor))) {
+ mkerror(req,400,0);
+ return;
+ }
+ if (filename[0] == '/') {
+ strcpy(req->uri,filename);
+ } else {
+ port = 0;
+ *proto = 0;
+ if (4 != sscanf(filename,"%4[a-zA-Z]://%64[a-zA-Z0-9.-]:%d%255[^ \t\r\n]",
+ proto,req->hostname,&port,req->uri) &&
+ 3 != sscanf(filename,"%4[a-zA-Z]://%64[a-zA-Z0-9.-]%255[^ \t\r\n]",
+ proto,req->hostname,req->uri)) {
+ mkerror(req,400,0);
+ return;
+ }
+ if (*proto != 0 && 0 != strcasecmp(proto,"http")) {
+ mkerror(req,400,0);
+ return;
+ }
+ }
+
+ unquote(req->path,req->uri);
+ if (debug)
+ fprintf(stderr,"%03d: %s \"%s\" HTTP/%d.%d\n",
+ req->fd, req->type, req->path, req->major, req->minor);
+
+ if (0 != strcmp(req->type,"GET") &&
+ 0 != strcmp(req->type,"HEAD")) {
+ mkerror(req,501,0);
+ return;
+ }
+
+ if (0 == strcmp(req->type,"HEAD")) {
+ req->head_only = 1;
+ }
+
+ /* checks */
+ if (req->path[0] != '/') {
+ mkerror(req,400,0);
+ return;
+ }
+
+ /* parse header lines */
+ req->keep_alive = req->minor;
+ for (h = req->hreq; h - req->hreq < req->lreq;) {
+ h = strchr(h,'\n');
+ if (NULL == h)
+ break;
+ h++;
+
+ if (0 == strncasecmp(h,"Connection: ",12)) {
+ req->keep_alive = (0 == strncasecmp(h+12,"Keep-Alive",10));
+
+ } else if (0 == strncasecmp(h,"Host: ",6)) {
+ sscanf(h+6,"%64[a-zA-Z0-9.-]",req->hostname);
+ }
+ }
+
+ /* make sure we have a hostname */
+ if (req->hostname[0] == '\0' || canonicalhost)
+ strncpy(req->hostname,server_host,64);
+
+ /* lowercase hostname */
+ for (h = req->hostname; *h != 0; h++) {
+ if (*h < 'A' || *h > 'Z')
+ continue;
+ *h += 32;
+ }
+
+ /* handle request (more to come) */
+ buildpage(req);
+ return;
+}
diff --git a/vbistuff/response.c b/vbistuff/response.c
new file mode 100644
index 0000000..b544f47
--- /dev/null
+++ b/vbistuff/response.c
@@ -0,0 +1,190 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <syslog.h>
+#include <time.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "httpd.h"
+
+/* ---------------------------------------------------------------------- */
+
+static struct HTTP_STATUS {
+ int status;
+ char *head;
+ char *body;
+} http[] = {
+ { 200, "200 OK", NULL },
+ { 400, "400 Bad Request", "*PLONK*\n" },
+ { 404, "404 Not Found", "videotext page not found in cache\n" },
+ { 408, "408 Request Timeout", "Request Timeout\n" },
+ { 500, "500 Internal Server Error", "Sorry folks\n" },
+ { 501, "501 Not Implemented", "Sorry folks\n" },
+ { 0, NULL, NULL }
+};
+
+/* ---------------------------------------------------------------------- */
+
+#define RESPONSE_START \
+ "HTTP/1.1 %s\r\n" \
+ "Server: %s\r\n" \
+ "Connection: %s\r\n" \
+ "Accept-Ranges: bytes\r\n"
+#define RFCTIME \
+ "%a, %d %b %Y %H:%M:%S GMT"
+#define BOUNDARY \
+ "XXX_CUT_HERE_%ld_XXX"
+
+void
+mkerror(struct REQUEST *req, int status, int ka)
+{
+ int i;
+ for (i = 0; http[i].status != 0; i++)
+ if (http[i].status == status)
+ break;
+ req->status = status;
+ req->body = http[i].body;
+ req->lbody = strlen(req->body);
+ if (!ka)
+ req->keep_alive = 0;
+ req->lres = sprintf(req->hres,
+ RESPONSE_START
+ "Content-Type: text/plain\r\n"
+ "Content-Length: %d\r\n",
+ http[i].head,server_name,
+ req->keep_alive ? "Keep-Alive" : "Close",
+ req->lbody);
+ if (401 == status)
+ req->lres += sprintf(req->hres+req->lres,
+ "WWW-Authenticate: Basic, realm=\"webfs\"\r\n");
+ req->lres += strftime(req->hres+req->lres,80,
+ "Date: " RFCTIME "\r\n\r\n",
+ gmtime(&now));
+ req->state = STATE_WRITE_HEADER;
+ if (debug)
+ fprintf(stderr,"%03d: error: %d, connection=%s\n",
+ req->fd, status, req->keep_alive ? "Keep-Alive" : "Close");
+}
+
+void
+mkredirect(struct REQUEST *req)
+{
+ req->status = 302;
+ req->body = req->path;
+ req->lbody = strlen(req->body);
+ req->lres = sprintf(req->hres,
+ RESPONSE_START
+ "Location: http://%s:%d%s\r\n"
+ "Content-Type: text/plain\r\n"
+ "Content-Length: %d\r\n",
+ "302 Redirect",server_name,
+ req->keep_alive ? "Keep-Alive" : "Close",
+ req->hostname,tcp_port,req->path,
+ req->lbody);
+ req->lres += strftime(req->hres+req->lres,80,
+ "Date: " RFCTIME "\r\n\r\n",
+ gmtime(&now));
+ req->state = STATE_WRITE_HEADER;
+ if (debug)
+ fprintf(stderr,"%03d: 302 redirect: %s, connection=%s\n",
+ req->fd, req->path, req->keep_alive ? "Keep-Alive" : "Close");
+}
+
+void
+mkheader(struct REQUEST *req, int status, time_t mtime)
+{
+ int i;
+ for (i = 0; http[i].status != 0; i++)
+ if (http[i].status == status)
+ break;
+ req->status = status;
+ req->lres = sprintf(req->hres,
+ RESPONSE_START,
+ http[i].head,server_name,
+ req->keep_alive ? "Keep-Alive" : "Close");
+ req->lres += sprintf(req->hres+req->lres,
+ "Content-Type: %s\r\n"
+ "Content-Length: %d\r\n",
+ req->mime,
+ req->lbody);
+ if (mtime != -1)
+ req->lres += strftime(req->hres+req->lres,80,
+ "Last-Modified: " RFCTIME "\r\n",
+ gmtime(&mtime));
+ req->lres += strftime(req->hres+req->lres,80,
+ "Date: " RFCTIME "\r\n\r\n",
+ gmtime(&now));
+ req->state = STATE_WRITE_HEADER;
+ if (debug)
+ fprintf(stderr,"%03d: %d, connection=%s\n",
+ req->fd, status, req->keep_alive ? "Keep-Alive" : "Close");
+}
+
+/* ---------------------------------------------------------------------- */
+
+void write_request(struct REQUEST *req)
+{
+ int rc;
+
+ for (;;) {
+ switch (req->state) {
+ case STATE_WRITE_HEADER:
+ rc = write(req->fd,req->hres + req->written,
+ req->lres - req->written);
+ switch (rc) {
+ case -1:
+ if (errno == EAGAIN)
+ return;
+ if (errno == EINTR)
+ continue;
+ xperror(LOG_INFO,"write",req->peerhost);
+ /* fall through */
+ case 0:
+ req->state = STATE_CLOSE;
+ return;
+ default:
+ req->written += rc;
+ req->bc += rc;
+ if (req->written != req->lres)
+ return;
+ }
+ req->written = 0;
+ if (req->head_only) {
+ req->state = STATE_FINISHED;
+ return;
+ } else {
+ req->state = STATE_WRITE_BODY;
+ }
+ break;
+ case STATE_WRITE_BODY:
+ rc = write(req->fd,req->body + req->written,
+ req->lbody - req->written);
+ switch (rc) {
+ case -1:
+ if (errno == EAGAIN)
+ return;
+ if (errno == EINTR)
+ continue;
+ xperror(LOG_INFO,"write",req->peerhost);
+ /* fall through */
+ case 0:
+ req->state = STATE_CLOSE;
+ return;
+ default:
+ req->written += rc;
+ req->bc += rc;
+ if (req->written != req->lbody)
+ return;
+ }
+ req->state = STATE_FINISHED;
+ return;
+ } /* switch(state) */
+ } /* for (;;) */
+}
diff --git a/vbistuff/top.html.in b/vbistuff/top.html.in
new file mode 100644
index 0000000..a67ab20
--- /dev/null
+++ b/vbistuff/top.html.in
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<html><head>
+<title>page %03x/%02x</title>
+<link rel="stylesheet" type="text/css" href="/alevt.css">
+</head>
+<body>
+<pre class=vt>

Privacy Policy