s4

view s4-funcs.sh @ 191:24e0f2b4d51b

Variable name mistake fixed
author HIROSE Yuuji <yuuji@gentei.org>
date Sat, 23 Apr 2016 21:10:55 +0859
parents 5ba50e0850c9
children b1847cc3912a
line source
1 #!/bin/sh
2 # Here's global variable table. Do not use this names.
3 # $HGid$
5 [ -f s4-config.sh ] && . ./s4-config.sh
7 myname=`basename ${SCRIPT_NAME:-$0}`
8 mydir=`dirname ${SCRIPT_FILENAME:-$0}`
9 myargs="$@"
10 PATH=/usr/local/sqlite3/bin:/usr/local/vim7/bin:/usr/iekei/ImageMagick/bin:/usr/local/ImageMagick/bin:$PATH
11 tmpdir=${TMPDIR:-tmp}
12 dbdir=${DBDIR:-db}
13 tmpfiles=""
14 db=${DB:-$dbdir/cgi.sq3}
15 admin=${ADMIN:-hostmaster@example.org}
16 templ=${TEMPL:-templ}
17 layout=${LAYOUT:-$templ/default}
18 formdir=${FORMDIR:-$templ/form}
19 imgdir=${IMGDIR:-img}
20 url=${URL:-"${REQUEST_SCHEME:-http${HTTPS:+s}}://$HTTP_HOST$REQUEST_URI"}
21 urlbase=${url%%\?*}
22 msgdir=$templ/msg
23 timeout="+2 days"
24 memoplimitdays="7"
25 dumpcollen=20
26 #thumbxy=120x120
27 thumbxy=96x96
28 maximagexy=1600x1600
29 ### maximagexy=400x400
30 tconfs=""
31 imgcached=imgcache.`date +%Y/%m`
32 conftbl=_tblconf
33 nl="
34 "
35 . ./s4-cgi.sh
37 : <<EOF
39 資料配布、グループ管理・ML、ファイル交換、クリッカー、アンケート
40 レポート提出管理
41 ひとつのarticleをheadingにして新規ツリーを作成、あるといいかも。
43 8/4 ○グループに承認加入モードを追加
44 ○グループに参加していない場合は grpaction できない
45 Web
46 締切設定
48 8/2 ○s4.cgi生成系 → index.cgi生成
49 ○自分の提出物リスト
51 7/19 ○設置
52 ○一斉送信
53 ○getparfilename の tmpd の扱い
54 ○やっぱりs4にしようかな
55 7/18 ○書込著者からホームへのリンク
56 7/17 ○個人blogに「レポート提出用」がついたときの挙動
57 ○添付ファイル回収
58 ○imgcacheは別ディレクトリにしないと + .htaccess
59 7/15 ○レポート提出モードの表示を付ける
60 管理者権限での削除? → まだいいか
62 7/13 ○前回アクセス基準の新着数は欲しいなあ
63 ○レポート提出はどうしよう
64 → ○blogにモードを追加:
65 ○レポート提出モード
66 添付ファイル (誰が見たかログ)
67 クリッカーは別立てメニューにしないと(管理者がON/OFF)
68 ○添付ファイルの読み出し権(6/22から) ← モードで対処
71 7/9 ○管理者の追加
72 △グループメンバの操作 → 要不要を吟味
73 ○グループ情報編集の行先はそのグループがいい?
74 ○新規グループの作成はどこから入るか
75 △グループホームとユーザホームを揃える
77 7/8 ○グループ一覧をユーザ一覧と揃える。
79 7/6の次 ○グループのconf編集の入口
80 ○グループ検索
82 6/22の次 ○ホーム画面、○招待状、親記事追跡、○編集ボタン、削除ボタン、
85 6/7の次 ○blogを作ってみる || userconfig || _mのまとめ編集(削除)
86 6/7の次の次 ○userconfigの画面だけ作ってみる。
88 ○ 5/28の次 edittableに「削除」ボタンを足す
89 ○6/1 par2tableを triplex 対応に
90 select "yuuji@gentei.org",var,"text",NULL,val from par where var in (select col from _tblconf where tbl="/user" and keytype in ('p', 's'));
91 →とすると 一気に
93 ## form.def を考えなおそう:
94 ## userのように必須カラムを決まった位置に付ける?
95 ## 必須カラム、owner(foreign key passwd(name)), update datetime
96 ## ユーザ管理とグループ管理はデフォルトで持たせてしまえ
98 ## 縦持ちデータの入力/編集を供給する関数 single + multi
99 ## 持てるテーブル構造はシステム標準5種 + ユーザ定義2種類
100 ## 1. passwd
101 ## 2. grp
102 ## 3. grp_mem
103 ## 4. topic 記事のIDとなる
104 ## 5. topic_cont 特定IDの記事の内容物
105 ## 6. list 繰り返し登場あり
106 ## 7. hash 繰り返し登場なし
108 ## ● listの定義:
109 ## create table list(id unique, parentID, type, value);
110 ## ● hashの定義:
111 ## create table hash(parentID, type, value, primary key(parentID, type));
113 ## グループ属性: community, friend
114 ## ○ blob使えるのかな。streamで行けるのか? xxdで行けた。ありがたい。
115 ## form-defとtableは1対1対応でいいか
116 ## csv2sq3 で .csv.sq3 の Makefile
118 ## 書き込みオブジェクトとは何か?
119 ## topic : id, belongto, title, owner, mode
120 ## type := root | comment
121 ## topic_cont : id, topicid(F), ppath, contenttype, filename, content,
122 ## unique(id, filename)
123 ## type := body(single) | attachment(multi)
125 ## group := name(P), tag, gecos, owner(F), mode
126 ## tag := personal | friend | ... any string
127 ## group_member := gname(F), type, name(F), UNIQUE(gname, type, name)
128 ## type := "u" | "g"
129 ## できたー!
130 ## with recursive allmem as (select * from grp_mem where gname='bar' union all select grp_mem.* from grp_mem,allmem where allmem.name=grp_mem.gname) select * from allmem where type='u';
131
132 ↓以下に変更
133 with recursive allmem as
134 (select gname,val from grp_m where gname='foo'
135 union all select grp_m.gname,grp_m.val from
136 grp_m,allmem where allmem.val=grp_m.gname)
137 select val from allmem where val in (select name from user);
140 with recursive allmem as
141 (select gname,val from grp_m where gname='foo'
142 union all select grp_m.gname,grp_m.val from grp_m,allmem
143 where allmem.val=grp_m.gname)
144 select a.*, coalesce(b.val,a.val) from allmem a left join grp_mem_s b
145 on a.gname=b.gname and a.val=b.user and b.key='email'
146 where a.val in (select name from user);
149 ## triggerもできた。
150 ## 5/22から:グループ作成画面
151 ## 埋め込み画像 data:CONTENT-TYPE;base64,.....
153 ## 考え得るノードタイプ
154 ## 日報 - 個人所属かグループ所属か
155 ## 課題提出 - 個人所属かグループ所属か
156 ## グループ管理
157 ## 個人情報管理
158 ##
160 ## 例: group:sip - topic:1:sip:Aperture:yuuji:rw
161 ## - topic:2:sip:ISO:yuuji:rw
162 ## topic_cont 1:1:/:body:text...Aperture
163 ## 2:1:/1:body:text..Aperture
164 ## 3:1:/1:attachment:binary..Aperture
165 ## 4:1:/2:body:text..Aperture
166 ## 5:1:/2:attachment:binary..Aperture
167 ## 6:2:/:body:text..ISO
168 ## 7:2:/6:body:text..ISO
169 ## 8:2:/6:attachment:binary..
171 ## ログテーブル
172 ## time, who, action, tbl, id idなんか取れるかな
176 ■表設計
177 * 3つの表に分散管理
178 id格納表 + hash表 + list表
179 * *_s *_m
183 user, user_map, user_col
185 ■抽象エントリタイプ
186 * user
187 idとして機能 → table中の owner に自動挿入(?)
188 * group
189 権限判定に利用
190 * serial
191 自動idとして機能
192 * password
193 入力 type=passwordで入力
194 変更 oldpasswd, password×2 で確認後修正
195 * session
196 password認証後のセッションキーとして機能
197 * text
198 入力 type=text
199 * textarea
200 入力 textarea
201 * image|document
202 入力 type=fileで入力し、mime-typeを確認
203 * owner
204 入力時の $user で、外部キー制約が付く
205 * gowner
206 グループとしての所有者で、外部キー制約が付く
207 * timestamp
208 datetime()
209 * parent
210 木構造の場合の親の位置
211 * path
212 木構造の場合の自分の位置
214 格納タイプ
215 * list
216 表 parentID, key, val でUNIQUE(parentID, key, val)
217 * hash
218 表 parentID, key, val でUNIQUE(parentID, key)
220 オブジェクトタイプ
221 * entry
222 id, title, owner
223 * textpart
224 id, parentID, text
225 * binarypart
226 id, parentID, contenttype, filename, content
227 * content
228 hash(textpart), list(binarypart)
229 * topic
230 id, hash(content), list(reply)
231 * reply
232 id, parentID, content
233 * blog
234 list(entry)
235 blog = [topic, list(reply)]
238 blog = [ {"title" => "hoge", "owner" => "yuuji", "date" => "2015-04-27",
239 "text" => "hogehoge ..",
240 "reply" => [ {"serial" => 1,
241 "author" => "taro",
242 "date" => "2015-04-28",
243 "parent" => "/",
244 "path" => "/1",
245 "text" => "blah, blah, ....",
246 "image" => ["a.jpg", "b.jpg"] },
247 {"serial" => 2,
248 "author" => "hanako",
249 "date" => "2015-04-29",
250 "parent" => "/",
251 "path" => "/2",
252 "text" => "blah, blah, ....",
253 "image" => [] }]},
254 {"title" => "buha", ...} ]
257 user:=
258 ユーザ名(英数字):name:p:text:length="20" maxlength="40"
259 パスワード:pswd:s:password:length="20" maxlength="40"
260 説明(日本語OK):gecos:s:text:length="20" maxlength="40"
261 セッションキー:skey:s:session
262 メイルアドレス:email:m:text:length="20" maxlength="40"
263 住所:address:m:textarea:maxlength="400"
264 プロフィール画像:profimg:m:image:maxlength="400K"
265 履歴書:profpdf:m:document:maxlength="4M"
267 変換表
268 /user/email=m
270 blog:=
271 シリアル:id:p:serial
272 タイトル:title:s:text:
273 所有者:owner:s:owner:
274 時刻:ctime:s:stamp:
275 リード文:heading:s:textarea:
276 リプライ:reply:m:*article:
278 article:=
279 シリアル:id:p:serial
280 筆者:author:s:owner
281 時刻:ctime:s:stamp:
282 参照元:parent:s:parent:
283 パス:path:s:path:
284 本文:text:s:textarea:
285 画像:image:m:image:
287 履歴書:profpdf:m:document:maxlength="4M"
290 EOF
292 sq() {
293 # ./args.rb -cmd ".timeout 3000" "$@"
294 sqlite3 -cmd 'PRAGMA foreign_keys=ON' -cmd ".timeout 3000" "$@"
295 }
296 dbsetup() {
297 [ -d $tmpdir ] || mkdir -m 1777 $tmpdir
298 [ -d $dbdir ] || mkdir -m 1775 $dbdir
299 sqi=$tmpdir/sqi.$$
300 sqo=$tmpdir/sqo.$$
301 mkfifo $sqi $sqo
302 #tail -f $sqi | sq $db & # "tail -f" is too heavy. DO NOT USE!!
303 sq $db < $sqi &
304 sq3pid="`jobs -p` $!"
305 exec 2>> $tmpdir/error.out
306 exec 3>> $tmpdir/debug.out
307 exec 5> $sqi # Turning $sqi access through fd5 for continuous open state
308 rm $sqi
309 }
310 cleanup() {
311 echo .quit >&5
312 kill $sq3pid
313 kill $sq3pid
314 rm -f $sqo $sqi
315 rm -rf $tmpfiles
316 }
317 query() {
318 echo ".once $sqo" >&5
319 if [ -z "$1" ]; then
320 cat
321 else
322 echo "$@"
323 fi >&5
324 cat $sqo
325 }
326 _m4() {
327 #_S4NAME_=f,f,f
328 m4 ${_S4NAME_:+"-D_S4NAME_=${_S4NAME_}"} "$@"
329 }
330 ismember() {
331 # $1=user, $2=group
332 err ismem: "select user from grp_mem where gname=$(sqlquote $2) and user='$1';"
333 test -n "`query \"select user from grp_mem where gname=$(sqlquote $2) and user='$1';\"`"
334 }
335 isuser() { # Check if $1 is a valid user
336 test -n "`query \"select name from user where name='$1';\"`"
337 }
338 isgroup() { # Check if $1 is a valid group
339 err isgroup: "select gname from grp where gname=$(sqlquote $1);"
340 test -n "`query \"select gname from grp where gname=$(sqlquote $1);\"`"
341 }
342 isgrpowner() (
343 # $1=user, $2=group
344 gn=`sqlquote "$2"`
345 sql="select user from grp_adm where gname=$gn and user='$1';"
346 err isgrpowner: $sql
347 test -n "`query $sql`"
348 )
349 getgroupadminmails() {
350 # $1=group
351 for i in $(getgroupadmins $1); do
352 email4group "$1" "$i" ;
353 done
354 }
355 getgroupadmins() { # $1=group
356 # This function is called in a backquote, so needn't to be subshellized
357 qgrp=`sqlquote "$1"`
358 query "select user from grp_adm where gname=$qgrp;"
359 }
360 getgroupattr() { # $1=group $2=attr
361 # This function is called in a backquote, so needn't to be subshellized
362 getvalbyid grp $2 \
363 $(query "select rowid from grp where gname=`sqlquote $1`;")
364 }
365 getgroupbyid() {
366 # $1=id|gname
367 sql="select coalesce((select gname from grp where gname=$(sqlquote $1)),
368 (select gname from grp where rowid=$(sqlquote $1)));"
369 # err ggbyid: `echo $sql`
370 query $sql
371 }
372 isfilereadable() { # $1=user $2=tbl $3=rowid
373 # Return true if user($1) can read attachment files in tbl($2):rowid($3)
374 [ -z "$1" -o -z "$2" -o -z "$3" ] && return 1 # invalid argument
376 # Return true when anonymous mode
377 [ "$anonymousmode" ] && return 0
378 # case `getvalbyid blog mode $2` in
379 # normal|*open*|"") return 0 ;;
380 # *closed*)
381 # owner=`getvalbyid blog owner $2`
382 # if isgrp $owner; then
383 # isgrpowner $1 $owner && return 0 || return 1
384 # elif isuser $owner; then
385 # [ x"$1" = x"$owner" ] && return 0 || return 1
386 # fi
387 # esac
388 # ↑ 要はこういう処理を↓で一気にやっている
389 sql="with getblog as (\
390 select key,val from blog_s where id=(\
391 select blogid from article where id in\
392 (select id from $2 where rowid=$3))),\
393 getowner as (select val from getblog where key='owner'),\
394 getmode as (select val from getblog where key='mode')\
395 select case\
396 when (select author from article where\
397 id=(select id from $2 where rowid=$3))='$1' \
398 then 'author'\
399 when (select val from getmode) in ('report-open', 'normal')\
400 then 'open'\
401 when (select val from getmode) is null \
402 then 'open'
403 when (select val from getowner) in (select gname from grp)\
404 then (select user from grp_adm where \
405 gname=(select val from getowner) and \
406 user='$1')\
407 when (select author from article where\
408 id=(select id from $2 where rowid=$3))='$1'
409 then 'user+author'
410 else '' end;"
411 err isfilereadable: sql="`echo $sql`"
412 # caseのネストで内側のcaseがスカラーtrueを返しても外側はtrue扱いにならない
413 result=`query "$sql"`
414 [ -n "$result" ] && return 0
415 return 2
416 }
417 linkhome() {
418 # $1=UserOrGroup
419 echo -n '<a href="?'
420 if isuser $1; then
421 err "select 'home+'||rowid from user where name='$1';"
422 query "select 'home+'||rowid from user where name='$1';"
423 else
424 echo -n "grp+$1"
425 fi
426 echo "\">`gecos $1`</a>"
427 }
428 acclog() (
429 # $1=table, $2=rowid
430 n=${2%%[!0-9]*} # Remove non-digit chars from $2(should be rowid)
431 if [ -n "$n" ]; then
432 now=`date +"%F %T"`
433 #query "replace into acclog values('$user', '$1', '$n', '$now');"
434 query "replace into acclog values('$user', '$1', $n, '$now');"
435 fi
436 )
437 gecos() (
438 u=`sqlquote ${1:-$user}`
439 #gecos=`query "select val from user_s where name='$u' and key='gecos';"`
440 # sql="select case when (select name from user where name=$u) is not null
441 # then coalesce(
442 # (select val from user_s where name=$u and key='gecos'),
443 # $u)
444 # when (select gname from grp where gname=$u) is not null
445 # then coalesce(
446 # (select val from grp_s where gname=$u and key='gecos'),
447 # $u)
448 # else $u
449 # end;"
450 #query "$sql"
451 query "select gecos from gecoses where name=$u;"
452 )
453 setpar() {
454 query "replace into par values('$session', '$1', '$2', \"$3\");"
455 }
456 replpar() {
457 query "update par set val=\"$3\" where sessid='$session' and var='$1' and type='$2';"
458 }
459 getpar() {
460 err getpar: "select val from par where var='$1' and sessid='$session' $2;"
461 val=`query "select val from par where var='$1' and sessid='$session' $2;"`
462 err getpar/val1: "val=[$val]"
463 if [ -z "$val" ]; then
464 val=`query "select val from cookie where var='$1' and sessid='$session' $2;"`
465 fi
466 err getpar/val2: "val=[$val]"
467 case "$var" in
468 owner)
469 if [ x"$user" = x"$val" ]; then
470 echo $user; return
471 elif ismember $user $val; then
472 echo $val; return
473 fi ;;
474 esac
475 err getpar/ret: "val=[$val]"
476 echo "$val"
477 }
479 getpartype() {
480 query "select type from par where var='$1' and sessid='$session' $2;"
481 }
482 getparcount() {
483 query "select count(*) from par where var='$1' and sessid='$session' $2;"
484 }
485 getparfilename() {
486 # null if type of $1 is not file
487 (f=`query "select val from par where var='$1' and sessid='$session' and type='file' $2;"`
488 [ -n "$f" ] && echo $f)
489 }
490 sqlquote() {
491 (v="$1"
492 case "$v" in
493 "") return ;; # null
494 "X'"*) # quoted hex string
495 echo $1 ;;
496 *\"*) # string including dbl-quote"
497 v=`echo "$v"|sed -e 's/\"/\"\"/g'`
498 echo "\"$v\""
499 return ;;
500 *.*.*|*-*-*|*[Ee]*[Ee]*|[Ee]*|*[\ -,:-df-~]*) # string
501 echo "\"$v\""
502 return ;;
503 *)
504 if expr "$v" : '[-0-9.Ee][-0-9.Ee]*$' >/dev/null 2>&1; then
505 echo $v # MAYBE numeric, maybe...
506 else
507 echo "\"$v\""
508 fi ;;
509 esac)
510 }
511 mktempd() {
512 TMPDIR=$tmpd mktemp -d -t $session
513 }
514 getcachedir() { # $1=maintable
515 if [ -n "$imgcached" ]; then
516 echo $tmpdir/$imgcached/$(echo ${1:-hoge}|md5)/$thumbxy
517 else
518 echo $tmpd/$thumbxy
519 fi
520 }
521 getval() {
522 # $1=table $2=col $3(optional)=condition
523 case `gettbl_coltype "/$1/$2"` in
524 user|author) # author added 2015-06-18 for article(author)
525 echo "$user" ;;
526 stamp|datetime)
527 date "+%F %T" ;;
528 serial)
529 (s=`getpar $2`
530 if [ -n "$s" ]; then echo $s; else echo "`date +%s`x$$"; fi) ;;
531 *)
532 getpar "$2" "$3";;
533 esac
534 }
536 getvalquote() {
537 # $1=table $2=col $3(optional)=condition
538 (v=`getval "$@"`
539 case "$v" in
540 "") echo NULL ;;
541 *) sqlquote "$v" ;;
542 esac)
543 }
544 getparquote() {
545 sqlquote `getpar $1`
546 }
547 getbinbyid() {
548 # $1=tbl $2=col $3=rowid $4=tmpdirForBinary
550 }
551 getvalbyid() {
552 # $1=tbl $2=col $3=rowid $4=tmpdirForBinary
553 # If two or more values found, save them to $tmpd/${column}.$N and
554 # store the number of files into $tmpd/${column}.count and
555 # their each rowid stored into $tmpd/${column}.$N.rowid.
556 ## err gtb-$1=`gettblcols $1`, tbl=$1, col=$2, '$3'=$3
558 (for c in `gettblcols $1`; do
559 if [ x"$2" = x"$c" ]; then
560 err "select $2 from $1 where rowid=$3"
561 ###sq $db "select $2 from $1 where rowid=$3"
562 query "select $2 from $1 where rowid=$3;"
563 return
564 fi
565 done
566 pk=`gettblpkey $1`
567 key=`query "select $pk from $1 where rowid=$3;"`
568 getkey="(select $pk from $1 where rowid=$3)"
569 ### err "select $pk from $1 where rowid=$3" - key=$key '$4(tmp)'=$4
570 for kt in s m; do
571 td=${4:-$tmpd}
572 [ -d $td ] || mkdir -p $td
573 t=${1}_$kt
574 for c in `gettbl_${kt}_cols $1`; do
575 vcount=1 # count(val)
576 if [ x"$2" = x"$c" ]; then
577 #### cond="$t where $pk=\"$key\" and key=\"$c\"" #2015-07-22
578 cond="$t where $pk=$getkey and key=\"$c\""
579 val=`query "select val from $cond limit 1;"`
580 type=`query "select type from $cond limit 1;"`
581 if [ $kt = m ]; then
582 ###vcount=`sq $db "select count(val) from $cond"`
583 # Reset val to store filenames if type is string
584 val=`query "select val from $cond and type like 'file:%' order by rowid;"`
585 err gvb1-sql: "select count(val) from $cond;"
586 vcount=`query "select count(val) from $cond;"`
587 echo $vcount > $td/$c.count
588 i=0
589 err gvbid: i=$i vcount=$vcount
590 while [ $i -lt $vcount ]; do
591 slice="order by rowid limit 1 offset $i"
592 i=$((i+1))
593 fn=$c.$i
594 err td=$td, fn=$fn, type=$type, val="[$val]"
595 case $type in
596 file:*)
597 #file=$td/$val
598 file=$td/`query "select val from $cond $slice;"`
599 # FOR SPEED: Skip file generation if imgcache exists
600 [ -s $file -a -s $td/$fn.rowid -a -s $file.rowid ] && continue
601 # err gvbid-get="select quote(bin) from $cond $slice;"
602 sq $db<<EOF | unhexize > $file
603 .output $td/$fn.rowid
604 select rowid from $cond $slice;
605 .output $td/$fn
606 select val from $cond $slice;
607 .output $file.content-type
608 select substr(type, 6) from $cond $slice;
609 .output stdout
610 select quote(bin) from $cond $slice;
611 EOF
612 ## err gvbid-get2: "`ls -lF $file`"
613 ## err i=$i - file=$file rowid=`cat $td/$fn.rowid`
614 cp $td/$fn.rowid $file.rowid 2>&3 # for convenience
615 cp $file $file.orig 2>&3
616 ls -lh $file | awk '{print $5"B"}'|sed 's/BB/B/' > $file.size
617 case $type in
618 *:[Ii]mage*) mogrify -geometry $thumbxy $file ;;
619 ### ここのアイコンを増やしたい
620 *|*:[Aa]pplication*)
621 convert -geometry $thumbxy $imgdir/file-icon.png \
622 png:- > $file
623 ;;
624 esac
625 ;;
626 *)
627 sq $db<<EOF
628 .output $td/$fn.rowid
629 select rowid from $cond $slice;
630 .output $td/$fn
631 select val from $cond $slice;
632 EOF
633 val=$val${val:+$nl}"`echo $fn`" # should be delimited by newline
634 ;;
635 esac
636 done
637 else
638 rm -f $td/$c.count
639 case $type in
640 file:*)
641 echo "$val" \
642 | while read fn; do
643 file=$td/$fn
644 if [ ! -s $file ]; then
645 ## sq $db "select quote(bin) from $cond and val=\"$fn\"" \
646 query "select quote(bin) from $cond and val=\"$fn\";" \
647 | unhexize > $file
648 echo ${type#file:} > $file.content-type
649 # err TTTTTTTTTTTTTTTT: $type
650 case $type in
651 *:[Ii]mage*) mogrify -geometry $thumbxy $file ;;
652 *:[Aa]pplication*)
653 convert -geometry $thumbxy $imgdir/file-icon.png \
654 png:- > $file ;;
655 esac
656 fi
657 done
658 ;;
659 esac
660 fi
661 echo "$val" # Keep newlines by ""
662 return
663 fi
664 done
665 done)
666 }
667 getvalbypkey() (
668 # $1=tbl $2=col $3=pkey $4=tmpdirForBinary
669 pk=`gettblpkey $1`
670 rowid=`query "select rowid from $1 where $pk='$3';"`
671 getvalbyid "$1" "$2" $rowid $4
672 )
673 getvalbycond() {
674 # $1=tbl $2=col $3=SQL-Condition
675 ###rowid=`sq $db "select rowid from $1 where $3"`
676 rowid=`query "select rowid from $1 where $3;"`
677 if [ -n "$rowid" ]; then
678 getvalbyid "$1" "$2" $rowid "$4"
679 fi
680 }
681 getpwfield() {
682 # getpwfield user column
683 # val=`sqlite3 $db "select $2 from passwd where name='$1' $3"`
684 val=`getvalbycond user $2 "name='$1'"`
685 if [ -n "$val" ]; then
686 echo "$val"
687 return 0
688 else
689 return 1
690 fi
691 }
692 encode() {
693 if [ -z "$sha1" ]; then
694 if type sha1 >/dev/null 2>&1; then
695 sha1=sha1
696 elif type sha1sum >/dev/null 2>&1; then
697 sha1=sha1sum
698 elif type gsha1sum >/dev/null 2>&1; then
699 sha1=gsha1sum
700 fi
701 fi
702 $sha1 "$@" | cut -d' ' -f1
703 }
704 mycrypt() (
705 key=$1 salt=$2
706 err \$2=$2
707 case $2 in
708 '$'*'$'*) salt=${salt#\$4\$}
709 salt=${salt%\$*} ;;
710 esac
711 echo -n '$4$'"$salt"'$'
712 echo "$salt$key" | encode || exit 1 # Abort if fail to call encode
713 )
714 hexize() {
715 if [ -z "$hexize" ]; then
716 if type xxd >/dev/null 2>&1; then
717 hexize="xxd -p"
718 else
719 hexize_hd() {
720 hexdump -ve '1/1 "%.2x"'
721 }
722 hexize="hexize_hd"
723 fi
724 fi
725 cat $1 | $hexize | tr -d '\n'
726 }
727 unhexize() {
728 if [ -z "$unhex" ]; then
729 if type xxd >/dev/null 2>&1; then
730 unhex="xxd -p -r"
731 elif type perl >/dev/null 2>&1; then
732 cat >$tmpd/unhex.pl<<EOF
733 s/([0-9a-f]{2})/print chr hex \$1/gie
734 EOF
735 # Perl refuses -e in setuid circumstances, which can be absurdly
736 # avoided by creating scripts in a file where its parent directory is
737 # world writable...:)
738 unhex="perl -n $tmpd/unhex.pl"
739 fi
740 fi
741 cat $1 | $unhex
742 # cat $1 | tee /tmp/uh.in| $unhex | tee /tmp/uh.out
743 }
744 percenthex() {
745 hexize $1 | sed 's/\(..\)/%\1/g'
746 }
747 htmlescape() {
748 sed -e 's/\&/\&amp;/g' -e 's/"/\&quot;/g' -e "s/'/\&apos;/g" \
749 -e "s/</\&lt;/g; s/>/\&gt;/g"
750 }
751 enascii() {
752 if [ -z "$enascii" ]; then
753 if type kakasi >/dev/null 2>&1; then
754 enascii="kakasi -Ha -Ka -Ja -Ea -ka"
755 else
756 enascii_now=`date +%FT%T`
757 enascii_sed() {
758 nkf -Z0Z1Z2 \
759 | sed -e "s/^/$enascii_now/" -e "s|[^-0-9.A-z/,()_=]|x|g"
760 }
761 enascii="enascii_sed"
762 fi
763 fi
764 cat "$@" | $enascii
765 }
766 size_h() {
767 i="$1" oi=$1
768 set -- B B KB MB GB TB
769 while [ $((i)) -gt 9 -a -n "$1" ]; do # -gt 9 means $oi > 1024
770 oi=$i
771 i=$((i/1024))
772 shift
773 done
774 echo ${oi}$1
775 }
776 gettblconf() {
777 if [ -z "$tconfs" ]; then
778 ## tconfs=`sq $db \
779 tconfs=`query \
780 "select tbl||'/'||col||'='||keytype||'/'||objtype from $conftbl;"`
781 fi
782 # /tb1/col1=p/text /tb1/col2=s/text /tb1/col3=m/image /tb2/col1=p/text ...
783 }
784 gettblkeys() {
785 # $1=tbl
786 gettblconf
787 echo "$tconfs" | fgrep "/$1/" | \
788 (type="" keys="" fks="" cols="" scols="" mcols="" hcols=""
789 while IFS='=' read tc conf; do # tc=/tb1/col1 conf=s/text
790 col=${tc##*/} type=${conf%%/*}
791 case $type in
792 *p*)
793 cols=$cols"${cols:+:}$col"
794 keys=$keys"${keys:+:}$col" ;;
795 *f*) cols=$cols"${cols:+:}$col"
796 fks=$fks"${fks:+:}$col" ;;
797 *m*) mcols=$mcols"${mcols:+:}$col" ;;
798 *s*) scols=$scols"${scols:+:}$col" ;;
799 esac
800 case $type in
801 *h*) hcols=$hcols"${hcols:+:}$col" ;;
802 esac
803 done
804 echo "_keys=$keys _fks=$fks _cols=$cols _scols=$scols _mcols=$mcols _hcols=$hcols")
805 }
806 gettblpkey() {
807 # $1=tbl
808 gettblkeys $1 | cut -d ' ' -f 1 | sed -e 's/.*=//' -e 's/:/ /g'
809 }
810 gettblfkey() {
811 (x=`gettblkeys $1`
812 x=${x#*_fks=} # cut before "_fks=" including
813 echo ${x%% *} | tr ':' ' ')
814 }
815 gettblcols() {
816 (x=`gettblkeys $1`
817 x=${x#*_cols=} # cut before "_cols=" including
818 echo ${x%% *} | tr ':' ' ')
819 }
820 gettbl_s_cols() {
821 (x=`gettblkeys $1`
822 x=${x#*_scols=} # cut before "_scols=" including
823 echo ${x%% *} | tr ':' ' ')
824 }
825 gettbl_m_cols() {
826 (x=`gettblkeys $1`
827 x=${x#*_mcols=} # cut before "_mcols=" including
828 echo ${x%% *} | tr ':' ' ')
829 }
830 gettbl_h_cols() {
831 (x=`gettblkeys $1`
832 x=${x#*_hcols=} # cut before "_hcols=" including
833 echo ${x%% *} | tr ':' ' ')
834 }
835 gettbl_coltype() (
836 gettblconf
837 x=`echo "$tconfs"|fgrep $1=`
838 x=${x#*=} # cut before =
839 echo ${x#*/} # cut before p/ including
840 )
841 is_hidden() {
842 # $1=Tbl $2=col
843 gettblconf
844 x=`echo "$tconfs"|fgrep /$1/$2=`
845 x=${x#*=} # cut before =
846 x=${x%%/*} # cut after /
847 case $x in
848 *h*) return 0 ;;
849 *) return 1 ;;
850 esac
851 }
853 dbsetbyid() {
854 # $1=tbl $2=id $3=col $4=val/filename - &optional - $5=content-type
855 (t0=$1 t=$1 p=$2 c=$3
856 tsc=$t/$c val=$4
857 unset primary update
858 gettblconf
859 #err tsc=$tsc, tconfs="$tconfs"
860 conf=`echo "$tconfs"|fgrep "$tsc"=`
861 #err conf=$conf
862 case ${conf#*=} in
863 p*) primary=1 ;;
864 f*) update=1 ;;
865 u*) ;;
866 m*) t=${t}_m;;
867 s*) t=${t}_s;;
868 esac
869 #err t=$t
870 type=text fn=""
871 case $conf in
872 */password)
873 type=encoded ### val=`echo $val|encode`
874 ;;
875 */image*|*/document*)
876 type=`file --mime-type $val`
877 bin="X'`hexize $val`'"
878 ;;
879 esac
880 pkey=`echo "$tconfs"|grep "${t0}/.*=p"|sed 1q`
881 pkey=${pkey#/*/} # cut $tbl/
882 pkey=${pkey%=p/*} # cut =p/... -> primary key
883 if [ "$primary" ]; then
884 nulls=`echo "$tconfs"|grep "$t/.*=[fu]/"|sed 's/^.*/, NULL/'|tr -d '\n'`
885 ###sq $db "replace into $t values(\"$val\"$nulls)"
886 query "replace into $t values(\"$val\"$nulls);"
887 elif [ "$update" ]; then
888 ###sq $db "update $1 set $c=\"$val\" where $pkey=\"$p\""
889 query "update $1 set $c=\"$val\" where $pkey=\"$p\";"
890 else
891 #err "replace into $t values(\"$p\", \"$c\", \"$type\", \"$val\", \"$bin\")"
892 ###sq $db "replace into $t values(\"$p\", \"$c\", \"$type\", \"$val\", \"$bin\")"
893 query "replace into $t values(\"$p\", \"$c\", \"$type\", \"$val\", \"$bin\");"
894 fi
895 )
896 }
897 expire() (
898 at="${1:-$timeout}"
899 FMT="${2:-%F %T}"
900 TZ=GMT gdate -d "$at" +"$FMT"
901 )
902 addsession() {
903 # expireをセット
904 # loginの先にどの画面に行くかの状態遷移表書式を決める
905 expire=`expire ${2:-"+1min"}`
906 err addsession "$1" exp=$expire
907 sq $db "replace into session values('$1', '$expire')"
908 # Remove old session parameters
909 now=`expire now`
910 sq $db "delete from session where expire < '$now'"
911 }
912 gencookie() (
913 for kv; do
914 expire="`expire '' '%a, %d-%b-%Y %H:%M:%S GMT'`"
915 echo "Set-Cookie: $kv; expires=$expire"
916 done
917 )
918 contenttype() {
919 echo "Content-type: ${1:-text/html; charset=utf-8}"
920 contenttype() {} # Only need to work once
921 }
922 putheader() {
924 }
925 putfooter() {
926 _m4 -D_TITLE_="${TITLE:-$myname}" $layout/footer.m4.html
927 }
928 getcookie() (
929 for kv in `echo $HTTP_COOKIE|sed 's/[;, ]/ /g'`; do
930 k="${kv%%=*}"
931 v="`echo ${kv#*=}|nkf -Ww -mQ|sed -e 's/\"/\"\"/g'`"
932 query "replace into cookie values('$session', '$k', 'string', \"$v\");"
933 done
934 )
935 genrandom() {
936 # $1=columns (default: 10)
937 dd if=/dev/urandom count=1 2>/dev/null|nkf -MB|fold -w${1:-10}|sed -n 10p
938 }
939 smail() {
940 # smail rcpts subj (file)
941 # $SMAIL_TO <- Recipient value of To: header
942 rcpt=`echo $1` # strip newlines
943 subj=`echo $2|nkf -jM|tr -d '\n'`
944 (_m4 -D_RCPT_="${SMAIL_TO:-$rcpt}" -D_SUBJ_="\`$subj'" -D_FROM_=$admin $msgdir/mail-header.m4
945 cat $3 | nkf -jd ) | sendmail -f $admin $rcpt
946 }
947 setviastring() {
948 table=$1
949 oifs="$IFS"
950 IFS="&"
951 for us in $2; do
952 k=${us%%=*}
953 v="`echo ${us#*=}|tr '%+' '= '|nkf -Ww -mQ|sed -e 's/\"/\"\"/g'`"
954 sq $db "replace into $table values('$session', '$k', 'string', \"$v\")";
955 #echo $k=$v
956 done
957 IFS="$oifs"
958 }
959 checkdomain() (
960 # Check the validity of domain by referring DNS
961 item=$1
962 err checkdomain $1
963 host ${item#*@} 1>&3 2>&3
964 host ${item#*@} >/dev/null 2>&1
965 )
966 pwcheck() {
967 # $1=passwd
968 dbpswd=`getpwfield $user pswd`
969 encpswd=`mycrypt "$1" "$dbpswd"`
970 err user=$user, pswd=$1, db=$dbpswd, enc=$encpswd
971 [ x"$dbpswd" = x"$encpswd" ]
972 }
973 mypwhash() {
974 mycrypt `cat` `genrandom 5`
975 }
976 wasureta() {
977 user=$1
978 if ! checkdomain $user; then
979 contenttype; echo
980 _m4 -D_TITLE_='Invalid email' $layout/title-only.html
981 echo "ユーザ名($user)には正しいメイルアドレスが必要です。" | html p
982 putfooter
983 exit 0
984 fi
985 newpswd=`genrandom` # newsalt=`genrandom 5`
986 #encpswd=`mycrypt "$newpswd" "$newsalt"`
987 encpswd=`echo $newpswd|mypwhash`
988 dbsetbyid user $user pswd "$encpswd"
989 # Avoid $user substitution with m4, because $url comes from user input.
990 _m4 -D_PSWD_="$newpswd" -D_URL_="$url" -D_ADMIN_="$admin" \
991 $msgdir/mail-newaccount.m4 \
992 | sed "s/_USER_/$user/g" \
993 | smail $user "New Account"
994 }
995 checkauth() {
996 user=`getpar user`
997 skc=`getpar skey` # from cookie
998 [ -z "$user" ] && return 3
999 skey="`getpwfield $user skey`"
1000 err user=$user skey=$skey
1001 if [ -n "$skey" ]; then
1002 if [ x"$skey" = x"$skc" ]; then
1003 return 0
1004 fi
1005 fi
1006 pswd=`getpar pswd`
1007 if [ x"$pswd" = x"wasureta" ]; then
1008 wasureta $user
1009 return 1 # wasureta error
1010 fi
1011 # dbpswd="`sq $db \"select pswd from passwd where name='$user'\"`"
1012 # putheader; echo; echo user=$user, db=$dbpswd, enc=$encpswd
1013 if pwcheck "$pswd"; then
1014 newsession=`genrandom 50`
1015 dbsetbyid user $user skey "$newsession"
1016 gencookie "user=$user" "skey=$newsession"
1017 return 0
1018 fi
1019 return 2 # Password mismatch
1021 showlogin() {
1022 args=`echo $myargs|tr ' ' '+'`
1023 _m4 -D_SYSNAME_="Welcome" -D_MYNAME_="$myname${args+?}$args" \
1024 $layout/login.m4.html
1025 exit 0
1027 dologin() {
1028 checkauth
1029 st=$?
1030 if [ $st != 0 ]; then
1031 contenttype; echo
1032 _m4 -D_USER_="$user" -D_URL_="$url" -D_ADMIN_="$admin" \
1033 $msgdir/login-fail-$st.m4.html
1034 showlogin # and EXIT
1035 fi
1038 # Do instant jobs here
1039 dbsetup
1040 trap cleanup INT HUP EXIT TERM PIPE
1041 # trap cleanup INT HUP
1043 err() {
1044 echo "$@" 1>&3
1047 cgiinit() {
1048 session=`date +%F-$$`
1049 tmpf=tmp/stream
1050 tmpd=`tmpd=$tmpdir mktempd`
1051 tmpfiles=$tmpfiles" $tmpd"
1052 addsession $session
1053 getcookie
1054 case "$REQUEST_METHOD" in
1055 get|GET) s="$QUERY_STRING" ;;
1056 post|POST) ## dd count=$CONTENT_LENGTH bs=1 of=$tmpf 2>/dev/null #slow
1057 ## dd bs=$CONTENT_LENGTH count=1 of=$tmpf # NOT working
1058 # cat > $tmpf # too much?
1059 head -c $CONTENT_LENGTH > $tmpf # safe?
1060 (echo CL=$CONTENT_LENGTH; ls -lF $tmpf) 1>&3
1061 s="`cat tmp/stream`"
1062 tmpfiles=$tmpfiles"${tmpfiles+ }$tmpf"
1063 ;;
1064 esac
1065 case "$CONTENT_TYPE" in
1066 *boundary*)
1067 bndry=${CONTENT_TYPE#*boundary=}
1068 #for us in `LC_CTYPE=C ./mpsplit.rb "$bndry" $tmpd < $tmpf`
1069 for us in `LC_CTYPE=C ./mpsplit.pl "$bndry" $tmpd < $tmpf`
1070 do
1071 k=${us%%\=*}
1072 #echo u=$us
1073 #v="`echo ${us#*=}|nkf -Ww -mQ|sed -e 's/\"/\"\"/g'`"
1074 v="`echo ${us#*=}|unhexize|sed -e 's/\"/\"\"/g'`"
1075 # err k=$k v=$v
1076 case "$k" in
1077 *:filename)
1078 type='file'; k=${k%:filename}
1079 (echo k=$k; ls -lF $tmpd/$v; file --mime-type $tmpd/$v) 1>&3
1080 case `file --mime-type $tmpd/$v|cut -d' ' -f2` in
1081 [Ii]mage/x-xcf)
1082 bzip2 $tmpd/$v
1083 v=${v}.bz2
1084 ;;
1085 [Ii]mage/x-*|*/vnd.*) ;;
1086 [Ii]mage/*)
1087 mogrify -resize $maximagexy'>' $tmpd/$v
1088 ;;
1089 esac
1090 ;;
1091 *)
1092 type='string'
1093 ;;
1094 esac
1095 #sq $db "replace into par values('$session', '$k', '$type', \"$v\")"
1096 setpar "$k" "$type" "$v"
1097 done
1098 ;;
1099 *)
1100 setviastring par "$s"
1101 ;;
1102 esac
1104 email4group() {
1105 # Get for-$1=group email address(es) for $2...=users
1106 qgrp=`sqlquote "$1"`; shift
1107 users=`for i; do sqlquote "$i"; done`
1108 users=`echo $users|tr ' ' ','`
1109 sql="select coalesce(s.val, g.user) from grp_mem g
1110 left join grp_mem_s s on g.gname=s.gname and g.user=s.user
1111 and s.key='email'
1112 where g.gname=$qgrp and g.user in ($users);"
1113 err `echo $sql`
1114 query "$sql"
1116 email4groupbyuid() {
1117 # Get for-$1=group email address(es) for $2...=user-ids
1118 qgrp=`sqlquote "$1"`; shift
1119 err em4gbid-IN: "\$1=$1 qgrp=$qgrp"
1120 uids=`echo "$@"`
1121 uids=`echo $uids|tr ' ' ','`
1122 sql="select coalesce(s.val, g.user) from grp_mem g
1123 left join grp_mem_s s on g.gname=s.gname and g.user=s.user
1124 and s.key='email'
1125 where g.gname=$qgrp and g.user in
1126 (select name from user where rowid in ($uids));"
1127 err email4gByid `echo $sql`
1128 query "$sql"
1130 collectemail() (
1131 # Collect email addresses for group $1
1132 for e; do
1133 if isuser "$e"; then
1134 em=`query "select val from user_m where name='$e' and key='email';"`
1135 [ -n "$em" ] && echo "$em" || echo "$e"
1136 else
1137 # sql="with recursive allmem as
1138 # (select gname,val from grp_m where gname='$1'
1139 # union all select grp_m.gname,grp_m.val from grp_m,allmem
1140 # where allmem.val=grp_m.gname)
1141 # select coalesce(b.val,a.val)
1142 # from allmem a left join grp_mem_s b
1143 # on a.gname=b.gname and a.val=b.user and b.key='email'
1144 # where a.val in (select name from user) limit 10000;"
1145 # tmp tmp tmp tmp tmp tmp tmp
1146 qgrp=`sqlquote "$e"`
1147 sql="select coalesce(s.val,um.val,g.user) from
1148 grp_mem g left join grp_mem_s s
1149 on g.gname=s.gname and g.user=s.user and s.key='email'
1150 left join user_m um on g.user=um.name and um.key='email'
1151 where g.gname=$qgrp;"
1152 err CollectEmail: `echo "$sql"`
1153 query "$sql"
1154 fi
1155 done
1157 sendinvitation() (
1158 # $1=email
1159 iss="invite-`date +%s`-$user"
1160 addsession $iss +${memoplimitdays}days # 1 week due date
1161 query "replace into par values('$iss', 'invite', 'string', \"$1\");"
1162 gecos=`gecos`
1163 name=$user"${gecos:+($gecos)}"
1164 regist="$urlbase?reg+$iss"
1165 _m4 -D_URL_="$urlbase" \
1166 -D_USER_="$name" \
1167 -D_EMAIL_="$1" \
1168 -D_REGIST_="$regist" \
1169 -D_ADMIN_="$admin" \
1170 $msgdir/mail-invite.m4 \
1171 | smail $1 "BBSへの御招待"
1172 return 0
1174 emaildomaincheck() {
1175 case "$1" in
1176 *@*@*) echo "無効なアドレスです"; return 1 ;;
1177 *@*)
1178 local=${1%@*} domain=${1#*@}
1179 if ! host $domain >/dev/null 2>&1; then
1180 echo "ドメイン($domain)が見付かりません。"
1181 return 2
1182 fi
1183 return 0
1184 ;;
1185 *) echo "正しいメイルアドレスをいれてください"; return 3 ;;
1186 esac
1188 invite() {
1189 email=`getpar email`
1190 case $email in
1191 *@*@*) repo="無効なアドレスです" ;;
1192 *@*)
1193 local=${email%@*} domain=${email#*@}
1194 if ! repo=`emaildomaincheck $email`; then
1195 repo="招待アドレスのエラー: $repo"
1196 elif [ -n "`query \"select * from user where name='$email';\"`" ]; then
1197 repo="$email さんは既に加入しています。"
1198 elif sendinvitation $email; then
1199 repo="アドレス($email)宛に案内を送信しました。"
1200 fi ;;
1201 "") repo="招待したい人のメイルアドレスを入力してください。" ;;
1202 *) repo="無効なアドレスです" ;;
1203 esac
1204 addr=`query "select val from par where sessid like 'invite-%-$user';"`
1205 err addr=$addr
1206 if [ -n "$addr" ]; then
1207 susp="<h2>招待済みで加入待ちのアドレス</h2><pre>$addr</pre>"
1208 fi
1209 _m4 -D_TITLE_="招待" -D_REPORT_="\`$repo'" -D_ACTION_="?invite" \
1210 -D_BODYCLASS_="default" -D_SUSPENDED_="$susp" \
1211 $layout/html.m4.html $layout/invite.m4.html
1213 regist() {
1214 # $1=session-id-for-invitation
1215 _m4 -D_TITLE_="Invitation" $layout/html.m4.html
1216 if [ -z "$1" ]; then
1217 echo "bye bye" | html p
1218 reutrn
1219 fi
1220 email=`session=$1 getpar invite`
1221 if [ -z "$email" ];then
1222 cat<<EOF
1223 <p>無効な招待状チケットです。</p>
1224 <p>招待状の有効期限(1週間)が切れているか、チケット番号が異なっています。
1225 加入している人に、再度招待してもらいましょう。</p>
1226 EOF
1227 return
1228 fi
1229 echo "$email さんようこそ" | html h2
1230 query "replace into user values('$email');"
1231 # Fake login password to wasureta
1232 query "replace into par values('$session', 'pswd', 'string', 'wasureta'),
1233 ('$session', 'user', 'string', '$email');"
1234 wasureta $email
1235 echo "このアドレスに初期パスワードを送信しました。" |html p
1236 echo "新着メイルを確認してログインしてください。" |html p
1237 addsession $1 # for removal after 1 minute
1238 _m4 -D_SYSNAME_="Initial Login" -D_MYNAME_="$myname?userconf" \
1239 $layout/login.m4.html
1240 return
1242 newgrpchk() {
1243 # Check if $1 is existing and
1245 groupupdate() {
1246 gname=`getpar gname`
1247 qgname=`sqlquote $gname`
1248 err Enter:groupupdate
1249 if [ -n "$gname" ]; then
1250 # See ALSO same job in showgroup()
1251 newgname=`echo "$gname"|tr -d '\"'"'"`
1252 err newgname=$newgname
1253 if [ x"$newgname" != x"$gname" ]; then
1254 err NewGNAME: gname=$newgname
1255 gname=$newgname
1256 echo "使用禁止文字を除去し $gname としました。" | html p
1257 replpar gname string "$gname"
1258 fi
1259 # Name confliction check
1260 parow=`getpar rowid`
1261 err parow=$parow
1262 qgname=`sqlquote $gname` # Set again in case gname modified
1263 query "BEGIN EXCLUSIVE;"
1264 err "select count(gname) from grp where rowid != ${parow:-0} and gname = $qgname;"
1265 count=$(query "select count(gname) from grp where rowid != ${parow:-0} and gname = $qgname;")
1266 if [ $count -gt 0 ]; then
1267 echo "そのグループ名は既にあります。" | html p
1268 query "END;"
1269 return
1270 fi
1271 par2table $formdir/grp.def
1272 query "END TRANSACTION;"
1273 # Remove orphan
1274 : <<EOF
1275 select a.id,b.val from (select * from blog where id in
1276 (select id from blog_s where key='owner'
1277 and val not in (select name from user union select gname from grp)))
1278 a left join blog_s b on a.id=b.id and b.key='owner';
1279 EOF
1280 rm=`getpar rm` cfm=`getpar confirm`
1281 err groupupdate:::: after par2tbl rmcfm=$rm$cfm
1282 if [ x"$rm$cfm" = x"yesyes" ]; then
1283 if [ -z "`query \"select gname from grp where gname=$qgname;\"`" ]; then
1284 sql="delete from blog where id in
1285 (select id from blog_s where key='owner' and val=$qgname);"
1286 err rm-grp cleaning sql=`echo $sql`
1287 query "$sql";
1288 fi
1289 fi
1290 [ -z "$parow" ] && joingrp "$gname" "$user" yes "$user" as-admin
1291 fi
1292 sql="select rowid from grp where gname=$qgname;"
1293 grid=$(query $sql)
1294 err grpupdate:new-grid=$grid, sql=$sql
1295 grp $grid
1297 groupman() {
1298 note="<p>グループ名に使用できない文字は自動的に削除されます。</p>"
1300 GF_STAGE="grpconf"
1301 GF_STAGE=groupupdate
1302 DT_VIEW=grp dumptable html grp 'gname gecos:DESC mtime:TIME' 'order by b.TIME desc' \
1303 |_m4 -D_TITLE_="グループ作成" \
1304 -D_FORM_="$note`genform $formdir/grp.def`" \
1305 -D_DUMPTABLE_="syscmd(cat)" \
1306 $layout/html.m4.html $layout/form+dump.m4.html
1308 userconf() {
1309 [ -n "`getpar rowid`" ] && par2table $formdir/user.def
1310 _m4 -D_BODYCLASS_=userconf -D_TITLE_="ユーザ情報編集" $layout/html.m4.html
1311 GF_ACTION="?home" edittable "$formdir/user.def" "user" "$user"
1313 groupconf() {
1314 # $1=rowid in grp (2015-07-21 changed from gname)
1315 [ -n "`getpar rowid`" ] && par2table $formdir/grp.def
1316 _m4 -D_BODYCLASS_=groupconf -D_TITLE_="グループ情報編集" $layout/html.m4.html
1317 #rowid=`query "select rowid from grp where gname='$1';"`
1318 rowid=${1%%[!A-Z0-9a-z_]*}
1319 err gcon \$1=$1 rowid=$rowid
1320 # GF_ACTION="?grp+$1" edittable "$formdir/grp.def" "grp" "$rowid" #2015-0804
1321 GF_STAGE="groupupdate" edittable "$formdir/grp.def" "grp" "$rowid"
1323 mems() {
1324 _m4 -D_TITLE_="参加者一覧" -D_BODYCLASS_=listmember $layout/html.m4.html
1325 kwd=`getpar kwd`
1326 listmember $kwd
1328 grps() {
1329 _m4 -D_TITLE_="グループ一覧" -D_BODYCLASS_=listgroup $layout/html.m4.html
1330 kwd=`getpar kwd`
1331 listgroup $kwd \
1332 | _m4 -D_DUMPTABLE_="syscmd(cat)" \
1333 -D_TITLE_="グループ関連操作" \
1334 -D_FORM_="<a href=\"?groupman\">新規グループ作成</a>" \
1335 $layout/form+dump.m4.html
1337 grp() { # $1=group-rowid
1338 gpg=`getpar grp`
1339 grid=${1:-$gpg}
1340 grp=`getgroupbyid "$grid"`
1341 err grp: getpar-grp"(gpg)=[$grp]"
1342 ## . ./s4-blog.sh
1343 jg=`getpar joingrp`
1344 if [ -n "$jg" ]; then
1345 err jg=$jg, grp=$grp
1346 [ -n "$jg" -a -n "$grp" ] &&
1347 joingrp "$grp" "$user" "$jg" "`getpar email`"
1348 fi
1349 echo "グループ $grp"|_m4 -D_TITLE_="syscmd(\`cat')" $layout/html.m4.html
1350 showgroup "$grid"
1352 listnewblogsql() { # $1=user
1353 cat<<EOF
1354 with article_ctime as (
1355 select id,blogid,author,val ctime
1356 from article join article_s s using(id)
1357 where s.key='ctime'
1358 ), blog_title_owner as (
1359 select blog.rowid, id,
1360 max(case key when 'title' then val end) title,
1361 max(case key when 'owner' then val end) owner
1362 from blog, blog_s using(id) group by id
1363 ), acclog_user as (
1364 select * from acclog where user='$user' and tbl='blog'
1365 ), blogall as (
1366 select b.id, b.rowid, b.owner, b.title, ctime, ac.author
1367 from blog_title_owner b, article_ctime ac
1368 on b.id=ac.blogid
1370 select rowid ID,
1371 sum(case when l.time is null then 1
1372 when l.time < ctime then 1
1373 else 0
1374 end) "新着",
1375 count(id) "総数", ctime, title,
1376 /* owner gecos */
1377 (select gecos from gecoses where name=bl.owner) gecos
1378 from blogall bl left join acclog_user l
1379 on bl.rowid=round(l.tblrowid)
1380 where case when (select name from user where owner=name) is not null
1381 then 1 /* user's diary is always readable */
1382 when (select val from grp_s
1383 where gname=owner and key='regmode') = 'moderated'
1384 and -- moderated group and nonmember can't read, not counting
1385 (select user from grp_mem
1386 where gname=owner and user='$user') is null
1387 then 0
1388 else 1
1389 end
1390 group by bl.id
1391 having "新着" > 0 order by "新着" desc, bl.id
1392 limit 10;
1393 EOF
1396 showhome() {
1397 # $1=userRowIdToShow
1398 err showhome \$1=$1
1399 case "$1" in
1400 *@*) uname=`getvalbypkey user name "$1"` ;;
1401 *) uname=`getvalbyid user name $1` ;;
1402 esac
1403 err ShowHome: uname=$uname
1404 gecos=`gecos "$uname"`
1405 err SH:gecos=$gecos
1406 GF_VIEWONLY=1
1407 cond="gname in (select gname from grp_mem where user='$uname')"
1408 if [ x"$user" = x"$uname" ]; then
1409 conflink="<a href=\"?userconf\">プロフィールの編集</a> /
1410 <a href=\"?blog\">新規話題の作成</a>"
1411 # Display folders
1412 sql="select count(id) from article_m where id
1413 in (select id from article where author='$user')
1414 and type like 'file:%';"
1415 err nfile-sql=`echo "$sql"`
1416 nfile=`query "$sql"`
1417 err nfile=$nfile
1418 if [ $nfile -gt 0 ]; then
1419 conflink="$conflink /
1420 <a href=\"?lsmyfile\">過去の提出ファイル</a>"
1421 fi
1422 fi
1423 . ./s4-blog.sh
1425 tf=$tmpd/title.$$ pf=$tmpd/profile.$$ bf=$tmpd/blogs.$$
1426 echo "$gecos さん" > $tf
1427 viewtable $formdir/user.def user $1 > $pf
1429 sqcond="WHERE name='$uname' AND key='profimg' AND type LIKE 'file:image%'"
1430 img=`query "SELECT type FROM user_m $sqcond LIMIT 1;"`
1431 imf=$tmpd/profimg.$$; touch $imf
1432 if [ -n "$img" ]; then
1433 { printf '%s' "<img src=\"data:${img#file:},"
1434 query "SELECT hex(bin) FROM user_m $sqcond LIMIT 1;" \
1435 | sed 's/\(..\)/%\1/g'
1436 echo '">'
1437 } > $imf
1438 fi
1439 nblog=`query "SELECT count(id) FROM blog_s WHERE key='owner' AND \
1440 val='$uname';"`
1441 listblog $uname > $bf
1442 listgroupbytable $formdir/grp.def $cond \
1443 | _m4 -D_BODYCLASS_=home -D_TITLE_="spaste(\`$tf')" \
1444 -D_PROFILE_="spaste(\`$pf')$conflink" \
1445 -D_PROFIMG_="spaste(\`$imf')" \
1446 -D_BLOGS_="spaste(\`$bf')" \
1447 -D_NBLOG_="$nblog" \
1448 -D_GROUPS_="syscmd(\`cat')" \
1449 $layout/html.m4.html $layout/home.m4.html
1451 if [ x"$user" = x"$uname" ]; then
1452 # Display NEWS
1453 if false; then
1454 cond="where 新着 > 0 order by 新着 desc,ctime desc limit 10"
1455 new10=`DT_CHLD=article:blogid \
1456 DT_VIEW=replyblog dumptable html blog "ctime title gecos" "$cond"`
1457 else
1458 # 2016-02-19 Counting NEWS without using dumptable.
1459 sql=`listnewblogsql "$user"`
1460 new10=`DT_SQL="$sql" DT_VIEW=replyblog dumptable html blog`
1461 fi
1462 cont=`echo "$new10"|grep "^<TR>"|wc -l`
1463 cont=$((cont-1))
1464 err newcount=$cont
1465 if [ $cont -gt 0 ]; then
1466 echo "全体の新着記事${cont}傑" | html h2
1467 echo "$new10"
1468 fi
1469 cat<<EOF
1470 <div class="fold">
1471 `cgi_checkbox srchall yes id="sa"`<label
1472 for="sa">SNS全体から検索</label>
1473 <div>
1474 EOF
1475 cgi_form searchart<<EOF
1476 <label>`cgi_text kwd`という語を含む記事を全部から検索</label>
1477 EOF
1478 cat<<EOF
1479 </div></div>
1480 EOF
1481 fi
1483 # Record access log
1484 [ -n "$1" ] && [ x"$1" != x"$user" ] && acclog user $1
1486 commission() { # $1=grp-rowid $2=user-rowid
1487 contenttype; echo
1488 err commission: "$@"
1489 gname=`getgroupbyid $1`
1490 echo "グループ $gname 管理者委任" \
1491 | _m4 -D_TITLE_="syscmd(\`cat')" $layout/html.m4.html
1492 if [ -n "$2" ]; then
1493 grp_reg_adm "$@"
1494 else
1495 echo "無効な指定です。普通のアクセスならここに来ないはず。"|html p
1496 fi
1498 listgroupbytable() {
1499 # $1=deffile $2...=condition
1500 tagline=`grep :tag: $1`; shift
1501 and="${1:+and }" where=${1:+where }
1502 href="<a href=\"$myname?grp+"
1503 echo '<div class="listgroup">'
1504 NGsql="select distinct tag from\
1505 (select gname, max(case key when 'tag' then val end) as tag, \
1506 max(case key when 'ctime' then val end) as ctime\
1507 from grp_s group by gname order by ctime);"
1508 sql="select val from grp_s where key='tag' $and$* group by val;"
1509 err ListGRP: query sql="$sql"
1510 for tag in `query "$sql"`
1511 do
1512 err ListGrp: tag=$tag
1513 tn=${tagline%%=${tag}*}
1514 tn=${tn##*[ :]}
1515 sql="select rowid||':'||gname as 'グループ名',説明 from
1516 (select (select rowid from grp g where g.gname=grp_s.gname)
1517 as rowid,
1518 gname,
1519 max(case key when 'gecos' then val end) as '説明',
1520 max(case key when 'tag' then val end) as 'tag',
1521 max(case key when 'mtime' then val end) as mtime from grp_s
1522 $where$* group by gname having tag='$tag' order by mtime desc);"
1523 err PersonalGroupList= `echo $sql`
1524 echo "<h2>$tn</h2>"
1525 echo '<table class="b listgroup">'
1526 sq -header -html $db "$sql" \
1527 | sed "s,\(<TR><TD>\)\([0-9]*\):\([^ ]*\)</TD>,\1$href\2\">\3</a>,"
1528 echo '</table>'
1529 done
1530 echo '</div>'
1532 iconhref() (
1533 # $1=icon-file, $2=Href $3=title $4...=anchor
1534 data=`percenthex $1`
1535 ct=`file --mime-type $1|cut -d' ' -f2`
1536 err iconhref: \$1=$1 \$2=$2 \$3="$@"
1537 href=$2; title=$3; shift 3
1538 echo "<a href=\"$href\"><img title=\"$title\" src=\"data:$ct,$data\">$@</a>"
1540 iconhref2() (
1541 # $1=icon-file, $2=Href $3=title $4...=anchor
1542 src=$1
1543 href=$2; title=$3; shift 3
1544 echo "<a href=\"$href\"><img title=\"$title\" src=\"$src\">$@</a>"
1546 listentry() (
1547 # $1=user/group $2=SearchKeyword $3=condition(if any)
1548 # Referring variable $iamowner=$grp to attach owner-request links
1549 err listentry: \$1=$1 \$2=$2 \$3=$3
1550 cond=''
1551 offset=`getpar offset`
1552 offset=${offset%%[!0-9]*}
1553 offset=$((offset + 0)) # change to numeric forcibly
1554 [ $offset -lt 0 ] && offset=0
1555 limit=30
1556 dir=`getcachedir "$1"`
1557 if [ x"$1" = x"user" ]; then
1558 hrb="$myname?home"
1559 deficon=person-default.png
1560 entity="ユーザ" tbl=user link=rowid nm=name stage=mems
1561 gcs=gecos
1562 else # if group
1563 hrb="$myname?grp"
1564 deficon=group-default.png
1565 entity="グループ" tbl=grp link=rowid nm=gname stage=grps
1566 gcs=name
1567 tagline=`grep :tag: $formdir/grp.def|cut -d: -f5-`
1568 if [ -n "$tagline" ]; then
1569 tagconv=`echo $tagline|sed 's/\([^= :]*\)=\([^= :]*\)/-D\2=\1/g'`
1570 err tagconv=$tagconv
1571 fi
1572 fi
1573 if [ ! -d $dir ]; then
1574 mkdir -p $dir
1575 fi
1576 if [ ! -s $dir/$deficon ]; then
1577 convert -geometry $thumbxy $imgdir/$deficon $dir/$deficon
1578 fi
1579 if [ -n "$2" ]; then
1580 cond="where nick like '%$2%' or b.name like '%$2%'"
1581 fi
1583 # XX: これ複雑すぎるかな。もっとシンプルにしたい。$3条件も。2015-07-08
1584 # grpは呼出し元の動的スコープ変数でよくないな...
1585 ##qgrp=`sqlquote $grp`
1586 getgrp="(select gname from grp where rowid=${rowid:--1})"
1587 sql="select a.rowid, a.$link, coalesce(b.$gcs, a.$nm) as nick,
1588 coalesce(b.gecos, a.$nm) as name,
1589 b.tag,
1590 case when a.$nm in (select user from grp_adm
1591 where gname=$getgrp) then '(管理者)'
1592 when '$user' in (select user from grp_adm where gname=a.$nm)
1593 then '(ADMIN)'
1594 when '$user' in (select user from grp_mem where gname=a.$nm)
1595 then '(Member)'
1596 when '$iamowner' = '' then ''
1597 else ',not='||a.rowid end as ownerlink
1598 from $tbl a left join
1599 (select $nm as name,
1600 max(case key when 'gecos' then val end) as gecos,
1601 max(case key when 'tag' then val end) as tag
1602 from ${tbl}_s group by $nm)
1603 b on a.$nm=b.name $cond $3
1604 order by b.tag desc, a.rowid asc"
1605 err LE:sql.1="$sql"
1606 total=`query "with x as ($sql) select count(*) from x;"`
1607 echo "${entity} 一覧" | html h2
1608 if [ $total -gt $limit ]; then
1609 echo '<div class="right">'
1610 cgi_form $stage <<EOF
1611 <label>次の語を含む${entity}で検索:
1612 `cgi_text kwd $kwd`</label>
1613 EOF
1614 echo '</div>'
1615 fi
1616 cat<<EOF
1617 <p>${total}件中の$((offset+1))件めから${kwd:+" - 検索語: $kwd"}</p>
1618 EOF
1619 if [ $((offset+limit)) -lt $total ]; then
1620 cat<<EOF
1621 <div class="right"><form action="$myname" method="POST">
1622 `cgi_submit 次の${limit}件`
1623 `cgi_hidden kwd "$kwd"`
1624 `cgi_hidden stage "$stage"`
1625 `cgi_hidden offset $((offset + limit))`</form></div>
1626 EOF
1627 fi
1628 if [ $offset -gt 0 ]; then
1629 cat<<EOF
1630 <form action="$myname" method="POST">
1631 `cgi_submit 前の${limit}件`
1632 `cgi_hidden stage "$stage"`
1633 `cgi_hidden kwd "$kwd"`
1634 `cgi_hidden offset $((offset - limit))`</form>
1635 EOF
1636 fi
1638 err ListEntry: `echo "$sql"\;`
1640 query "$sql limit $limit ${offset:+offset $offset};" \
1641 | while IFS='|' read id lnk name gecos tag ownerp; do
1642 err name=$name owner=$ownerp lnk=$lnk
1643 err newlnk=$lnk
1644 files=`getvalbyid $tbl profimg $id $dir`
1645 # Pick up only first icon
1646 echo "<div class=\"iconlist xy$thumbxy\"><p class=\"tag _$tag\">$tag</p>" \
1647 | _m4 $tagconv
1648 if [ -n "$files" ]; then
1649 icon=`echo "$files"|head -1`
1650 iconhref $dir/$icon "$hrb+$lnk" "$gecos"
1651 else
1652 iconhref $dir/$deficon "$hrb+$lnk" "$gecos"
1653 fi
1654 echo "<br>$name${ownerp:+<br>$ownerp}"
1655 echo "</div>"
1656 done
1658 listmember() {
1659 listentry user "$@"
1661 listgroup() {
1662 listentry group "$@"
1664 showgroup() { # $1=group-rowid
1665 # gname=`getpar gname`
1666 # if [ -n "$gname" ]; then
1667 # err UPdating/Removing of group:::::::
1668 # # See ALSO same job in groupman()
1669 # newgname=`echo "$gname"|tr -d '\"'"'"`
1670 # ###newgname=$gname
1671 # err gname=$gname newgname=$newgname
1672 # if [ x"$newgname" != x"$gname" ]; then
1673 # err NewGNAME: gname=$newgname
1674 # gname=$newgname
1675 # echo "使用禁止文字を除去し $gname としました。" | html p
1676 # replpar gname string "$gname"
1677 # fi
1678 # par2table $formdir/grp.def
1679 # fi
1680 grp=`getgroupbyid $1`
1681 err showgroup2: grp=$grp qgrp="[$(sqlquote $grp)]"
1682 if isgroup "$grp"; then
1683 tf=$tmpd/title.$$
1684 echo "グループ $grp" > $tf
1685 showgroupsub $formdir/grp.def "$1" | \
1686 _m4 -D_TITLE_="spaste(\`$tf')" \
1687 -D_FORM_="syscmd(\`cat')" \
1688 -D_DUMPTABLE_="" \
1689 $layout/form+dump.m4.html
1690 else # if $grp is removed at par2table
1691 listgroup
1692 fi
1694 showgroupsub() {
1695 # $1=def-file $2=group-rowid
1696 rowid=$2
1697 grp=`getgroupbyid $2`
1698 qgrp=`sqlquote $grp`
1699 #rowid=`sq $db "select rowid from grp where gname=$qgrp"`
1700 if [ -z "$rowid" ]; then
1701 #rowid=`sq $db "select rowid from grp where rowid=$grp"`
1702 #grp=`sq $db "select gname from grp where rowid=$grp"`
1703 echo "showgroupsub: invalid argument($1 $2)" | html p
1704 return
1705 fi
1706 val=`getvalbyid grp profimg $rowid $tmpd`
1707 # 6/14の次グループのHOMEで出す情報を作る Done
1708 viewtable $1 grp $rowid
1709 if isgrpowner "$user" "$grp"; then
1710 echo "<p><a href=\"?groupconf+$rowid\">グループ情報の編集</a>"
1711 iamowner=$rowid
1712 fi
1713 if ismember "$user" "$grp"; then
1714 echo "${iamowner:+ / }<a href=\"?blog+$rowid\">グループの新規話題作成</a>"
1715 echo "/ <a href=\"?grpaction+$rowid\">メンバーを個別選択しての操作</a></p>"
1716 # div.fold input[type="checkbox"]:checked ~ div {display: block;}
1717 cat<<EOF
1718 <form action="?send2mem" method="POST" enctype="multipart/form-data">
1719 <div class="fold">
1720 `cgi_checkbox send yes id="send"`<label
1721 for="send">グループ全員にメッセージ送信</label>
1722 <div>
1723 `cgi_textarea message "" "cols=60"`
1724 `cgi_submit 送信`
1725 `cgi_reset リセット`
1726 </div>
1727 `cgi_hidden grp $rowid`
1728 </div></form>
1729 EOF
1730 fi
1731 # 加入ボタン + 加入者リスト
1732 err ismember $user $grp
1733 ismember "$user" "$grp" && ismem='checked' || nomem='checked'
1734 # このグループでの加入アドレス
1735 eml=`query "select val from grp_mem_s where gname='$2' and user='$user' \
1736 and key='email';"`
1737 err EML: "select val from grp_mem_s where gname='$2' and user='$user' \
1738 and key='email';"
1739 err email=$eml
1740 cat <<EOF
1741 <div class="fold">
1742 `cgi_checkbox reg yes id="reg"`<label
1743 for="reg">自身の参加状態を操作する</label>
1744 <div>
1745 EOF
1746 cgi_form grp <<EOF
1747 <p>このグループに</p>
1748 <table class="b">
1749 <tr><th>メンバーとして</th><td>
1750 <label>`cgi_radio joingrp "yes" $ismem`参加</label> /
1751 <label>`cgi_radio joingrp "no" $nomem`参加しない</label></td></tr>
1752 <tr><th>参加する場合のメイルアドレス<br>
1753 <small>(メインのアドレスとは違うものにする場合に記入<br>
1754 同じでよい場合は空欄に)</small></th>
1755 <td>`cgi_text email $eml`</td></tr>
1756 </table>
1757 `cgi_hidden grp $rowid`
1758 EOF
1759 if [ x`getgroupattr $grp regmode` = x'moderated' -a -z "$ismem" ]; then
1760 echo "moderated (承認加入の)グループなので実際に参加できるのは
1761 グループ管理者が承認操作をした後になります。" | html p 'class="warn"'
1762 fi
1763 echo '</div></div>'
1764 echo '<h2>話題一覧</h2>'
1765 cgi_form searchart<<EOF
1766 <label>`cgi_text kwd`という語を含むコメントを検索</label>
1767 `cgi_hidden owner $grp`
1768 EOF
1769 cond="where a.id in (select id from blog_s where key='owner' and val=$qgrp) order by ctime desc"
1770 DT_CHLD=article:blogid \
1771 DT_VIEW=replyblog dumptable html blog 'ctime title heading' "$cond"
1773 getgname="(select gname from grp where rowid=$rowid)"
1774 c="group by b.name having b.name in (select user from grp_mem where gname=$getgname)"
1775 cm="?commission+$rowid"
1776 thumbxy=50x50 listmember "" "$c" \
1777 |sed -e "s|\(<br>\),not=\(.*\)|\1|" # 間違って押しやすい
1778 ## |sed -e "s|\(<br>\),not=\(.*\)|\1<a href=\"$cm+\2\">管理者委託</a>|"
1780 grpaction() { # $1=group-rowid
1781 err GRP_ACTION:IN
1782 grid=${1:-`getpar grp`}
1783 grp=`getgroupbyid "$grid"`
1784 if [ -z "$grp" ]; then
1785 echo "無効な指定です。" | html p; return
1786 fi
1787 if ! ismember $user $grp; then
1788 echo "加入者のみに許可された操作です。" | html p; return
1789 fi
1790 echo "グループ $grp 個別選択操作" \
1791 | _m4 -D_TITLE_="syscmd(\`cat')" $layout/html.m4.html
1793 isowner=""
1794 isgrpowner "$user" "$grp" && isowner="yes"
1795 usel=`getpar usel`
1796 if [ -n "$usel" ]; then
1797 uids=$(echo `echo $usel`|tr ' ' ',')
1798 err grpaction-1: grp=$grp, `echo $sql`
1799 text=`getpar text`
1801 rm=`getpar rm` cfm=`getpar confirm`
1802 err rm=$rm cfm=$cfm
1803 if [ x"$rm" = x"yes" ]; then
1804 if [ "$isowner" ]; then
1805 if [ x"$rm$cfm" = x"yesyes" ]; then
1806 # Eliminate
1807 cond="where gname=(select gname from grp where rowid=$grid) and user in (select name from user where rowid in ($uids))"
1808 for tbl in grp_mem grp_mem_s grp_mem_m; do
1809 sql="delete from $tbl $cond;"
1810 # echo "sql=$sql"
1811 query "$sql"
1812 err rmGRPuser "$sql"
1813 done
1814 num=`query "select count(*) from user where rowid in ($uids);"`
1815 #err num=$num
1816 if [ 0$num -gt 0 ]; then
1817 sql="select coalesce(b.val,a.name) from user a left join \
1818 user_s b on a.name=b.name and key='gecos' where a.rowid in ($uids);"
1819 # err `echo "$sql"`
1820 html pre<<EOF
1821 以下の${num}名のグループ $grp 登録を解除しました。
1822 `query "$sql"`
1823 EOF
1824 fi
1825 else
1826 echo "確認のチェックがないのでやめておきます。" | html p
1827 return
1828 fi
1829 else # not Group Owner
1830 echo "グループ管理者でないのでメンバー操作はできません。" | html p
1831 return
1832 fi
1833 cat<<EOF
1835 EOF
1836 elif [ x"$rm" = x"send" ]; then # if sendmsg mode
1837 if [ -z "$text" ]; then # if msg is empty
1838 echo "なにかメッセージを..." | html p
1839 return 0
1840 fi
1841 smail "`email4groupbyuid "$grp" $usel` $user" \
1842 "`gecos $user` さんからのメッセージ" <<EOF
1843 $url
1844 のグループ「$grp」のメンバーである `gecos $user` さんから、
1845 あなた宛へのメッセージです。
1846 ----------------------------------------------------------
1847 $text
1848 EOF
1849 if [ $? = 0 ]; then
1850 echo "Note: 以下のメンバーにメッセージを送信しました。" | html p
1851 sql="select coalesce(b.val, a.name) from
1852 (select name from user where rowid in ($uids)) a
1853 left join user_s b on a.name=b.name and b.key='gecos';"
1854 html pre<<EOF
1855 `query "$sql"`
1856 (送信者である `gecos $user` さんも含まれます)
1857 EOF
1858 err SendDone: `echo $sql`
1859 fi
1860 elif [ x"$rm" = x"commission" ]; then
1861 grp_reg_adm $grid $usel
1862 fi
1863 fi
1864 # New entry
1865 sql="select u.rowid||','||j.* from user u
1866 join (select a.user as name,coalesce(b.val,a.user)
1867 from (select gname,user from grp_mem
1868 where gname=(select gname from grp where rowid=$1))
1869 a left join (select gname,user,val from grp_mem_s where key='email')
1870 b using(gname,user))
1871 j using(name) order by u.name;"
1872 sql="select /* Ahh, ugly SQL, I wanna fix... */
1873 case
1874 when (select user from grp_adm where
1875 gname=(select gname from grp where rowid=$grid)
1876 and user=a.name) is not null
1877 then 'k'
1878 else ''
1879 end || a.rowid||
1880 ','||coalesce(val,a.name) as name from
1881 (select rowid,name from user where name in
1882 (select user from grp_mem where
1883 gname=(select gname from grp where rowid=$grid)))
1884 a left join user_s on a.name=user_s.name and key='gecos'
1885 order by coalesce(val,a.name);"
1886 err grpaction: `echo $sql`
1887 b1='<label> <input type="checkbox" name="usel" value="'
1888 ba='<label class="admin"><input type="checkbox" name="usel" value="'
1889 b2='"> <span>' b3='</span></label>'
1890 tf=$tmpd/title.$$
1891 echo "グループ[$grp]参加メンバーに対する操作" > $tf
1892 cmmsg="<div class=\"fold\">
1893 `cgi_radio rm commission id=\"cmadmin\"` <label
1894 for=\"cmadmin\">下でチェックした人にグループ管理者委任</label>
1895 <div><p>このグループでの全権を付与します。信頼できる人に託してください。
1896 </p></div></div>"
1897 excmsg="<div class=\"fold\">
1898 `cgi_radio rm yes ` 下でチェックした人のグループ登録解除
1899 <div>
1900 本当に消します! `cgi_checkbox confirm yes` 確認
1901 <p>この操作による通知は本人に行きません。
1902 あらかじめ通知するか、登録解除してよい状況かしっかり確認してください。</p>
1903 </div>
1904 </div>"
1905 cgi_form grpaction<<EOF \
1906 | sed -e "s|^\(<TR><TD>\)k\([0-9]*\),\([^<]*\)|\1$ba\2$b2\3$b3|" \
1907 -e "s|^\(<TR><TD>\)\([0-9]*\),\([^<]*\)|\1$b1\2$b2\3$b3|" \
1908 | _m4 -D_TITLE_="spaste(\`$tf')" \
1909 -D_SUBTITLE_="チェック後操作ボタン" \
1910 -D_FORM_="syscmd(cat)" -D_DUMPTABLE_="" \
1911 $layout/form+dump.m4.html
1912 <div class="fold">
1913 `cgi_radio rm send id="sendmsg"`<label
1914 for="sendmsg">下でチェックした人にメッセージを送信する</label>
1915 <div>
1916 `cgi_textarea text "" cols=40`
1917 </div>
1918 </div>
1919 ${isowner:+$cmmsg$excmsg}
1920 <h4>$grp 参加者一覧</h4>
1921 <table>
1922 `sq $db -html "$sql"`
1923 `cgi_hidden grp $grid`
1924 </table>
1925 EOF
1927 editheading() { # $1=rowid-of-heading
1928 rowid=${1%%[!A-Z0-9a-z_]*}
1929 if [ -z "$rowid" ]; then
1930 echo "話題番号が未指定です。" | html p
1931 return
1932 fi
1933 owner=`getvalbyid blog owner $rowid`
1934 title=`getvalbyid blog title $rowid`
1935 GF_ACTION="?blog" edittable $formdir/blog.def blog $rowid \
1936 | _m4 -D_TITLE_="修正" \
1937 -D_SUBTITLE_="[$title]@$owner" -D_DIARY_="" \
1938 -D_BLOGS_="" -D_DUMPTABLE_="" \
1939 -D_FORM_="syscmd(\`cat')" \
1940 $layout/html.m4.html $layout/form+dump.m4.html
1942 editart() { # $1=article-rowid $2=blogrowid
1943 rowid=${1%%[!A-Z0-9a-z_]*}
1944 blogrowid=${2%%[!A-Z0-9a-z_]*}
1945 if [ -z "$rowid" -o -z "$blogrowid" ]; then
1946 echo "表示する記事番号が未指定です。" | html p
1947 return
1948 fi
1949 owner=`getvalbyid blog owner $blogrowid`
1950 title=`getvalbyid blog title $blogrowid`
1951 author=`getvalbyid article author $rowid`
1952 err EDITart: owner=$owner, author=$author
1953 if isgrpowner $user $owner; then
1954 : EDIT OK
1955 elif [ x"$owner" != x"$user" -a x"$author" != x"$user" ]; then
1956 echo "本人か所有者しか編集できません." | html p
1957 return
1958 fi
1959 aid=`query "select id from article where rowid=$rowid;"`
1960 tmpout=$tmpd/editart.$$.out
1961 GF_ACTION="?replyblog+$blogrowid#$aid" \
1962 edittable $formdir/article.def article $rowid \
1963 > $tmpout
1964 rm -f /tmp/editart.out
1965 # Cannot use pipelining to m4 with genform() because of stdin stack
1966 _m4 -D_TITLE_="コメントの修正" -D_DIARY_="" \
1967 -D_FORM_="syscmd(cat $tmpout)" \
1968 -D_SUBTITLE_="`gecos $owner`の「$title」" \
1969 -D_BLOGS_= -D_DUMPTABLE_= \
1970 $layout/html.m4.html $layout/form+dump.m4.html
1972 send2mem() {
1973 rowid=`getpar grp`
1974 if [ -z "$rowid" ]; then
1975 echo "グループが未指定です。" | html p
1976 return
1977 fi
1978 message=`getpar message`
1979 if [ -z "$message" ]; then
1980 echo "文章を入れてください。" | html p
1981 return
1982 fi
1983 grp=`getgroupbyid $rowid`
1984 members=`collectemail $grp`
1985 # smail rcpt subj (file)
1986 smail "$members" "グループ $grp 宛メッセージ(from `gecos $user`)" <<EOF
1987 $urlbase?grp+$rowid
1988 グループ $grp に所属する
1989 `gecos $user` さんよりメッセージ:
1991 $message
1992 EOF
1993 cat<<EOF
1994 <p>以下の宛先に送信しました。</p>
1995 <pre>
1996 $members
1997 </pre>
1998 <p><a href="?grp+$rowid">グループ $grp</a>に戻る。</p>
1999 EOF
2001 joingrpadmit() {
2002 # $1=yes/no $2=session-key
2003 if [ -z "$2" ]; then
2004 echo "bye bye" | html p; return
2005 fi
2006 t_usr=`session=$2 getpar user`
2007 t_grp=`session=$2 getpar group`
2008 err joingrpadmit: t_usr=$t_usr, t_grp=$t_grp
2009 _m4 -D_TITLE_="joingrp" $layout/html.m4.html
2010 if [ -z "$t_usr" -o -z "$t_grp" ]; then
2011 echo "無効な加入依頼です。" | html p
2012 echo "有効期限が切れたか、
2013 他の管理者がいる場合は処理済みの可能性があります。" | html p
2014 return
2015 fi
2016 if ! isgrpowner "$user" $t_grp; then
2017 echo "グループ管理者のみの機能です。" | html p; return
2018 fi
2019 case $1 in
2020 yes) joingrp "$t_grp" "$t_usr" yes "$t_usr" ;;
2021 no) joingrp "$t_grp" "$t_usr" no "$t_usr" ;;
2022 *)
2023 echo "無効な指定です($1)。" | html p
2024 return ;;
2025 esac
2026 gid=$(query "select rowid from grp where gname=`sqlquote $t_grp`;")
2027 rcpts="`getgroupadminmails $t_grp` $user"
2028 err admit: msgdir=$msgdir, rcpts="["$rcpts"]"
2029 body="グループ $t_grp
2030
2031 $t_usr
2032 `[ x$1 = xyes ] && echo 'を追加' || echo 'の解除操作を'`
2033 しました。"
2034 (echo "$body"; echo; echo "$url?grp+$gid") | smail "$rcpts" "joingrp $1"
2035 query "delete from session where id='$2';"
2036 echo "$body" | html p
2039 joingrprequest() {
2040 # $1=group $2=user $3=yes/no $4=email(if any $5=AsAdmin)
2041 jss="joingrp-`date +%s`-`genrandom 12`"
2042 addsession $jss +${memoplimitdays}days
2043 query "replace into par values('$jss', 'group', 'string', `sqlquote $1`),
2044 ('$jss', 'user', 'string', `sqlquote $user`);"
2045 smail "$(collectemail `getgroupadmins $1`)" "Join request to $1"<<EOF
2046 $url
2047 グループ $1
2048 に加入依頼がありました。
2050 承認する:
2051 $urlbase?joingrpadmit+yes+$jss
2053 白紙に戻す:
2054 $urlbase?joingrpadmit+no+$jss
2055 EOF
2056 echo "管理者に加入依頼を出しました。
2057 ${memoplimitdays}日以内に加入承認操作がされれば加入できますが、
2058 グループ運用方針に懸かることですので直接の問い合わせが重要です。" | html p
2060 joingrp() {
2061 # $1=group $2=user $3=yes/no $4=email(if any $5=AsAdmin)
2062 err joingrp: \$1=$1 \$2=$2 \$3=$3 \$4=$4
2063 isgrpowner "$user" "$1" && isowner="yes" || isowner=""
2064 err jg:isgrpowner: isowner="$isowner"
2065 if [ -n "$isowner" ]; then
2066 : # GROUP OWNER CAN DO EVERYTHING ABOUT REGISTRATION/RETIREMENT
2067 elif [ x"$2" != x"$user" ]; then # if user is not login user
2068 echo "本人か、グループ管理者しか加入操作はできません。" | html p
2069 return
2070 elif [ x"$3" = x"no" ]; then
2071 : # Do not pursue those who leave
2072 else # adding user is $user itself
2073 case `getgroupattr $1 regmode` in
2074 moderated)
2075 joingrprequest "$@" # Request only
2076 return
2077 ;;
2078 *)
2079 ;;
2080 esac
2081 fi
2082 qgname=`sqlquote $1`
2083 cond="where gname=$qgname and user='$2'"
2084 if [ x"$3" = x"yes" ]; then
2085 query "replace into grp_mem values($qgname, '$2');"
2086 if [ -n "$4" ]; then
2087 if msg=`emaildomaincheck "$4"`; then
2088 err "replace into grp_mem_s values($qgname, '$user', 'email', \
2089 'string', '$4', NULL);"
2090 query "replace into grp_mem_s values($qgname, '$user', 'email', \
2091 'string', '$4', NULL);"
2092 if [ -n "$5" ]; then # as ADMIN
2093 # Coming here means newly created group
2094 sql="select case\
2095 when (select count(*) from grp_mem where gname=$qgname)=1\
2096 then (select user from grp_mem\
2097 where gname=$qgname and user='$user')\
2098 else '' end;"
2099 err NewGrpChk: $sql
2100 if [ -n "`query \"$sql\"`" ]; then
2101 err ADMIN: "replace into grp_adm values($qgname, '$user');"
2102 query "replace into grp_adm values($qgname, '$user');"
2103 fi
2104 fi
2105 else
2106 echo $msg
2107 fi
2108 else
2109 query "delete from grp_mem_s $cond and key='email';"
2110 fi
2111 else
2112 query "delete from grp_mem $cond;
2113 delete from grp_mem_s $cond;
2114 delete from grp_mem_m $cond;"
2115 fi
2117 grp_reg_adm() {
2118 # $1=grp-rowid $2...=user-rowid
2119 grid=$1
2120 grp=`getgroupbyid "$1"`
2121 if [ -z "$grp" ]; then
2122 echo "無効なグループIDです" | html p; return
2123 fi
2124 if ! isgrpowner $user "$grp"; then
2125 echo "$grp グループの管理者しかこの操作はできません。" | html p; return
2126 fi
2127 shift
2128 for urid; do
2129 newadm=`query "select name from user where rowid=$urid;"`
2130 if [ -z "$newadm" ]; then
2131 echo "指定ユーザIDがおかしいようです。" | html p; return
2132 fi
2133 err GRP_reg_adm: "replace into grp_adm values(`sqlquote $grp`, '$newadm');"
2134 err ismember $newadm $grp
2135 if ismember $newadm $grp; then
2136 # OK, go ahead
2137 getgname="(select gname from grp where rowid=$grid)"
2138 query "replace into grp_adm values($getgname, '$newadm');"
2139 # confirm insertion
2140 sql="select * from grp_adm where gname=$getgname and user='$newadm'"
2141 if [ -n "`query \"$sql;\"`" ]; then
2142 echo "追加完了: $newadm" | html p
2143 else
2144 echo "追加失敗($1 $urid)" | html p
2145 fi
2146 fi
2147 showgroup $grid
2148 done
2150 dumptable() {
2151 # $1=mode $2=Table $3=column-list-of-*_s(defaults to *) $4=conditions(if any)
2152 # textのフィールドだけ全てダンプにしたほうがいいか
2153 # $DT_VIEW sets link
2154 # 6/17の次: editリンクじゃなくてスレッドVIEWリンクでいいんちゃう?
2155 ### elink="<a href=\"$myname?edittable+$2+\\2\">EDIT</a>"
2156 VIEW=${DT_VIEW-replyblog}
2157 if [ -n "$VIEW" ]; then
2158 dvlink=" <a href=\"$myname?$VIEW+\\2\">VIE</a><a href=\"$myname?$VIEW+\\2#bottom\">W</a>"
2159 fi
2160 # $DT_CHLD=ChildTable:BindColumn
2161 if [ -n "$DT_CHLD" ]; then
2162 _t=${DT_CHLD%:*} _i=${DT_CHLD#*:}
2163 cntall="(select count($_i) from $_t where $_i=a.id)"
2164 cntnew="(select count(val) from ${_t}_s where key='ctime' \
2165 and id in (select id from $_t where $_i=a.id) \
2166 and val > coalesce((select time from acclog where \
2167 user='$user' and tbl='$2' and tblrowid=a.rowid),\
2168 '1970-01-01'))"
2169 cnt="$cntnew as '新着', $cntall as '総数',"
2170 dt_class=" td2r td3r"
2171 fi
2172 # Construct join expression
2173 eav="" scols=""
2174 pk=`gettblpkey $2`
2175 substr=${dumpcollen:+"substr(val, 0, $dumpcollen)"}
2176 substr=${substr:-val}
2177 for col in ${3:-`gettbl_s_cols $2`}; do
2178 case $col in
2179 gecos) scols="$scols${scols:+, }${col#}"
2180 continue ;; # built-in column name
2181 *:*) col=${col%:*} as=${col#*:} ;;
2182 *) as=${col} ;;
2183 esac
2184 eav=$eav${eav:+,}" max(case key when '$col' then $substr end) as $as"
2185 scols="$scols${scols:+, }b.$as"
2186 done
2187 #case author when '$user' then a.rowid else '---' end as ID,
2188 sql=${DT_SQL:-"select \
2189 a.rowid as ID,\
2190 $cnt\
2191 $scols from $2 a left join\
2192 (select $pk,$eav,
2193 max(case key when 'owner'
2194 then coalesce((select val from user_s us
2195 where us.key='gecos' and us.name=c.val), val) end) as gecos
2196 from ${2}_s c group by $pk) b on a.$pk=b.$pk $4;"}
2197 err SQL=`echo "$sql"`
2198 cat<<EOF | sed "s,\(<TR><TD>\)\([1-9][0-9]*\)</TD>,\1$elink$dvlink</TD>,"
2199 <div class="dumptable">
2200 <table class="b$dt_class">
2201 `sq -header -cmd ".mode $1" $db "$sql"`
2202 </table>
2203 </div> <!-- dumptable -->
2204 EOF
2207 par2table() (
2208 # copy current parameters of par into destination table
2209 # $1=definition-file
2210 # Using $user and $session
2211 # Return value:
2212 # 0: Stored successfully
2213 # 1: Insufficient fillings
2214 # 2: No permission to modify the record
2215 # 3: Invalid rowid
2216 # 4: SUCCESS to delete
2217 # 5: Stop deletion for lack of confirm check
2218 # 6: Password length too short
2219 # 7: Password mismatch
2220 # 8: Old password incorrect
2221 rowid=`getpar rowid`
2222 err ...........rowid=$rowid
2223 if [ ! -e $1 ]; then
2224 echo "テーブル定義ファイルが見付かりません" | html p
2225 exit 1
2226 fi
2227 tbl=${1%.def}
2228 tbl=${tbl##*/}
2229 if [ -n "$rowid" ]; then # Modify existing entry
2230 if [ x"$tbl" = x"user" ]; then
2231 rowowner=`query "select name from $tbl where rowid=$rowid;"`
2232 elif [ x"$tbl" = x"grp" ]; then
2233 sql="select gname from $tbl where rowid=$rowid;"
2234 ##err p2t:grp:q $sql
2235 isgrpowner $user "`query $sql`" && rowowner=$user
2236 else
2237 rowowner=`query "select owner from $tbl where rowid=$rowid;"`
2238 rowowner=${rowowner:-`query "select author from $tbl
2239 where rowid=$rowid;"`}
2240 fi
2241 ### err rowowner=$rowowner
2242 if [ x"$user" != x"$rowowner" ]; then
2243 echo "他人のレコードはいじれないの" | html p
2244 return 2
2245 elif [ -z "$rowowner" ]; then
2246 echo "指定したレコードはないみたい" | html p
2247 return 3
2248 fi
2249 rm=`getpar rm` cfm=`getpar confirm`
2250 # Editing existent entry
2251 if [ x"$rm" = x"yes" ]; then
2252 if [ x"$rm$cfm" = x"yesyes" ]; then
2253 query "delete from $tbl where rowid=$rowid;"
2254 return 4
2255 else
2256 echo "消去確認のチェックがないので消さなかったの..." | html p
2257 return 5
2258 fi
2259 fi
2260 fi
2261 # XX: Subshelling here is unnecessary 2015-07-05
2262 (ts=${tbl}_s tm=${tbl}_m val="" pval="" formaster=""
2263 if [ -n "$rowid" ]; then
2264 # Update of existing record
2265 for col in `gettblcols $tbl`; do
2266 val=`getparquote $col`
2267 [ -z "$val" ] && continue
2268 err query "update $tbl set $col=$val where rowid=$rowid"
2269 ## XX: THIS IS DIRTY hack to ensure non-foreign key in blog_s
2270 sql="update $tbl set $col=$val where rowid=$rowid;"
2271 if [ x"$tbl" = x"grp" -a x"$col" = x"gname" \
2272 -o x"tbl" = x"user" -a x"$col" = x"name" ]; then
2273 ## User name cannot be changed with interface provided with this
2274 ## script. But we offer the trigger to change owner user
2275 ## of blog_s table.
2276 err "select quote($col) from $tbl where rowid=$rowid;"
2277 old=`query "select quote($col) from $tbl where rowid=$rowid;"`
2278 cat<<-EOF | query
2279 BEGIN;
2280 $sql
2281 update blog_s set val=$val
2282 where key='owner' and val=$old;
2283 COMMIT;
2284 EOF
2285 ## XX: DIRTY Hack Ends here
2286 ## We should keep blog's owner as a single column which has
2287 ## foreign key constraint with primary key of grp/user.
2288 else
2289 query "$sql"
2290 fi
2291 done
2292 # Then, set up $pval for further insertion of tbl_s and tbl_m
2293 for col in `gettblpkey $tbl`; do
2294 val=`query "select $col from $tbl where rowid=$rowid;"|sed -e 's/\"/\"\"/g'`
2295 pval="$pval${pval:+, }\"$val\""
2296 done
2297 else
2298 # New entry
2299 # Generate values() for primary keys
2300 for col in `gettblpkey $tbl`; do
2301 # Genuine primary keys for _m and _s
2302 val=`getvalquote $tbl $col`
2303 [ -z "$val" ] && continue
2304 pval="$pval${pval:+, }$val"
2305 done
2306 err pval=$pval
2307 for col in `gettblfkey $tbl`; do
2308 # args for values() to insertion into master table
2309 val=`getvalquote $tbl $col`
2310 [ -z "$val" ] && continue
2311 formaster=$formaster"${formaster:+, }$val"
2312 done
2313 formaster="$pval${formaster:+, }$formaster"
2314 err formaster=$formaster
2315 if [ -z "$formaster" ]; then
2316 echo "項目を全て埋めてください" | html pre
2317 return 1
2318 fi
2319 err "replace into $tbl values($formaster);"
2320 query "replace into $tbl values($formaster);"
2321 ## Insertion to master table, done
2322 fi
2324 for kt in s m; do
2325 tb2=${tbl}_$kt
2326 for col in `gettbl_${kt}_cols $tbl`; do
2327 ptype=`getpartype $col "limit 1"`
2329 # First, check update of existing entries in _m
2330 if [ $kt = m ]; then
2331 # sessID|address.1.22|string|Somewhere-x.y.z
2332 sql=""
2333 err dots from query "select var from par where var like '$col.%';"
2334 for v in `query "select var from par where var like '$col.%';"`; do
2335 # v=address.1.22
2336 st_rowid=${v##*.}
2337 origcol=${v%%.*} # original column derived from
2338 err Updating for $v st_rowid=$st_rowid, partype=`getpartype $v`
2339 ##case `getpartype $v` in
2340 err CASE `gettbl_coltype $tbl/$origcol` in
2341 err edit flag = `getpar action.$v`
2342 case `getpar action.$v` in
2343 rm)
2344 if [ x`getpar confirm.$v` = x"yes" ]; then
2345 newsql="delete from $tb2"
2346 else
2347 echo "削除確認未チェック" | html p
2348 fi ;;
2349 edit)
2350 case `gettbl_coltype $tbl/$origcol` in
2351 image|document|binary)
2352 file=$tmpd/`getparfilename $v`
2353 err type=file=$file
2354 [ -z "$file" ] && continue
2355 bn=${file##*/}
2356 bin="X'"$(hexize $file)"'"
2357 ct=`file --mime-type $file|cut -d' ' -f2`
2358 type=\"file:$ct\"
2359 newsql="update $tb2 set val='$bn', type=$type, bin=$bin"
2360 cachedir=`getcachedir "$tbl/$rowid"`
2361 err getcache tbl/rowid=$tbl/$rowid, rm -r $cachedir
2362 rm -r $cachedir
2363 ;;
2364 *)
2365 newsql="update $tb2 set val=(select val from par where var \
2366 like '$col.%.$st_rowid')"
2367 ;;
2368 esac
2369 ;;
2370 *) # maybe "keep", do not modify value
2371 continue
2372 ;;
2373 esac
2374 # err newsql=$newsql
2375 sql=$sql$nl"$newsql where rowid=$st_rowid;"
2376 done
2378 if [ x"$bin" = x"NULL" ]; then
2379 err repl:normal sql=`echo $sql`
2380 query "$sql
2381 delete from $tb2 where type='string' and val='';"
2382 err repl:normal done
2383 else
2384 sqlfile="$tmpd/sqlf.$$"
2385 echo "$sql" > $sqlfile
2386 err repl:sqlfile=`ls -lF $sqlfile`
2387 query ".read $sqlfile"
2388 err repl:done
2389 fi
2390 # Rest of kt==m: set multiple mode
2391 nr=`getparcount $col`
2392 else
2393 nr=1 # for kt==s, number of records is 1
2394 fi
2396 i=0
2397 while [ $i -lt $nr ]; do
2398 limit="limit 1 offset $i"
2399 i=$((i+1)) # increase beforehand against continue
2400 val=`getvalquote $tbl $col "$limit"`
2401 [ -z "$val" -o x"$val" = x'""' -o x"$val" = x"NULL" ] && continue
2402 err $col=$val
2403 bin=NULL
2404 err partype$col=`getpartype $col "$limit"`
2405 case $ptype in
2406 file) file=$tmpd/`getparfilename $col "$limit"`
2407 err parfile-$col=$file
2408 [ -z "$file" ] && continue
2409 bin="X'"$(hexize $file)"'"
2410 ct=`file --mime-type $file|cut -d' ' -f2`
2411 type=\"file:$ct\" ;;
2412 "*"*) continue ;; # foreign table
2413 *) type=\"string\" ;;
2414 esac
2415 case `gettbl_coltype $tbl/$col` in
2416 password) # special care for password
2417 # name={password,pswd1,pswd2}
2418 p1=`getpar pswd1 "$limit"`
2419 if [ -z "$p1" ]; then
2420 continue # SKIP password setting, if p1 is empty
2421 else
2422 pswd=`getpar pswd "$limit"` p2=`getpar pswd2 "$limit"`
2423 ## err pswd=$pswd
2424 if pwcheck "$pswd"; then
2425 if [ x"$p1" = x"$p2" ]; then
2426 case "$p1" in
2427 ??????????*) ;;
2428 *) echo "パスワードは10字以上にしてください。" | html p
2429 return 6;;
2430 esac
2431 val="\"`echo $p1|mypwhash`\""
2432 else
2433 echo "2つの新パスワード不一致" | html p
2434 return 7
2435 fi
2436 else
2437 echo "旧パスワード違います" | html p
2438 return 8
2439 fi
2440 fi
2441 ;;
2442 esac
2443 err p2t: "replace into $tb2 values($pval, \"$col\", $type, $val, bin...);"
2444 #query "replace into $tb2 values($pval, \"$col\", $type, $val, $bin);"
2445 sql="replace into $tb2 values($pval, \"$col\", $type, $val, $bin);"
2446 if [ x"$bin" = x"NULL" ]; then
2447 err Normal-query: `echo $sql`
2448 query "$sql"
2449 else
2450 sqlfile="$tmpd/query.$$"
2451 echo "$sql" > $sqlfile
2452 err sqlfile=`ls -lF $sqlfile`
2453 query ".read $sqlfile"
2454 fi
2455 err p2t done
2456 done
2457 done
2458 done
2459 return 0
2460 err donee)
2462 par2table_old() {
2463 # copy current parameters of par into destination table
2464 # $1=dst-table $2=definition-file
2465 # Using $user and $session
2466 rowid=`getpar rowid`
2467 if [ -n "$rowid" ]; then
2468 rm=`getpar rm` cfm=`getpar confirm`
2469 if [ x"$rm$cfm" = x"yesyes" ]; then
2470 sq $db "delete from $1 where rowid=$rowid and owner=\"$user\""
2471 return
2472 fi
2473 fi
2474 cat $2 \
2475 | (cols=""
2476 while IFS=: read prompt name type args; do
2477 [ x"$name" = x"stage" ] && continue
2478 if [ -n "$rowid" ]; then
2479 val=`getpar $name|sed -e 's/\"/\"\"/g'`
2480 sq $db "update $1 set $name=\"$val\" where rowid=$rowid and owner=\"$user\""
2481 else
2482 eav=$eav${eav+,}" max(case var when '$name' then val end)"
2483 fi
2484 done
2485 [ -n "$rowid" ] && return
2486 cond="where sessid='$session' group by sessid"
2487 sq $db "replace into $1 select $eav,\"$user\" from par $cond"
2488 # Think over again about putting $user
2491 genform() {
2492 # $1 = form definition file
2493 # $2, $3 (optional)= table name and ROWID
2494 # If $GF_VIEWONLY set and nonNull, output values without form
2495 # If $GF_HIDDEN set, use it hidden values
2496 # If $GF_OWNER set, use it as value of name="owner"
2497 # If $GF_STAGE set, use it as value of name="stage"
2498 forms="" hiddens="" rowid=$3
2499 if [ ! -e "$1" ]; then
2500 echo "そのようなデータベースはないようです($2)。" | html p
2501 return
2502 elif [ -n "$2" ]; then
2503 err genform1: "select * from $2 where rowid='$rowid'"
2504 rec=`query "select * from $2 where rowid='$rowid';"`
2505 if [ -z "$rec" ]; then
2506 pk=`gettblpkey $2`
2507 ###rec=`sq $db "select rowid from $2 where $pk='$rowid'"`
2508 err "select rowid from $2 where $pk='$rowid';"
2509 rec=`query "select rowid from $2 where $pk='$rowid';"`
2510 err rec-rowid=$rec
2511 rowid=$rec
2512 rec=$3
2513 fi
2514 if [ -z "$rec" ]; then
2515 echo "そんなレコードはないみたいね..." | html p
2516 return
2517 fi
2518 fi
2519 if [ -z "$GF_VIEWONLY" ]; then
2520 rm='<input id="rm" name="rm" type="checkbox"
2521 value="yes"><label for="rm">このエントリの削除</label>
2522 <span>ほんとうに消しますよ(確認)!
2523 <input name="confirm" type=checkbox value="yes">はい</span>'
2524 fi
2525 # Image Cache dir
2526 err genform: getcache=$2/$rowid
2527 td=`getcachedir "$2/$rowid"`
2528 while IFS=: read prompt name keytype type args; do
2529 [ -z "${prompt%%\#*}" ] && continue # skip comment line(#)
2530 sp="${args:+ }"
2531 form="" val=""
2532 if [ -n "$rowid" ]; then
2533 # err genform2a: Seeking for "$2.$name, type=$type"
2534 val=`getvalbyid $2 $name $rowid $td|htmlescape`
2535 err genform3a: getvalbyid $2 $name $rowid $td
2536 err genform3b: val="[$val]"
2537 fi
2538 if [ -n "$GF_VIEWONLY" ]; then
2539 is_hidden "$2" "$name" && continue
2540 fi
2541 case "$type" in
2542 text*)
2543 cgiform=cgi_multi_$type
2544 if [ -s $td/$name.count -a -n "$val" ]; then
2545 form=`$cgiform $name $td`
2546 val=$(echo "$val"|
2547 while read fn; do
2548 echo "<tr><td>`cat $td/$fn`</td></tr>$nl"
2549 done)
2550 val="<table>$nl$val$nl</table>"
2551 else
2552 #form="<input name=\"$name\" value=\"$val\" type=\"$type\"$sp$args>$nl"
2553 err genform: cgi_$type $name $val "$args"
2554 form=`cgi_$type $name "$val" "$args"`
2555 fi
2556 ;;
2557 [Rr][Aa][Dd][Ii][Oo])
2558 fh="<label><input type=\"radio\" name=\"$name\""
2559 form="`echo $args|sed -e \
2560 \"s,\([^ =][^=]*\)=\([^= ][^= ]*\),$fh value=\\"\2\\">\1</label>,g\"`"
2561 ;;
2562 [Cc][Hh][Ee][Cc][Kk][Bb][Oo][Xx])
2563 form="<label><input type=\"checkbox\" name=\"$name\" value=\"${args#*=}\">${args%=*}</label>"
2564 ;;
2565 [Ss][Ee][Ll][Ee][Cc][Tt])
2566 fh="<select name=\"$name\">$nl"
2567 form=$(for l in $args; do
2568 echo "<option value=\"${l#*=}\">${l%=*}</option>"
2569 done)
2570 if [ -n "$val" ]; then
2571 form=`echo $form|sed -e "s,\(value=.$val.\),\\1 selected,"`
2572 fi
2573 form="$fh$form</select>"
2574 ;;
2575 [Ii][Mm][Aa][Gg][Ee]|[Dd][Oo][Cc][Uu][Mm][Ee][Nn][Tt]|[Bb]inary)
2576 if [ -s $td/$name.count ]; then
2577 form=`cgi_multi_file $name $td "$args"`
2578 if [ -n "$val" ]; then
2579 hrfb="$myname?showattc+$2_m"
2580 val=$(echo "$val" \
2581 | while read fn; do
2582 data=`percenthex $td/$fn`
2583 #ct=`cat $td/$fn.content-type`
2584 ct=`file --mime-type $td/$fn|cut -d' ' -f2`
2585 ri=`cat $td/$fn.rowid`
2586 ## err fn=$fn, name=$name, ri=$ri; ls -lF $td 1>&3
2587 #imgsrc="<img src=\"data:$ct,$data\">"
2588 #echo "<a href=\"$hrfb+$ri\">$imgsrc</a><br>"
2589 iconhref $td/$fn "$hrfb+$ri" ""
2590 done)
2591 fi
2592 else
2593 form="<input type=\"file\" name=\"$name\" $args>"
2594 if [ -n "$val" ]; then
2595 imgs=$(echo "$val"\
2596 |while read fn;do
2597 data=`percenthex $td/$fn`
2598 echo "<img src=\"data:image/png,$data\">$fn<br>"
2599 done)
2600 form=$form"<br>$imgs"
2601 val=$imgs # 2015-06-15
2602 else
2603 form="<input type=\"file\" name=\"$name\" $args>"
2604 fi
2605 fi
2606 ;;
2607 [Hh][Ii][Dd][Dd][Ee][Nn])
2608 if [ -n "$GF_STAGE" -a x"$name" = x"stage" ]; then
2609 args="value=\"$GF_STAGE\""
2610 fi
2611 form="<input type=\"hidden\" name=\"$name\" $args>"
2612 prompt='' # Remove prompt
2613 ;;
2614 [Aa][Uu][Tt][Hh][Oo][Rr])
2615 form="<input type=\"hidden\" name=\"author\" value=\"$user\">"
2616 prompt="" ;;
2617 [Oo][Ww][Nn][Ee][Rr])
2618 val=${GF_OWNER:-$val}
2619 val=${val:-$user}
2620 form="<input type=\"hidden\" name=\"owner\" value=\"$val\">"
2621 prompt="" ;;
2622 [Uu][Ss][Ee][Rr])
2623 # XXX: is null $user ok?
2624 #form="<input type=\"hidden\" name=\"user\" value=\"$user\">"
2625 [ -n "$GF_VIEWONLY" ] && continue
2626 form="$user"
2627 ;;
2628 [Pp]assword)
2629 [ -n "$GF_VIEWONLY" ] && continue
2630 form="`cgi_passwd`"
2631 val=""
2632 ;;
2633 [Ss][Ee][Rr][Ii][Aa][Ll]|[Ss][Tt][Aa][Mm][Pp])
2634 if [ -z "$rowid" ]; then
2635 val=$((($(date +%s)-1433084400)/10))c$$
2636 fi
2637 ## form="<input type=\"hidden\" name=\"serial\" value=\"$val\">"
2638 ## 2015-07-31
2639 form="<input type=\"hidden\" name=\"$name\" value=\"$val\">"
2640 prompt="" ;;
2641 [Ss][Ee][Ss][Ss][Ii][Oo][Nn])
2642 prompt=""
2643 ;;
2644 parent|path|blog*)
2645 prompt=""
2646 ;;
2647 "*"*)
2648 tail=$tail"``"
2649 continue ;;
2650 esac
2651 if [ -n "$prompt" ]; then
2652 if [ -n "${GF_VIEWONLY}" ]; then
2653 form=$val
2654 else
2656 fi
2657 forms=$forms" <tr class=\"$name\"><th>$prompt</th><td>$form</td></tr>$nl"
2658 else
2659 hiddens=$hiddens$nl"$form"
2660 fi
2661 done < $1
2662 # enctype="multipart/form-data"
2663 cat<<EOF
2664 <form action="${GF_ACTION:-$myname}" method="POST" enctype="multipart/form-data">
2665 ${rowid:+$rm}
2666 <table class="b $2">
2667 $forms
2668 </table>$hiddens
2669 ${GF_STAGE:+`cgi_hidden stage $GF_STAGE`}
2670 ${rowid:+<input type="hidden" name="rowid" value="$rowid">}
2671 EOF
2672 if [ -z $GF_VIEWONLY ]; then
2673 cat<<EOF
2674 <input type="submit" name="sub" value="OK">
2675 <input type="reset" name="res" value="Reset">
2676 EOF
2677 fi
2678 cat<<EOF
2679 </form>
2680 $tail
2681 EOF
2683 edittable() {
2684 # $1=form-def $2=table $3 rowid
2685 genform "$@"
2687 viewtable() {
2688 GF_VIEWONLY=1 genform "$@"
2690 showattc() {
2691 # $1=table_m $2=rowid
2692 err \$1=$1 \$2=$2
2693 if ! isfilereadable $user $1 $2; then
2694 contenttype; echo
2695 echo "このファイルは管理者のみしか見られません" | html p
2696 putfooter; exit
2697 fi
2698 idir=`umask 002; mktempd` || exit 1
2699 # tmpfiles=$tmpfiles"${tmpfiles+ }$idir"
2700 bin=$idir/$myname-$$.bin
2701 sql="select quote(bin) from $1 where rowid='$2';"
2702 err showattc: sql: $sql
2703 sq $db "$sql" | unhexize > $bin
2704 tv=`query "select type,val from $1 where rowid='$2';"`
2705 type=${tv%\|*} fn=${tv#*\|}
2706 err tv=$tv type=$type fn=$fn, tp2=${tv%\|*}
2707 ct=${type#file:}
2708 case $ct in # all text/* changed to text/plain
2709 text/*)
2710 charset=`nkf -g $bin|cut -d' ' -f1`
2711 case $charset in
2712 ASCII*) charset="" ;;
2713 esac
2714 ct="text/plain${charset:+; charset=$charset}"
2715 ;;
2716 esac
2717 contenttype "$ct"
2718 echo "Content-Disposition: filename=\"$fn\""
2719 echo "Content-Length: " `cat $bin | wc -c`; echo
2720 #echo "Content-Type: " ${type#file:}; echo
2721 cat $bin
2724 # Some default stupid handler on CGI values
2726 default_storedb() {
2727 # ARG: $1=table-def-file
2728 # RET: $tbl=table-name, $col=mail-column, $cols=columns
2729 tbl=`basename $1`
2730 tbl=${tbl%.def}
2731 cols="`grep :text $1|cut -d: -f2`"
2732 col=`echo "$cols"|head -1`
2733 vcol=`getpar $col`
2734 err default0: \$1=$1 col=$col cols="[$cols]" vcol=$vcol
2735 if [ -n "$vcol" ]; then
2736 par2table $1
2737 else
2738 return 2 # No insertion occurred
2739 fi
2742 default_view() { # $1=def-file
2743 ### DT_VIEW="edittable+$tbl" dumptable html $tbl "$cols" \
2744 ## DT_VIEW="edittable+$tbl" dumptable html $tbl "name memo file" \
2745 default_storedb "$@"
2746 query "select rowid from $tbl order by rowid desc;" \
2747 | while read rowid; do
2748 viewtable $1 $tbl $rowid
2749 done | _m4 -D_TITLE_="$tbl" \
2750 -D_FORM_="`genform $1`" \
2751 -D_DUMPTABLE_="syscmd(cat)" \
2752 $layout/html.m4.html $layout/form+dump.m4.html
2754 default_viewtext() { # $1=def-file
2755 ### DT_VIEW="edittable+$tbl" dumptable html $tbl "$cols" \
2756 default_storedb "$@"
2757 DT_VIEW="viewtable+$tbl" dumptable html $tbl "name memo file" \
2758 | _m4 -D_TITLE_="$tbl" \
2759 -D_FORM_="`genform $1`" \
2760 -D_DUMPTABLE_="syscmd(cat)" \
2761 $layout/html.m4.html $layout/form+dump.m4.html
2763 default_smail() {
2764 default_storedb "$@"
2765 if [ $? -eq 2 ]; then
2766 _m4 -D_TITLE_="入力" \
2767 -D_FORM_="`genform $1`" \
2768 -D_DUMPTABLE_="" \
2769 $layout/html.m4.html $layout/form+dump.m4.html
2770 return
2771 fi
2772 cond=""
2773 for pk in `gettblpkey $tbl`; do
2774 pv=$(sqlquote $(getpar $pk))
2775 cond="$cond${cond:+ and }$pk=$pv"
2776 done
2777 sql="select rowid from $tbl where $cond;"
2778 rowid=`query "$sql"`
2779 err smail1 - "$sql" "-> rowid=$rowid"
2781 while IFS=: read prompt name keytype type args; do # Read from $1
2782 val=`getpar $name`
2783 if [ -n "$val" ]; then
2784 text="$text
2785 $prompt
2786 $name=$val
2787 ---------------------------------------------------------"
2788 fi
2789 case "$type" in
2790 image|document|file)
2791 fn="`getvalbyid $tbl $name $rowid $tmpd`"
2792 fns=$(echo "$fn"|while read fn; do
2793 err mv $tmpd/$fn.orig $tmpd/$fn
2794 mv $tmpd/$fn.orig $tmpd/$fn
2795 rm $tmpd/$fn.rowid # Remove cache flag
2796 err "`ls $tmpd/$fn`"
2797 echo $fn
2798 done)
2799 files="$files $fns"
2800 ;;
2801 esac
2802 done < $1
2803 err FILES=$files "`ls -lF $tmpd`"
2804 subj="from ${REMOTE_ADDR}"
2805 (echo "$url"
2806 echo "への書き込みがありました。"
2807 echo "------"
2808 echo "$text"
2809 ) | (cd $tmpd &&
2810 err LS="`ls -lF`" &&
2811 $mydir/sendmultipart.sh -t "$admin" -s "$subj" $files)
2812 _m4 -D_TITLE_="入力完了" $layout/html.m4.html
2813 echo "以下の内容で送信しました。" | html p
2814 viewtable $1 $tbl \
2815 `query "select rowid from $tbl order by rowid desc limit 1;"`
2816 echo "戻る" | html a "href=\"?\""