comparison after5.rb @ 0:8f811f47ac60 draft

RCS-revision 1.1 date: 2003/12/29 16:05:57; author: yuuji; state: Exp; Initial revision =============================================================================
author HIROSE Yuuji <yuuji@gentei.org>
date Mon, 29 Dec 2003 16:05:57 +0859
parents
children 8145b15d3d6f
comparison
equal deleted inserted replaced
-1:000000000000 0:8f811f47ac60
1 #!/usr/local/bin/ruby
2 #
3 # Associative Scheduling Table - after5
4 # (C)2003 by HIROSE Yuuji [yuuji@gentei.org]
5 # $Id: after5.rb,v 1.1 2003/12/29 16:05:57 yuuji Exp $
6 # Last modified Tue Dec 30 00:46:49 2003 on firestorm
7 # See http://www.gentei.org/~yuuji/software/after5/
8 # このスクリプトはEUCで保存してください。
9
10 require 'kconv'
11
12 $charset = 'EUC-JP'
13
14 class HTMLout
15 def contenttype(type = "text/html", charset = $charset)
16 sprintf "Content-type: %s; charset=%s\n\n", type, charset
17 end
18 def initialize(title = "Document")
19 @title = title
20 @eltstack = []
21 end
22 def resetstack()
23 @eltstack = []
24 end
25 def head(title = @title, css = "style.css")
26 sprintf <<__EOS__, title, css
27 <html>
28 <head>
29 <title>%s</title>
30 <link rel="stylesheet" type="text/css" href="%s">
31 </head>
32 __EOS__
33 end
34
35 def startelement(elt, attrs = {}, nl = true)
36 attr = ""
37 if attrs.is_a?(Hash)
38 for k in attrs.keys
39 attr += " %s=\"%s\"" % [k, attrs[k]]
40 end
41 end
42 @eltstack.push(elt)
43 sprintf "<%s%s>%s", elt, attr, nl ? "\n" : ""
44 end
45 def endelement(elt = nil, nl = true)
46 if elt
47 x = elt
48 @eltstack.pop
49 else
50 x = @eltstack.pop
51 end
52 sprintf "</%s>%s", x, nl ? "\n" : ""
53 end
54 def element(elt, attrs = nil, nl = nil)
55 attr = ""
56 lf = nl ? "\n" : ""
57 if attrs.is_a?(Hash)
58 for k in attrs.keys
59 attr += " %s=\"%s\"" % [k, attrs[k]]
60 end
61 end
62 body = yield
63 sprintf "<%s%s>%s%s%s</%s>%s", elt, attr, lf, body, lf, elt, lf
64 end
65 def elementln(elt, attr=nil)
66 body = yield
67 element(elt, attr, true){body}
68 end
69 def a(href, anchor = nil, attrs = {})
70 attr = attrs
71 attr['href'] = href
72 element("a", attr){
73 anchor or href
74 }
75 end
76 def p(msg, attrs=nil)
77 element("p", attrs){msg}
78 end
79 def text(name, value='', size=nil, maxlength=nil)
80 sprintf "<input type=\"text\" name=\"%s\" value=\"%s\"%s%s>",
81 name, value,
82 size ? " size=\"%s\""%size.to_s : '',
83 maxlength ? " maxlength=\"%s\""%maxlength.to_s : ''
84 end
85 def hidden(name, value='')
86 sprintf "<input type=\"hidden\" name=\"%s\" value=\"%s\">", name, value
87 end
88 def radio(name, value, text='', checked=nil)
89 sprintf "<input type=\"radio\" name=\"%s\" value=\"%s\"%s>%s",
90 name, value, checked ? " checked" : "", text
91 end
92 def checkbox(name, value, text='', checked=nil)
93 sprintf "<input type=\"checkbox\" name=\"%s\" value=\"%s\"%s>%s",
94 name, value, checked ? " checked" : "", text
95 end
96 def submit(name, value, text='')
97 sprintf "<input type=\"submit\" name=\"%s\" value=\"%s\">%s\n",
98 name, value, text
99 end
100 def reset(name, value, text='')
101 sprintf "<input type=\"reset\" name=\"%s\" value=\"%s\">\n",
102 name, value, text
103 end
104 def submit_reset(name)
105 submit(name, "GO")+reset(name, "Reset")
106 end
107
108 def select_integer(name, b, e, selected=nil)
109 start = (b<e ? b : e)
110 last = (b>e ? b : e)
111 "<select name=\"#{name}\">\n" + \
112 (start..last).collect{|i|
113 sprintf "<option%s>%d%s",
114 (selected.to_s==i.to_s) ? " selected" : "",
115 i,
116 i%6==0 ? "\n" : ''
117 }.join + \
118 "\n</select>\n"
119 end
120 end
121
122 class PasswdMgr
123 def initialize(name, mode=0640)
124 require 'dbm'
125 @pdb = DBM.open(name, mode)
126 end
127 def checkpasswd(user, passwd)
128 if @pdb[user] then
129 @pdb[user] == passwd.crypt(@pdb[user])
130 end
131 end
132 def setpasswd(user, passwd)
133 salt = [rand(64),rand(64)].pack("C*").tr("\x00-\x3f","A-Za-z0-9./")
134 @pdb[user] = passwd.crypt(salt)
135 end
136 def userexist?(user)
137 @pdb[user] ? true : false
138 end
139 def getpasswd(user)
140 @pdb[user]
141 end
142 def delete(user)
143 @pdb.delete(user)
144 end
145 def close()
146 @pdb.close()
147 end
148 def newpasswd(length)
149 srand(Time.now.to_i)
150 left = "qazxswedcvfrtgb12345"
151 right = "yhnmjuik.lop;/67890-"
152 array = [left, right]
153 (1..length).collect{|i|
154 a = array[i%array.length]
155 a[rand(a.length), 1]
156 }.join('')
157 end
158 def users()
159 @pdb.keys
160 end
161 private :newpasswd
162 def setnewpasswd(user, length=8)
163 newp = newpasswd(length)
164 setpasswd(user, newp)
165 newp
166 end
167 end
168
169 class ScheduleDir
170 def initialize(dir = "s")
171 @dir = dir
172 @schedulefile = "sched"
173 @usermapdir = File.join(@dir, "usermap")
174 @usermap = mkusermap()
175 @groupmapdir = File.join(@dir, "groupmap")
176 @groupmap = mkgroupmap()
177 @crondir = File.join(@dir, "crondir")
178
179 end
180 def mkusermap()
181 map = {}
182 unless test(?d, @usermapdir)
183 mkdir_p(@usermapdir)
184 end
185 Dir.foreach(@usermapdir){|u|
186 next if /^\./ =~ u
187 newu = ''
188 u.split('').each{|c| # for security wrapping
189 newu << c[0].chr if /[-A-Z_.@]/i =~ c
190 }
191 u = newu
192 map[u] = {}
193 d = File.join(@usermapdir, u)
194 next unless test(?d, d)
195 Dir.foreach(d){|attr|
196 next if /^\./ =~ attr
197 attr.untaint if /^[A-Za-z]+$/ =~ attr
198 file = File.join(@usermapdir, u, attr)
199 next unless test(?s, file) && test(?r, file)
200 map[u][attr] = IO.readlines(file).join().strip
201 }
202 }
203 map
204 end
205 def putuserattr(user, attr, text)
206 # if text==nil, remove it
207 d = File.join(@usermapdir, user)
208 Dir.mkdir(d) unless test(?d, d)
209 file = File.join(d, attr)
210 begin
211 @usermap[user][attr] = text
212 if text==nil
213 File.unlink(file)
214 else
215 open(file, "w"){|w| w.puts @usermap[user][attr]}
216 end
217 rescue
218 return nil
219 end
220 return {attr => text}
221 end
222 def getuserattr(user, attr)
223 # Should we distinguish between attribute is nil and "" ?
224 if @usermap.has_key?(user) && @usermap[user][attr].is_a?(String) &&
225 @usermap[user][attr] > ''
226 return @usermap[user][attr]
227 else
228 return nil
229 end
230 end
231
232 def nickname(user)
233 if @usermap.has_key?(user) && @usermap[user]['name'].is_a?(String) &&
234 @usermap[user]['name'] > ''
235 return @usermap[user]['name']
236 else
237 return user.sub(/@.*/, '')
238 end
239 end
240 def setnickname(user, nickname)
241 putuserattr(user, 'name', nickname)
242 end
243
244 #
245 # make group map
246 def collectmembers(gname)
247 @visitedgroup=[] unless @visitedgroup
248 return [] unless @visitedgroup.grep(gname).empty?
249 @visitedgroup.push(gname)
250 mdir = File.join(@groupmapdir, gname, 'members')
251 return [] unless test(?d, mdir)
252 members = []
253 Dir.foreach(mdir){|item|
254 next if /^\./ =~ item
255 item.untaint
256 next unless test(?f, File.join(mdir, item))
257 if /.+@.+/ =~ item
258 members << item
259 else
260 members += collectmembers(item)
261 end
262 }
263 @visitedgroup.pop
264 members
265 end
266 def mkgroupmap()
267 map = {}
268 return map unless test(?d, @groupmapdir)
269 @visitedgroup = []
270 Dir.foreach(@groupmapdir){|g|
271 next if /^\./ =~ g
272 newg = ''
273 next unless /^[-a-z0-9_.]+$/i =~ g
274 #g.untaint ## untaintじゃだめだ。map{g} のkeyがtaintedになっちゃうよ
275 gg = '' # for security wrapping
276 g.split('').each{|c| gg << c[0].chr if c != '`'}
277 g = gg
278 map[gg] = {}
279 d = File.join(@groupmapdir, g)
280 next unless test(?d, d)
281 # get group name
282 gnf = File.join(d, 'name')
283 if test(?r, gnf) && test(?s, gnf)
284 n = IO.readlines(gnf)[0].to_s.strip
285 map[g]['name'] = if n > '' then n else g end
286 else
287 map[g]['name'] = g
288 end
289 # get administrators
290 #
291 gad = File.join(d, 'admin')
292 map[g]['admin'] = []
293 if test(?d, gad)
294 Dir.foreach(gad){|a|
295 # administrator should be a person (not group)
296 next unless /@/ =~ a
297 map[g]['admin'] << a
298 }
299 end
300 # collect members
301 #map[g]['members'] = collectmembers(g)
302 memd = File.join(d, 'members')
303 map[g]['members'] = []
304 if test(?d, memd)
305 Dir.foreach(memd){|a|
306 next if /^\./ =~ a
307 map[g]['members'] << a
308 }
309 end
310 }
311 map
312 end
313 def groupmap()
314 @groupmap
315 end
316 def groups()
317 @groupmap.keys
318 end
319 def addgroup(grp, users, remove=nil, role='members')
320 return nil unless @groupmap[grp]
321 for u in users
322 next unless account_exists(u)
323 mdir = File.join(@groupmapdir, grp, role)
324 file = File.join(mdir, u)
325 if remove
326 @groupmap[grp][role].delete(u)
327 File.unlink(file) if test(?e, file)
328 else
329 @groupmap[grp][role] << u
330 @groupmap[grp][role].uniq
331 Dir.mkdir(file) unless test(?d, mdir)
332 open(file, "w"){|x|} # Touch it
333 end
334 end
335 grp
336 end
337 def setgroupname(grp, name)
338 return nil unless @groupmap[grp]
339 mdir = File.join(@groupmapdir, grp)
340 nfile = File.join(mdir, 'name')
341 @groupmap[grp]['name'] = name
342 if grp == name
343 # remove the name file because it is default name
344 File.unlink(nfile) if test(?e, nfile)
345 else
346 Dir.mkdir(mdir) unless test(?d, mdir)
347 open(nfile, "w"){|n| n.puts name.to_s.strip}
348 end
349 name
350 end
351 def creategroup(grp, grpname="", admin=[])
352 return nil unless /^[-A-Z0-9._:!$%,]+$/i =~ grp
353 grp.untaint
354 gdir = File.join(@groupmapdir, grp)
355 mkdir_p(gdir) # Should not care errors here
356 Dir.mkdir(File.join(gdir, "admin"))
357 Dir.mkdir(File.join(gdir, "members"))
358 @groupmap[grp] = {}
359 if grpname == ''
360 @groupmap[grp]['name'] = grp
361 else
362 setgroupname(grp, grpname)
363 end
364 @groupmap[grp]['members'] = []
365 @groupmap[grp]['admin'] = []
366 addgroup(grp, admin)
367 addgroup(grp, admin, nil, 'admin')
368 return @groupmap[grp]
369 end
370 def createuser(user, email = nil)
371 return nil unless /@/ =~ user
372 return nil if %r@[\/()\;|,$\%^!\#&\'\"]@ =~ user
373 email = email || user
374 @usermap[user] = {}
375 Dir.mkdir(File.join(@usermapdir, user))
376 putuserattr(user, 'email', email)
377 end
378 def deleteuser(user)
379 return nil unless @usermap[user]
380 begin
381 @usermap[user] # return value
382 ensure
383 @usermap.delete(user)
384 rm_rf(File.join(@usermapdir, user))
385 rm_rf(File.join(@groupmapdir, "*/members/#{user}"))
386 rm_rf(File.join(@crondir, "[1-9]*-*-*/#{user}"))
387 rm_rf(File.join(@dir, "[1-9]*/[0-9][0-9]/[0-9][0-9]/[0-9]???/#{user}"))
388 end
389 end
390 def destroygroup(grp)
391 return nil unless @groupmap[grp]
392 begin
393 @groupmap[grp] # return value
394 ensure
395 @groupmap.delete(grp)
396 rm_rf(File.join(@groupmapdir, grp))
397 rm_rf(File.join(@groupmapdir, "*/members/#{grp}"))
398 rm_rf(File.join(@crondir, "[1-9]*-*-*/#{grp}"))
399 rm_rf(File.join(@dir, "[1-9]*/[0-9][0-9]/[0-9][0-9]/[0-9]???/#{grp}"))
400 end
401 end
402 def rm_rf(path)
403 if (list = Dir.glob(path))[0]
404 for p in list
405 system "/bin/rm -rf \"#{p}\""
406 end
407 cleanup_files(list[0])
408 end
409 end
410 def account_exists(instance)
411 if /@/ =~ instance
412 true
413 else
414 ! @groupmap.select{|k, v| k==instance}.empty?
415 end
416 end
417 def ismember(user, grouporuser)
418 return true if user==grouporuser
419 if @groupmap[grouporuser]
420 @groupmap[grouporuser]['members'].grep(user)[0]
421 end
422 end
423 def isgroup(grp)
424 @groupmap[grp]
425 end
426 def isadmin(user, group)
427 @groupmap[group] and @groupmap[group]['admin'].grep(user)[0]
428 end
429 def members(grp)
430 @groupmap[grp] and ####################@groupmap[grp]['members']
431 collectmembers(grp)
432 end
433 def admins(grp)
434 @groupmap[grp] and @groupmap[grp]['admin']
435 end
436 def groupname(grp)
437 @groupmap[grp] && @groupmap[grp]['name']
438 end
439 def day_all(d, user=nil)
440 y, m, d = d.scan(%r,(\d\d\d\d+)/(\d+)/(\d+),)[0]
441 #daydir = File.join(@dir, "%04d"%y.to_i, "%02d"%m.to_i, "%02d"%d.to_i)
442 daydir = File.join("s", "%04d"%y.to_i, "%02d"%m.to_i, "%02d"%d.to_i)
443 sched = {}
444 return sched unless test(?d, daydir)
445 Dir.foreach(daydir) {|time|
446 next if /^\./ =~ time
447 next unless /^\d\d\d\d$/ =~ time
448 time.untaint
449 t = File.join(daydir, time)
450 next unless test(?d, t)
451 sched[time] = {}
452 Dir.foreach(t){|who|
453 next if /^\./ =~ who
454
455 visible = false
456 #next unless /@/ =~ who # user must be as user@do.ma.in
457 next unless account_exists(who)
458 who.untaint
459 dir = File.join(t, who)
460 next unless test(?d, dir) && test(?x, dir)
461 pub = File.join(dir, 'pub')
462 if test(?f, pub) && test(?r, pub) && test(?s, pub)
463 if IO.readlines(pub)[0].to_i > 0
464 visible = true
465 end
466 end
467
468
469 if ismember(user, who) || visible
470 sched[time][who] = {}
471 file = File.join(dir, @schedulefile)
472 if test(?s, file) && test(?r, file) && test(?s, file)
473 sched[time][who]['sched'] = IO.readlines(file).join().chomp!
474 end
475 sched[time][who]['pub'] = visible
476 end
477 } #|who|
478 sched.delete(time) if sched[time].empty?
479 }
480 sched
481 end
482
483 def scheduledir(user, y, m, d, time)
484 sprintf("%s/%04d/%02d/%02d/%04d/%s",
485 @dir, y.to_i, m.to_i, d.to_i, time.to_i, user)
486 end
487 def schedulefile(user, y, m, d, time)
488 File.join(scheduledir(user, y, m, d, time), @schedulefile)
489 end
490 def mkdir_p(path, mode=0777)
491 # Do not mkdir `path' for
492 # absolute paths
493 # those paths which contains `../'
494 # for the sake of security reason
495 return false if %r,\.\./|^/, =~ path
496 p = 0
497 i=0
498 while p=path.index("/", p)
499 dir = path[0..p].chop
500 p += 1
501 break if i > 10 # overprotecting
502 next if test(?d, dir)
503 Dir.mkdir(dir, mode)
504 i += 1
505 end
506 Dir.mkdir(path, mode) unless test(?d, path)
507 end
508
509 #
510 # register schedule for user
511 #
512 def register(user, year, month, day, time, text, replace=nil)
513 # return code: 0 = succesfull new registration
514 # 1 = succesfull appending registration
515 dir = scheduledir(user, year, month, day, time)
516 file = schedulefile(user, year, month, day, time)
517 ret = 0
518 um = File.umask(027)
519 begin
520 if !replace && test(?s, file)
521 ret = 1
522 else
523 mkdir_p(dir, 0777)
524 end
525 ensure
526 File.umask(um)
527 end
528 open(file, replace ? "w" : "a"){|out|out.print text}
529 return ret
530 end
531 def getschedule(user, year, month, day, time)
532 file = schedulefile(user, year, month, day, time)
533 if test(?r, file) && test(?s, file)
534 return IO.readlines(file).join
535 end
536 return nil
537 end
538 def remove(user, year, month, day, time)
539 file = schedulefile(user, year, month, day, time)
540 dir = File.dirname(file)
541 if test(?r, file) && test(?s, file)
542 File.unlink(file)
543 end
544 for f in Dir.glob(File.join(dir, "*"))
545 f.untaint
546 File.unlink(f)
547 end
548 Dir.rmdir(dir) if test(?d, dir)
549 begin
550 Dir.rmdir(File.dirname(dir))
551 rescue
552 end
553 end
554 #
555 # register file
556 #
557 def putfile(user, year, month, day, time, file, contents)
558 scback = @schedulefile
559 begin
560 @schedulefile = File.basename(file)
561 register(user, year, month, day, time, contents, true)
562 ensure
563 @schedulefile = scback
564 end
565 end
566 def getfile(user, year, month, day, time, file)
567 scback = @schedulefile
568 begin
569 @schedulefile = File.basename(file)
570 getschedule(user, year, month, day, time)
571 ensure
572 @schedulefile = scback
573 end
574 end
575 def removefile(user, year, month, day, time, file)
576 dir = scheduledir(user, year, month, day, time)
577 file = File.join(dir, file)
578 if test(?e, file)
579 File.unlink(file)
580 end
581 end
582 #
583 # registration to crondir
584 #
585 def cronlink_file(nt_time, user, y, m, d, time)
586 subdir = nt_time.strftime("%Y-%m-%d-%H%M/#{user}")
587 cdir = File.join(@crondir, subdir)
588 File.join(cdir, sprintf("%04d-%02d-%02d-%04d", y, m, d, time))
589 end
590 def register_crondir(nt_time, user, y, m, d, time)
591 linkfile = cronlink_file(nt_time, user, y, m, d, time)
592 mkdir_p(File.dirname(linkfile))
593 scfile = schedulefile(user, y, m, d, time)
594 if test(?s, scfile)
595 sclink = File.join("../../..", scfile.sub!(Regexp.quote(@dir+'/'), ''))
596 File.symlink(sclink, linkfile) unless test(?e, linkfile)
597 return linkfile
598 end
599 return false
600 end
601 def remove_crondir(nt_time, user, y, m, d, time)
602 linkfile = cronlink_file(nt_time, user, y, m, d, time)
603 scfile = schedulefile(user, y, m, d, time)
604 if test(?e, linkfile)
605 File.unlink(linkfile)
606 begin
607 dir = linkfile
608 2.times {|x|
609 dir = File.dirname(dir)
610 if Dir.open(dir).collect.length <= 2 # is empty dir
611 Dir.rmdir(dir)
612 else
613 break
614 end
615 }
616 rescue
617 end
618 return linkfile
619 end
620 return false
621 end
622
623 #
624 # return the Hash of crondir {user => files}
625 def notify_list(asof)
626 slack = 5*60
627 gomifiles = []
628 ntl = {}
629 return ntl unless test(?d, @crondir)
630 Dir.foreach(@crondir){|datedir|
631 dd = File.join(@crondir, datedir)
632 next unless test(?d, dd)
633 next unless /(\d\d\d\d+)-(\d+)-(\d+)-(\d\d\d\d)/ =~ dd
634 y, m, d, hm = $1.to_i, $2.to_i, $3.to_i, $4.to_i
635 hh = hm/100 % 60
636 mm = (hm%100) % 60
637 t = Time.mktime(y, m, d, hh, mm)
638 next if t-slack > asof
639 #
640 # collect them
641 Dir.foreach(dd){|user|
642 next unless /@/ =~ user || isgroup(user)
643 ud = File.join(dd, user)
644 next unless test(?d, ud)
645 ntl[user] = {}
646 Dir.foreach(ud){|date|
647 next if /^\./ =~ date
648 unless /(\d\d\d\d+)-(\d+)-(\d+)-(\d\d\d\d)/ =~ date
649 gomifiles << File.join(ud, date)
650 next
651 end
652 f = File.join(ud, date)
653 if test(?s, f)
654 ntl[user][date] = {}
655 ntl[user][date]['file'] = f
656 ntl[user][date]['text'] = IO.readlines(f)
657 else
658 File.unlink(f) # symlink points to nonexistent file
659 end
660 }
661 if ntl[user].empty?
662 # if ud does not contain valid cron symlinks,
663 # ud had been left badly. Remove it.
664 ntl.delete(user)
665 cleanup_files(gomifiles)
666 end
667 }
668 }
669 ntl
670 end
671 #
672 # cleanup file and directories
673 def cleanup_crondir(time)
674 Dir.foreach(@crnondir){|datedir|
675 dd = File.join(@crondir, datedir)
676 next unless test(?d, dd)
677 next unless /(\d\d\d\d+)-(\d+)-(\d+)-(\d\d\d\d)/ =~ dd
678 y, m, d, hm = $1.to_i, $2.to_i, $3.to_i, $4.to_i
679 hh = hm/100 % 60
680 mm = (hm%100) % 60
681 t = Time.mktime(y, m, d, hh, mm)
682 if t < time
683 system "rm -rf #{dd}"
684 end
685 }
686 end
687 #
688 # remove files in FILES, and remove parent directory if possible
689 def cleanup_files(files)
690 sentinel = File.stat(@dir).ino
691 for f in files
692 File.unlink(f) if test(?e, f)
693 d = f
694 loop {
695 d = File.dirname(d)
696 break if d.length < 2
697 break if File.stat(d).ino == sentinel
698 begin
699 puts "rmdir #{d}" if $DEBUG
700 Dir.rmdir(d)
701 rescue
702 break
703 end
704 }
705 end
706 end
707 end
708
709 class StringIO<IO
710 def initialize()
711 @str=""
712 end
713 def foo=(str)
714 @str = str
715 end
716 def print(str)
717 @str << str
718 end
719 def puts(str)
720 @str << str+"\n"
721 end
722 def printf(*args)
723 @str << sprintf(*args)
724 end
725 def write(bytes)
726 print(bytes)
727 end
728 def gets()
729 return nil if @str == ''
730 p = @str.index(?\n)
731 if p
732 r = @str[0..p]
733 @str=@str[p+1..-1]
734 else
735 r = @str
736 end
737 return r
738 end
739 def readline()
740 this.gets()
741 end
742 def readlines()
743 r = @str
744 @str=''
745 r
746 end
747
748 def p(*obj)
749 STDOUT.p(*obj)
750 end
751 end
752
753 class CMDTimeout < Exception
754 def initialize()
755 @pw = IO.pipe
756 @pr = IO.pipe
757 @pe = IO.pipe
758 @timeout = false
759 end
760 def start(cmd, timeout, mixstderr=false)
761 if @pid=fork
762 @pw[0].close
763 @pr[1].close
764 @pe[1].close
765 # puts "parent!"
766 if @tk=fork
767 # main
768 else
769 @pw[1].close
770 @pr[0].close
771 @pe[0].close
772 trap(:INT){exit 0}
773 sleep timeout
774 begin
775 @timeout = true
776 STDERR.puts "TIMEOUT"
777 Process.kill :INT, @pid
778 rescue
779 #puts "Already done"
780 end
781 exit 0
782 end
783 else
784 # Running this block with pid=@pid
785 trap(:INT){@timeout = true; exit 0}
786 @pw[1].close
787 STDIN.reopen(@pw[0])
788 @pw[0].close
789
790 @pr[0].close
791 STDOUT.reopen(@pr[1])
792 if mixstderr
793 STDERR.reopen(@pr[1])
794 else
795 STDERR.reopen(@pe[1])
796 end
797 @pr[1].close
798 @pe[0].close
799 @pe[1].close
800
801 exec *cmd
802 exit 0
803 end
804 return [@pw[1], @pr[0], @pe[0]]
805 end
806 def wait()
807 Process.waitpid(@pid, nil)
808 end
809 def close()
810 @pr.each{|p| p.close unless p.closed?}
811 @pw.each{|p| p.close unless p.closed?}
812 @pe.each{|p| p.close unless p.closed?}
813 begin
814 Process.kill :INT, @tk
815 rescue
816 end
817 end
818 def timeout()
819 @timeout
820 end
821 end
822
823 class Holiday
824 def initialize(dir = ".")
825 @@dir = dir
826 defined?(@@holiday) || setupHoliday
827 end
828 def setupHoliday(file = "holiday")
829 @@holiday = {}
830 return unless test(?f, file) && test(?s, file)
831 IO.foreach(file){|line|
832 line.strip
833 next if /^#/ =~ line
834 date, what = line.scan(/(\S+)\s+(.*)/)[0]
835 if %r,(\d+)/(\d+)/(\d+), =~ date
836 cdate = sprintf("%d/%d/%d", $1.to_i, $2.to_i, $3.to_i)
837 @@holiday[cdate] || @@holiday[cdate] = []
838 @@holiday[cdate] << what
839 elsif %r,(\d+)/(\d+), =~ date
840 cdate = sprintf("%d/%d", $1.to_i, $2.to_i)
841 @@holiday[cdate] || @@holiday[cdate] = []
842 @@holiday[cdate] << what
843 elsif %r,(\d+)/(\w+), =~ date
844 cdate = sprintf("%d/%s", $1.to_i, $2.downcase)
845 @@holiday[cdate] || @@holiday[cdate] = []
846 @@holiday[cdate] << what
847 end
848 }
849 end
850 def isHoliday(y, m, d, wday=nil)
851 y, m, d = y.to_i, m.to_i, d.to_i
852 wname = %w[sun mon tue wed thu fri sat]
853 holiday = @@holiday[sprintf("%d/%d/%d", y, m, d)] ||
854 @@holiday[sprintf("%d/%d", m, d)]
855 unless holiday
856 wday = wname[wday || Time.mktime(y, m, d).wday]
857 nthweek = (d-1)/7+1
858 holiday = @@holiday[sprintf("%d/w%d%s", m, nthweek, wday)]
859 end
860 holiday
861 end
862 def holidays()
863 @@holiday
864 end
865 end
866
867 class After5
868 def initialize()
869 @me = File.expand_path($0)
870 @mydir, @myname = File.dirname(@me), File.basename(@me)
871 @mydir.untaint
872 Dir.chdir @mydir
873 # @mybase = @myname.sub(/\.\w+$/, '')
874 @mybase = "after5" ########################################### secure?
875 @myname='a5.cgi' # if test(?f, File.join(@mydir, "a5.cgi"))
876 @conf = nil
877 @schedulearea = {'rows'=>'4', 'cols'=>'60', 'name'=>'schedule'}
878 @oldagent = (%r,Mozilla/4, =~ ENV['HTTP_USER_AGENT'])
879 @opt = {
880 'conf' => @mybase+".cf",
881 'css' => @mybase+".css",
882 'logfile' => @mybase+".log",
883 "sendmail" => "/usr/sbin/sendmail",
884 'hostcmd' => '/usr/bin/host',
885 'nslookup' => '/usr/sbin/nsookup',
886 'bg' => 'ivory',
887 'at_bsd' => '%H:%M %b %d %Y',
888 'at_solaris' => '%H:%M %b %d,%Y',
889 'schedir' => 's',
890 'tdskip' => '<br>',
891 'forgot' => 'wasureta',
892 'size' => @oldagent ? '15' : '40',
893 'morning' => '6',
894 'night' => '22',
895 'alldaydir' => '3000',
896 'pswdlen' => 4,
897 'pswddb' => 'a5pswd',
898 }
899 @ntlist = [
900 ['nt10m', "10"+msg('minutes', 'before')],
901 ['nt30m', "30"+msg('minutes', 'before')],
902 ['nt60m', "60"+msg('minutes', 'before')],
903 ['nttoday', msg('theday')],
904 ['nt1d', "1"+msg('days', 'before')],
905 ['nt2d', "2"+msg('days', 'before')],
906 ['nt3d', "3"+msg('days', 'before')],
907 ['nt7d', "7"+msg('days', 'before')],
908 ['nt30d', "30"+msg('days', 'before')],
909 ]
910 ##@job = "today"
911 @job = "login"
912 @sc = ScheduleDir.new
913 @O = StringIO.new
914 @H = HTMLout.new()
915 @umback = File.umask
916 @author = 'yuuji@gentei.org'
917 @after5url = 'http://www.gentei.org/~yuuji/software/after5/'
918 File.umask(007)
919 end
920 def doit()
921 @params = getarg()
922 @cookie = getcookie()
923 p @cookie if $DEBUG
924 p @params if $DEBUG
925
926 @O.print @H.contenttype()
927 @O.print @H.head("After 5", @opt['css'])
928 @O.print @H.startelement("body", true)
929
930 ######### @O.puts @H.p(@cookie.inspect) #cookie check!
931
932 ## x = {"align"=>'center'}
933 ## @H.element("p", x, "hoge", nil)
934 ## @H.element("p", nil, "buha", nil)
935
936 if !@params['passwd'] && @cookie['passwd']
937 @params['passwd'] = @cookie['passwd']
938 end
939 if !@params['user'] && @cookie['user']
940 @params['user'] = @cookie['user']
941 end
942 @params['user'] = safecopy(@params['user'])
943 eval @job
944 # @job.call
945 @O.print @H.endelement(nil, true) # body
946 @O.print "</html>\n" # html
947 setcookie()
948
949 print @O.readlines
950 end
951 def msg(*keyword)
952 unless defined?(@msg)
953 @msg = {
954 'title' => ['みんなの予定表 <img src="after5.png" alt="「アフター5」" width="107" height="53">', 'Schedule table for us all <img src="after5.png" alt="After 5" width="107" height="53">'],
955 'login' => ['ログイン', 'Login'],
956 'loginfirst' => ['最初にログインすべし', 'Login first'],
957 'autherror' => ['認証エラーがあったと管理者に伝えてくれっす',
958 'Unexpected authentication error. Please tell this to the administrator'],
959 'yourmail' => ['あなたのメイルアドレス', 'Your email address'],
960 'passwd' => ['パスワード<br>(初回時は空欄)',
961 'Passowrd<br>Left blank, first time'],
962 'error' => ['エラー:', 'Error: '],
963 'mailerror' => ['メイルアドレスが違います', 'Invalid email address'],
964 'pswderror' => ['パスワードが違います', 'Password incorrect'],
965 'fmtdaysschedule'=> ['%sの予定', 'Schedule on %s'],
966 'noplan' => ['登録されている予定はありません', 'No plans'],
967 'allday' => ['全日', 'whole day'],
968 'addsched' => ['新規予定項目の登録', 'Register new schedule'],
969 'defthisday' => ['デフォルトの日付はこの日になってま', ''],
970 '24hour' => ['24時間制4桁でね<br>(0000〜2359)<br>%sは時刻指定なし', 'in 24-hour<br>(0000-2359)<br>%s for whole day'],
971 'reqnotify' => ['通知メイルいるけ?', 'Previous notification'],
972 'rightnow' => ['登録時にすぐ', 'Right now on registration'],
973 'immediatenote' => ['に以下の予定を登録しました',
974 ", Your schedule has been registered as follows;"],
975 'registerer_is' => ['登録名義: ', 'Register as '],
976 'registerer' => ['登録者: ', 'registerer: '],
977 'about' => ['約', 'about'],
978 'minutes' => ['分', 'minutes'],
979 'hours' => ['時間', 'hour(s)'],
980 'days' => ['日', 'day(s)'],
981 'before' => ['前', 'before'],
982 'theday' => ['当日朝', "the day's morning"],
983 'night' => ['(夜)', '(night)'],
984 'publicok' => ['メンバーに<br>見せてもええね?',
985 'visible from other members?'],
986 'public' => ['公', 'pub'],
987 'nonpublic' => ['非', 'sec'],
988 'yes' => ['はいな', 'yes'],
989 'no' => ['やだ', 'nope'],
990 'invaliddate' => ['日付指定が変みたい', 'Invalid time string'],
991 'past' => ['それはもう過去の話ね', 'It had Pasted'],
992 'putsomething' => ['何か書こうや', 'Write some message please'],
993 'appended' => ['既存の予定に追加しました', 'Appended'],
994 'append' => ['追加', 'append'],
995 'join' => ['参加', 'join'],
996 'regist' => ['登録', 'register'],
997 'remove' => ['削除', 'remove'],
998 'deletion' => ['完全消去', 'deletion'],
999 'deletionwarn' => ['OK押したら即消去。確認とらないぞ',
1000 'Hitting OK immediately delets this group, be carefully!'],
1001 'deluser' => ['%s ユーザ消してええかの?', "Delete the user `%s'"],
1002 'delgroup' => ['%s グループ消してええかの?', "Delete the group `%s'"],
1003 'really?' => ['ほんまにええけ?', 'Really?'],
1004 'chicken' => ['ふっ、腰抜けめ', 'Hey, chicken boy'],
1005 'modify' => ['修正', 'modify'],
1006 'done' => ['完了', 'done'],
1007 'success' => ['成功', 'success'],
1008 'failure' => ['失敗', 'failure'],
1009 'tomonthlist' => ['%s の一覧', 'all %s table'],
1010 'notifysubj' => @mybase+"'s reminder for your plan",
1011 'introduce' => ['はいこんにちは、'+@mybase+'ですよ〜。',
1012 "Hi, this is #{@mybase}'s notification."],
1013 'notifymail' => ['こんな予定がありまっせ。',
1014 "You have some eschedule below;"],
1015 'notification' => ['の通知', 'notification'],
1016 'newaccount' => ["新しいアカウントを作りました。\n"+
1017 "パスワードは %s さん宛に送信しておきました。\n",
1018 "You got new account for #{@mybase}\n" +
1019 "Password was sent to %s.\nThank you.\n"],
1020 'accessfrom' => ["%s からのアクセスによる送信\n",
1021 "This mail was sent by the access from %s\n"],
1022 'newpassword' => ["%s さんのパスワードは %s です。\n",
1023 "The password of %s is %s\n"],
1024 'mischief' => ["身に覚えのない場合はいたずらです。どうしましょ。",
1025 'If you have no idea for getting this message, '+
1026 'it is mischief by someone else'],
1027 'user' => ['ユーザ', 'user'],
1028 'group' => ['グループ', 'group'],
1029 'personal' => ['個人で', 'personal'],
1030 'registas' => ['グループ予定として登録', 'Register as group'],
1031 'joinquit' => ['入退', 'joining/quiting'],
1032 'of' => ['の', "'s"],
1033 'id' => ['ID(ローマ字1単語空白なしで)', 'ID(without spaces)'],
1034 'name' => ['名前', 'name'],
1035 'anystring' => ['(日本語OK)', '(any length, any characters)'],
1036 'setto' => ['を設定 → ', 'set to '],
1037 'management' => ['管理', 'management'],
1038 'administrator' => ['管理者', 'Administrator'],
1039 'newgroup' => ['新規グループ作成', 'Create new group'],
1040 'adminop' => ['管理<br>操作', "Administrative<br>operation"],
1041 'member' => ['メンバー', 'Member'],
1042 'addedtogroup' => ['をグループに追加 →', 'added to the group:'],
1043 'removedfromgp' => ['をグループから削除:', 'removed from the group:'],
1044 'soleadmin' => ['%s は %s の唯一の管理者なのでやめられないのだ',
1045 "%s is sole administrator of %s. Cannot retire."],
1046 'recursewarn' => ['個人では加入してないが、別の加入グループがこのグループに入っているので実質参加していることになっている。',
1047 'Though this member does not join to this group, it is assumed to be joining this group because other group where one joins is joined to this group.'],
1048 'regaddress' => ['登録アカウント名', 'Account id'],
1049 'existent' => ['既にあるんすよ → ', 'Already exists: '],
1050 'mailaddress' => ['通知送付先アドレス', 'Notification email address'],
1051 'weburl' => ['ゲストブックとかURL<br><small>(予定への反応を書いて欲しい場所)</small>', 'Your guest book URL'],
1052 'usermodwarn' => ['いちいち yes/no とか確認取らないから押したら最後、気いつけて。',
1053 'This is the final decision. Make sure and sure.'],
1054 'joinmyself' => ['自分自身が既存のグループに対して入る(IN)か出る(OUT)かを決めるのがここ。自分管理のグループに誰かを足すなら「管理操作」、新たにグループを作るなら',
1055 'In this page, you can decide put yourself IN or OUT of the existing groups. If you want to manage the member of your own group, go to'],
1056 'groupwarn' => ['自分が参加してないグループAに、自分が参加しているグループBが含まれている場合、グループAにも加入していると見なされるので気をつけよう。管理者はグループのニックネームを変えられるよ。',
1057 'Though you are not member of group A, you are treated as a member of A, if you join to the group B, which is a member of A. Think the nesting of groups carefully, please. Group administrator can change the group nickname.'],
1058 'wholemembers' => ['グループ内グループを考慮した上で、現在グループ %s への通知は以下のメンバーに送られる。',
1059 "Consiering the groups registered in another group, notification to the group `%s' is send to members as follows."],
1060 'noadmingroup' => ['管理できるグループはないっす',
1061 "'There's no groups under your administration."],
1062 'multiplemail' => ['複数の宛先に通知したいなら..',
1063 'Wanna send notify to multiple address...'],
1064 'nickname' => ['ニックネーム', 'nickname'],
1065 'shortnameplz' => ['表が崩れるほど長すぎるニックネームは嫌われるよ。短めにね。',
1066 'Because nickname is displayed many times in table, shorter name is prefered.'],
1067 'nicknamenote' => ['ニックネームを消去するとデフォルト名になりんす.',
1068 'Default name is displayed if you remove nickname.'],
1069 'nothingtodo' => ['って何もやることあらへんかったで',
1070 'Nothing to do for this transaction.']
1071 }
1072 end
1073 lang=0
1074 keyword.collect{|k|
1075 if @msg[k].is_a?(Array)
1076 @msg[k][lang]
1077 elsif @msg[k].is_a?(String)
1078 @msg[k]
1079 else
1080 ''
1081 end
1082 }.join(['', ' '][lang])
1083 end
1084
1085 def setcookie()
1086 cookievals = %w[user passwd ^nt]
1087 p = {}
1088 @params.keys.grep(/^(user$|passwd$|nt)/){|v|
1089 p[v] = @params[v].to_s.strip
1090 }
1091 c = gencookie(p, 3600*6*1)
1092 printf "Set-Cookie: %s\n", c if c
1093 end
1094
1095 def encode(string) # borrowed from cgi.rb
1096 string.gsub(/([^ a-zA-Z0-9_.-]+)/n) do
1097 '%' + $1.unpack('H2' * $1.size).join('%').upcase
1098 end.tr(' ', '+')
1099 end
1100 def decode!(string)
1101 string.gsub!(/\+/, ' ')
1102 string.gsub!(/%(..)/){[$1.hex].pack("c")}
1103 end
1104
1105 def gencookie(a, expire)
1106 x = a.collect{|k, v|
1107 sprintf("%s=%s", k, encode(v)) if v
1108 }
1109 x.delete(nil)
1110 return nil if x.empty?
1111 str = x.join('&')
1112 ex = (Time.new+expire).to_s
1113 sprintf "value=%s; expires=%s", encode(str), ex
1114 end
1115
1116 def login()
1117 @O.print @H.elementln("h1", nil){msg('title')}
1118 @O.print @H.elementln("h2", nil){msg('login')}
1119 format = {'method'=>'POST', 'action'=>@myname+"?-today"}
1120 @O.print @H.elementln("form", format){
1121 @H.elementln("table", nil){
1122 @H.elementln("tr", nil){
1123 @H.element("td", nil){msg('yourmail')} + \
1124 @H.element("td", nil){
1125 sprintf '<input type="text" size="%s" name="user">', @opt['size']
1126 }
1127 } + \
1128 @H.elementln("tr", nil){
1129 @H.element("td", nil){msg('passwd')} + \
1130 @H.element("td", nil){
1131 sprintf '<input type="password" size="%s" name="passwd">', @opt['size']
1132 }
1133 }
1134 } + '<input type="submit" value="LOGIN">'
1135 }
1136 @O.print footer2()
1137 end
1138 def open_pm()
1139 begin
1140 PasswdMgr.new(@opt['pswddb'])
1141 rescue
1142 STDERR.printf "Cannot open pswd file [%s]\n", @opt['pswddb']
1143 STDERR.printf "euid=#{Process.euid}, uid=#{Process.uid}\n", @opt['pswddb']
1144 nil
1145 end
1146 end
1147 def outputError(*msg)
1148 @O.print @H.p(msg('error')+sprintf(*msg))
1149 end
1150 def mailaddress(user)
1151 email = @sc.getuserattr(user, "email")
1152 email || user
1153 end
1154 def webpage(user)
1155 @sc.getuserattr(user, "webpage")
1156 end
1157 def checkauth()
1158 auth = catch (:auth) {
1159 unless @params['user']
1160 outputError(@H.a(@myname, msg('loginfirst')))
1161 throw :auth, nil
1162 end
1163 unless pm=open_pm()
1164 outputError(msg('autherror'))
1165 throw :auth, nil
1166 end
1167 user, passwd = @params['user'], @params['passwd']
1168 email = mailaddress(user)
1169 if !checkmail(user)
1170 outputError(msg('mailerror'))
1171 throw :auth, nil
1172 end
1173 if pm.userexist?(user)
1174 if pm.checkpasswd(user, passwd)
1175 throw :auth, true
1176 elsif passwd == @opt['forgot']
1177 newp = pm.setnewpasswd(user, @opt['pswdlen'])
1178 sendMail(email, "#{@mybase} password",
1179 "(#{ENV['REMOTE_ADDR']} からのアクセスによる送信)\n" +
1180 "#{@mybase} 用の #{user} さんのパスワードは\n" +
1181 (newp || "未定義") + "\nです。\n")
1182 @O.print @H.p("#{email} 宛に送信しておきました")
1183 throw :auth, nil
1184 else
1185 outputError(msg('pswderror'))
1186 throw :auth, nil
1187 end
1188 else
1189 newp = pm.setnewpasswd(user, @opt['pswdlen'])
1190 @sc.createuser(user, user)
1191 sendMail(email, "#{@mybase} new account",
1192 sprintf(msg('accessfrom'), ENV['REMOTE_ADDR']) +
1193 sprintf(msg('newpassword'), user, newp) +
1194 sprintf(msg('mischief')))
1195 @O.print @H.p(sprintf(msg('newaccount'), user))
1196 @O.print @H.p(@H.a(@myname, msg('login')))
1197 throw :auth, nil
1198 end
1199 }
1200 if auth
1201 return true
1202 else
1203 return false
1204 end
1205 end
1206 def safecopy(string)
1207 return nil unless string
1208 if $SAFE > 0
1209 cpy=''
1210 string.split('').each{|c|
1211 cpy << c[0].chr if c[0] != ?` # `
1212 }
1213 cpy
1214 else
1215 string
1216 end
1217 end
1218 def checkmail(mail)
1219 account, domain = mail.scan(/(.*)@(.*)/)[0]
1220 return false unless account != nil && domain != nil
1221 return false unless /^[-0-9a-z_.]+$/oi =~ domain
1222 domain = safecopy(domain)
1223 require 'socket'
1224 begin
1225 TCPSocket.gethostbyname(domain)
1226 return true
1227 rescue
1228 if test(?x, @opt["hostcmd"])
1229 open("| #{@opt['hostcmd']} -t mx #{domain}.", "r") {|ns|
1230 #p ns.readlines.grep(/\d,\s*mail exchanger/)
1231 return ! ns.readlines.grep(/is handled .*(by |=)\d+/).empty?
1232 }
1233 elsif test(?x, @opt["nslookup"])
1234 open("| #{@opt['nslookup']} -type=mx #{domain}.", "r") {|ns|
1235 #p ns.readlines.grep(/\d,\s*mail exchanger/)
1236 return ! ns.readlines.grep(/\d,\s*mail exchanger/).empty?
1237 }
1238 end
1239 return false
1240 end
1241 end # checkmail
1242
1243 # Logging
1244 #
1245 def putLog(msg)
1246 msg += "\n" unless /\n/ =~ msg
1247 open(@opt["logfile"], "a+") {|lp|
1248 lp.print Time.now.to_s + " " + msg
1249 }
1250 end
1251
1252 def sendnotify(whom, subj, body)
1253 users = users()
1254 if grepgroup(whom)
1255 recipients = @sc.members(whom)
1256 else
1257 recipients=[whom]
1258 end
1259 for u in recipients
1260 if users.grep(u)[0]
1261 sendMail(mailaddress(u), subj, body)
1262 end
1263 end
1264 end
1265
1266 def sendMail(to, subject, body)
1267 body = Kconv::tojis(body)
1268 subject = Kconv.tojis(subject)
1269 to = safecopy(to) # cleanup tainted address
1270 if /\e/ =~ subject # If contains JIS chars...
1271 subject = subject.split(//,1).pack('m')
1272 subject = "=?iso-2022-jp?B?#{subject}?="
1273 end
1274 subject.gsub!(/\n/, '')
1275 begin
1276 if (m=open("|-", "w"))
1277 m.print "To: #{to}\n"
1278 m.print "Subject: #{subject}\n"
1279 m.print "Mime-Version: 1.0
1280 Content-Transfer-Encoding: 7bit
1281 Content-Type: Text/Plain; charset=iso-2022-jp
1282
1283 "
1284 m.print body, "\n"
1285 m.close
1286 else
1287 # exec(@attr['mail'], "-s", subject, to)
1288 exec(@opt['sendmail'], to)
1289 exit 0;
1290 end
1291 putLog("Sent '#{subject}' to #{to}\n")
1292 return true
1293 rescue
1294 putLog("FAILED! - Sent '#{subject}' to #{to}\n")
1295 return nil
1296 end
1297 end # sendMail
1298
1299 def today()
1300 today = Time.now
1301 showtable(today)
1302 end
1303 def isleap?(y)
1304 if y%400 == 0
1305 true
1306 elsif y%100 == 0 || y%4 != 0
1307 false
1308 else
1309 true
1310 end
1311 end
1312 def daysofmonth(year, month)
1313 dl = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
1314 if month != 2 || !isleap?(year)
1315 dl[month-1]
1316 else
1317 29
1318 end
1319 end
1320 #
1321 # Return the Time object at the last day of last month
1322 def lastmonth(today)
1323 Time.at(Time.mktime(today.year, today.month)-3600*24)
1324 end
1325 #
1326 # Return the Time object at the first day of next month
1327 def nextmonth(today)
1328 y, m = today.year, today.month
1329 Time.at(Time.mktime(y, m, daysofmonth(y, m))+3600*24)
1330 end
1331
1332 def month(month)
1333 y, m = month.scan(%r,(\d\d\d\d+)/(\d+),)[0]
1334 if y && m
1335 showtable(Time.mktime(y, m, 1))
1336 else
1337 outputError "%s %s", msg('invaliddate'), month
1338 return nil
1339 end
1340 end
1341 def footer1()
1342 "<br>" + \
1343 @H.element("p"){
1344 me = @myname+"?-"; delim = " / "
1345 @H.a(me+'userman', msg('user', 'management')) + delim + \
1346 @H.a(me+'groupman', msg('group', 'management'))
1347 }
1348 end
1349
1350 def footer2()
1351 "<hr>" + \
1352 @H.element("code") {
1353 "This " + \
1354 @H.a(@after5url, "After5") + \
1355 " board is maintained by " + \
1356 @opt['maintainer'].sub('@', "&#x40;") + "."
1357 }
1358 end
1359 def footer()
1360 footer1+footer2
1361 end
1362 def nickname(userORgroup)
1363 if grepgroup(userORgroup)
1364 @sc.groupname(userORgroup)
1365 else
1366 @sc.nickname(userORgroup)
1367 end
1368 end
1369 #
1370 # show specified month's calendar
1371 def showtable(day)
1372 if !checkauth
1373 return nil
1374 end
1375
1376 month = day.month.to_s
1377 first = Time.mktime(day.year, day.month, 1)
1378 last = daysofmonth(day.year, day.month)
1379 wday1 = first.wday
1380 start = 1-wday1
1381 wname = %w[sun mon tue wed thu fri sat]
1382 today = Time.now
1383 todaym = today.month
1384 todayd = today.day
1385 tdclass = {}
1386 tdclass["width"] = "64px" if @oldagent # workaround for NN4
1387
1388 holiday = Holiday.new
1389 # create dayofweek header
1390 @O.print @H.elementln("h1", nil){sprintf "%d/%d", day.year, day.month}
1391 @O.print @H.startelement("table", {'border'=>"1", 'class'=>'main'})
1392
1393 # day of week
1394 @O.print @H.startelement("tr")
1395 for w in wname
1396 @O.print @H.element("th", {'class'=>w}){w.capitalize}
1397 end
1398 @O.print "\n"+@H.endelement(nil, true)
1399
1400 # create day table
1401 column = start
1402 ## p day, last
1403 while column <= last
1404 @O.print @H.elementln("tr", nil){
1405 (column..column+6).collect{|d|
1406 todayp = (day.month==todaym && d==todayd)
1407 wd=d-column
1408 hd = holiday.isHoliday(day.year, day.month, d, wd)
1409 tdclass['class'] = (hd ? 'holiday' : wname[wd])
1410 @H.element("td", tdclass){
1411 if d>0 && d <= last
1412 date = "%d/%d/%d"%[day.year, day.month, d]
1413 @H.element("p", {'class'=>todayp ? 'todayline' : 'dayline'}){
1414 @H.a(@myname+"?-show+"+date, d.to_s)
1415 } + \
1416 # isHoliday?
1417 if hd
1418 @H.element("small"){hd.join("<br>")}
1419 end.to_s + \
1420 @H.element("p", {'class'=>'topic'}){
1421 s = @sc.day_all(date, @params['user'])
1422 if !s.empty?
1423 s.keys.sort.collect{|time|
1424 s[time].keys.sort.collect{|who|
1425 sprintf "%04s:%s",
1426 time == @opt['alldaydir'] ? msg('allday') : time,
1427 nickname(who)
1428 }.join
1429 }.join("<br>")
1430 else
1431 @opt['tdskip']
1432 end
1433 }
1434 else
1435 @opt['tdskip']
1436 end
1437 }
1438 }.join
1439 }
1440 # ここ活かしてない
1441 @H.elementln("tr", nil){
1442 (column..column+6).collect{|d|
1443 wd=d%7
1444 @H.element("td", {'class'=>wname[wd]}){
1445 @H.element("div", {'class'=>'scline'}){
1446 if d>0 && d <= last
1447 s = @sc.day_all("%d/%d/%d"%[day.year, day.month, d])
1448 unless s.empty?
1449 s.keys.sort.collect{|time|
1450 s[time].keys.sort.collect{|who|
1451 sprintf "%4s:%s", time, who.sub(/@.*/, '')
1452 }
1453 }.join("<br>")
1454 else
1455 @opt['tdskip']
1456 end
1457 else
1458 @opt['tdskip']
1459 end
1460 }
1461 }
1462 }.join
1463 }
1464 column += 7
1465 end
1466
1467 # month-link
1468 @O.print @H.elementln("tr", {'class'=>'monthlink'}){
1469 lm1 = lastmonth(day)
1470 lm2 = lastmonth(lm1)
1471 lm3 = lastmonth(lm2)
1472 nm1 = nextmonth(day)
1473 nm2 = nextmonth(nm1)
1474 nm3 = nextmonth(nm2)
1475 [lm3, lm2, lm1, nil, nm1, nm2, nm3].collect{|t|
1476 @H.element("td"){
1477 if t.is_a?(Time)
1478 ym = sprintf("%d/%d", t.year, t.month)
1479 @H.a(sprintf("%s?-month+%s", @myname, ym), ym)
1480 else
1481 sprintf "%d/%d", day.year, day.month
1482 end
1483 }
1484 }.join("\n")
1485 }
1486 @O.print "\n"+@H.endelement(nil, true)
1487
1488 @O.print "showtable" if @params['user'] == @author
1489 @O.print footer
1490 ##schedule.day_all("2003/12/22")
1491 # @O.print @H.endelement()
1492 end
1493
1494 #
1495 # Put carrying values
1496 def hiddenvalues()
1497 h = %w[user].collect{|v|
1498 if @params[v]
1499 sprintf "<input type=\"hidden\" name=\"%s\" value=\"%s\">\n",
1500 v, @params[v]
1501 end
1502 }
1503 h.delete(nil)
1504 h.join
1505 end
1506 #
1507 # Return the string of table
1508 def dayTableString(s, user, date)
1509 r = ''
1510 r << @H.startelement("table", {'border'=>'1'}, true)
1511 r << @H.element("tr", nil){
1512 @H.element("th", {'class'=>'time'}){'TIME'} + \
1513 @H.element("th", nil){'Schedule'}
1514 }
1515 for time in s.keys
1516 tstr = case time
1517 when @opt['alldaydir']
1518 msg('allday')
1519 else
1520 sprintf "%02d:%02d", time.to_i/100, time.to_i%100
1521 end
1522 r << @H.startelement("tr", nil, true)
1523 r << @H.element("th", {'class'=>'time'}){tstr}
1524 r << @H.element("td"){
1525 @H.elementln("table"){
1526 s[time].keys.collect{|who|
1527 editable = (user==who || @sc.ismember(user, who))
1528 groupp = grepgroup(who)
1529 @H.element("tr"){
1530 @H.element("td", {'class'=>groupp ? 'group' : 'who'}){
1531 if !groupp && webpage(who)
1532 @H.a(webpage(who), nickname(who))
1533 else
1534 nickname(who)
1535 end
1536 } + \
1537 @H.element("td"){
1538 if editable
1539 s[time][who]['pub'] ? msg('public') :
1540 msg('nonpublic')
1541 else
1542 @opt['tdskip']
1543 end
1544 } + \
1545 @H.element("td"){
1546 if editable
1547 @H.a(@myname+"?-modify+#{date}/#{time}/#{who}",
1548 msg('modify'))
1549 else
1550 @opt['tdskip']
1551 end
1552 } + \
1553 @H.element("td"){
1554 if editable
1555 @H.a(@myname+"?-remove+#{date}/#{time}/#{who}",
1556 msg('remove'))
1557 else
1558 @opt['tdskip']
1559 end
1560 } + \
1561 @H.element("td"){s[time][who]['sched']}
1562 }
1563 }.join("\n")
1564 }
1565 }
1566 r << @H.endelement()
1567 end
1568 r << @H.endelement()
1569 r
1570 end
1571 #
1572 # show the schedule list of specified date
1573 #
1574 def show(date)
1575 if !checkauth
1576 return nil
1577 end
1578 border1 = {'border'=>'1'}
1579 user = safecopy(@params['user'])
1580 s = @sc.day_all(date, user)
1581 mygroup = @sc.groups().select{|g|@sc.ismember(user, g)}
1582
1583 @O.print @H.element("h1", nil){
1584 sprintf msg('fmtdaysschedule'), date
1585 }
1586 if s.empty?
1587 @O.print @H.p(msg('noplan'))
1588 else
1589 @O.print dayTableString(s, user, date)
1590 end #is_empty?
1591 thisyear, thismonth, thisday = date.scan(%r,(\d\d\d\d+)/(\d+)/(\d+),)[0]
1592 mstr = sprintf "%04d/%02d", thisyear.to_i, thismonth.to_i
1593 @O.print @H.a(@myname+"?-month+"+mstr,
1594 sprintf(msg('tomonthlist'), mstr))
1595 #
1596 # Link button to add new plan
1597 #now = Time.now+3600*24
1598 now = Time.mktime(thisyear, thismonth, thisday.to_i, Time.now.hour)
1599 y, m, d, h, min = now.year, now.month, now.day, now.hour, now.min
1600 @O.print @H.element('h2', nil, true){msg('addsched')}
1601 @O.print @H.element('p', nil){msg('defthisday')}
1602 @O.print @H.element("form", {'action'=>@myname+"?-addsched", 'method'=>'POST'}){
1603 @H.elementln('table', border1){
1604 @H.elementln('tr'){
1605 @H.element('th'){'Name'} + \
1606 @H.element('td'){
1607 hiddenvalues() + @sc.nickname(user)
1608 }
1609 } + \
1610 @H.elementln('tr'){
1611 @H.element('th'){'Year'} + \
1612 @H.element('td'){@H.select_integer("year", y, y+5, y)}
1613 } + \
1614 @H.elementln('tr'){
1615 @H.element('th'){'Month'} + \
1616 @H.element('td'){@H.select_integer("month", 1, 12, m)}
1617 } + \
1618 @H.elementln('tr'){
1619 @H.element('th'){'Day'} + \
1620 @H.element('td'){@H.select_integer("day", 1, 31, d)}
1621 } + \
1622 @H.elementln('tr'){
1623 @H.element('th'){'Time<br>'+ \
1624 sprintf(msg('24hour'), @opt['alldaydir'])} + \
1625 @H.element('td'){
1626 '<input type=text name="time" value="3000" size=8 maxlength="4">'
1627 }
1628 } + \
1629 @H.elementln('tr'){
1630 @H.element('th'){msg('publicok')} + \
1631 @H.element('td'){
1632 @H.radio('pub', 'yes', msg('yes')+'<br>', true) + \
1633 @H.radio('pub', 'no', msg('no'))
1634 }
1635 }
1636 ## table
1637 } + \
1638 @H.elementln("p"){ # put notify mail checkbox
1639 msg('reqnotify') + '<br>' + \
1640 @ntlist.collect{|n, v|
1641 @H.checkbox(n, 'yes', v, @params[n])
1642 }.join("\n") + \
1643 " " + @H.checkbox('rightnow', 'yes', msg('rightnow'), true) + \
1644 "\n"
1645 } + \
1646 if mygroup[0]
1647 @H.elementln("p"){ # put "register as"
1648 msg('registas') + "<br>\n" + \
1649 mygroup.collect{|g|
1650 @H.radio('registas', g, @sc.groupname(g))
1651 }.join(' ') + "\n/ " + \
1652 @H.radio('registas', 'no', msg('personal'))
1653 }
1654 end.to_s + "\n" + \
1655 @H.element("textarea", @schedulearea){} + "<br>\n" + \
1656 @H.submit_reset("GO")
1657 } #form
1658 @O.print "show" if user == @author
1659 end
1660
1661 #
1662 # call process
1663 def call_process(cmd, input=nil, timeout=10)
1664 prc = CMDTimeout.new
1665 fds = prc.start(cmd, timeout, true)
1666 if input
1667 Thread.start {
1668 fds[0].sync = true
1669 fds[0].print.input
1670 fds[0]
1671 }
1672 end
1673 begin
1674 fds[1].readlines
1675 ensure
1676 prc.close()
1677 end
1678 end
1679 #
1680 # notification registerer
1681 def notify_time(year, month, day, time, symbol)
1682 if (t = time.to_i) > 2359
1683 hh = mm = 0
1684 else
1685 hh, mm = t/100, t%100
1686 end
1687 base = Time.mktime(year.to_i, month.to_i, day.to_i, hh, mm)
1688 if /nt(\d+)([mh])$/ =~ symbol
1689 return nil if t > 2359
1690 num, unit = $1.to_i, $2.downcase
1691 rate = {'h'=>3600, 'm'=>60}[unit] || 3600
1692 return Time.at(base-rate*num)
1693 elsif /nt(\d+)d/ =~ symbol
1694 seconds = $1.to_i*3600*24
1695 targetday= Time.at(base-seconds).to_a
1696 targetnight =
1697 Time.mktime(*(targetday.indexes(5,4,3)+[@opt['night'].to_i]))
1698 elsif "nttoday" == symbol
1699 Time.mktime(year.to_i, month.to_i, day.to_i, @opt['morning'])
1700 end
1701 end
1702 def reg_notify(user, year, month, day, time, text, cancelall = nil)
1703 threshold = 5*60 # Omit notifycation within 30min future
1704
1705 y, m, d, t, = year.to_i, month.to_i, day.to_i, time.to_i
1706 if t > 2359
1707 hh = mm = 0
1708 else
1709 hh = t/100
1710 mm = t%100
1711 end
1712 now = Time.now
1713
1714 filearg = [user, year, month, day, t]
1715 @ntlist.each{|k, v|
1716 nt_time = notify_time(year, month, day, t, k)
1717 if !nt_time
1718 # do nothing for allday schedule's notification before some minutes
1719 elsif cancelall || nt_time < now+threshold ||
1720 /yes|on|true|1/ !~ @params[k] || !@params[k]
1721 # cancel
1722 uf = @sc.remove_crondir(nt_time, user, year, month, day, t)
1723 @sc.removefile(*(filearg+[k]))
1724 else
1725 # register
1726 lf = @sc.register_crondir(nt_time, user, year, month, day, t)
1727 @sc.putfile(*(filearg+[k, lf]))
1728 end
1729 }
1730 end
1731 def cancel_notify(user, year, month, day, time)
1732 reg_notify(user, year, month, day, time, 'dummy', true)
1733 end
1734 #
1735 # add or remove a schedule
1736 #
1737 def add_remove(remove = nil)
1738 if !checkauth
1739 return nil
1740 end
1741 user = registerer = @params['user']
1742 as = @params['registas']
1743 if as && as > '' && /^no$/ !~ as && @sc.ismember(user, as)
1744 registerer = as
1745 end
1746 now = Time.now
1747 y, m, d, h, min = now.year, now.month, now.day, now.hour, now.min
1748
1749 $KCODE='e' if $DEBUG
1750 @O.print @params.inspect if $DEBUG
1751 #
1752 # Check the validity of specified time
1753 sy = @params['year'].to_i
1754 sm = @params['month'].to_i
1755 sd = @params['day'].to_i
1756 tm = @params['time'].to_i
1757 if tm > 2399
1758 timedir=@opt['alldaydir']
1759 sh, smin = 23, 59
1760 tmstr = msg('allday')
1761 else
1762 sh = (tm/100).to_i
1763 smin = (tm%100).to_i
1764 timedir = sprintf("%04d", tm)
1765 tmstr = sprintf("%d:%02d", sh, smin)
1766 end
1767 time = nil
1768 begin
1769 time = Time.mktime(sy, sm, sd, sh, smin)
1770 rescue
1771 outputError "%s<br>\nyear=%s<br>month=%s<br>day=%s<br>time=%s\n",
1772 msg('invaliddate'),
1773 @params['year'], @params['month'], @params['day'], @params['time']
1774 return nil
1775 end
1776 unless @params['schedule'] && @params['schedule'].strip > ''
1777 outputError msg('putsomething')
1778 return nil
1779 end
1780
1781 # do remove or addition
1782 if remove
1783 cancel_notify(registerer, sy, sm, sd, timedir)
1784 begin
1785 @sc.remove(registerer, sy, sm, sd, timedir)
1786 #########@O.print @H.p(msg('remove')+msg('done'))
1787 rescue
1788 outputError("Failed"+$!)
1789 end
1790 else
1791 if time < now
1792 outputError(msg('past'))
1793 return nil
1794 end
1795 begin
1796 (text = @params['schedule'].strip.gsub(/\r+\n/, $/)) << "\n"
1797 replace = (/modify/i =~ @params['editmode'])
1798 rc = @sc.register(registerer, sy, sm, sd, timedir, text, replace)
1799 if @params['pub'] && /yes/ =~ @params['pub']
1800 @sc.putfile(registerer, sy, sm, sd, timedir, 'pub', "1\n")
1801 else
1802 @sc.removefile(registerer, sy, sm, sd, timedir, 'pub')
1803 end
1804 ######## @O.print @H.p(msg('appended')) if rc == 1
1805 rescue
1806 outputError("Failed"+$!)
1807 end
1808 text = @sc.getschedule(registerer, sy, sm, sd, timedir)
1809 reg_notify(registerer, sy, sm, sd, timedir, text)
1810 if @params['rightnow'] && /yes/i =~ @params['rightnow']
1811 header = sprintf("%s/%s/%s %s %s\n%s%s%s\n%s\n",
1812 sy, sm, sd, tmstr, msg('immediatenote'),
1813 msg('registerer_is'), nickname(registerer),
1814 if user!=registerer
1815 sprintf(" (%s%s)",
1816 msg('registerer'), nickname(user))
1817 else
1818 ""
1819 end,
1820 "-"*70)
1821 sendnotify(registerer, "Registration completed", header+text)
1822 end
1823 end
1824 show(sprintf("%04d/%02d/%02d", sy, sm, sd))
1825 @O.print "add_remove" if user == @author
1826 end
1827
1828 # add
1829 def addsched()
1830 add_remove(/remove/i =~ @params['editmode'])
1831 end
1832
1833 #
1834 # Display remove or modify screen
1835 def remove_modify(datetime, remove)
1836 if !checkauth
1837 return nil
1838 end
1839
1840 user = @params['user']
1841 y, m, d, time, dummy, as =
1842 datetime.scan(%r,(\d\d\d\d+)/(\d+)/(\d+)/(\d+)(/(.+))?,)[0]
1843 # datetime always contains trailing slash generated by parsedate
1844 # but if the trailing part is a user(not a group), it is removed
1845 # because it filtered out by grepgroup() function
1846 if ! (y && m && d && time)
1847 outputError "Invalid time specification"
1848 return nil
1849 elsif as && as > ''
1850 unless @sc.ismember(user, as)
1851 outputError "You have no permission to edit group %s's schedule", as
1852 return nil
1853 end
1854 user = as
1855 end
1856 unless text=@sc.getschedule(user, y, m, d, time)
1857 outputError "%s %s", datetime, msg('noplan')
1858 return nil
1859 end
1860 @O.print @H.elementln("h1"){
1861 sprintf "%s %s", datetime, remove ? msg('remove') : msg('modify')
1862 }
1863 @O.print @H.elementln("form", {'action'=>@myname+"?-addsched", 'method'=>'POST'}){
1864 pubp=(@sc.getfile(user, y, m, d, time, 'pub').to_i > 0)
1865 if as
1866 @H.hidden("registas", as)
1867 end.to_s + \
1868 "<input type=\"hidden\" name=\"year\" value=\"%04d\">\n" % y.to_i + \
1869 "<input type=\"hidden\" name=\"month\" value=\"%02d\">\n" % m.to_i + \
1870 "<input type=\"hidden\" name=\"day\" value=\"%02d\">\n" % d.to_i + \
1871 "<input type=\"hidden\" name=\"time\" value=\"%04d\">\n" % time.to_i + \
1872 msg('reqnotify') + "<br>\n" + \
1873 @ntlist.collect{|nt, v|
1874 cronp = @sc.getfile(user, y, m, d, time, nt)
1875 sprintf "<input type=\"checkbox\" name=\"%s\"%s>%s \n",
1876 nt, (cronp ? " checked" : ""), v
1877 }.join + "<br>" + \
1878 @H.element("textarea", @schedulearea) {text} + "<br>" + \
1879 @H.radio("editmode", "append", msg('append')) + ' / ' + \
1880 @H.radio("editmode", "modify", msg('modify'), !remove) + ' / ' + \
1881 @H.radio("editmode", "remove", msg('remove'), remove) + ' / ' + \
1882 "<br>\n" + \
1883 msg('publicok') + \
1884 @H.radio("pub", "yes", msg('yes'), pubp) + \
1885 @H.radio("pub", "no", msg('no'), !pubp) + \
1886 '<br>' + \
1887 @H.submit_reset("GO")
1888 }
1889 @O.print "remove_modify" if user == @author
1890 end
1891 def remove(datetime)
1892 remove_modify(datetime, true)
1893 end
1894 def modify(datetime)
1895 remove_modify(datetime, false)
1896 end
1897
1898 def prohibitviahttp()
1899 %w[REMOTE_ADDR REMOTE_HOST SERVER_NAME].each{|v|
1900 if ENV[v]
1901 print "Content-type: text/plain\n\n"
1902 print "Do not call this via CGI"
1903 exit 0
1904 end
1905 }
1906 end
1907 #
1908 # notify: call via cron
1909 def notify()
1910 prohibitviahttp()
1911 unless @opt['maintainer']
1912 STDERR.printf "Set maintainer(email-address) in %s\n", @opt['conf']
1913 STDERR.print "(ex) maintainer=yuuji@gentei.org\n"
1914 exit 0
1915 end
1916 Dir.chdir @mydir
1917 line = "-"*25
1918 indent = " "
1919 now = Time.now
1920 p "notifylist", @sc.notify_list(now) if $DEBUG
1921 @sc.notify_list(now).each{|u, datehash|
1922 dellist = []
1923 content = datehash.sort.collect{|date, filehash|
1924 next unless /(\d\d\d\d+)-(\d+)-(\d+)-(\d\d\d\d)/ =~ date
1925 y, m, d, t = $1.to_i, $2.to_i, $3.to_i, $4.to_i
1926 if t > 2359
1927 hhmm = msg('allday')
1928 comment = msg('theday')
1929 else
1930 hhmm = sprintf "%02d:%02d", t/100, t%100
1931 diff = Time.mktime(y, m, d, t/100, 5%100) - now
1932 if diff < 7200
1933 comment = "%d%s" % [diff/60, msg('minutes', 'before')]
1934 elsif (ddiff=(Time.mktime(y, m, d)-Time.mktime(now.year, now.month, now.day))/3600/24) == 0
1935 comment = "%s%d%s" %
1936 [msg('about'), diff/3600, msg('hours', 'before')]
1937 else
1938 comment = "%d%s" % [ddiff, msg('days', 'before')]
1939 end
1940 end
1941 dellist << filehash['file']
1942 sprintf("%s[[[%d/%d/%d %s]]]%s\n", line, y, m, d, hhmm, line) + \
1943 sprintf("(%s %s)\n", comment, msg('notification')) + \
1944 indent+filehash['text'].join(indent) + "\n\n"
1945 }
1946 # content.delete(nil)
1947 if content
1948 if $DEBUG
1949 print content
1950 else
1951 content.unshift(msg('introduce')+"\n"+msg('notifymail')+"\n")
1952 content.unshift(@opt['url'].to_s+"\n")
1953 if sendnotify(u, msg('notifysubj'), content.join)
1954 # send mail completed
1955 begin
1956 @sc.cleanup_files(dellist)
1957 rescue
1958 end
1959 end
1960 end
1961 end
1962 }
1963 if !(list=@sc.notify_list(now)).empty?
1964 subj = @mybase+": Undeleted old cron files detected"
1965 files = list.collect{|who, whash|
1966 whash.sort.collect{|date, fhash| fhash['file']}.join("\n")
1967 }.join("\n")
1968 sendMail(@opt['maintainer'], subj,
1969 "This is `#{@mybase}' in #{@mydir}\n" +
1970 "You'd better check and remove these files.\n\n"+files)
1971 end
1972
1973 exit 0
1974 end
1975
1976 #
1977 # user management
1978 def userman()
1979 if !checkauth
1980 return nil
1981 end
1982 user=@params['user']
1983 nickname = @sc.nickname(user)
1984 tdclass = {}
1985 tdclass["width"] = "80px" if @oldagent # workaround for NN4
1986
1987 @O.print @H.elementln("h1"){
1988 @mybase+' '+msg('user', 'management')
1989 }
1990 @O.print @H.p(@sc.mkusermap.inspect) if $DEBUG
1991 @O.print @H.p(msg('usermodwarn'))
1992 @O.print \
1993 @H.elementln("form", {'action'=>@myname+"?-usermod", 'method'=>'POST'}){
1994 @H.elementln("table"){
1995 @H.elementln("tr"){
1996 @H.element("td", tdclass) {msg('regaddress')} + \
1997 @H.element("td") {
1998 @H.element("code"){user}
1999 }
2000 } + \
2001 @H.elementln("tr"){
2002 @H.element("td", tdclass) {msg('mailaddress')} + \
2003 @H.element("td") {
2004 @H.text("newmail", mailaddress(user), @opt['size'], 80)
2005 }
2006 } + \
2007 @H.elementln("tr"){
2008 @H.element("td", tdclass) {msg('weburl')} + \
2009 @H.element("td") {
2010 @H.text("webpage", webpage(user), @opt['size'], 80)
2011 }
2012 } + \
2013 @H.elementln("tr"){
2014 @H.element("td") {msg('nickname')} + \
2015 @H.element("td") {
2016 @H.text("nickname", nickname, @opt['size'], 10)
2017 }
2018 }
2019 } + \
2020 @H.elementln("p"){
2021 msg('shortnameplz') + "<br>\n" + \
2022 @H.a(@after5url+"multiplenotify.html", msg('multiplemail'))
2023 } + \
2024 '<br>' + \
2025 @H.submit_reset("GO")
2026 } # form
2027
2028 #
2029 # Next section, REMOVE USER!
2030 @O.print @H.elementln("h2"){
2031 sprintf "%s %s %s", msg('user'), user, msg('deletion')
2032 }
2033 @O.print @H.p(msg('deletionwarn'))+"\n"
2034 @O.print @H.elementln("form", {'action'=>@myname+"?-delusersub+#{user}", 'method'=>'POST'}){
2035 @H.hidden("user", user) + "\n" + \
2036 @H.elementln("table"){
2037 @H.elementln("tr"){
2038 @H.elementln("td"){
2039 sprintf msg('deluser'), user
2040 } + \
2041 @H.elementln("td"){
2042 @H.radio("delete", "yes", msg('yes')) + ' ' + \
2043 @H.radio("delete", "no", msg('no'), true)
2044 }
2045 } + \
2046 @H.elementln("tr"){
2047 @H.elementln("td"){
2048 sprintf msg('really?'), user
2049 } + \
2050 @H.elementln("td"){
2051 @H.radio("delete2", "yes", msg('yes')) + ' ' + \
2052 @H.radio("delete2", "no", msg('no'), true)
2053 }
2054 }
2055 } + \
2056 "<br>\n" + @H.submit_reset("GO")
2057 }
2058
2059
2060 end
2061 def usermod()
2062 if !checkauth
2063 return nil
2064 end
2065 @O.print @H.elementln("h1"){
2066 msg('user', 'management')+" "+msg('done')
2067 }
2068 user=@params['user']
2069 email = mailaddress(user)
2070 newmail = @params['newmail']
2071 nickname = @sc.nickname(user)
2072 newnn = @params['nickname'].to_s.strip
2073 webpage = webpage(user)
2074 newweb = @params['webpage']
2075 if email != newmail
2076 # change user's address
2077 if newmail == user
2078 newvalue = nil
2079 elsif checkmail(newmail)
2080 newvalue = newmail
2081 else
2082 @O.print @H.elementln("pre"){"Invalid mail address"}
2083 end
2084 @O.print @H.elementln("pre"){
2085 if @sc.putuserattr(user, 'email', newvalue)
2086 sprintf "new mail address=\"%s\"", mailaddress(user)
2087 else
2088 sprintf "Setting new mail address to \"%s\" failed", newvalue
2089 end
2090 }
2091 end
2092 if nickname != newnn
2093 if @sc.setnickname(user, newnn)
2094 @O.print @H.p(msg('success'))
2095 @O.print @H.elementln("pre"){
2096 sprintf "user=\"%s\"\nnickname=\"%s\"", user, @sc.nickname(user)
2097 }
2098 @O.print @H.p(msg('nicknamenote')) if newnn == ''
2099 else
2100 @O.print @H.p(msg('failure'))
2101 end
2102 end
2103 if newweb > '' && webpage != newweb
2104 if @sc.putuserattr(user, "webpage", newweb)
2105 @O.print @H.p(msg('success'))
2106 @O.print @H.elementln("pre"){
2107 sprintf "user=\"%s\"\nwebpage=\"%s\"", user, webpage(user)
2108 }
2109 else
2110 @O.print @H.p("Update webpage"+msg('failure'))
2111 end
2112 end
2113 end
2114 #
2115 # Display form of group management
2116 def groupman()
2117 if !checkauth
2118 return nil
2119 end
2120 user=@params['user']
2121 nickname = @sc.nickname(user)
2122 tdclass = {}
2123 tdclass["width"] = "80px" if @oldagent # workaround for NN4
2124 admclass = {'class'=>'admin'}
2125 grmap = @sc.groupmap
2126
2127 @O.print @H.elementln("h1"){
2128 @mybase+' '+msg('group', 'management')
2129 }
2130 $KCODE='e' if $DEBUG
2131 @O.print grmap.inspect if $DEBUG
2132 @O.print @H.p(msg('joinmyself')+
2133 @H.a(@myname+"?-newgroup", msg('newgroup')))
2134 @O.print @H.p(msg('usermodwarn'))
2135 @O.print \
2136 @H.elementln("form", {'action'=>@myname+"?-groupmod", 'method'=>'POST'}){
2137 @H.elementln("table", {'border'=>'1', 'vertical-align'=>'top'}){
2138 grmap.collect{|g, ghash|
2139 @H.elementln("tr"){
2140 @H.element("td", @sc.isadmin(user, g) ? admclass : nil){
2141 g
2142 } + \
2143 @H.element("td"){
2144 @H.element("div", {'class'=>'c'}) {
2145 if @sc.isadmin(user, g)
2146 @H.a(@myname+"?-admgroup+#{g}", msg('adminop'))
2147 else
2148 '--'
2149 end
2150 }
2151 } + \
2152 @H.element("td"){
2153 memberp = @sc.ismember(user, g)
2154 if ghash['admin'].grep(user)[0]
2155 @H.text("groupname-#{g}", ghash['name'], nil, 20)
2156 else
2157 ghash['name']
2158 end + '<br>' + \
2159 @H.radio("groupadd-#{g}", "yes", "IN", memberp) + " / " + \
2160 @H.radio("groupadd-#{g}", "no", "OUT", !memberp)
2161 } + \
2162 @H.element("td"){
2163 ghash['members'].collect{|u|
2164 @sc.nickname(u)
2165 }.join(", ")
2166 }
2167 }
2168 }
2169 } + \
2170 '' + \
2171 @H.p(msg('groupwarn', 'shortnameplz')) + \
2172 @H.submit_reset("GO")
2173 } # form
2174 end
2175 def groupmod()
2176 if !checkauth
2177 return nil
2178 end
2179 @O.print @H.elementln("h1"){
2180 msg('group', 'management')+" "+msg('done')
2181 }
2182 user=@params['user']
2183 @O.print @params.inspect if $DEBUG
2184
2185 for grp in @sc.groups()
2186 #
2187 # as a member, participate or retire
2188 key = "groupadd-#{grp}"
2189 removep = (/no/i =~ @params[key])
2190 memberp = @sc.ismember(user, grp)
2191 if @params[key]
2192 if (!removep) ^ memberp
2193 @sc.addgroup(grp, [user], removep)
2194 @O.print @H.elementln("p"){
2195 sprintf "%s [%s] %s %s", msg('user'), user,
2196 removep ? msg('removedfromgp') : msg('addedtogroup'), grp
2197 }
2198 end
2199 end
2200 #
2201 # as a owner, change the name of group
2202 if @sc.isadmin(user, grp) &&
2203 (newname = @params["groupname-#{grp}"]) &&
2204 @sc.groupname(grp) != newname
2205 @sc.setgroupname(grp, newname)
2206 @O.print @H.elementln("p"){
2207 sprintf "%s %s%s %s",
2208 msg('group'), grp, msg('of', 'name', 'setto'), newname
2209 }
2210 end
2211 end
2212 end
2213 def users()
2214 unless pm=open_pm()
2215 outputError(msg('autherror'))
2216 return nil
2217 end
2218 pm.users
2219 end
2220 def grepgroup(gname)
2221 gr = @sc.groups.grep(gname)[0]
2222 end
2223 def admgroup(group = nil)
2224 # if group==nil, create new
2225 if !checkauth
2226 return nil
2227 end
2228 @O.print @H.elementln("h1"){
2229 msg('group', 'management')
2230 }
2231 user=@params['user']
2232
2233 # Check the existent group's validity
2234 if group
2235 unless (gr=grepgroup(group))
2236 @O.print @H.p("No such group #{group}")
2237 return nil
2238 end
2239 group = gr
2240 unless @sc.isadmin(user, group)
2241 @O.print @H.p("You are not administrator of #{group}.")
2242 return nil
2243 end
2244 @O.print @H.elementln("h2"){
2245 msg('group')+" #{group}" +
2246 if group != @sc.groupname(group)
2247 " (#{@sc.groupname(group)})"
2248 end.to_s
2249 }
2250 actionmethod={'action'=>@myname+"?-admgroupsub", 'method'=>'POST'}
2251 else
2252 # New group creation
2253 @O.print @H.elementln("h2"){
2254 msg('newgroup')
2255 }
2256 actionmethod={'action'=>@myname+"?-newgroupsub", 'method'=>'POST'}
2257 end
2258
2259 userlist = ([user] + users()).uniq
2260 myselfclass = {'class'=>'admin'}
2261 colspan2 = {'colspan'=>'2'}
2262 warnclass = {'class'=>'warn'}
2263 warnp = nil
2264
2265 @O.print @H.elementln("form", actionmethod){
2266 @H.hidden('group', group) + "\n" + \
2267 if group
2268 ""
2269 else
2270 # new group creation
2271 grps = @sc.groups()
2272 i=1
2273 defname = "group%03d"%i
2274 while grps.grep(defname)[0]
2275 defname = "group%03d"%(i+=1)
2276 end
2277 @H.element("pre"){
2278 msg('group', 'of', 'id')+"\n"+@H.text("group", defname) + "\n" + \
2279 msg('group', 'of', 'name', 'anystring')+"\n"+ \
2280 @H.text("gname", '') + "\n"
2281 }
2282 end + \
2283 @H.elementln("table", {'border'=>'1'}){
2284 @H.elementln("tr"){
2285 @H.element("th"){msg('join')} + \
2286 @H.element("th"){msg('administrator')} + \
2287 @H.element("th"){msg('member')}
2288 } + \
2289 userlist.collect{|u|
2290 recursememp = nil
2291 if group
2292 memberp = (@sc.ismember(u, group) && true)
2293 adminp = (@sc.isadmin(u, group) && true)
2294 if !memberp && @sc.members(group).grep(u)[0]
2295 recursememp = true
2296 end
2297 else
2298 memberp = adminp = (u == user)
2299 end
2300 @H.elementln("tr", (u==user ? myselfclass : nil)){
2301 @H.element("td"){
2302 @H.radio('mem-'+u, 'yes', 'YES / ', memberp) + \
2303 @H.radio('mem-'+u, 'no', 'NO', !memberp)
2304 } + \
2305 @H.element("td"){
2306 @H.radio('adm-'+u, 'yes', 'YES / ', adminp) + \
2307 @H.radio('adm-'+u, 'no', 'NO', !adminp)
2308 } + \
2309 @H.element("td"){
2310 @sc.nickname(u) + \
2311 if recursememp
2312 warnp = true
2313 @H.element("span", warnclass){"(*)"}
2314 end.to_s
2315 }
2316 }
2317 }.join + \
2318 # group names
2319 @H.elementln("tr"){
2320 @H.element("th", colspan2){msg('join')} + \
2321 @H.element("th"){msg('group')}
2322 } + \
2323 @sc.groups().collect{|g|
2324 next if group == g
2325 memberp = @sc.ismember(g, group)
2326 @H.element("tr"){
2327 @H.element("td", colspan2){
2328 @H.radio('mem-'+g, 'yes', 'YES / ', memberp) + \
2329 @H.radio('mem-'+g, 'no', 'NO', !memberp)
2330 } + \
2331 @H.element("td"){
2332 if @sc.isadmin(user, g)
2333 @H.a(@myname+"?-admgroup+#{g}", @sc.groupname(g))
2334 else
2335 @sc.groupname(g)
2336 end
2337 }
2338 }
2339 }.join
2340 } + "<br>\n" + \
2341 @H.submit_reset("GO")
2342 } # form
2343 @O.print @H.p(@H.element("span", warnclass){"(*)"}+
2344 msg('recursewarn')) if warnp
2345 if group
2346 @O.print @H.p(sprintf(msg('wholemembers'), group))
2347 @O.print @H.elementln("p", {'class'=>'listup'}){
2348 @sc.members(group).collect{|u|@sc.nickname(u)}.join(", ")}
2349 end
2350
2351 #
2352 # Next section, REMOVE GROUP!
2353 return nil unless group
2354 @O.print @H.elementln("h2"){
2355 sprintf "%s %s %s", msg('group'), group, msg('deletion')
2356 }
2357 @O.print @H.p(msg('deletionwarn'))+"\n"
2358 @O.print @H.elementln("form", {'action'=>@myname+"?-delgroupsub+#{group}", 'method'=>'POST'}){
2359 @H.hidden("group", group) + "\n" + \
2360 @H.elementln("table"){
2361 @H.elementln("tr"){
2362 @H.elementln("td"){
2363 sprintf msg('delgroup'), group
2364 } + \
2365 @H.elementln("td"){
2366 @H.radio("delete", "yes", msg('yes')) + ' ' + \
2367 @H.radio("delete", "no", msg('no'), true)
2368 }
2369 } + \
2370 @H.elementln("tr"){
2371 @H.elementln("td"){
2372 sprintf msg('really?'), group
2373 } + \
2374 @H.elementln("td"){
2375 @H.radio("delete2", "yes", msg('yes')) + ' ' + \
2376 @H.radio("delete2", "no", msg('no'), true)
2377 }
2378 }
2379 } + \
2380 "<br>\n" + @H.submit_reset("GO")
2381 }
2382
2383 @O.print footer()
2384 end
2385 def newgroup()
2386 admgroup(nil)
2387 end
2388
2389 def delgroupsub(group)
2390 if !checkauth
2391 return nil
2392 end
2393 user = @params['user']
2394 if group != @params['group']
2395 @O.print @H.p("Group mismatch")
2396 return nil
2397 end
2398 unless (gr=grepgroup(group))
2399 @O.print @H.p("No such group #{group}")
2400 return nil
2401 end
2402 group = gr
2403 unless @sc.isadmin(user, group)
2404 @O.print @H.p("You are not administrator of #{group}.")
2405 return nil
2406 end
2407 unless @params['delete'] && /yes/i =~ @params['delete'] \
2408 && @params['delete2'] && /yes/i =~ @params['delete2']
2409 @O.print @H.p(msg('chicken'))
2410 return nil
2411 end
2412 @O.print @H.elementln("h1"){
2413 msg('group')+" #{group} "+msg('deletion')
2414 }
2415 @O.print @H.p(@sc.destroygroup(group) ? msg("done") : msg("failure"))
2416
2417 @O.print footer()
2418 end
2419
2420 def deleteuser(user)
2421 @sc.deleteuser(user) &&
2422 begin
2423 pm = open_pm
2424 pm.delete(user)
2425 pm.close()
2426 true
2427 rescue
2428 nil
2429 end
2430 end
2431 def delusersub(user)
2432 if !checkauth
2433 return nil
2434 end
2435 user = @params['user']
2436 if user != @params['user']
2437 @O.print @H.p("User mismatch")
2438 return nil
2439 end
2440 unless (us=users().grep(user)[0])
2441 @O.print @H.p("No such user #{user}")
2442 return nil
2443 end
2444 user = us
2445 unless @params['delete'] && /yes/i =~ @params['delete'] \
2446 && @params['delete2'] && /yes/i =~ @params['delete2']
2447 @O.print @H.p(msg('chicken'))
2448 return nil
2449 end
2450 @O.print @H.elementln("h1"){
2451 msg('user')+" #{user} "+msg('deletion')
2452 }
2453 @O.print @H.p(deleteuser(user) ? msg("done") : msg("failure"))
2454
2455 @O.print footer()
2456 end
2457
2458 def admgroupsub()
2459 if !checkauth
2460 return nil
2461 end
2462 user = @params['user']
2463 group = @params['group']
2464 unless (gr=grepgroup(group))
2465 @O.print @H.element("pre"){"No such group #{group.inspect}"}
2466 return nil
2467 end
2468 unless @sc.isadmin(user, group)
2469 @O.print @H.p("You are not administrator of #{group}.")
2470 return nil
2471 end
2472 gorup = gr
2473 @O.print @H.elementln("h1"){
2474 msg('group', 'management', 'done')
2475 }
2476 @O.print @H.elementln("h2"){
2477 msg('group')+" #{group}" +
2478 if group != @sc.groupname(group)
2479 " (#{@sc.groupname(group)})"
2480 end.to_s
2481 }
2482 somethingdone = nil
2483 for u in users()
2484 for var, kind in {
2485 "mem"=>['members', 'member'], 'adm'=>['admin', 'administrator']}
2486 memv = "#{var}-#{u}"
2487 if @params[memv]
2488 joinp = ((/^yes/i =~ @params[memv]) && true)
2489 membp = if var=='mem'
2490 @sc.ismember(u, group)
2491 else # admin
2492 @sc.isadmin(u, group)
2493 end && true
2494 if var=='adm' && @sc.admins(group).length == 1 && membp && !joinp
2495 @O.print @H.p(sprintf(msg('soleadmin'), u, group))
2496 elsif joinp ^ membp
2497 somethingdone = true
2498 @sc.addgroup(group, [u], !joinp, kind[0])
2499 @O.print @H.elementln("p"){
2500 sprintf "%s [%s](%s) %s %s", msg('user'), u,
2501 msg(kind[1]),
2502 joinp ? msg('addedtogroup'): msg('removedfromgp'), group
2503 }
2504 end
2505 end
2506 end
2507 end # users()
2508
2509 # add or remove for group in groups
2510 for g in @sc.groups()
2511 next if g == group
2512 memv = "mem-#{g}"
2513 if @params[memv]
2514 joinp = ((/^yes/i =~ @params[memv]) && true)
2515 membp = (@sc.ismember(g, group) && true)
2516 if joinp ^ membp
2517 somethingdone = true
2518 @sc.addgroup(group, [g], !joinp)
2519 @O.print @H.elementln("p"){
2520 sprintf "%s [%s] %s %s", msg('group'), g,
2521 joinp ? msg('addedtogroup'): msg('removedfromgp'), group
2522 }
2523 end
2524 end
2525 end # groups
2526 unless somethingdone
2527 # @O.print @H.p(msg('nothingtodo'))
2528 end
2529 @O.print footer()
2530 end
2531 def newgroupsub()
2532 if !checkauth
2533 nil
2534 end
2535 user = @params['user']
2536 newgroup = @params['group']
2537 newgname = @params['gname']
2538
2539
2540 if @sc.groups.grep(newgroup)[0]
2541 @O.print @H.p(msg('existent')+newgroup)
2542 return nil
2543 end
2544 @sc.creategroup(newgroup, newgname, [user])
2545 admgroup(newgroup)
2546 end
2547
2548 def setpasswd(user)
2549 prohibitviahttp()
2550 pm = open_pm()
2551 exit 1 unless pm
2552 if pm.userexist?(user) then
2553 begin
2554 system "stty -echo"
2555 STDERR.print "New passwd: "
2556 p1 = STDIN.gets
2557 STDERR.print "\nAgain: "
2558 p2 = STDIN.gets
2559 ensure
2560 system "stty echo"
2561 end
2562 if (p1 == p2) then
2563 pm.setpasswd(user, p1.chop!)
2564 end
2565 STDERR.print "New password for #{user} set successfully\n"
2566 else
2567 STDERR.print "User #{user} not found\n"
2568 end
2569 pm.close()
2570 exit 0
2571 end
2572 def adduser(user)
2573 prohibitviahttp()
2574 pm = open_pm()
2575 exit 1 unless pm
2576 newpwd = pm.setnewpasswd(user, 4)
2577 print "#{user}'s password is #{newpwd}\n"
2578 pm.close()
2579 exit 0
2580 end
2581 def deluser(user)
2582 prohibitviahttp()
2583 pm = open_pm()
2584 exit 1 unless pm
2585 pm.delete(user)
2586 pm.close()
2587 exit 0
2588 end
2589
2590 # read configuratoin file
2591 def readconf(conf)
2592 cf = "after5.cf" #conf # || @opt['conf']
2593 cf = File.join(@mydir, cf) unless test(?s, cf)
2594 cf = File.join(ENV["HOME"], cf) unless test(?s, cf)
2595 return unless test(?s, cf)
2596 begin
2597 IO.foreach(cf){|line|
2598 next if /^\s *#/ =~ line
2599 line.chop!
2600 line.sub!(/^\s*#.*/, '')
2601 next if /^$/ =~ line
2602 case line
2603 # title, type = line.split(/\t+/)
2604 when /^([a-z]+)=(.*)/oi
2605 key, value = $1, $2
2606 case value
2607 when /^(no|none|null|nil)$/io
2608 @opt[key] = nil
2609 else
2610 @opt[key] = value
2611 end
2612 print "#{key} set to #{value}\n" if $DEBUG
2613 end
2614 }
2615 rescue
2616 STDERR.print "Configuration file %s not readable\n", cf
2617 end
2618 end
2619
2620 def parsedate(string)
2621 if %r,^(\d\d\d\d+)/(\d+)/(\d+)/(\d+)/([^/]+)$, =~ string
2622 sprintf "%04d/%02d/%02d/%04d/%s", $1.to_i, $2.to_i, $3.to_i, $4.to_i,
2623 grepgroup($5)
2624 elsif %r,^(\d\d\d\d+)/(\d+)/(\d+)/(\d+), =~ string
2625 sprintf "%04d/%02d/%02d/%04d", $1.to_i, $2.to_i, $3.to_i, $4.to_i
2626 elsif %r,^(\d\d\d\d+)/(\d+)/(\d+), =~ string
2627 sprintf "%04d/%02d/%02d", $1.to_i, $2.to_i, $3.to_i
2628 elsif %r,^(\d\d\d\d+)/(\d+), =~ string
2629 sprintf "%04d/%02d", $1.to_i, $2.to_i
2630 elsif %r,^(\d\d\d\d+)/(\d+), =~ string
2631 sprintf "%04d", $1.to_i
2632 end
2633 end
2634
2635 def getarg()
2636 argument = {}
2637
2638 while /^-/ =~ ARGV[0]
2639 case ARGV[0]
2640 when '-f'
2641 conf = ARGV[1]
2642 ARGV.shift
2643 when "-d"
2644 $DEBUG = true
2645 when "-install"
2646 when "-addsched"
2647 @job = "addsched"
2648 when "-today"
2649 @job = "today"
2650 when "-remove"
2651 ARGV.shift
2652 @job = 'remove "'+parsedate(ARGV[0])+'"'
2653 when "-modify"
2654 ARGV.shift
2655 @job = 'modify "'+parsedate(ARGV[0])+'"'
2656 when "-month"
2657 ARGV.shift
2658 @job = 'month "'+parsedate(ARGV[0])+'"'
2659 when "-show"
2660 ARGV.shift
2661 # @job = "show '"+ARGV[0]+"'"
2662 @job = "show '"+parsedate(ARGV[0])+"'"
2663 when "-login"
2664 @job = "login"
2665 when "-userman"
2666 @job = "userman"
2667 when "-usermod"
2668 @job = "usermod"
2669 when "-groupinout"
2670 @job = "groupinout"
2671 when "-groupsubmit"
2672 @job = "groupsubmit"
2673 when "-groupman"
2674 @job = "groupman"
2675 when "-groupmod"
2676 @job = "groupmod"
2677 when "-notify"
2678 @job = 'notify' # + exit
2679 when "-newgroup"
2680 @job = 'newgroup'
2681 when "-admgroup"
2682 ARGV.shift
2683 gr = safecopy(grepgroup(ARGV[0]))
2684 ##gr.untaint
2685 @job = 'admgroup "'+gr+'"'
2686 when "-admgroupsub"
2687 @job = 'admgroupsub'
2688 when "-newgroupsub"
2689 @job = 'newgroupsub'
2690 when "-delusersub"
2691 ARGV.shift
2692 usr = users().grep(ARGV[0])[0]
2693 @job = 'delusersub "'+usr+'"'
2694 when "-delgroupsub"
2695 ARGV.shift
2696 gr = grepgroup(ARGV[0])
2697 @job = 'delgroupsub "'+gr+'"'
2698 when /-(setpasswd|deluser|adduser)$/
2699 ARGV.shift
2700 @job = $1+ " '#{ARGV[0]}'" # + exit
2701 when ""
2702 end
2703 ARGV.shift
2704 end
2705
2706 readconf(@conf)
2707
2708 query = ''
2709 method = ENV["REQUEST_METHOD"]
2710 if /POST/i =~ method then
2711 length = ENV["CONTENT_LENGTH"].to_i
2712 query = STDIN.read(length)
2713 elsif /GET/i =~ method then
2714 query = ENV["QUERY_STRING"]
2715 else # executed from command line
2716 query = ARGV.join("&")
2717 end
2718
2719 for unit in query.split(/\&/)
2720 if /^([a-z][-_0-9@%a-z.]*)=(.*)/i =~ unit
2721 key, value = $1, $2
2722 #value.gsub!(/%(..)/){[$1.hex].pack("c")} # これでURLデコードが一発
2723 decode!(value)
2724 decode!(key)
2725 value = Kconv::toeuc(value) # EUCに変換
2726 printf "[%s] = %s\n", key, value if $DEBUG
2727 argument[key] = value
2728 end
2729 end
2730 argument
2731 end
2732 def getcookie()
2733 cookie = {}
2734 if /value=(.*)/ =~ ENV['HTTP_COOKIE']
2735 # value=$1.gsub!(/%(..)/){[$1.hex].pack("c")}
2736 value=decode!($1)
2737 for line in value.split("&")
2738 if /(\w+)=(.*)/ =~ line
2739 key, value = $1, $2
2740 #value.gsub!(/%(..)/){[$1.hex].pack("c")} # これでURLデコードが一発
2741 decode!(value)
2742 value = Kconv::toeuc(value) # EUCに変換
2743 printf "cookie[%s] = %s\n", key, value if $DEBUG
2744 cookie[key] = value
2745 end
2746 end
2747 end
2748 cookie
2749 end
2750 end
2751
2752 After5.new.doit
2753
2754 if __FILE__ == $0
2755 end
2756
2757
2758 # Local variables:
2759 # buffer-file-coding-system: euc-jp
2760 # End:

yatex.org