1## dbhelper.sh -- shell program helpers for HotCRP database access
2## Copyright (c) 2006-2018 Eddie Kohler; see LICENSE.
3
4echo_n () {
5        # suns can't echo -n, and Mac OS X can't echo "x\c"
6        echo "$@" | tr -d '
7'
8}
9
10findoptions () {
11    if test -n "$options_file" -a \( -r "$options_file" -o -n "$1" \); then echo "$options_file"
12    elif test -n "$options_file"; then echo /dev/null; return 1
13    elif test -r "${CONFDIR}options.php" -o -n "$1"; then echo "${CONFDIR}options.php"
14    elif test -r "${CONFDIR}options.inc"; then echo "${CONFDIR}options.inc"
15    elif test -r "${OLDCONFDIR}options.inc"; then echo "${OLDCONFDIR}options.inc"
16    else echo /dev/null; return 1; fi
17}
18
19getdbopt () {
20    (cd $MAINDIR && perl -e 'my(%Opt);
21my($Confname) = "'"$CONFNAME"'";
22
23sub unslash ($) {
24   my($a) = @_;
25   my($b) = "";
26   while ($a ne "") {
27      if ($a =~ m|\A\\|) {
28         if ($a =~ m|\A\\([0-7]{1,3})(.*)\z|s) {
29            $b .= chr(oct($1));
30            $a = $2;
31         } elsif ($a =~ m|\A\\([nrftvb])(.*)\z|s) {
32            $b .= eval("\"\\$1\"");
33            $a = $2;
34         } else {
35            $b .= substr($a, 1, 1);
36            $a = substr($a, 2);
37         }
38      } else {
39         $b .= substr($a, 0, 1);
40         $a = substr($a, 1);
41      }
42   }
43   $b;
44}
45
46sub process ($) {
47   my($t) = @_;
48
49   $t =~ s|/\*.*?\*/||gs;
50   $t =~ s|//.*$||gm;
51
52   while ($t =~ m&\$Opt\[['"'"'"](.*?)['"'"'"]\]\s*=\s*\"((?:[^\"\\]|\\.)*)\"&g) {
53       $Opt{$1} = unslash($2);
54   }
55   while ($t =~ m&\$Opt\[['"'"'"](.*?)['"'"'"]\]\s*=\s*'"'"'([^'"'"']*)'"'"'&g) {
56       $Opt{$1} = $2;
57   }
58   while ($t =~ m&\$Opt\[['"'"'"](.*?)['"'"'"]\]\s*=\s*([\d.]+|true)&g) {
59       $Opt{$1} = $2;
60   }
61   while ($t =~ m&\$Opt\[['"'"'"](.*?)['"'"'"]\]\s*=\s*(?:array\(|\[)(.*)[\)\]]\s*;\s*$&gm) {
62       my($n, $x, $a) = ($1, $2, []);
63       while (1) {
64           if ($x =~ m&\A[\s,]*\"((?:[^\"\\]|\\.)*)\"(.*)\z&) {
65               push @$a, unslash($1);
66               $x = $2;
67           } elsif ($x =~ m&\A[\s,]*'"'"'([^'"'"']*)'"'"'(.*)\z&) {
68               push @$a, $1;
69               $x = $2;
70           } else {
71               last;
72           }
73       }
74       $Opt{$n} = $a;
75   }
76}
77
78undef $/;
79process(<STDIN>);
80if (exists($Opt{"include"})) {
81    $Opt{"include"} = [$Opt{"include"}] if !ref $Opt{"include"};
82    my($confname, @flist) = $Confname ? $Confname : $Opt{"dbName"};
83    foreach my $f (@{$Opt{"include"}}) {
84        $f =~ s,\$\{conf(?:id|name)\}|\$conf(?:id|name)\b,$confname,g;
85        @flist = ($f =~ m,[\[\]\*\?], ? glob($f) : $f);
86        foreach my $ff (@flist) {
87            if (open(F, "<", $ff)) {
88                process(<F>);
89                close(F);
90            } else {
91                print STDERR "$ff: $!\n";
92            }
93        }
94    }
95}
96
97sub fixshell ($) {
98    my($a) = @_;
99    $a =~ s|'"'"'|'"'"'"'"'"'"'"'"'|g;
100    $a;
101}
102
103if ($Opt{"multiconference"} && $Confname ne "") {
104   foreach my $i ("dbName", "dbUser", "dbPassword",
105                  "sessionName", "downloadPrefix", "conferenceSite") {
106       $Opt{$i} =~ s,\*|\*\{conf(?:id|name)\}|\$conf(?:id|name)\b,$Confname,g if exists($Opt{$i});
107   }
108}
109
110if ("'$1'" =~ /^db/
111    && (($Opt{"multiconference"} && $Confname eq "")
112        || exists($Opt{"dsn"})
113        || !exists($Opt{"dbName"}))) {
114    print "";
115} else {
116    $Opt{"dbUser"} = $Opt{"dbName"} if !exists($Opt{"dbUser"}) && exists($Opt{"dbName"});
117    $Opt{"dbPassword"} = $Opt{"dbName"} if !exists($Opt{"dbPassword"}) && exists($Opt{"dbName"});
118    print "'"'"'", fixshell($Opt{"'$1'"}), "'"'"'";
119}') < "`findoptions`"
120}
121
122sql_quote () {
123    sed -e 's,\([\\"'"'"']\),\\\1,g' | sed -e 's,,\\Z,g'
124}
125
126json_quote () {
127    echo_n '"'
128    perl -pe 's{([\\\"])}{\\$1}g;s{([\000-\017])}{sprintf("\\%03o", ord($1))}eg'
129    # sed -e 's,\([\\"]\),\\\1,g' | tr -d '\n'
130    echo_n '"'
131}
132
133check_mysqlish () {
134    local m="`eval echo '$'$1`"
135    if test -n "$m"; then :;
136    elif $2 --version >/dev/null 2>&1; then m=$2;
137    elif ${2}5 --version >/dev/null 2>&1; then m=${2}5;
138    else m=$2;
139    fi
140
141    if $m --version >/dev/null 2>&1; then :; else
142        echo "I can't find a working $m program." 1>&2
143        echo "Install MySQL, or set the $1 environment variable and try again." 1>&2
144        exit 1
145    fi
146    eval ${1}="$m"
147}
148
149set_myargs () {
150    myargs=""
151    if test -n "$1"; then myargs=" -u$1"; fi
152    local password="$2"
153    if expr "$2" : "'" >/dev/null 2>&1; then :; else password="'$password'"; fi
154    if test "$password" = "''"; then
155        myargs_redacted="$myargs"
156    else
157        myargs_redacted="$myargs -p<REDACTED>"
158        if test "$no_password_file" = true; then
159            PASSWORDFILE=
160        else
161            PASSWORDFILE="`mktemp -q /tmp/hotcrptmp.XXXXXX`"
162        fi
163        if test -n "$PASSWORDFILE"; then
164            echo "[client]" > "$PASSWORDFILE"
165            chmod 600 "$PASSWORDFILE" # should be redundant
166            echo "password=$password" >> "$PASSWORDFILE"
167            myargs=" --defaults-extra-file=$PASSWORDFILE$myargs"
168            trap "rm -f $PASSWORDFILE" EXIT 2>/dev/null
169        else
170            PASSWORDFILE=""
171            myargs="$myargs -p$password"
172        fi
173    fi
174    if test -n "$dbhost" -a "$dbhost" != "''"; then
175        if expr "$dbhost" : "'" >/dev/null 2>&1; then
176            myargs="$myargs -h$dbhost"
177        else
178            myargs="$myargs -h'$dbhost'"
179        fi
180    fi
181}
182
183generate_random_ints () {
184    random="`head -c 48 /dev/urandom 2>/dev/null | tr -d '\000'`"
185    test -z "$random" && random="`head -c 48 /dev/random 2>/dev/null | tr -d '\000'`"
186    test -z "$random" && random="`openssl rand 48 2>/dev/null | tr -d '\000'`"
187    echo "$random" | awk '
188BEGIN { for (i = 0; i < 256; ++i) { ord[sprintf("%c", i)] = i; } }
189{ for (i = 1; i <= length($0); ++i) { printf("%d\n", ord[substr($0, i, 1)]); } }'
190    # generate some very low-quality random bytes in case all the
191    # higher-quality mechanisms fail
192    awk 'BEGIN { srand(); for (i = 0; i < 256; ++i) { printf("%d\n", rand() * 256); } }' < /dev/null
193}
194
195generate_password () {
196    awk 'BEGIN {
197    npwchars = split("a e i o u y a e i o u y a e i o u y a e i o u y a e i o u y b c d g h j k l m n p r s t u v w tr cr br fr th dr ch ph wr st sp sw pr sl cl 2 3 4 5 6 7 8 9 - @ _ + =", pwchars, " ");
198    pw = ""; nvow = 0;
199}
200{   x = ($0 % npwchars); if (x < 30) ++nvow;
201    pw = pw pwchars[x + 1];
202    if (length(pw) >= '"$1"' + nvow / 3) exit;
203}
204END { printf("%s\n", pw); }'
205}
206
207parse_common_argument () {
208    case "$1" in
209    -c|--co|--con|--conf|--confi|--config)
210        test "$#" -gt 1 -a -z "$options_file" || usage
211        options_file="$2"; shift=2;;
212    -c*)
213        test -z "$options_file" || usage
214        options_file="`echo "$1" | sed 's/^-c//'`"; shift=1;;
215    --co=*|--con=*|--conf=*|--confi=*|--config=*)
216        test -z "$options_file" || usage
217        options_file="`echo "$1" | sed 's/^[^=]*=//'`"; shift=1;;
218    -n|--n|--na|--nam|--name)
219        test "$#" -gt 1 -a -z "$CONFNAME" || usage
220        CONFNAME="$2"; shift=2;;
221    -n*)
222        test -z "$CONFNAME" || usage
223        CONFNAME="`echo "$1" | sed 's/^-n//'`"; shift=1;;
224    --n=*|--na=*|--nam=*|--name=*)
225        test -z "$CONFNAME" || usage
226        CONFNAME="`echo "$1" | sed 's/^-n//'`"; shift=1;;
227    --no-password-f|--no-password-fi|--no-password-fil|--no-password-file)
228        no_password_file=true; shift=1;;
229    *)
230        shift=0;;
231    esac
232}
233
234get_dboptions () {
235    dbname="`getdbopt dbName 2>/dev/null`"
236    dbuser="`getdbopt dbUser 2>/dev/null`"
237    dbpass="`getdbopt dbPassword 2>/dev/null`"
238    dbhost="`getdbopt dbHost 2>/dev/null`"
239    if test -z "$dbname" -o -z "$dbuser" -o -z "$dbpass"; then
240        echo "$1: Can't extract database options from `findoptions`!" 1>&2
241        if test "`getdbopt multiconference 2>/dev/null`" '!=' "''"; then
242            echo "This is a multiconference installation; check your '-n CONFNAME' option." 1>&2
243        fi
244        exit 1
245    fi
246}
247
248# slash-terminate LIBDIR
249expr "$LIBDIR" : '.*[^/]$' >/dev/null && LIBDIR="$LIBDIR/"
250# remove '/./' components
251LIBDIR="`echo "$LIBDIR" | sed ':a
252s,/\./,/,g
253ta'`"
254# set MAINDIR from LIBDIR
255if test "$LIBDIR" = ./; then
256    MAINDIR=../
257elif expr "$LIBDIR" : '[^/]*[/]$' >/dev/null; then
258    MAINDIR=./
259elif ! expr "$LIBDIR" : '.*\.\.' >/dev/null; then
260    MAINDIR="`echo "$LIBDIR" | sed 's,\(.*/\)[^/]*/$,\1,'`"
261else
262    MAINDIR="${LIBDIR}../"
263fi
264# set CONFDIR and SRCDIR from MAINDIR
265CONFDIR="`echo "${MAINDIR}conf/" | sed 's,^\./\(.\),\1,'`"
266OLDCONFDIR="`echo "${MAINDIR}Code/" | sed 's,^\./\(.\),\1,'`"
267SRCDIR="`echo "${MAINDIR}src/" | sed 's,^\./\(.\),\1,'`"
268export MAINDIR LIBDIR CONFDIR OLDCONFDIR SRCDIR
269