From: mdw Date: Wed, 1 Oct 2003 00:08:57 +0000 (+0000) Subject: Collection of miscellaneous ill-documented tools. X-Git-Tag: 1.1.1~11 X-Git-Url: https://git.distorted.org.uk/~mdw/misc/commitdiff_plain/8d769cc9146d71188f2ffe9cd5a9160706614f1d Collection of miscellaneous ill-documented tools. --- 8d769cc9146d71188f2ffe9cd5a9160706614f1d diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..3910527 --- /dev/null +++ b/.cvsignore @@ -0,0 +1 @@ +cdb-check-domain cdb-probe not qmail-checkspam diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..841e7fd --- /dev/null +++ b/Makefile @@ -0,0 +1,36 @@ +## Makefile for miscellaneous stuff +## +## No proper build system here. Just kludgy hacks. + +PROGS = \ + qmail-checkspam not \ + cdb-probe cdb-check-domain \ + xtitle.so + +CC = gcc +LD = gcc +CFLAGS = -O2 -g -pedantic -Wall +LINK = $(LD) $(LDFLAGS) -o $@ $^ + +all: $(PROGS) + +qmail-checkspam: qmail-checkspam.o + $(LINK) -lspamc + +cdb-probe: cdb-probe.o + $(LINK) -lfreecdb + +cdb-check-domain: cdb-check-domain.o + $(LINK) -lfreecdb + +not: not.o + $(LINK) + +xtitle.o: xtitle.c + $(CC) $(CFLAGS) -c -fpic -I/usr/include/bash -DBASH_BUILTIN -o $@ $^ +xtitle.so: xtitle.o + $(LINK) -shared + +clean:; rm -f *.o $(PROGS) + +.PHONY: all clean diff --git a/cdb-assign b/cdb-assign new file mode 100755 index 0000000..5f2f44e --- /dev/null +++ b/cdb-assign @@ -0,0 +1,15 @@ +#! /usr/bin/perl + +use CDB_File; + +@ARGV >= 1 or die "usage: $0 CDB [INPUT ...]\n"; +$f = shift; +$c = CDB_File->new($f, "$f.new") or die "CDB_File->new: $!\n"; +while (<>) { + chomp; + next if m'^\s*(\#|$)'; + m'^\s*([-\w]+)\s*=\s*(.*\S|)\s*$' or die "bad assignment `$_'\n"; + $c->insert($1, $2); +} +$c->finish(); +exit 0; diff --git a/cdb-check-domain.c b/cdb-check-domain.c new file mode 100644 index 0000000..bac6481 --- /dev/null +++ b/cdb-check-domain.c @@ -0,0 +1,38 @@ +#include +#include +#include +#include + +#include "freecdb.h" + +const char *prog; + +static void check(const char *p) +{ + int rc; + uint32_t l; + + rc = cdb_seek(0, p, strlen(p), &l); + if (rc < 0) { + fprintf(stderr, "%s: cdb_seek: %s\n", prog, strerror(errno)); + exit(111); + } + if (rc) exit(0); +} + +int main(int argc, char *argv[]) +{ + const char *p; + + prog = argv[0]; + if (argc != 2) { + fprintf(stderr, "usage: %s KEY = 1 or die "usage: $0 CDB [INPUT ...]\n"; +$f = shift; +$c = CDB_File->new($f, "$f.new") or die "CDB_File->new: $!\n"; +while (<>) { + chomp; + next if m'^\s*(\#|$)'; + m'^\s*(.*\S|)\s*$'; + $c->insert($1, ""); +} +$c->finish(); +exit 0; diff --git a/cdb-probe.c b/cdb-probe.c new file mode 100644 index 0000000..cb19a29 --- /dev/null +++ b/cdb-probe.c @@ -0,0 +1,32 @@ +#include +#include +#include +#include + +#include "freecdb.h" + +const char *prog; + +static void check(const char *p) +{ + int rc; + uint32_t l; + + rc = cdb_seek(0, p, strlen(p), &l); + if (rc < 0) { + fprintf(stderr, "%s: cdb_seek: %s\n", prog, strerror(errno)); + exit(111); + } + if (rc) exit(0); +} + +int main(int argc, char *argv[]) +{ + prog = argv[0]; + if (argc != 2) { + fprintf(stderr, "usage: %s KEY +#include +#include +#include + +#include +#include + +int main(int argc, char *argv[]) +{ + pid_t kid; + int rc; + + if (argc < 2) { + fprintf(stderr, "usage: %s PROG [ARGS]\n", argv[0]); + exit(111); + } + kid = fork(); + if (kid < 0) { + fprintf(stderr, "%s: fork: %s\n", argv[0], strerror(errno)); + exit(111); + } + if (!kid) { + execvp(argv[1], argv + 1); + fprintf(stderr, "%s: exec %s: %s\n", argv[0], argv[1], strerror(errno)); + exit(111); + } + if (waitpid(kid, &rc, 0) <= 0) { + fprintf(stderr, "%s: wait: %s\n", argv[0], strerror(errno)); + exit(111); + } + if (!WIFEXITED(rc) || WEXITSTATUS(rc) == 111) + exit(111); + exit(WEXITSTATUS(rc) == 0 ? 100 : 0); +} diff --git a/qmail-checkspam.c b/qmail-checkspam.c new file mode 100644 index 0000000..de9c425 --- /dev/null +++ b/qmail-checkspam.c @@ -0,0 +1,164 @@ +/* -*-c-*- + * + * $Id: qmail-checkspam.c,v 1.1 2003/10/01 00:08:57 mdw Exp $ + * + * Filter messages for spam + * + * (c) 2003 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: qmail-checkspam.c,v $ + * Revision 1.1 2003/10/01 00:08:57 mdw + * Collection of miscellaneous ill-documented tools. + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include +#include +#include + +#include +#include + +#include + +/*----- Main code ---------------------------------------------------------*/ + +static const char *strenv(const char *e, const char *d) +{ + const char *p = getenv(e); + if (!p) return (d); + return (p); +} + +static double dblenv(const char *e, double d) +{ + const char *p = getenv(e); + char *q; + int err = errno; + double f; + if (!p) return (d); + errno = 0; + f = strtod(p, &q); + if (errno) return (d); + errno = err; + return (f); +} + +static int intenv(const char *e, int d) +{ + const char *p = getenv(e); + char *q; + int err = errno; + long l; + if (!p) return (d); + errno = 0; + l = strtol(p, &q, 0); + if (errno) return (d); + errno = err; + if (l < 0 || l > INT_MAX) return (d); + return ((int)l); +} + +int shovel(int from, int to) +{ + char buf[4096]; + ssize_t n; + char *p; + size_t r; + + for (;;) { + n = read(from, buf, sizeof(buf)); + if (n < 0 && errno != EINTR && errno != EAGAIN) + return (-1); + else if (!n) + return (0); + p = buf; + r = n; + while (r) { + n = write(to, p, n); + if (n <= 0 && errno != EINTR && errno != EAGAIN) + return (-1); + r -= n; + p += n; + } + } +} + +int main(int argc, char *argv[]) +{ + struct sockaddr sa; + struct message m; + int fd_m[2], fd_e[2]; + pid_t kid; + int rc; + + m.max_len = intenv("QMAIL_CHECKSPAM_MAXLEN", 2 * 1024 * 1024); + m.timeout = intenv("QMAIL_CHECKSPAM_TIMEOUT", 300); + rc = message_read(0, 0, &m); + if (rc != 0 && rc != EX_TOOBIG) + return (54); + if (!rc) { + if (lookup_host(strenv("QMAIL_CHECKSPAM_SPAMDHOST", "localhost"), + intenv("QMAIL_CHECKSPAM_SPAMDPORT", 783), + &sa)) + return (56); + if (message_filter(&sa, "spamd", 0, &m)) + return (74); + if (m.score >= dblenv("QMAIL_CHECKSPAM_THRESH", m.threshold)) + return (31); + } + if (pipe(fd_m) || pipe(fd_e)) + return (56); + if ((kid = fork()) < 0) + return (56); + if (!kid) { + close(fd_m[0]); + close(fd_e[0]); + if (message_write(fd_m[1], &m) < 0) + _exit(127); + if (rc == EX_TOOBIG && shovel(0, fd_m[1])) + _exit(127); + close(fd_m[1]); + if (shovel(1, fd_e[1])) + _exit(127); + close(fd_e[1]); + _exit(0); + } + + dup2(fd_m[0], 0); + dup2(fd_e[0], 1); + close(fd_m[0]); + close(fd_e[0]); + close(fd_m[1]); + close(fd_e[1]); + execlp(strenv("QMAIL_CHECKSPAM_QUEUE", "/var/qmail/bin/qmail-queue"), + (char *)0); + fprintf(stderr, "failed to exec: %s\n", strerror(errno)); + return (56); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/splitconf b/splitconf new file mode 100755 index 0000000..4a09f0a --- /dev/null +++ b/splitconf @@ -0,0 +1,217 @@ +#! /usr/bin/tclsh + +proc die {msg} { + global argv0 + puts stderr "$argv0: $msg" + exit 1 +} + +proc usage {file} { + global argv0 + puts $file "Usage: \n\ + $argv0 [-s] FILE\n + $argv0 -u OUTPUT FILE FILE ..." +} + +set job "split" +while {[llength $argv]} { + switch -glob -- [lindex $argv 0] { + "-u" - "--unsplit" { + set job "unsplit" + if {[llength $argv] < 2} { die "option `-u' needs an argument" } + set output [lindex $argv 1] + set argv [lrange $argv 1 end] + } + "-d" - "--delete" { set job "delete" } + "-s" - "--split" { set job "split" } + "-h" - "--help" { usage stdout; exit 0 } + "-" { break } + "--" { set argv [lrange $argv 1 end]; break } + "-*" { die "unknown option `[lindex $argv 0]'"; exit 1 } + default { break } + } + set argv [lrange $argv 1 end] +} + +proc clear-arrays {args} { + foreach i $args { + upvar 1 $i v + unset i + array set v {} + } +} + +proc write-safe {stuff {tidy {}}} { + global _ws_close _ws_del _ws_new + clear-arrays _ws_del _ws_new + set _ws_close {} + + if {[set rc [catch { + uplevel 1 $stuff + } err]]} { + foreach f $_ws_close { catch { close $f } } + foreach f [array names _ws_new] { catch { file delete -- $f.new } } + catch { uplevel 1 $tidy } + return -code $rc $err + } + foreach f $_ws_close { catch { close $f } } + clear-arrays all + foreach f [concat [array names _ws_old] [array names _ws_del]] { + set all($f) 0 + } + if {[set rc [catch { + foreach f [array names all] { + if {[file exists $f]} { + file delete -- $f.old + file copy -force -- $f $f.old + } + set old($f) 0 + } + foreach f [array names _ws_new] { file rename -force -- $f.new $f } + foreach f [array names _ws_del] { file delete -- $f } + } err]]} { + foreach f [array names _ws_new] { catch { file delete -- $f.new } } + foreach f [array names old] { file rename -force -- $f.old $f } + catch { uplevel 1 $tidy } + return -code $rc $err + } + foreach i [array names all] { catch { file delete -- $i.old } } + catch { uplevel 1 $tidy } + return {} +} + +proc write-safe-open {name {trans auto}} { + global _ws_close _ws_new + if {[file isdirectory $name]} { error "`$name' is a directory" } + set f [open $name.new w] + fconfigure $f -translation $trans + lappend _ws_close $f + set _ws_new($name) 0 + return $f +} + +proc write-safe-delete {name} { + global _ws_del + set _ws_del($name) 0 +} + +proc write-safe-file {name contents {trans auto}} { + set f [write-safe-open $name $trans] + puts -nonewline $f $contents + close $f +} + +proc read-file {name {trans auto}} { + set f [open $name] + fconfigure $f -translation $trans + set c [read $f] + close $f + return $c +} + +proc write-safe-manifest {f l} { + set f [write-safe-open $f.files] + foreach i $l { puts $f $i } + close $f +} + +proc old-files {conf} { + set old {} + if {[file exists $conf.files]} { + set f [open $conf.files] + while {[gets $f line] >= 0} { lappend old $line } + close $f + } + return $old +} + +set rc 0 +clear-arrays opt +array set opt { + prefix "" + before "" + after "" +} +switch $job { + "unsplit" { + set f "\#\# automatically generated by splitconf\n\n" + set ff {} + foreach i $argv { + if {[catch { + set c [read-file $i] + append f "\[$i\]\n$c\n" + lappend ff $i + } msg]} { + set rc 1 + } + } + write-safe { + write-safe-file $output $f + write-safe-manifest $output $ff + } + } + "delete" { + if {[llength $argv] != 1} { die "need exactly one filename" } + set conf [lindex $argv 0] + set old [old-files $conf] + write-safe { + foreach i $old { write-safe-delete $i } + write-safe-delete $conf.files + } + } + "split" { + if {[llength $argv] != 1} { die "need exactly one filename" } + set conf [lindex $argv 0] + set old [old-files $conf] + set c [open $conf r] + catch { unset o } + set file "" + set spill "" + array set new {} + write-safe { + while {[gets $c line] >= 0} { + if {[regexp -- {^\[(.*)\]\s*$} $line . name]} { + if {[info exists o]} { + puts -nonewline $o $file + close $o + } else { + exec "sh" "-c" $opt(before) + } + set name "$opt(prefix)$name" + set o [write-safe-open $name] + set new($name) 1 + set file "" + set spill "" + } elseif {[info exists o]} { + switch -regexp -- $line { + {^\s*$} { append spill "$line\n" } + {^\#\#} { } + {^\!} { append file "$spill[string range $line 1 end]\n" } + default { append file "$spill$line\n" } + } + } elseif {[regexp -- {^\s*(\#|$)} $line]} { + continue + } elseif {[regexp -- {^\s*([-\w]+)\s*=\s*(.*\S|)\s*$} $line . k v]} { + if {![info exists opt($k)]} { + error "unknown configuration option `$k'" + } else { + set opt($k) $v + } + } else { + error "unknown preamble directive" + } + } + if {[info exists o]} { + puts -nonewline $o $file + close $o + } + close $c + foreach i $old { + if {![info exists new($i)]} { write-safe-delete $i } + } + write-safe-manifest $conf [array names new] + } { + exec "sh" "-c" $opt(after) + } + } +} diff --git a/unfwd b/unfwd new file mode 100755 index 0000000..3f89ef6 --- /dev/null +++ b/unfwd @@ -0,0 +1,73 @@ +#! /usr/bin/perl + +use MIME::Parser; +use MIME::Entity; +use MIME::Head; +use MIME::Body; + +sub bounce { + print STDERR "$0: ", @_, "\n"; + exit 100; +} + +sub retry { + print STDERR "$0: ", @_, "\n"; + exit 111; +} + +$DONE = 0; +$FAIL = 0; + +sub fail { + print STDERR "$0: ", @_, "\n"; + $FAIL = 100; +} + +sub msg { + my ($body) = @_; + $DONE = 1; + pipe(IN, OUT); + my $kid = fork(); + defined($kid) or retry("couldn't fork: $!"); + if (!$kid) { + open(STDIN, "<&IN"); + close(OUT); + close(IN); + exec @ARGV; + print STDERR "$0: exec `", join(" ", @ARGV), "' failed: $!\n"; + exit 100; + } + close(IN); + $body->print(\*OUT) or fail "print failed: $!", last; + close(OUT) or fail "close failed: $!"; + waitpid($kid, 0) or fail "waitpid failed: $!"; + $? and fail "program `", join(" ", @ARGV), "' exited with status $?"; +} + +sub digest { + my ($e) = @_; + foreach my $i ($e->parts()) { + msg($i->bodyhandle()); + } +} + +$SIG{__DIE__} = sub { retry "DEAD: ", @_, "!" }; +$SIG{PIPE} = IGN; +@ARGV or retry "$0: no command given"; +my $pp = MIME::Parser->new(); +$pp->output_to_core(ALL); +$pp->extract_nested_messages(0); +my $top = $pp->parse(\*STDIN); +if ($top->effective_type =~ m'multipart/mixed'i) { + foreach my $i ($top->parts()) { + if ($i->effective_type =~ m'message/rfc822'i) { + msg($i->bodyhandle()); + } elsif ($i->effective_type =~ m'multipart/digest'i) { + digest($i); + } + } +} elsif ($top->effective_type =~ m'multipart/digest'i) { + digest($top); +} +if (!$DONE) { bounce "no forwarded message or digest"; } +exit $FAIL; diff --git a/xtitle.c b/xtitle.c new file mode 100644 index 0000000..94e9af6 --- /dev/null +++ b/xtitle.c @@ -0,0 +1,169 @@ +#include +#include +#include +#include +#include +#include +#include + +#ifdef BASH_BUILTIN +#include "bash/config.h" +#include "bash/shell.h" +#include "bash/builtins.h" +#include "bash/builtins/common.h" +#include "bash/builtins/bashgetopt.h" +#endif + +#ifdef BASH_BUILTIN +int xtitle_builtin(WORD_LIST *list) +#else +int main(int argc, char *argv[]) +#endif +{ + int query = 0; + int fd; + int openned = 0; + +#ifdef BASH_BUILTIN + reset_internal_getopt(); +#endif + for (;;) { +#ifdef BASH_BUILTIN + int i; + i = internal_getopt(list, "q"); +#else + int i = getopt(argc, argv, "q"); +#endif + if (i < 0) + break; + switch (i) { + case 'q': + query = 1; + break; + default: +#ifdef BASH_BUILTIN + builtin_usage(); +#else + fprintf(stderr, "usage: xtitle [-q] [string]\n"); +#endif + return (1); + } + } + +#ifdef BASH_BUILTIN + if (!query && loptend == 0) { +#else + if (!query && optind == argc) { +#endif + fprintf(stderr, "xtitle: no string to set\n"); + return (1); + } + + { + char *t = getenv("TERM"); + if (!t || strncmp(t, "xterm", 5)) + return (0); + } + + if (isatty(0)) + fd = 0; + else { + fd = open("/dev/tty", O_RDWR); + if (fd < 0) { + fprintf(stderr, "xtitle: couldn't open terminal: %s", strerror(errno)); + return (1); + } + openned = 1; + } + + if (!query) { +#ifdef BASH_BUILTIN + WORD_LIST *l = loptend; + char sp = ' '; + write(fd, "\33]0;", 4); + while (l) { + write(fd, l->word->word, strlen(l->word->word)); + if (l->next) + write(fd, &sp, 1); + l = l->next; + } + write(fd, "\7", 2); +#else + int i; + char sp = ' '; + write(fd, "\33]0;", 4); + for (i = optind; i < argc; i++) { + write(fd, argv[i], strlen(argv[i])); + if (i < argc - 1) + write(fd, &sp, 1); + } + write(fd, "\7", 2); +#endif + } else { + struct termios o, n; + char hack; + int state = 0; + + tcgetattr(fd, &o); + n = o; + n.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP + |INLCR|IGNCR|ICRNL|IXON); + n.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); + n.c_cflag &= ~(CSIZE|PARENB); + n.c_cflag |= CS8; + tcsetattr(fd, TCSAFLUSH, &n); + write(fd, "\33[21t", 5); + + while (state != -1) { + if (read(fd, &hack, 1) < 1) + break; + switch (state) { + case 0: + if (hack == '\33') state = 1; + break; + case 1: + if (hack == ']') state = 2; else state = 0; + break; + case 2: + if (hack == 'l') state = 3; else state = 0; + break; + case 3: + if (hack == '\33') state = 4; else putchar(hack); + break; + case 4: + if (hack == '\\') { state = -1; putchar('\n'); } + else { putchar('\33'); putchar(hack); state = 3; } + break; + } + } + + tcsetattr(fd, TCSAFLUSH, &o); + } + + if (openned) + close(fd); + + return (0); +} + + +#ifdef BASH_BUILTIN + +static char *xtitle_doc[] = { + "Either set or read the title of the current xterm window. With the", + "-q option, writes the current xterm title to standard output. Without", + "the -q option, sets the xterm title to be the arguments given,", + "separated by space characters. [By Mark Wooding, mdw@nsict.org]", + 0 +}; + +struct builtin xtitle_struct = { + "xtitle", + xtitle_builtin, + BUILTIN_ENABLED, + xtitle_doc, + "xtitle [-q] [arguments]", + 0 +}; + +#endif