Mercurial > hgrepos > hgweb.cgi > after5
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('@', "@") + "." | |
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: |