--- /dev/null
+++ b/sys/lib/dist/sources2web
@@ -0,0 +1,56 @@
+#!/bin/rc
+# sources2web source-file - massage source file into html
+rfork e
+contrib=`{cat /sys/lib/dist/contrib.note}
+contrib=`{echo $contrib | sed 's;/;\\/;g'}
+ifs='
+'
+file=$1
+safefile=`{cleanname -d `{pwd} $file | sed 's;/;\\/;g'} # safe for sed
+if(! ~ $safefile *contrib*)
+ contrib=''
+
+fn html {
+ echo Content-Type: text/html
+ echo
+}
+fn head {
+ sed -n '1,/END HEADER/p' /usr/web/plan9/sources.html | translate
+}
+fn tail {
+ sed -n '/BEGIN TAIL/,$p' /usr/web/plan9/sources.html | translate
+}
+fn translate {
+ sed 's/PATH/'$safefile'/g; s/CONTRIB/'$"contrib'/g;' $*
+}
+
+if(test -f $1){
+ type=`{file -m $1}
+ if(! ~ $type text/*){
+ len=`{ls -l $1 | awk '{print $6}'}
+ echo Content-Type: $type
+ echo Content-Length: $len
+ echo
+ cat $1
+ exit 0
+ }
+ html
+ head
+ cat $1 | aux/htmlsanitize
+ tail
+ exit 0
+}
+if(test -d $1){
+ html
+ head
+ # exclude stuff that we don't want to publish with grep -v
+ ls -lp $1 | grep -v ' _| snap$| (9k|nix|pac)' |
+ sed 's/ . [0-9]+ / /' |
+ aux/htmlsanitize |
+ sed 's; ([^ /]+)$; \1;'
+ tail
+ exit 0
+}
+
+echo not found
+exit 0
--- /dev/null
+++ b/sys/src/cmd/ip/httpd/9down.c
@@ -0,0 +1,327 @@
+/*
+ * Serve files from the Plan 9 distribution.
+ * Check that we're not giving out files to bad countries
+ * and then just handle the request normally.
+ * Beware: behaviour changes based on argv[0].
+ * Modified to serve /sys/src, not /n/sources.
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "httpd.h"
+#include "httpsrv.h"
+
+#define LOG "9down"
+
+#ifndef OUTSIDE
+#define OUTSIDE 1 /* paranoid by default */
+#endif
+
+static Hio houtb;
+static Hio *hout;
+static HConnect*connect;
+
+enum
+{
+ MNAMELEN = 64,
+};
+
+void cat(char*, int);
+void filter(char*, char*, char*, char*, char*);
+HRange* myfixrange(HConnect*, Dir*, int);
+void error(char*, ...);
+void interr(char*, ...);
+void system(char*, ...);
+
+int sources;
+
+void
+main(int argc, char **argv)
+{
+ int trailingslash;
+ HSPriv *hp;
+ char *file, *dir;
+ char ok[256];
+
+ quotefmtinstall();
+ fmtinstall('H', httpfmt);
+ fmtinstall('U', hurlfmt);
+
+ connect = init(argc, argv);
+ hout = &connect->hout;
+ if(hparseheaders(connect, HSTIMEOUT) < 0)
+ exits("failed");
+
+ if(strcmp(connect->req.meth, "GET") != 0 && strcmp(connect->req.meth, "HEAD") != 0){
+ hunallowed(connect, "GET, HEAD");
+ exits("unallowed");
+ }
+ if(connect->head.expectother || connect->head.expectcont){
+ hfail(connect, HExpectFail, nil);
+ exits("failed");
+ }
+
+ hp = connect->private;
+ /* rename 9down to sources to display sources tree! */
+ if(strstr(argv[0], "sources"))
+ sources = 1;
+ syslog(0, LOG, "%s %s ver %d.%d uri %s search %s",
+ sources ? "sources" : "9down",
+ hp->remotesys,
+ connect->req.vermaj, connect->req.vermin,
+ connect->req.uri, connect->req.search);
+
+ file = strdup(connect->req.uri);
+
+ dir = "/sys/lib/dist/web.protect";
+ if(sources)
+ dir = "/sys/src";
+ if(chdir(dir) < 0)
+ interr("cd %s: %r", dir);
+
+ trailingslash = (file[0] && file[strlen(file)-1] == '/');
+ if(file[0] == 0)
+ file = strdup("/");
+ cleanname(file);
+ if(file[0] == '/'){
+ if(file[1])
+ file++;
+ else
+ file = ".";
+ }
+ /* now file is not rooted, no dot dots */
+
+ if(file[0] == '#')
+ interr("bad file %s", file);
+ if(access(file, AEXIST) < 0)
+ error("404 %s not found", file);
+ if (OUTSIDE) {
+ if(access("/mnt/ipok/ok", AEXIST) < 0){
+ system("mount /srv/ipok /mnt/ipok >[2]/dev/null");
+ if(access("/mnt/ipok/ok", AEXIST) < 0)
+ interr("no /mnt/ipok/ok");
+ }
+ snprint(ok, sizeof ok, "/mnt/ipok/ok/%s", hp->remotesys);
+ if(access(ok, AEXIST) < 0){
+ syslog(0, LOG, "reject %s %s",
+ hp->remotesys, connect->req.uri);
+ file = "/sys/lib/dist/web/err/prohibited.html";
+ }
+ }
+ cat(file, trailingslash);
+ hflush(hout);
+ exits(nil);
+}
+
+static void
+doerr(char *msg)
+{
+ hprint(hout, "%s %s\r\n", hversion, msg);
+ writelog(connect, "Reply: %s\n", msg);
+ hflush(hout);
+ exits(nil);
+}
+
+/* must pass an http error code then a message, e.g., 404 not found */
+void
+error(char *fmt, ...)
+{
+ char *buf;
+ va_list arg;
+
+ va_start(arg, fmt);
+ buf = vsmprint(fmt, arg);
+ va_end(arg);
+
+ syslog(0, LOG, "error: %s", buf);
+ doerr(buf);
+}
+
+/* must not pass an http error code, just a message */
+void
+interr(char *fmt, ...)
+{
+ char *buf;
+ va_list arg;
+
+ va_start(arg, fmt);
+ buf = vsmprint(fmt, arg);
+ va_end(arg);
+
+ syslog(0, LOG, "internal error: %s", buf);
+ doerr("500 Internal Error");
+}
+
+void
+system(char *fmt, ...)
+{
+ char buf[512];
+ va_list arg;
+
+ va_start(arg, fmt);
+ vsnprint(buf, sizeof buf, fmt, arg);
+ va_end(arg);
+
+ if(fork() == 0){
+ execl("/bin/rc", "rc", "-c", buf, nil);
+ _exits("execl failed");
+ }
+ waitpid();
+}
+
+void
+cat(char *file, int trailingslash)
+{
+ int fd, m;
+ char *reply;
+ long rem;
+ HRange rr;
+ Dir *d;
+ HRange *r;
+ HConnect *c;
+ HSPriv *hp;
+ char buf[8192];
+
+ c = connect;
+ if((fd = open(file, OREAD)) < 0){
+ notfound:
+ hprint(hout, "%s 404 Not Found\r\n", hversion);
+ writelog(c, "Reply: 404 Not Found\n");
+ return;
+ }
+
+ d = dirfstat(fd);
+ if(d == nil){
+ close(fd);
+ goto notfound;
+ }
+
+ if(sources){
+ if((d->mode&DMDIR) && !trailingslash){
+ hprint(hout, "%s 301 Moved Permanently\r\n", hversion);
+ hprint(hout, "Date: %D\r\n", time(nil));
+ hprint(hout, "Connection: close\r\n");
+ if(strcmp(file, ".") == 0)
+ hprint(hout, "Location: /sys/src/\r\n");
+ else
+ hprint(hout, "Location: /sys/src/%s/\r\n", file);
+ hprint(hout, "\r\n");
+ hflush(hout);
+ exits(0);
+ }
+ hprint(hout, "%s 200 OK\r\n", hversion);
+ hprint(hout, "Server: Plan9\r\n");
+ hprint(hout, "Date: %D\r\n", time(nil));
+ hprint(hout, "Connection: close\r\n");
+ hprint(hout, "Last-Modified: %D\r\n", d->mtime);
+ hflush(hout);
+ dup(hout->fd, 1);
+ dup(hout->fd, 2);
+ system("/sys/lib/dist/sources2web %q", file);
+ exits(0);
+ }
+
+ if(d->mode&DMDIR)
+ goto notfound;
+
+ r = myfixrange(connect, d, 0);
+ hflush(hout);
+ if(r){
+ reply = "206";
+ if(seek(fd, r->start, 0) < 0)
+ syslog(0, LOG, "seek: %r");
+ rem = r->stop-r->start+1;
+ } else {
+ reply = "200";
+ rr.start = 0;
+ rr.stop = d->length-1;
+ r = &rr;
+ rem = d->length;
+ }
+ for(; rem > 0; rem -= m){
+ m = read(fd, buf, sizeof(buf));
+ if(m <= 0)
+ break;
+ if(m > rem)
+ m = rem;
+ if(hwrite(hout, buf, m) != m)
+ break;
+ }
+ hp = c->private;
+ syslog(0, LOG, "%s: rs %s %lud-%lud/%lud-%lud %s", file, hp->remotesys,
+ r->start, r->stop+1-rem,
+ r->start, r->stop+1,
+ c->head.client);
+ writelog(c, "Reply: %s\n", reply);
+ close(fd);
+ return;
+}
+
+HRange*
+myfixrange(HConnect *c, Dir *d, int align)
+{
+ HRange *r, *rv;
+
+ if(!c->req.vermaj)
+ return nil;
+
+ rv = nil;
+ r = c->head.range;
+
+ if(r == nil)
+ goto out;
+
+ if(c->head.ifrangeetag != nil)
+ goto out;
+
+ if(c->head.ifrangedate != 0 && c->head.ifrangedate != d->mtime)
+ goto out;
+
+ if(d->length == 0)
+ goto out;
+
+ /* we only support a single range */
+ if(r->next != nil)
+ goto out;
+
+ if(r->suffix){
+ r->start = d->length - r->stop;
+ if(r->start >= d->length)
+ r->start = 0;
+ r->stop = d->length - 1;
+ r->suffix = 0;
+ }
+ if(r->stop >= d->length)
+ r->stop = d->length - 1;
+ if(r->start > r->stop)
+ goto out;
+ if(align && (r->start%512) != 0)
+ goto out;
+
+ rv = r;
+
+out:
+ if(rv != nil)
+ hprint(hout, "%s 206 Partial Content\r\n", hversion);
+ else
+ hprint(hout, "%s 200 OK\r\n", hversion);
+ hprint(hout, "Server: Plan9\r\n");
+ hprint(hout, "Date: %D\r\n", time(nil));
+ hprint(hout, "Connection: close\r\n");
+ hprint(hout, "Content-type: application/octet-stream\r\n");
+ if(rv != nil){
+ hprint(hout, "Content-Range: bytes %uld-%uld/%lld\r\n", r->start, r->stop,
+ d->length);
+ hprint(hout, "Content-Length: %uld\r\n", r->stop-r->start+1);
+ }else
+ hprint(hout, "Content-Length: %lld\r\n", d->length);
+ hprint(hout, "Last-Modified: %D\r\n", d->mtime);
+ hprint(hout, "\r\n");
+ return rv;
+}
+
+