s4

view s4-funcs.sh @ 196:146f12edc26b

Cut too long titles within $dumpcollen chars.
author HIROSE Yuuji <yuuji@gentei.org>
date Sun, 24 Apr 2016 16:59:25 +0859
parents 40bef4883664
children ffc676bd8277
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 Most Recent Entry
1453 shortval=${dumpcollen:+"substr(val, 0, $dumpcollen)"}
1454 shortval=${shortval:-val}
1455 DT_SQL="SELECT b.rowid, ctime,
1456 (SELECT $shortval FROM blog_s WHERE key='title' AND id=b.id) title,
1457 (SELECT gecos FROM gecoses
1458 WHERE name=(SELECT val FROM blog_s
1459 WHERE key='owner' AND id=b.id)) owner
1460 FROM blog b
1461 JOIN
1462 (SELECT distinct blogid, max(val) ctime
1463 FROM article a, article_s s
1464 ON a.id=s.id AND a.author='$user' AND s.key='ctime'
1465 GROUP BY blogid ORDER BY val DESC LIMIT 50
1466 ) m
1467 ON b.id=m.blogid;"
1468 cat<<-EOF
1469 <div class="fold dumptable">
1470 `cgi_checkbox mrd yes id="mre"`<label for="mre">
1471 最近書き込んだ場所一覧</label>
1472 `DT_VIEW=replyblog dumptable html blog`
1473 </div>
1474 EOF
1475 unset DT_SQL
1476 # Display NEWS
1477 if false; then
1478 cond="where 新着 > 0 order by 新着 desc,ctime desc limit 10"
1479 new10=`DT_CHLD=article:blogid \
1480 DT_VIEW=replyblog dumptable html blog "ctime title gecos" "$cond"`
1481 else
1482 # 2016-02-19 Counting NEWS without using dumptable.
1483 sql=`listnewblogsql "$user"`
1484 new10=`DT_SQL="$sql" DT_VIEW=replyblog dumptable html blog`
1485 fi
1486 cont=`echo "$new10"|grep "^<TR>"|wc -l`
1487 cont=$((cont-1))
1488 err newcount=$cont
1489 if [ $cont -gt 0 ]; then
1490 echo "全体の新着記事${cont}傑" | html h2
1491 echo "$new10"
1492 fi
1493 cat<<EOF
1494 <div class="fold">
1495 `cgi_checkbox srchall yes id="sa"`<label
1496 for="sa">SNS全体から検索</label>
1497 <div>
1498 EOF
1499 cgi_form searchart<<EOF
1500 <label>`cgi_text kwd`という語を含む記事を全部から検索</label>
1501 EOF
1502 cat<<EOF
1503 </div></div>
1504 EOF
1505 fi
1507 # Record access log
1508 [ -n "$1" ] && [ x"$1" != x"$user" ] && acclog user $1
1510 commission() { # $1=grp-rowid $2=user-rowid
1511 contenttype; echo
1512 err commission: "$@"
1513 gname=`getgroupbyid $1`
1514 echo "グループ $gname 管理者委任" \
1515 | _m4 -D_TITLE_="syscmd(\`cat')" $layout/html.m4.html
1516 if [ -n "$2" ]; then
1517 grp_reg_adm "$@"
1518 else
1519 echo "無効な指定です。普通のアクセスならここに来ないはず。"|html p
1520 fi
1522 listgroupbytable() {
1523 # $1=deffile $2...=condition
1524 tagline=`grep :tag: $1`; shift
1525 and="${1:+and }" where=${1:+where }
1526 href="<a href=\"$myname?grp+"
1527 echo '<div class="listgroup">'
1528 NGsql="select distinct tag from\
1529 (select gname, max(case key when 'tag' then val end) as tag, \
1530 max(case key when 'ctime' then val end) as ctime\
1531 from grp_s group by gname order by ctime);"
1532 sql="select val from grp_s where key='tag' $and$* group by val;"
1533 err ListGRP: query sql="$sql"
1534 for tag in `query "$sql"`
1535 do
1536 err ListGrp: tag=$tag
1537 tn=${tagline%%=${tag}*}
1538 tn=${tn##*[ :]}
1539 sql="select rowid||':'||gname as 'グループ名',説明 from
1540 (select (select rowid from grp g where g.gname=grp_s.gname)
1541 as rowid,
1542 gname,
1543 max(case key when 'gecos' then val end) as '説明',
1544 max(case key when 'tag' then val end) as 'tag',
1545 max(case key when 'mtime' then val end) as mtime from grp_s
1546 $where$* group by gname having tag='$tag' order by mtime desc);"
1547 err PersonalGroupList= `echo $sql`
1548 echo "<h2>$tn</h2>"
1549 echo '<table class="b listgroup">'
1550 sq -header -html $db "$sql" \
1551 | sed "s,\(<TR><TD>\)\([0-9]*\):\([^ ]*\)</TD>,\1$href\2\">\3</a>,"
1552 echo '</table>'
1553 done
1554 echo '</div>'
1556 iconhref() (
1557 # $1=icon-file, $2=Href $3=title $4...=anchor
1558 data=`percenthex $1`
1559 ct=`file --mime-type $1|cut -d' ' -f2`
1560 err iconhref: \$1=$1 \$2=$2 \$3="$@"
1561 href=$2; title=$3; shift 3
1562 echo "<a href=\"$href\"><img title=\"$title\" src=\"data:$ct,$data\">$@</a>"
1564 iconhref2() (
1565 # $1=icon-file, $2=Href $3=title $4...=anchor
1566 src=$1
1567 href=$2; title=$3; shift 3
1568 echo "<a href=\"$href\"><img title=\"$title\" src=\"$src\">$@</a>"
1570 listentry() (
1571 # $1=user/group $2=SearchKeyword $3=condition(if any)
1572 # Referring variable $iamowner=$grp to attach owner-request links
1573 err listentry: \$1=$1 \$2=$2 \$3=$3
1574 cond=''
1575 offset=`getpar offset`
1576 offset=${offset%%[!0-9]*}
1577 offset=$((offset + 0)) # change to numeric forcibly
1578 [ $offset -lt 0 ] && offset=0
1579 limit=30
1580 dir=`getcachedir "$1"`
1581 if [ x"$1" = x"user" ]; then
1582 hrb="$myname?home"
1583 deficon=person-default.png
1584 entity="ユーザ" tbl=user link=rowid nm=name stage=mems
1585 gcs=gecos
1586 else # if group
1587 hrb="$myname?grp"
1588 deficon=group-default.png
1589 entity="グループ" tbl=grp link=rowid nm=gname stage=grps
1590 gcs=name
1591 tagline=`grep :tag: $formdir/grp.def|cut -d: -f5-`
1592 if [ -n "$tagline" ]; then
1593 tagconv=`echo $tagline|sed 's/\([^= :]*\)=\([^= :]*\)/-D\2=\1/g'`
1594 err tagconv=$tagconv
1595 fi
1596 fi
1597 if [ ! -d $dir ]; then
1598 mkdir -p $dir
1599 fi
1600 if [ ! -s $dir/$deficon ]; then
1601 convert -geometry $thumbxy $imgdir/$deficon $dir/$deficon
1602 fi
1603 if [ -n "$2" ]; then
1604 cond="where nick like '%$2%' or b.name like '%$2%'"
1605 fi
1607 # XX: これ複雑すぎるかな。もっとシンプルにしたい。$3条件も。2015-07-08
1608 # grpは呼出し元の動的スコープ変数でよくないな...
1609 ##qgrp=`sqlquote $grp`
1610 getgrp="(select gname from grp where rowid=${rowid:--1})"
1611 sql="select a.rowid, a.$link, coalesce(b.$gcs, a.$nm) as nick,
1612 coalesce(b.gecos, a.$nm) as name,
1613 b.tag,
1614 case when a.$nm in (select user from grp_adm
1615 where gname=$getgrp) then '(管理者)'
1616 when '$user' in (select user from grp_adm where gname=a.$nm)
1617 then '(ADMIN)'
1618 when '$user' in (select user from grp_mem where gname=a.$nm)
1619 then '(Member)'
1620 when '$iamowner' = '' then ''
1621 else ',not='||a.rowid end as ownerlink
1622 from $tbl a left join
1623 (select $nm as name,
1624 max(case key when 'gecos' then val end) as gecos,
1625 max(case key when 'tag' then val end) as tag
1626 from ${tbl}_s group by $nm)
1627 b on a.$nm=b.name $cond $3
1628 order by b.tag desc, a.rowid asc"
1629 err LE:sql.1="$sql"
1630 total=`query "with x as ($sql) select count(*) from x;"`
1631 echo "${entity} 一覧" | html h2
1632 if [ $total -gt $limit ]; then
1633 echo '<div class="right">'
1634 cgi_form $stage <<EOF
1635 <label>次の語を含む${entity}で検索:
1636 `cgi_text kwd $kwd`</label>
1637 EOF
1638 echo '</div>'
1639 fi
1640 cat<<EOF
1641 <p>${total}件中の$((offset+1))件めから${kwd:+" - 検索語: $kwd"}</p>
1642 EOF
1643 if [ $((offset+limit)) -lt $total ]; then
1644 cat<<EOF
1645 <div class="right"><form action="$myname" method="POST">
1646 `cgi_submit 次の${limit}件`
1647 `cgi_hidden kwd "$kwd"`
1648 `cgi_hidden stage "$stage"`
1649 `cgi_hidden offset $((offset + limit))`</form></div>
1650 EOF
1651 fi
1652 if [ $offset -gt 0 ]; then
1653 cat<<EOF
1654 <form action="$myname" method="POST">
1655 `cgi_submit 前の${limit}件`
1656 `cgi_hidden stage "$stage"`
1657 `cgi_hidden kwd "$kwd"`
1658 `cgi_hidden offset $((offset - limit))`</form>
1659 EOF
1660 fi
1662 err ListEntry: `echo "$sql"\;`
1664 query "$sql limit $limit ${offset:+offset $offset};" \
1665 | while IFS='|' read id lnk name gecos tag ownerp; do
1666 err name=$name owner=$ownerp lnk=$lnk
1667 err newlnk=$lnk
1668 files=`getvalbyid $tbl profimg $id $dir`
1669 # Pick up only first icon
1670 echo "<div class=\"iconlist xy$thumbxy\"><p class=\"tag _$tag\">$tag</p>" \
1671 | _m4 $tagconv
1672 if [ -n "$files" ]; then
1673 icon=`echo "$files"|head -1`
1674 iconhref $dir/$icon "$hrb+$lnk" "$gecos"
1675 else
1676 iconhref $dir/$deficon "$hrb+$lnk" "$gecos"
1677 fi
1678 echo "<br>$name${ownerp:+<br>$ownerp}"
1679 echo "</div>"
1680 done
1682 listmember() {
1683 listentry user "$@"
1685 listgroup() {
1686 listentry group "$@"
1688 showgroup() { # $1=group-rowid
1689 # gname=`getpar gname`
1690 # if [ -n "$gname" ]; then
1691 # err UPdating/Removing of group:::::::
1692 # # See ALSO same job in groupman()
1693 # newgname=`echo "$gname"|tr -d '\"'"'"`
1694 # ###newgname=$gname
1695 # err gname=$gname newgname=$newgname
1696 # if [ x"$newgname" != x"$gname" ]; then
1697 # err NewGNAME: gname=$newgname
1698 # gname=$newgname
1699 # echo "使用禁止文字を除去し $gname としました。" | html p
1700 # replpar gname string "$gname"
1701 # fi
1702 # par2table $formdir/grp.def
1703 # fi
1704 grp=`getgroupbyid $1`
1705 err showgroup2: grp=$grp qgrp="[$(sqlquote $grp)]"
1706 if isgroup "$grp"; then
1707 tf=$tmpd/title.$$
1708 echo "グループ $grp" > $tf
1709 showgroupsub $formdir/grp.def "$1" | \
1710 _m4 -D_TITLE_="spaste(\`$tf')" \
1711 -D_FORM_="syscmd(\`cat')" \
1712 -D_DUMPTABLE_="" \
1713 $layout/form+dump.m4.html
1714 else # if $grp is removed at par2table
1715 listgroup
1716 fi
1718 showgroupsub() {
1719 # $1=def-file $2=group-rowid
1720 rowid=$2
1721 grp=`getgroupbyid $2`
1722 qgrp=`sqlquote $grp`
1723 #rowid=`sq $db "select rowid from grp where gname=$qgrp"`
1724 if [ -z "$rowid" ]; then
1725 #rowid=`sq $db "select rowid from grp where rowid=$grp"`
1726 #grp=`sq $db "select gname from grp where rowid=$grp"`
1727 echo "showgroupsub: invalid argument($1 $2)" | html p
1728 return
1729 fi
1730 val=`getvalbyid grp profimg $rowid $tmpd`
1731 # 6/14の次グループのHOMEで出す情報を作る Done
1732 viewtable $1 grp $rowid
1733 if isgrpowner "$user" "$grp"; then
1734 echo "<p><a href=\"?groupconf+$rowid\">グループ情報の編集</a>"
1735 iamowner=$rowid
1736 fi
1737 if ismember "$user" "$grp"; then
1738 echo "${iamowner:+ / }<a href=\"?blog+$rowid\">グループの新規話題作成</a>"
1739 echo "/ <a href=\"?grpaction+$rowid\">メンバーを個別選択しての操作</a></p>"
1740 # div.fold input[type="checkbox"]:checked ~ div {display: block;}
1741 cat<<EOF
1742 <form action="?send2mem" method="POST" enctype="multipart/form-data">
1743 <div class="fold">
1744 `cgi_checkbox send yes id="send"`<label
1745 for="send">グループ全員にメッセージ送信</label>
1746 <div>
1747 `cgi_textarea message "" "cols=60"`
1748 `cgi_submit 送信`
1749 `cgi_reset リセット`
1750 </div>
1751 `cgi_hidden grp $rowid`
1752 </div></form>
1753 EOF
1754 fi
1755 # 加入ボタン + 加入者リスト
1756 err ismember $user $grp
1757 ismember "$user" "$grp" && ismem='checked' || nomem='checked'
1758 # このグループでの加入アドレス
1759 eml=`query "select val from grp_mem_s where gname='$2' and user='$user' \
1760 and key='email';"`
1761 err EML: "select val from grp_mem_s where gname='$2' and user='$user' \
1762 and key='email';"
1763 err email=$eml
1764 cat <<EOF
1765 <div class="fold">
1766 `cgi_checkbox reg yes id="reg"`<label
1767 for="reg">自身の参加状態を操作する</label>
1768 <div>
1769 EOF
1770 cgi_form grp <<EOF
1771 <p>このグループに</p>
1772 <table class="b">
1773 <tr><th>メンバーとして</th><td>
1774 <label>`cgi_radio joingrp "yes" $ismem`参加</label> /
1775 <label>`cgi_radio joingrp "no" $nomem`参加しない</label></td></tr>
1776 <tr><th>参加する場合のメイルアドレス<br>
1777 <small>(メインのアドレスとは違うものにする場合に記入<br>
1778 同じでよい場合は空欄に)</small></th>
1779 <td>`cgi_text email $eml`</td></tr>
1780 </table>
1781 `cgi_hidden grp $rowid`
1782 EOF
1783 if [ x`getgroupattr $grp regmode` = x'moderated' -a -z "$ismem" ]; then
1784 echo "moderated (承認加入の)グループなので実際に参加できるのは
1785 グループ管理者が承認操作をした後になります。" | html p 'class="warn"'
1786 fi
1787 echo '</div></div>'
1788 echo '<h2>話題一覧</h2>'
1789 cgi_form searchart<<EOF
1790 <label>`cgi_text kwd`という語を含むコメントを検索</label>
1791 `cgi_hidden owner $grp`
1792 EOF
1793 cond="where a.id in (select id from blog_s where key='owner' and val=$qgrp) order by ctime desc"
1794 DT_CHLD=article:blogid \
1795 DT_VIEW=replyblog dumptable html blog 'ctime title heading' "$cond"
1797 getgname="(select gname from grp where rowid=$rowid)"
1798 c="group by b.name having b.name in (select user from grp_mem where gname=$getgname)"
1799 cm="?commission+$rowid"
1800 thumbxy=50x50 listmember "" "$c" \
1801 |sed -e "s|\(<br>\),not=\(.*\)|\1|" # 間違って押しやすい
1802 ## |sed -e "s|\(<br>\),not=\(.*\)|\1<a href=\"$cm+\2\">管理者委託</a>|"
1804 grpaction() { # $1=group-rowid
1805 err GRP_ACTION:IN
1806 grid=${1:-`getpar grp`}
1807 grp=`getgroupbyid "$grid"`
1808 if [ -z "$grp" ]; then
1809 echo "無効な指定です。" | html p; return
1810 fi
1811 if ! ismember $user $grp; then
1812 echo "加入者のみに許可された操作です。" | html p; return
1813 fi
1814 echo "グループ $grp 個別選択操作" \
1815 | _m4 -D_TITLE_="syscmd(\`cat')" $layout/html.m4.html
1817 isowner=""
1818 isgrpowner "$user" "$grp" && isowner="yes"
1819 usel=`getpar usel`
1820 if [ -n "$usel" ]; then
1821 uids=$(echo `echo $usel`|tr ' ' ',')
1822 err grpaction-1: grp=$grp, `echo $sql`
1823 text=`getpar text`
1825 rm=`getpar rm` cfm=`getpar confirm`
1826 err rm=$rm cfm=$cfm
1827 if [ x"$rm" = x"yes" ]; then
1828 if [ "$isowner" ]; then
1829 if [ x"$rm$cfm" = x"yesyes" ]; then
1830 # Eliminate
1831 cond="where gname=(select gname from grp where rowid=$grid) and user in (select name from user where rowid in ($uids))"
1832 for tbl in grp_mem grp_mem_s grp_mem_m; do
1833 sql="delete from $tbl $cond;"
1834 # echo "sql=$sql"
1835 query "$sql"
1836 err rmGRPuser "$sql"
1837 done
1838 num=`query "select count(*) from user where rowid in ($uids);"`
1839 #err num=$num
1840 if [ 0$num -gt 0 ]; then
1841 sql="select coalesce(b.val,a.name) from user a left join \
1842 user_s b on a.name=b.name and key='gecos' where a.rowid in ($uids);"
1843 # err `echo "$sql"`
1844 html pre<<EOF
1845 以下の${num}名のグループ $grp 登録を解除しました。
1846 `query "$sql"`
1847 EOF
1848 fi
1849 else
1850 echo "確認のチェックがないのでやめておきます。" | html p
1851 return
1852 fi
1853 else # not Group Owner
1854 echo "グループ管理者でないのでメンバー操作はできません。" | html p
1855 return
1856 fi
1857 cat<<EOF
1859 EOF
1860 elif [ x"$rm" = x"send" ]; then # if sendmsg mode
1861 if [ -z "$text" ]; then # if msg is empty
1862 echo "なにかメッセージを..." | html p
1863 return 0
1864 fi
1865 smail "`email4groupbyuid "$grp" $usel` $user" \
1866 "`gecos $user` さんからのメッセージ" <<EOF
1867 $url
1868 のグループ「$grp」のメンバーである `gecos $user` さんから、
1869 あなた宛へのメッセージです。
1870 ----------------------------------------------------------
1871 $text
1872 EOF
1873 if [ $? = 0 ]; then
1874 echo "Note: 以下のメンバーにメッセージを送信しました。" | html p
1875 sql="select coalesce(b.val, a.name) from
1876 (select name from user where rowid in ($uids)) a
1877 left join user_s b on a.name=b.name and b.key='gecos';"
1878 html pre<<EOF
1879 `query "$sql"`
1880 (送信者である `gecos $user` さんも含まれます)
1881 EOF
1882 err SendDone: `echo $sql`
1883 fi
1884 elif [ x"$rm" = x"commission" ]; then
1885 grp_reg_adm $grid $usel
1886 fi
1887 fi
1888 # New entry
1889 sql="select u.rowid||','||j.* from user u
1890 join (select a.user as name,coalesce(b.val,a.user)
1891 from (select gname,user from grp_mem
1892 where gname=(select gname from grp where rowid=$1))
1893 a left join (select gname,user,val from grp_mem_s where key='email')
1894 b using(gname,user))
1895 j using(name) order by u.name;"
1896 sql="select /* Ahh, ugly SQL, I wanna fix... */
1897 case
1898 when (select user from grp_adm where
1899 gname=(select gname from grp where rowid=$grid)
1900 and user=a.name) is not null
1901 then 'k'
1902 else ''
1903 end || a.rowid||
1904 ','||coalesce(val,a.name) as name from
1905 (select rowid,name from user where name in
1906 (select user from grp_mem where
1907 gname=(select gname from grp where rowid=$grid)))
1908 a left join user_s on a.name=user_s.name and key='gecos'
1909 order by coalesce(val,a.name);"
1910 err grpaction: `echo $sql`
1911 b1='<label> <input type="checkbox" name="usel" value="'
1912 ba='<label class="admin"><input type="checkbox" name="usel" value="'
1913 b2='"> <span>' b3='</span></label>'
1914 tf=$tmpd/title.$$
1915 echo "グループ[$grp]参加メンバーに対する操作" > $tf
1916 cmmsg="<div class=\"fold\">
1917 `cgi_radio rm commission id=\"cmadmin\"` <label
1918 for=\"cmadmin\">下でチェックした人にグループ管理者委任</label>
1919 <div><p>このグループでの全権を付与します。信頼できる人に託してください。
1920 </p></div></div>"
1921 excmsg="<div class=\"fold\">
1922 `cgi_radio rm yes ` 下でチェックした人のグループ登録解除
1923 <div>
1924 本当に消します! `cgi_checkbox confirm yes` 確認
1925 <p>この操作による通知は本人に行きません。
1926 あらかじめ通知するか、登録解除してよい状況かしっかり確認してください。</p>
1927 </div>
1928 </div>"
1929 cgi_form grpaction<<EOF \
1930 | sed -e "s|^\(<TR><TD>\)k\([0-9]*\),\([^<]*\)|\1$ba\2$b2\3$b3|" \
1931 -e "s|^\(<TR><TD>\)\([0-9]*\),\([^<]*\)|\1$b1\2$b2\3$b3|" \
1932 | _m4 -D_TITLE_="spaste(\`$tf')" \
1933 -D_SUBTITLE_="チェック後操作ボタン" \
1934 -D_FORM_="syscmd(cat)" -D_DUMPTABLE_="" \
1935 $layout/form+dump.m4.html
1936 <div class="fold">
1937 `cgi_radio rm send id="sendmsg"`<label
1938 for="sendmsg">下でチェックした人にメッセージを送信する</label>
1939 <div>
1940 `cgi_textarea text "" cols=40`
1941 </div>
1942 </div>
1943 ${isowner:+$cmmsg$excmsg}
1944 <h4>$grp 参加者一覧</h4>
1945 <table>
1946 `sq $db -html "$sql"`
1947 `cgi_hidden grp $grid`
1948 </table>
1949 EOF
1951 editheading() { # $1=rowid-of-heading
1952 rowid=${1%%[!A-Z0-9a-z_]*}
1953 if [ -z "$rowid" ]; then
1954 echo "話題番号が未指定です。" | html p
1955 return
1956 fi
1957 owner=`getvalbyid blog owner $rowid`
1958 title=`getvalbyid blog title $rowid`
1959 GF_ACTION="?blog" edittable $formdir/blog.def blog $rowid \
1960 | _m4 -D_TITLE_="修正" \
1961 -D_SUBTITLE_="[$title]@$owner" -D_DIARY_="" \
1962 -D_BLOGS_="" -D_DUMPTABLE_="" \
1963 -D_FORM_="syscmd(\`cat')" \
1964 $layout/html.m4.html $layout/form+dump.m4.html
1966 editart() { # $1=article-rowid $2=blogrowid
1967 rowid=${1%%[!A-Z0-9a-z_]*}
1968 blogrowid=${2%%[!A-Z0-9a-z_]*}
1969 if [ -z "$rowid" -o -z "$blogrowid" ]; then
1970 echo "表示する記事番号が未指定です。" | html p
1971 return
1972 fi
1973 owner=`getvalbyid blog owner $blogrowid`
1974 title=`getvalbyid blog title $blogrowid`
1975 author=`getvalbyid article author $rowid`
1976 err EDITart: owner=$owner, author=$author
1977 if isgrpowner $user $owner; then
1978 : EDIT OK
1979 elif [ x"$owner" != x"$user" -a x"$author" != x"$user" ]; then
1980 echo "本人か所有者しか編集できません." | html p
1981 return
1982 fi
1983 aid=`query "select id from article where rowid=$rowid;"`
1984 tmpout=$tmpd/editart.$$.out
1985 GF_ACTION="?replyblog+$blogrowid#$aid" \
1986 edittable $formdir/article.def article $rowid \
1987 > $tmpout
1988 rm -f /tmp/editart.out
1989 # Cannot use pipelining to m4 with genform() because of stdin stack
1990 _m4 -D_TITLE_="コメントの修正" -D_DIARY_="" \
1991 -D_FORM_="syscmd(cat $tmpout)" \
1992 -D_SUBTITLE_="`gecos $owner`の「$title」" \
1993 -D_BLOGS_= -D_DUMPTABLE_= \
1994 $layout/html.m4.html $layout/form+dump.m4.html
1996 send2mem() {
1997 rowid=`getpar grp`
1998 if [ -z "$rowid" ]; then
1999 echo "グループが未指定です。" | html p
2000 return
2001 fi
2002 message=`getpar message`
2003 if [ -z "$message" ]; then
2004 echo "文章を入れてください。" | html p
2005 return
2006 fi
2007 grp=`getgroupbyid $rowid`
2008 members=`collectemail $grp`
2009 # smail rcpt subj (file)
2010 smail "$members" "グループ $grp 宛メッセージ(from `gecos $user`)" <<EOF
2011 $urlbase?grp+$rowid
2012 グループ $grp に所属する
2013 `gecos $user` さんよりメッセージ:
2015 $message
2016 EOF
2017 cat<<EOF
2018 <p>以下の宛先に送信しました。</p>
2019 <pre>
2020 $members
2021 </pre>
2022 <p><a href="?grp+$rowid">グループ $grp</a>に戻る。</p>
2023 EOF
2025 joingrpadmit() {
2026 # $1=yes/no $2=session-key
2027 if [ -z "$2" ]; then
2028 echo "bye bye" | html p; return
2029 fi
2030 t_usr=`session=$2 getpar user`
2031 t_grp=`session=$2 getpar group`
2032 err joingrpadmit: t_usr=$t_usr, t_grp=$t_grp
2033 _m4 -D_TITLE_="joingrp" $layout/html.m4.html
2034 if [ -z "$t_usr" -o -z "$t_grp" ]; then
2035 echo "無効な加入依頼です。" | html p
2036 echo "有効期限が切れたか、
2037 他の管理者がいる場合は処理済みの可能性があります。" | html p
2038 return
2039 fi
2040 if ! isgrpowner "$user" $t_grp; then
2041 echo "グループ管理者のみの機能です。" | html p; return
2042 fi
2043 case $1 in
2044 yes) joingrp "$t_grp" "$t_usr" yes "$t_usr" ;;
2045 no) joingrp "$t_grp" "$t_usr" no "$t_usr" ;;
2046 *)
2047 echo "無効な指定です($1)。" | html p
2048 return ;;
2049 esac
2050 gid=$(query "select rowid from grp where gname=`sqlquote $t_grp`;")
2051 rcpts="`getgroupadminmails $t_grp` $user"
2052 err admit: msgdir=$msgdir, rcpts="["$rcpts"]"
2053 body="グループ $t_grp
2054
2055 $t_usr
2056 `[ x$1 = xyes ] && echo 'を追加' || echo 'の解除操作を'`
2057 しました。"
2058 (echo "$body"; echo; echo "$url?grp+$gid") | smail "$rcpts" "joingrp $1"
2059 query "delete from session where id='$2';"
2060 echo "$body" | html p
2063 joingrprequest() {
2064 # $1=group $2=user $3=yes/no $4=email(if any $5=AsAdmin)
2065 jss="joingrp-`date +%s`-`genrandom 12`"
2066 addsession $jss +${memoplimitdays}days
2067 query "replace into par values('$jss', 'group', 'string', `sqlquote $1`),
2068 ('$jss', 'user', 'string', `sqlquote $user`);"
2069 smail "$(collectemail `getgroupadmins $1`)" "Join request to $1"<<EOF
2070 $url
2071 グループ $1
2072 に加入依頼がありました。
2074 承認する:
2075 $urlbase?joingrpadmit+yes+$jss
2077 白紙に戻す:
2078 $urlbase?joingrpadmit+no+$jss
2079 EOF
2080 echo "管理者に加入依頼を出しました。
2081 ${memoplimitdays}日以内に加入承認操作がされれば加入できますが、
2082 グループ運用方針に懸かることですので直接の問い合わせが重要です。" | html p
2084 joingrp() {
2085 # $1=group $2=user $3=yes/no $4=email(if any $5=AsAdmin)
2086 err joingrp: \$1=$1 \$2=$2 \$3=$3 \$4=$4
2087 isgrpowner "$user" "$1" && isowner="yes" || isowner=""
2088 err jg:isgrpowner: isowner="$isowner"
2089 if [ -n "$isowner" ]; then
2090 : # GROUP OWNER CAN DO EVERYTHING ABOUT REGISTRATION/RETIREMENT
2091 elif [ x"$2" != x"$user" ]; then # if user is not login user
2092 echo "本人か、グループ管理者しか加入操作はできません。" | html p
2093 return
2094 elif [ x"$3" = x"no" ]; then
2095 : # Do not pursue those who leave
2096 else # adding user is $user itself
2097 case `getgroupattr $1 regmode` in
2098 moderated)
2099 joingrprequest "$@" # Request only
2100 return
2101 ;;
2102 *)
2103 ;;
2104 esac
2105 fi
2106 qgname=`sqlquote $1`
2107 cond="where gname=$qgname and user='$2'"
2108 if [ x"$3" = x"yes" ]; then
2109 query "replace into grp_mem values($qgname, '$2');"
2110 if [ -n "$4" ]; then
2111 if msg=`emaildomaincheck "$4"`; then
2112 err "replace into grp_mem_s values($qgname, '$user', 'email', \
2113 'string', '$4', NULL);"
2114 query "replace into grp_mem_s values($qgname, '$user', 'email', \
2115 'string', '$4', NULL);"
2116 if [ -n "$5" ]; then # as ADMIN
2117 # Coming here means newly created group
2118 sql="select case\
2119 when (select count(*) from grp_mem where gname=$qgname)=1\
2120 then (select user from grp_mem\
2121 where gname=$qgname and user='$user')\
2122 else '' end;"
2123 err NewGrpChk: $sql
2124 if [ -n "`query \"$sql\"`" ]; then
2125 err ADMIN: "replace into grp_adm values($qgname, '$user');"
2126 query "replace into grp_adm values($qgname, '$user');"
2127 fi
2128 fi
2129 else
2130 echo $msg
2131 fi
2132 else
2133 query "delete from grp_mem_s $cond and key='email';"
2134 fi
2135 else
2136 query "delete from grp_mem $cond;
2137 delete from grp_mem_s $cond;
2138 delete from grp_mem_m $cond;"
2139 fi
2141 grp_reg_adm() {
2142 # $1=grp-rowid $2...=user-rowid
2143 grid=$1
2144 grp=`getgroupbyid "$1"`
2145 if [ -z "$grp" ]; then
2146 echo "無効なグループIDです" | html p; return
2147 fi
2148 if ! isgrpowner $user "$grp"; then
2149 echo "$grp グループの管理者しかこの操作はできません。" | html p; return
2150 fi
2151 shift
2152 for urid; do
2153 newadm=`query "select name from user where rowid=$urid;"`
2154 if [ -z "$newadm" ]; then
2155 echo "指定ユーザIDがおかしいようです。" | html p; return
2156 fi
2157 err GRP_reg_adm: "replace into grp_adm values(`sqlquote $grp`, '$newadm');"
2158 err ismember $newadm $grp
2159 if ismember $newadm $grp; then
2160 # OK, go ahead
2161 getgname="(select gname from grp where rowid=$grid)"
2162 query "replace into grp_adm values($getgname, '$newadm');"
2163 # confirm insertion
2164 sql="select * from grp_adm where gname=$getgname and user='$newadm'"
2165 if [ -n "`query \"$sql;\"`" ]; then
2166 echo "追加完了: $newadm" | html p
2167 else
2168 echo "追加失敗($1 $urid)" | html p
2169 fi
2170 fi
2171 showgroup $grid
2172 done
2174 dumptable() {
2175 # $1=mode $2=Table $3=column-list-of-*_s(defaults to *) $4=conditions(if any)
2176 # textのフィールドだけ全てダンプにしたほうがいいか
2177 # $DT_VIEW sets link
2178 # 6/17の次: editリンクじゃなくてスレッドVIEWリンクでいいんちゃう?
2179 ### elink="<a href=\"$myname?edittable+$2+\\2\">EDIT</a>"
2180 VIEW=${DT_VIEW-replyblog}
2181 if [ -n "$VIEW" ]; then
2182 dvlink=" <a href=\"$myname?$VIEW+\\2\">VIE</a><a href=\"$myname?$VIEW+\\2#bottom\">W</a>"
2183 fi
2184 # $DT_CHLD=ChildTable:BindColumn
2185 if [ -n "$DT_CHLD" ]; then
2186 _t=${DT_CHLD%:*} _i=${DT_CHLD#*:}
2187 cntall="(select count($_i) from $_t where $_i=a.id)"
2188 cntnew="(select count(val) from ${_t}_s where key='ctime' \
2189 and id in (select id from $_t where $_i=a.id) \
2190 and val > coalesce((select time from acclog where \
2191 user='$user' and tbl='$2' and tblrowid=a.rowid),\
2192 '1970-01-01'))"
2193 cnt="$cntnew as '新着', $cntall as '総数',"
2194 dt_class=" td2r td3r"
2195 fi
2196 # Construct join expression
2197 eav="" scols=""
2198 pk=`gettblpkey $2`
2199 substr=${dumpcollen:+"substr(val, 0, $dumpcollen)"}
2200 substr=${substr:-val}
2201 for col in ${3:-`gettbl_s_cols $2`}; do
2202 case $col in
2203 gecos) scols="$scols${scols:+, }${col#}"
2204 continue ;; # built-in column name
2205 *:*) col=${col%:*} as=${col#*:} ;;
2206 *) as=${col} ;;
2207 esac
2208 eav=$eav${eav:+,}" max(case key when '$col' then $substr end) as $as"
2209 scols="$scols${scols:+, }b.$as"
2210 done
2211 #case author when '$user' then a.rowid else '---' end as ID,
2212 sql=${DT_SQL:-"select \
2213 a.rowid as ID,\
2214 $cnt\
2215 $scols from $2 a left join\
2216 (select $pk,$eav,
2217 max(case key when 'owner'
2218 then coalesce((select val from user_s us
2219 where us.key='gecos' and us.name=c.val), val) end) as gecos
2220 from ${2}_s c group by $pk) b on a.$pk=b.$pk $4;"}
2221 err SQL=`echo "$sql"`
2222 cat<<EOF | sed "s,\(<TR><TD>\)\([1-9][0-9]*\)</TD>,\1$elink$dvlink</TD>,"
2223 <div class="dumptable">
2224 <table class="b$dt_class">
2225 `sq -header -cmd ".mode $1" $db "$sql"`
2226 </table>
2227 </div> <!-- dumptable -->
2228 EOF
2231 par2table() (
2232 # copy current parameters of par into destination table
2233 # $1=definition-file
2234 # Using $user and $session
2235 # Return value:
2236 # 0: Stored successfully
2237 # 1: Insufficient fillings
2238 # 2: No permission to modify the record
2239 # 3: Invalid rowid
2240 # 4: SUCCESS to delete
2241 # 5: Stop deletion for lack of confirm check
2242 # 6: Password length too short
2243 # 7: Password mismatch
2244 # 8: Old password incorrect
2245 rowid=`getpar rowid`
2246 err ...........rowid=$rowid
2247 if [ ! -e $1 ]; then
2248 echo "テーブル定義ファイルが見付かりません" | html p
2249 exit 1
2250 fi
2251 tbl=${1%.def}
2252 tbl=${tbl##*/}
2253 if [ -n "$rowid" ]; then # Modify existing entry
2254 if [ x"$tbl" = x"user" ]; then
2255 rowowner=`query "select name from $tbl where rowid=$rowid;"`
2256 elif [ x"$tbl" = x"grp" ]; then
2257 sql="select gname from $tbl where rowid=$rowid;"
2258 ##err p2t:grp:q $sql
2259 isgrpowner $user "`query $sql`" && rowowner=$user
2260 else
2261 rowowner=`query "select owner from $tbl where rowid=$rowid;"`
2262 rowowner=${rowowner:-`query "select author from $tbl
2263 where rowid=$rowid;"`}
2264 fi
2265 ### err rowowner=$rowowner
2266 if [ x"$user" != x"$rowowner" ]; then
2267 echo "他人のレコードはいじれないの" | html p
2268 return 2
2269 elif [ -z "$rowowner" ]; then
2270 echo "指定したレコードはないみたい" | html p
2271 return 3
2272 fi
2273 rm=`getpar rm` cfm=`getpar confirm`
2274 # Editing existent entry
2275 if [ x"$rm" = x"yes" ]; then
2276 if [ x"$rm$cfm" = x"yesyes" ]; then
2277 query "delete from $tbl where rowid=$rowid;"
2278 return 4
2279 else
2280 echo "消去確認のチェックがないので消さなかったの..." | html p
2281 return 5
2282 fi
2283 fi
2284 fi
2285 # XX: Subshelling here is unnecessary 2015-07-05
2286 (ts=${tbl}_s tm=${tbl}_m val="" pval="" formaster=""
2287 if [ -n "$rowid" ]; then
2288 # Update of existing record
2289 for col in `gettblcols $tbl`; do
2290 val=`getparquote $col`
2291 [ -z "$val" ] && continue
2292 err query "update $tbl set $col=$val where rowid=$rowid"
2293 ## XX: THIS IS DIRTY hack to ensure non-foreign key in blog_s
2294 sql="update $tbl set $col=$val where rowid=$rowid;"
2295 if [ x"$tbl" = x"grp" -a x"$col" = x"gname" \
2296 -o x"tbl" = x"user" -a x"$col" = x"name" ]; then
2297 ## User name cannot be changed with interface provided with this
2298 ## script. But we offer the trigger to change owner user
2299 ## of blog_s table.
2300 err "select quote($col) from $tbl where rowid=$rowid;"
2301 old=`query "select quote($col) from $tbl where rowid=$rowid;"`
2302 cat<<-EOF | query
2303 BEGIN;
2304 $sql
2305 update blog_s set val=$val
2306 where key='owner' and val=$old;
2307 COMMIT;
2308 EOF
2309 ## XX: DIRTY Hack Ends here
2310 ## We should keep blog's owner as a single column which has
2311 ## foreign key constraint with primary key of grp/user.
2312 else
2313 query "$sql"
2314 fi
2315 done
2316 # Then, set up $pval for further insertion of tbl_s and tbl_m
2317 for col in `gettblpkey $tbl`; do
2318 val=`query "select $col from $tbl where rowid=$rowid;"|sed -e 's/\"/\"\"/g'`
2319 pval="$pval${pval:+, }\"$val\""
2320 done
2321 else
2322 # New entry
2323 # Generate values() for primary keys
2324 for col in `gettblpkey $tbl`; do
2325 # Genuine primary keys for _m and _s
2326 val=`getvalquote $tbl $col`
2327 [ -z "$val" ] && continue
2328 pval="$pval${pval:+, }$val"
2329 done
2330 err pval=$pval
2331 for col in `gettblfkey $tbl`; do
2332 # args for values() to insertion into master table
2333 val=`getvalquote $tbl $col`
2334 [ -z "$val" ] && continue
2335 formaster=$formaster"${formaster:+, }$val"
2336 done
2337 formaster="$pval${formaster:+, }$formaster"
2338 err formaster=$formaster
2339 if [ -z "$formaster" ]; then
2340 echo "項目を全て埋めてください" | html pre
2341 return 1
2342 fi
2343 err "replace into $tbl values($formaster);"
2344 query "replace into $tbl values($formaster);"
2345 ## Insertion to master table, done
2346 fi
2348 for kt in s m; do
2349 tb2=${tbl}_$kt
2350 for col in `gettbl_${kt}_cols $tbl`; do
2351 ptype=`getpartype $col "limit 1"`
2353 # First, check update of existing entries in _m
2354 if [ $kt = m ]; then
2355 # sessID|address.1.22|string|Somewhere-x.y.z
2356 sql=""
2357 err dots from query "select var from par where var like '$col.%';"
2358 for v in `query "select var from par where var like '$col.%';"`; do
2359 # v=address.1.22
2360 st_rowid=${v##*.}
2361 origcol=${v%%.*} # original column derived from
2362 err Updating for $v st_rowid=$st_rowid, partype=`getpartype $v`
2363 ##case `getpartype $v` in
2364 err CASE `gettbl_coltype $tbl/$origcol` in
2365 err edit flag = `getpar action.$v`
2366 case `getpar action.$v` in
2367 rm)
2368 if [ x`getpar confirm.$v` = x"yes" ]; then
2369 newsql="delete from $tb2"
2370 else
2371 echo "削除確認未チェック" | html p
2372 fi ;;
2373 edit)
2374 case `gettbl_coltype $tbl/$origcol` in
2375 image|document|binary)
2376 file=$tmpd/`getparfilename $v`
2377 err type=file=$file
2378 [ -z "$file" ] && continue
2379 bn=${file##*/}
2380 bin="X'"$(hexize $file)"'"
2381 ct=`file --mime-type $file|cut -d' ' -f2`
2382 type=\"file:$ct\"
2383 newsql="update $tb2 set val='$bn', type=$type, bin=$bin"
2384 cachedir=`getcachedir "$tbl/$rowid"`
2385 err getcache tbl/rowid=$tbl/$rowid, rm -r $cachedir
2386 rm -r $cachedir
2387 ;;
2388 *)
2389 newsql="update $tb2 set val=(select val from par where var \
2390 like '$col.%.$st_rowid')"
2391 ;;
2392 esac
2393 ;;
2394 *) # maybe "keep", do not modify value
2395 continue
2396 ;;
2397 esac
2398 # err newsql=$newsql
2399 sql=$sql$nl"$newsql where rowid=$st_rowid;"
2400 done
2402 if [ x"$bin" = x"NULL" ]; then
2403 err repl:normal sql=`echo $sql`
2404 query "$sql
2405 delete from $tb2 where type='string' and val='';"
2406 err repl:normal done
2407 else
2408 sqlfile="$tmpd/sqlf.$$"
2409 echo "$sql" > $sqlfile
2410 err repl:sqlfile=`ls -lF $sqlfile`
2411 query ".read $sqlfile"
2412 err repl:done
2413 fi
2414 # Rest of kt==m: set multiple mode
2415 nr=`getparcount $col`
2416 else
2417 nr=1 # for kt==s, number of records is 1
2418 fi
2420 i=0
2421 while [ $i -lt $nr ]; do
2422 limit="limit 1 offset $i"
2423 i=$((i+1)) # increase beforehand against continue
2424 val=`getvalquote $tbl $col "$limit"`
2425 [ -z "$val" -o x"$val" = x'""' -o x"$val" = x"NULL" ] && continue
2426 err $col=$val
2427 bin=NULL
2428 err partype$col=`getpartype $col "$limit"`
2429 case $ptype in
2430 file) file=$tmpd/`getparfilename $col "$limit"`
2431 err parfile-$col=$file
2432 [ -z "$file" ] && continue
2433 bin="X'"$(hexize $file)"'"
2434 ct=`file --mime-type $file|cut -d' ' -f2`
2435 type=\"file:$ct\" ;;
2436 "*"*) continue ;; # foreign table
2437 *) type=\"string\" ;;
2438 esac
2439 case `gettbl_coltype $tbl/$col` in
2440 password) # special care for password
2441 # name={password,pswd1,pswd2}
2442 p1=`getpar pswd1 "$limit"`
2443 if [ -z "$p1" ]; then
2444 continue # SKIP password setting, if p1 is empty
2445 else
2446 pswd=`getpar pswd "$limit"` p2=`getpar pswd2 "$limit"`
2447 ## err pswd=$pswd
2448 if pwcheck "$pswd"; then
2449 if [ x"$p1" = x"$p2" ]; then
2450 case "$p1" in
2451 ??????????*) ;;
2452 *) echo "パスワードは10字以上にしてください。" | html p
2453 return 6;;
2454 esac
2455 val="\"`echo $p1|mypwhash`\""
2456 else
2457 echo "2つの新パスワード不一致" | html p
2458 return 7
2459 fi
2460 else
2461 echo "旧パスワード違います" | html p
2462 return 8
2463 fi
2464 fi
2465 ;;
2466 esac
2467 err p2t: "replace into $tb2 values($pval, \"$col\", $type, $val, bin...);"
2468 #query "replace into $tb2 values($pval, \"$col\", $type, $val, $bin);"
2469 sql="replace into $tb2 values($pval, \"$col\", $type, $val, $bin);"
2470 if [ x"$bin" = x"NULL" ]; then
2471 err Normal-query: `echo $sql`
2472 query "$sql"
2473 else
2474 sqlfile="$tmpd/query.$$"
2475 echo "$sql" > $sqlfile
2476 err sqlfile=`ls -lF $sqlfile`
2477 query ".read $sqlfile"
2478 fi
2479 err p2t done
2480 done
2481 done
2482 done
2483 return 0
2484 err donee)
2486 par2table_old() {
2487 # copy current parameters of par into destination table
2488 # $1=dst-table $2=definition-file
2489 # Using $user and $session
2490 rowid=`getpar rowid`
2491 if [ -n "$rowid" ]; then
2492 rm=`getpar rm` cfm=`getpar confirm`
2493 if [ x"$rm$cfm" = x"yesyes" ]; then
2494 sq $db "delete from $1 where rowid=$rowid and owner=\"$user\""
2495 return
2496 fi
2497 fi
2498 cat $2 \
2499 | (cols=""
2500 while IFS=: read prompt name type args; do
2501 [ x"$name" = x"stage" ] && continue
2502 if [ -n "$rowid" ]; then
2503 val=`getpar $name|sed -e 's/\"/\"\"/g'`
2504 sq $db "update $1 set $name=\"$val\" where rowid=$rowid and owner=\"$user\""
2505 else
2506 eav=$eav${eav+,}" max(case var when '$name' then val end)"
2507 fi
2508 done
2509 [ -n "$rowid" ] && return
2510 cond="where sessid='$session' group by sessid"
2511 sq $db "replace into $1 select $eav,\"$user\" from par $cond"
2512 # Think over again about putting $user
2515 genform() {
2516 # $1 = form definition file
2517 # $2, $3 (optional)= table name and ROWID
2518 # If $GF_VIEWONLY set and nonNull, output values without form
2519 # If $GF_HIDDEN set, use it hidden values
2520 # If $GF_OWNER set, use it as value of name="owner"
2521 # If $GF_STAGE set, use it as value of name="stage"
2522 forms="" hiddens="" rowid=$3
2523 if [ ! -e "$1" ]; then
2524 echo "そのようなデータベースはないようです($2)。" | html p
2525 return
2526 elif [ -n "$2" ]; then
2527 err genform1: "select * from $2 where rowid='$rowid'"
2528 rec=`query "select * from $2 where rowid='$rowid';"`
2529 if [ -z "$rec" ]; then
2530 pk=`gettblpkey $2`
2531 ###rec=`sq $db "select rowid from $2 where $pk='$rowid'"`
2532 err "select rowid from $2 where $pk='$rowid';"
2533 rec=`query "select rowid from $2 where $pk='$rowid';"`
2534 err rec-rowid=$rec
2535 rowid=$rec
2536 rec=$3
2537 fi
2538 if [ -z "$rec" ]; then
2539 echo "そんなレコードはないみたいね..." | html p
2540 return
2541 fi
2542 fi
2543 if [ -z "$GF_VIEWONLY" ]; then
2544 rm='<input id="rm" name="rm" type="checkbox"
2545 value="yes"><label for="rm">このエントリの削除</label>
2546 <span>ほんとうに消しますよ(確認)!
2547 <input name="confirm" type=checkbox value="yes">はい</span>'
2548 fi
2549 # Image Cache dir
2550 err genform: getcache=$2/$rowid
2551 td=`getcachedir "$2/$rowid"`
2552 while IFS=: read prompt name keytype type args; do
2553 [ -z "${prompt%%\#*}" ] && continue # skip comment line(#)
2554 sp="${args:+ }"
2555 form="" val=""
2556 if [ -n "$rowid" ]; then
2557 # err genform2a: Seeking for "$2.$name, type=$type"
2558 val=`getvalbyid $2 $name $rowid $td|htmlescape`
2559 err genform3a: getvalbyid $2 $name $rowid $td
2560 err genform3b: val="[$val]"
2561 fi
2562 if [ -n "$GF_VIEWONLY" ]; then
2563 is_hidden "$2" "$name" && continue
2564 fi
2565 case "$type" in
2566 text*)
2567 cgiform=cgi_multi_$type
2568 if [ -s $td/$name.count -a -n "$val" ]; then
2569 form=`$cgiform $name $td`
2570 val=$(echo "$val"|
2571 while read fn; do
2572 echo "<tr><td>`cat $td/$fn`</td></tr>$nl"
2573 done)
2574 val="<table>$nl$val$nl</table>"
2575 else
2576 #form="<input name=\"$name\" value=\"$val\" type=\"$type\"$sp$args>$nl"
2577 err genform: cgi_$type $name $val "$args"
2578 form=`cgi_$type $name "$val" "$args"`
2579 fi
2580 ;;
2581 [Rr][Aa][Dd][Ii][Oo])
2582 fh="<label><input type=\"radio\" name=\"$name\""
2583 form="`echo $args|sed -e \
2584 \"s,\([^ =][^=]*\)=\([^= ][^= ]*\),$fh value=\\"\2\\">\1</label>,g\"`"
2585 ;;
2586 [Cc][Hh][Ee][Cc][Kk][Bb][Oo][Xx])
2587 form="<label><input type=\"checkbox\" name=\"$name\" value=\"${args#*=}\">${args%=*}</label>"
2588 ;;
2589 [Ss][Ee][Ll][Ee][Cc][Tt])
2590 fh="<select name=\"$name\">$nl"
2591 form=$(for l in $args; do
2592 echo "<option value=\"${l#*=}\">${l%=*}</option>"
2593 done)
2594 if [ -n "$val" ]; then
2595 form=`echo $form|sed -e "s,\(value=.$val.\),\\1 selected,"`
2596 fi
2597 form="$fh$form</select>"
2598 ;;
2599 [Ii][Mm][Aa][Gg][Ee]|[Dd][Oo][Cc][Uu][Mm][Ee][Nn][Tt]|[Bb]inary)
2600 if [ -s $td/$name.count ]; then
2601 form=`cgi_multi_file $name $td "$args"`
2602 if [ -n "$val" ]; then
2603 hrfb="$myname?showattc+$2_m"
2604 val=$(echo "$val" \
2605 | while read fn; do
2606 data=`percenthex $td/$fn`
2607 #ct=`cat $td/$fn.content-type`
2608 ct=`file --mime-type $td/$fn|cut -d' ' -f2`
2609 ri=`cat $td/$fn.rowid`
2610 ## err fn=$fn, name=$name, ri=$ri; ls -lF $td 1>&3
2611 #imgsrc="<img src=\"data:$ct,$data\">"
2612 #echo "<a href=\"$hrfb+$ri\">$imgsrc</a><br>"
2613 iconhref $td/$fn "$hrfb+$ri" ""
2614 done)
2615 fi
2616 else
2617 form="<input type=\"file\" name=\"$name\" $args>"
2618 if [ -n "$val" ]; then
2619 imgs=$(echo "$val"\
2620 |while read fn;do
2621 data=`percenthex $td/$fn`
2622 echo "<img src=\"data:image/png,$data\">$fn<br>"
2623 done)
2624 form=$form"<br>$imgs"
2625 val=$imgs # 2015-06-15
2626 else
2627 form="<input type=\"file\" name=\"$name\" $args>"
2628 fi
2629 fi
2630 ;;
2631 [Hh][Ii][Dd][Dd][Ee][Nn])
2632 if [ -n "$GF_STAGE" -a x"$name" = x"stage" ]; then
2633 args="value=\"$GF_STAGE\""
2634 fi
2635 form="<input type=\"hidden\" name=\"$name\" $args>"
2636 prompt='' # Remove prompt
2637 ;;
2638 [Aa][Uu][Tt][Hh][Oo][Rr])
2639 form="<input type=\"hidden\" name=\"author\" value=\"$user\">"
2640 prompt="" ;;
2641 [Oo][Ww][Nn][Ee][Rr])
2642 val=${GF_OWNER:-$val}
2643 val=${val:-$user}
2644 form="<input type=\"hidden\" name=\"owner\" value=\"$val\">"
2645 prompt="" ;;
2646 [Uu][Ss][Ee][Rr])
2647 # XXX: is null $user ok?
2648 #form="<input type=\"hidden\" name=\"user\" value=\"$user\">"
2649 [ -n "$GF_VIEWONLY" ] && continue
2650 form="$user"
2651 ;;
2652 [Pp]assword)
2653 [ -n "$GF_VIEWONLY" ] && continue
2654 form="`cgi_passwd`"
2655 val=""
2656 ;;
2657 [Ss][Ee][Rr][Ii][Aa][Ll]|[Ss][Tt][Aa][Mm][Pp])
2658 if [ -z "$rowid" ]; then
2659 val=$((($(date +%s)-1433084400)/10))c$$
2660 fi
2661 ## form="<input type=\"hidden\" name=\"serial\" value=\"$val\">"
2662 ## 2015-07-31
2663 form="<input type=\"hidden\" name=\"$name\" value=\"$val\">"
2664 prompt="" ;;
2665 [Ss][Ee][Ss][Ss][Ii][Oo][Nn])
2666 prompt=""
2667 ;;
2668 parent|path|blog*)
2669 prompt=""
2670 ;;
2671 "*"*)
2672 tail=$tail"``"
2673 continue ;;
2674 esac
2675 if [ -n "$prompt" ]; then
2676 if [ -n "${GF_VIEWONLY}" ]; then
2677 form=$val
2678 else
2680 fi
2681 forms=$forms" <tr class=\"$name\"><th>$prompt</th><td>$form</td></tr>$nl"
2682 else
2683 hiddens=$hiddens$nl"$form"
2684 fi
2685 done < $1
2686 # enctype="multipart/form-data"
2687 cat<<EOF
2688 <form action="${GF_ACTION:-$myname}" method="POST" enctype="multipart/form-data">
2689 ${rowid:+$rm}
2690 <table class="b $2">
2691 $forms
2692 </table>$hiddens
2693 ${GF_STAGE:+`cgi_hidden stage $GF_STAGE`}
2694 ${rowid:+<input type="hidden" name="rowid" value="$rowid">}
2695 EOF
2696 if [ -z $GF_VIEWONLY ]; then
2697 cat<<EOF
2698 <input type="submit" name="sub" value="OK">
2699 <input type="reset" name="res" value="Reset">
2700 EOF
2701 fi
2702 cat<<EOF
2703 </form>
2704 $tail
2705 EOF
2707 edittable() {
2708 # $1=form-def $2=table $3 rowid
2709 genform "$@"
2711 viewtable() {
2712 GF_VIEWONLY=1 genform "$@"
2714 showattc() {
2715 # $1=table_m $2=rowid
2716 err \$1=$1 \$2=$2
2717 if ! isfilereadable $user $1 $2; then
2718 contenttype; echo
2719 echo "このファイルは管理者のみしか見られません" | html p
2720 putfooter; exit
2721 fi
2722 idir=`umask 002; mktempd` || exit 1
2723 # tmpfiles=$tmpfiles"${tmpfiles+ }$idir"
2724 bin=$idir/$myname-$$.bin
2725 sql="select quote(bin) from $1 where rowid='$2';"
2726 err showattc: sql: $sql
2727 sq $db "$sql" | unhexize > $bin
2728 tv=`query "select type,val from $1 where rowid='$2';"`
2729 type=${tv%\|*} fn=${tv#*\|}
2730 err tv=$tv type=$type fn=$fn, tp2=${tv%\|*}
2731 ct=${type#file:}
2732 case $ct in # all text/* changed to text/plain
2733 text/*)
2734 charset=`nkf -g $bin|cut -d' ' -f1`
2735 case $charset in
2736 ASCII*) charset="" ;;
2737 esac
2738 ct="text/plain${charset:+; charset=$charset}"
2739 ;;
2740 esac
2741 contenttype "$ct"
2742 echo "Content-Disposition: filename=\"$fn\""
2743 echo "Content-Length: " `cat $bin | wc -c`; echo
2744 #echo "Content-Type: " ${type#file:}; echo
2745 cat $bin
2748 # Some default stupid handler on CGI values
2750 default_storedb() {
2751 # ARG: $1=table-def-file
2752 # RET: $tbl=table-name, $col=mail-column, $cols=columns
2753 tbl=`basename $1`
2754 tbl=${tbl%.def}
2755 cols="`grep :text $1|cut -d: -f2`"
2756 col=`echo "$cols"|head -1`
2757 vcol=`getpar $col`
2758 err default0: \$1=$1 col=$col cols="[$cols]" vcol=$vcol
2759 if [ -n "$vcol" ]; then
2760 par2table $1
2761 else
2762 return 2 # No insertion occurred
2763 fi
2766 default_view() { # $1=def-file
2767 ### DT_VIEW="edittable+$tbl" dumptable html $tbl "$cols" \
2768 ## DT_VIEW="edittable+$tbl" dumptable html $tbl "name memo file" \
2769 default_storedb "$@"
2770 query "select rowid from $tbl order by rowid desc;" \
2771 | while read rowid; do
2772 viewtable $1 $tbl $rowid
2773 done | _m4 -D_TITLE_="$tbl" \
2774 -D_FORM_="`genform $1`" \
2775 -D_DUMPTABLE_="syscmd(cat)" \
2776 $layout/html.m4.html $layout/form+dump.m4.html
2778 default_viewtext() { # $1=def-file
2779 ### DT_VIEW="edittable+$tbl" dumptable html $tbl "$cols" \
2780 default_storedb "$@"
2781 DT_VIEW="viewtable+$tbl" dumptable html $tbl "name memo file" \
2782 | _m4 -D_TITLE_="$tbl" \
2783 -D_FORM_="`genform $1`" \
2784 -D_DUMPTABLE_="syscmd(cat)" \
2785 $layout/html.m4.html $layout/form+dump.m4.html
2787 default_smail() {
2788 default_storedb "$@"
2789 if [ $? -eq 2 ]; then
2790 _m4 -D_TITLE_="入力" \
2791 -D_FORM_="`genform $1`" \
2792 -D_DUMPTABLE_="" \
2793 $layout/html.m4.html $layout/form+dump.m4.html
2794 return
2795 fi
2796 cond=""
2797 for pk in `gettblpkey $tbl`; do
2798 pv=$(sqlquote $(getpar $pk))
2799 cond="$cond${cond:+ and }$pk=$pv"
2800 done
2801 sql="select rowid from $tbl where $cond;"
2802 rowid=`query "$sql"`
2803 err smail1 - "$sql" "-> rowid=$rowid"
2805 while IFS=: read prompt name keytype type args; do # Read from $1
2806 val=`getpar $name`
2807 if [ -n "$val" ]; then
2808 text="$text
2809 $prompt
2810 $name=$val
2811 ---------------------------------------------------------"
2812 fi
2813 case "$type" in
2814 image|document|file)
2815 fn="`getvalbyid $tbl $name $rowid $tmpd`"
2816 fns=$(echo "$fn"|while read fn; do
2817 err mv $tmpd/$fn.orig $tmpd/$fn
2818 mv $tmpd/$fn.orig $tmpd/$fn
2819 rm $tmpd/$fn.rowid # Remove cache flag
2820 err "`ls $tmpd/$fn`"
2821 echo $fn
2822 done)
2823 files="$files $fns"
2824 ;;
2825 esac
2826 done < $1
2827 err FILES=$files "`ls -lF $tmpd`"
2828 subj="from ${REMOTE_ADDR}"
2829 (echo "$url"
2830 echo "への書き込みがありました。"
2831 echo "------"
2832 echo "$text"
2833 ) | (cd $tmpd &&
2834 err LS="`ls -lF`" &&
2835 $mydir/sendmultipart.sh -t "$admin" -s "$subj" $files)
2836 _m4 -D_TITLE_="入力完了" $layout/html.m4.html
2837 echo "以下の内容で送信しました。" | html p
2838 viewtable $1 $tbl \
2839 `query "select rowid from $tbl order by rowid desc limit 1;"`
2840 echo "戻る" | html a "href=\"?\""