1# Web Alpine Config options
2# $Id: alpine.tcl 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
3# ========================================================================
4# Copyright 2006-2008 University of Washington
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10#     http://www.apache.org/licenses/LICENSE-2.0
11#
12# ========================================================================
13
14encoding system "utf-8"
15
16set _wp(appname)	Alpine
17set _wp(admin)		admin@sample-domain.edu
18set _wp(helpdesk)	admin@sample-domain.edu
19set _wp(comments)	help@sample-domain.edu
20
21# List of userid's allowed to request the monitor script output
22set _wp(monitors)	{}
23
24# directory prefix web server uses for web alpine page requests
25# Note: set to {} if DocumentRoot set to the root of web alpine cgi scripts
26set _wp(urlprefix)	webmail
27
28# file system path to CGI application files
29# directory containing web alpine application scripts and supporting tools
30# The htdocs/ directory is located at /srv/www/htdocs. Here we copy the
31# web directory to /srv/www/Webalpine/web using "tar cf" followed by
32# "tar xf" which preserves symbolic links
33# Original configuration:
34#set _wp(fileroot)	/usr/local/libexec/alpine
35set _wp(fileroot)	/srv/www/Webalpine/web
36
37
38set _wp(tmpdir)		/tmp
39
40# NOTE: Make SURE tclsh and alpine.tcl symlinks in this directory
41set _wp(cgipath)	[file join $_wp(fileroot) cgi]
42
43# CGI scripts implementing U/I, session cookie scope
44set _wp(appdir)		alpine
45
46# UI versions
47set _wp(ui1dir)		1.0
48set _wp(ui2dir)		2.0
49
50# place for CGI scripts not requiring session-key
51set _wp(pubdir)		pub
52
53# place for binaries referenced by the CGI scripts
54set _wp(bin)		[file join $_wp(fileroot) bin]
55set _wp(servlet)	alpined
56set _wp(pc_servlet)	pc_alpined
57set _wp(pldap)          alpineldap
58
59# place for config files referenced by the CGI scripts
60set _wp(confdir)	[file join $_wp(fileroot) config]
61set _wp(conffile)	pine.conf
62set _wp(defconf)	$_wp(conffile)
63
64# place for library files used by CGI scripts
65set _wp(lib)		[file join $_wp(fileroot) lib]
66
67# directory used temporarily to stage attached and detached files
68# this directory is owned by wwwrun:www
69set _wp(detachpath)	[file join $_wp(fileroot) detach]
70
71set _wp(imagepath)	[file join / $_wp(urlprefix) images]
72
73set _wp(buttonpath)	[file join $_wp(imagepath) buttons silver]
74
75set _wp(staticondir)	env
76
77set _wp(servername)	[info hostname]
78
79# MUST specify SSL/TLS connection
80set _wp(serverport)	{}
81set _wp(serverpath)	https://[file join [join [eval list $_wp(servername) $_wp(serverport)] :] $_wp(urlprefix)]
82
83# MAY specify a plaintext connection (comment out if plain support undesired)
84set _wp(plainport)	{}
85set _wp(plainservpath)	http://[file join [join [eval list $_wp(servername) $_wp(plainport)] :] $_wp(urlprefix)]
86
87# url of faq page(s) available from initial greeting page
88#set _wp(faq)		"http://www.yourserver/faqs/alpine.html"
89
90# url of informational page accessible from initial greeting page
91set _wp(releaseblurb)	"$_wp(plainservpath)/alpine/help/release.html"
92
93# url of previous version server to be accessible from initial greeting page
94#set _wp(oldserverpath)	"https://previous.version.server.edu:444/"
95
96# session id length: make sure the integer count below matches what's built
97# into the pubcookie: "src/pubcookie/wp_uidmapper_lib.h:#define WP_KEY_LEN 6"
98set _wp(sessidlen)	6
99
100# Where and what format the alpined comm socket should take
101set _wp(sockdir)	$_wp(tmpdir)
102set _wp(sockpat)	wp%s
103
104# skin settings
105set _wp(bordercolor)	#FEFAC9
106set _wp(menucolor)	#3E2E6D
107set _wp(dialogcolor)	#FEFAC9
108set _wp(titlecolor)	#000000
109set _wp(logodir)	alpine
110
111# various timerouts, dimensions and feature settings
112set _wp(refresh)	600
113set _wp(timeout)	900
114set _wp(autodraft)	300
115set _wp(logoutpause)	60
116set _wp(indexlines)	20
117set _wp(indexlinesmax)	50
118set _wp(indexheight)	24
119set _wp(navheight)	28
120set _wp(width)		80
121set _wp(titleheight)	34
122set _wp(titlesep)	4
123set _wp(config)		remote_pinerc
124set _wp(motd)		motd
125set _wp(save_cache_max)	6
126set _wp(fldr_cache_max)	20
127set _wp(fldr_cache_def)	3
128set _wp(statushelp)	0
129set _wp(imgbuttons)	0
130set _wp(keybindings)	1
131set _wp(dictionary)	0
132set _wp(debug)		0
133set _wp(cmdtime)	0
134set _wp(evaltime)	0
135set _wp(menuargs)	{width="112" nowrap valign=top}
136set _wp(ispell)		/usr/local/bin/ispell
137
138# Yahoo! User Interface Library location
139set _wp(yui)		$_wp(serverpath)/$_wp(appdir)/$_wp(ui2dir)/lib/yui
140#set _wp(yui)		"http://yui.yahooapis.com/2.7.0"
141
142# usage reporter - input: username as first command line argument
143#                  output: space separated integers usage and total
144#set _wp(usage)		$_wp(bin)/usage.tcl
145#set _wp(usage_link)	"https://uwnetid.washington.edu/disk/"
146
147# limit uploads to 1 file at a time, maximum 20MB.
148set _wp(uplim_files)	1
149set _wp(uplim_bytes)	20000000
150
151# verify sessid from consistent REMOTE_ADDR (set to 0 for proxying clusters)
152set _wp(hostcheck)	0
153
154# set to list of domains for which ssl is NOT required
155#set _wp(ssl_safe_domains)	{}
156
157# set to list of address blocks or ranges for which ssl is NOT required
158#set _wp(ssl_safe_addrs)		{}
159
160# set this value to zero to turn OFF ssl by default
161set _wp(ssl_default)	1
162
163# allow connecting user to specify imap server on greeting page
164set	_wp(flexserver)	1
165
166# make sure tmp files and such are ours alone to read/write
167catch {exec umask 044}
168
169#fix up indexheight so it isn't too high or too low
170set _wp(indexheight) [expr {$_wp(indexheight) <= 20 ? 20 : $_wp(indexheight) >= 30 ? 30 : $_wp(indexheight)}]
171
172# SPAM reporting facility, if set "Report Spam" button appears at top of View Page
173#set _wp(spamaddr)	spamaddr@sample-domain.edu
174#set _wp(spamfolder)	junk-mail
175#set _wp(spamsubj)	"ATTACHED SPAM"
176
177# external mail filter config link
178#set _wp(filter_link)	http://delivery-filter.sample-domain.edu/filter/config
179
180# external vacation config link
181#set _wp(vacation_link)	http://vacation.sample-domain.edu/vacation/config
182
183#
184# Nickname server bindings.  If not present, prompt for the
185# destination of the default pinerc location.
186#
187set _wp(hosts) {
188    {
189      Gmail
190      imap.gmail.com/ssl
191      $_wp(confdir)/gmail.conf
192    }
193    {
194      GMX
195      imap.gmx.com/ssl
196      $_wp(confdir)/gmx.conf
197    }
198    {
199      Deskmail
200      $User.deskmail.washington.edu/ssl
201      $_wp(confdir)/conf.deskmail
202    }
203}
204
205# Everybody inherits the cgi, comm packages
206lappend auto_path $_wp(lib)
207
208package require cgi
209package require WPComm
210
211# Recipient of bad news bubbling up from cgi.tcl...
212cgi_admin_mail_addr $_wp(admin)
213
214cgi_sendmail {}
215
216#cgi_mail_relay localhost
217cgi_mail_relay smtpserver.sample-domain.edu
218
219# set permissions for owner-only handling
220cgi_tmpfile_permissions 0640
221
222#set upload limits
223cgi_file_limit $_wp(uplim_files) $_wp(uplim_bytes)
224
225# universal body tag parameters
226cgi_body_args link=#0000FF vlink=#000080 alink=#FF0000 marginwidth=0 marginheight=0 topmargin=0 leftmargin=0
227
228# Common Images Image definitions
229cgi_imglink logo		[file join $_wp(imagepath) logo $_wp(logodir) big.gif]	border=0	"alt=Web Alpine"
230cgi_imglink smalllogo		[file join $_wp(imagepath) logo $_wp(logodir) small.gif]	border=0	"alt=About Web Alpine"
231cgi_imglink background		[file join $_wp(imagepath) logo $_wp(logodir) back.gif]		border=0	align=top
232cgi_imglink dot			[file join $_wp(imagepath) dot2.gif]		border=0	align=top
233cgi_imglink increas		[file join $_wp(imagepath) increas4.gif]	border=0	align=absmiddle
234cgi_imglink decreas		[file join $_wp(imagepath) decreas4.gif]	border=0	align=absmiddle
235cgi_imglink expand		[file join $_wp(imagepath) b_plus.gif]	border=0	"alt=Expand"	height=9 width=9
236cgi_imglink contract		[file join $_wp(imagepath) b_minus.gif]	border=0	"alt=Collapse"	height=9 width=9
237cgi_imglink fullhdr		[file join $_wp(imagepath) hdr.gif]		border=0	"alt=Full Header"
238cgi_imglink nofullhdr		[file join $_wp(imagepath) hdrnon.gif]	border=0	"alt=Digested Header"
239cgi_imglink bang		[file join $_wp(imagepath) caution.gif]	border=0	"alt=!"
240cgi_imglink postmark		[file join $_wp(imagepath) postmark.gif]	border=0	"alt=New Mail"
241cgi_imglink gtab		[file join $_wp(imagepath) tabs gtab.gif]	border=0	align=top
242cgi_imglink gdtab		[file join $_wp(imagepath) tabs gdtab.gif]	border=0	align=top
243cgi_imglink abtab		[file join $_wp(imagepath) tabs abtab.gif]	border=0	align=top
244cgi_imglink abdtab		[file join $_wp(imagepath) tabs abdtab.gif]	border=0	align=top
245cgi_imglink ctab		[file join $_wp(imagepath) tabs ctab.gif]	border=0	align=top
246cgi_imglink cdtab		[file join $_wp(imagepath) tabs cdtab.gif]	border=0	align=top
247cgi_imglink ftab		[file join $_wp(imagepath) tabs ftab.gif]	border=0	align=top
248cgi_imglink fdtab		[file join $_wp(imagepath) tabs fdtab.gif]	border=0	align=top
249cgi_imglink mltab		[file join $_wp(imagepath) tabs mltab.gif]	border=0	align=top
250cgi_imglink mldtab		[file join $_wp(imagepath) tabs mldtab.gif]	border=0	align=top
251cgi_imglink mvtab		[file join $_wp(imagepath) tabs mvtab.gif]	border=0	align=top
252cgi_imglink mvdtab		[file join $_wp(imagepath) tabs mvdtab.gif]	border=0	align=top
253cgi_imglink rtab		[file join $_wp(imagepath) tabs rtab.gif]	border=0	align=top
254cgi_imglink rdtab		[file join $_wp(imagepath) tabs rdtab.gif]	border=0	align=top
255
256
257# Link definitions
258cgi_link Admin "Web Alpine Administrator" "mailto:$_wp(admin)"
259cgi_link Start "Web Alpine Home Page" "$_wp(serverpath)/session/greeting.tcl" target=_top
260
261# Internally referenced CGI directory root
262cgi_root $_wp(serverpath)
263cgi_suffix .tcl
264
265# have cgi.tcl convert eols in muiltipart/form-data
266set _cgi(no_binary_upload) 1
267
268proc WPSocketName {sessid} {
269  global _wp
270
271  return [file join $_wp(sockdir) [format $_wp(sockpat) $sessid]]
272}
273
274proc WPValidId {{sessid {}}} {
275    global _wp env
276
277    if {[string length $sessid] == 0} {
278      set created 1
279
280      # Session Handle: a bit reasonably random number.  the format
281      # is convenient for pubcookie auth'd support
282      set rnum {}
283      set rsrc /dev/urandom
284      set idbytelength [expr {$_wp(sessidlen) * 4}]
285      if {[file readable $rsrc] && [catch {open $rsrc r} fp] == 0} {
286	while {1} {
287	  for {set i 0} {$i < $idbytelength} {incr i} {
288	    if {$i && ($i % 4) == 0} {
289	      append rnum "."
290	    }
291
292	    if {[catch {read $fp 1} n] == 0} {
293	      binary scan $n c x
294	      set x [expr ($x & 0xff)]
295	      append rnum [format {%02x} $x]
296	    } else {
297	      set rnum {}
298	      break
299	    }
300	  }
301
302	  if {[file exists [WPSocketName $rnum]]} {
303	    set rnum {}
304	  } else {
305	    break
306	  }
307	}
308
309	close $fp
310      }
311
312      # second choice for random numbers
313      if {[string length $rnum] == 0} {
314	expr srand([clock seconds])
315	for {set i 0} {$i < $idbytelength} {incr i 4} {
316	  if {$i && ($i % 4) == 0} {
317	    append rnum "."
318	  }
319
320	  append rnum [format {%08x} [expr int((100000000 * rand()))]]
321	}
322      }
323
324      # generate a session ID
325      set _wp(sessid) $rnum
326    } else {
327      set sessidparts [split $sessid {@}]
328      switch [llength $sessidparts] {
329	1 {
330	  set _wp(sessid) $sessid
331	}
332	2 {
333	  if {[string compare [string tolower [lindex $sessidparts 1]] [string tolower [info hostname]]]} {
334	    regexp {^([a-zA-Z]*://).*} [cgi_root] match proto
335	    error [list redirect "${proto}[lindex $sessidparts 1]:$env(SERVER_PORT)?$env(QUERY_STRING)"]
336	  } else {
337	    set _wp(sessid) [lindex $sessidparts 0]
338	  }
339	}
340	default {
341	  error "Malformed Session ID: $sessid"
342	}
343      }
344    }
345
346    set _wp(sockname) [WPSocketName $_wp(sessid)]
347
348    if {[info exists _wp(cumulative)]} {
349      rename WPCmd WPCmd.orig
350      rename WPCmdTimed WPCmd
351    }
352
353    if {[info exists _wp(hostcheck)] && $_wp(hostcheck) == 1 && ![info exists created]
354	&& [catch {WPCmd set wp_client} client] == 0
355	&& (([info exists env(REMOTE_ADDR)] && [string length $env(REMOTE_ADDR)] && [string compare $client $env(REMOTE_ADDR)])
356	    || ([info exists env(REMOTE_HOST)] && [string length $env(REMOTE_HOST)] && [string compare $client $env(REMOTE_HOST)]))} {
357      error "Request from unrecognized client"
358    }
359}
360
361proc WPAbort {} {
362    WPCleanup
363    cgi_exit
364}
365
366proc WPCleanup {} {
367    global _wp
368
369    if {[info exists _wp(cleanup)]} {
370	foreach item $_wp(cleanup) {
371	    catch {eval $item}
372	}
373    }
374}
375
376proc WPEval {vars cmd} {
377    global _wp
378
379    if {$_wp(cmdtime) || $_wp(evaltime)} {
380	set _wp(cumulative) 0
381    }
382
383    set _wp(cmd) $cmd
384    set _wp(vars) [linsert $vars 0 [list sessid "Missing Session ID"]]
385
386    uplevel 1 {
387      cgi_eval {
388	if {$_wp(debug) > 1} {
389	    cgi_debug -on
390	}
391
392	# Session id?
393	if {[catch {WPGetInputAndID sessid}]} {
394	  return
395	}
396
397	foreach item $_wp(vars) {
398	  if {[catch {eval WPImport $item} errstr]} {
399	    WPInfoPage "Web Alpine Error" [font size=+2 $errstr] "Please close this window."
400	    return
401	  }
402	}
403
404	# evaluate the given script
405	if {[catch {cgi_buffer $_wp(cmd)} result]} {
406
407	    reset_cgi_state
408
409	    if {[string index $result 0] == "_"} {
410	      switch -- [lindex $result 0] {
411		_info {
412		  WPInfoPage [lindex $result 1] [font size=+2 [lindex $result 2]] [lindex $result 3]
413		}
414		_action {
415		  switch -regexp -- [lindex $result 2] {
416		    "[Ii]nactive [Ss]ession" {
417		      WPInactivePage
418		    }
419		    default {
420		      if {[string length [lindex $result 3]]} {
421			set remedy [lindex $result 3]
422		      } else {
423			set remedy "   Click your browser's Back button to return to previous page."
424		      }
425		      WPInfoPage "[lindex $result 1] Error" [font size=+2 [lindex $result 2]] \
426			  "Please report this to the [cgi_link Admin].$remedy"
427		    }
428		  }
429		}
430		_redirect {
431		  cgi_http_head {
432		    cgi_redirect [lindex $result 1]
433		  }
434
435		  cgi_html { cgi_body {} }
436		}
437		_close {
438		  if {[string length [lindex $result 1]] == 0} {
439		    set result "Indeterminate error"
440		  }
441
442		  WPInfoPage "Web Alpine Error" [font size=+2 [lindex $result 1]] "Please close this window."
443		}
444		default {
445		  if {[string length $result]} {
446		    WPInfoPage "Web Alpine Error" [font size=+2 "Eval Error: $result"] \
447			"Please complain to the [cgi_link Admin].   Click Back button to return to previous page."
448		  } else {
449		    WPInfoPage "Web Alpine Error" [font size=+2 "Indeterminate error response"] \
450			"Please complain to the [cgi_link Admin] and click Back button to return to previous page."
451		  }
452		}
453	      }
454	    } else {
455	      if {[regexp {[Ii]nactive [Ss]ession} $result]} {
456		WPInactivePage
457	      } else {
458		WPInfoPage "Web Alpine Error" [font size=+2 "Error: $result"] \
459		    "Please report this to the [cgi_link Admin]. Try clicking your browser's Back button to return to a working page."
460	      }
461	    }
462	} else {
463	    catch {cgi_puts $result}
464	}
465    }
466  }
467
468    # cleanup here
469    WPCleanup
470
471    if {[info exists _wp(cumulative)]} {
472      WPdebug "Cumulative Eval: $_wp(cumulative)"
473      unset _wp(cumulative)
474    }
475}
476
477proc WPGetInputAndID {_sessid} {
478  global _wp
479  upvar $_sessid sessid
480
481  # Import data and validate it
482  if {[catch {cgi_input "sessid=8543949466398&"} result]} {
483    WPInfoPage "Web Alpine Error" [font size=+2 $result] "Please close this window."
484    error "Cannot get CGI Input"
485  }
486
487  if {[catch {WPImport sessid "Missing Session ID"} errstr]} {
488    if {[regexp {.*sessid.*no such.*} $errstr]} {
489      WPInactivePage [list "Your browser may have failed to send the necessary <i>cookie</i> information.  Please verify your browser configuration has cookies enabled."]
490    } else {
491      WPInfoPage "Web Alpine Error" [font size=+2 $errstr] "Please close this window."
492    }
493
494    error "Session ID Failure"
495  } else {
496    # initialization here
497    if {[catch {WPValidId $sessid} result]} {
498      if {[string compare [lindex $result 0] redirect]} {
499	WPInfoPage "Web Alpine Error" [font size=+2 "$result"] \
500	    "Please complain to the [cgi_link Admin] and visit the [cgi_link Start] later."
501      } else {
502	cgi_http_head {
503	  cgi_redirect [lindex $result 1]
504	}
505      }
506
507      error "Unrecoverable Error"
508    } elseif {$_wp(sessid) == 0} {
509      WPInactivePage
510      error "Inactive Session"
511    }
512
513    if {[catch {WPCmd set serverroot} serverroot] == 0} {
514      cgi_root $serverroot
515    }
516  }
517}
518
519proc WPCmdEval {args} {
520  return [eval $args]
521}
522
523proc WPCmd {args} {
524  global _wp
525
526  return [WPSend $_wp(sockname) $args]
527}
528
529proc WPCmdTimed {args} {
530  global _wp
531
532  set t [lindex [time {set r [WPSend $_wp(sockname) $args]}] 0]
533  incr _wp(cumulative) $t
534
535  if {$_wp(cmdtime)} {
536    WPdebug "time $t : $args"
537  }
538
539  return $r
540}
541
542proc WPLoadCGIVar {_var} {
543  upvar $_var var
544
545  if {[catch {cgi_import_as $_var var} result]
546      && [catch {WPCmd set $_var} var]
547      && [catch {cgi_import_cookie_as $_var var} result]} {
548    error [list _action "Import Cookie $_var" $result]
549  }
550}
551
552proc WPLoadCGIVarAs {_var _varas} {
553    upvar $_varas varas
554
555  if {[catch {cgi_import_as $_var varas} result]
556      && [catch {WPCmd set $_var} varas]
557      && [catch {cgi_import_cookie_as $_var varas} result]} {
558    set varas ""
559  }
560}
561
562proc WPImport {valname {errstring ""} {default 0}} {
563    upvar $valname val
564
565    if {[catch {cgi_import_as $valname val} result]} {
566      if {[catch {WPCmd set $valname} val]} {
567	if {[catch {cgi_import_cookie_as $valname val} result]} {
568	  if {[string length $errstring] > 0} {
569	    error "$errstring: $result"
570	  } else {
571	    set val $default
572	  }
573	}
574      }
575    }
576}
577
578
579proc WPExportCookie {name value {scope ""}} {
580    global _wp
581
582    cgi_cookie_set $name=$value "path=[file join / $_wp(urlprefix) $scope]"
583}
584
585
586# handle dynamic sizing of images showing thread relationships
587proc WPThreadImageLink {t h} {
588    global _wp
589
590    return "<img src=\"[file join $_wp(imagepath) ${t}.gif]\" border=0 align=top height=${h} width=14>"
591}
592
593
594proc WPInactivePage {{reasons ""}} {
595  set l {}
596  foreach r $reasons {
597    append l "<li>$r"
598  }
599
600  WPInfoPage "Inactive Session" \
601	"[font size=+2 "Web Alpine Session No Longer Active"]" \
602      "There are several reasons why a session might become inactive.<ul><li>A bookmarked reference to a Web Alpine page.<li>Failed periodic page reload due to browser/system suspension associated with power saving mode, etc.${l}</ul><p>Please visit the [cgi_link Start] to start a new session."
603}
604
605proc WPInfoPage {title exp1 {exp2 ""} {imgurl {}} {exp3 ""}} {
606  global _wp
607
608  catch {
609
610    cgi_html {
611      cgi_head {
612	cgi_title $title
613	cgi_stylesheet [file join / $_wp(urlprefix) $_wp(pubdir) standard.css]
614      }
615
616      cgi_body {
617	cgi_table height="20%" {
618	  cgi_table_row {
619	    cgi_table_data {
620	      cgi_puts [cgi_nbspace]
621	    }
622	  }
623	}
624
625	cgi_center {
626	  cgi_table border=0 width=500 cellpadding=3 {
627	    cgi_table_row {
628	      cgi_table_data align=center rowspan=3 {
629		if {[string length $imgurl]} {
630		  cgi_put [cgi_url [cgi_imglink logo] $imgurl]
631		} else {
632		  cgi_put [cgi_imglink logo]
633		}
634	      }
635
636	      cgi_table_data rowspan=3 {
637		cgi_put [nbspace]
638		cgi_put [nbspace]
639	      }
640
641	      cgi_table_data {
642		cgi_puts $exp1
643	      }
644
645	    }
646
647	    if {[string length $exp3]} {
648	      cgi_table_row {
649		cgi_table_data "style=\"border: 1px solid red; background-color: pink\"" {
650		  cgi_puts $exp3
651		}
652	      }
653	    }
654
655	    if {[string length $exp2]} {
656	      cgi_table_row {
657		cgi_table_data {
658		  cgi_puts $exp2
659		}
660	      }
661	    }
662	  }
663	}
664      }
665    }
666  }
667}
668
669proc WPimg {image {extension gif}} {
670    global _wp
671
672    return [file join $_wp(imagepath) ${image}.${extension}]
673}
674
675proc WPCharValue {c} {
676  scan "$c" %c n
677  return $n
678}
679
680proc WPPercentQuote {arg {exclude {}}} {
681  set t "\[^0-9a-zA-Z_${exclude}\]"
682  if {[regsub -all $t $arg {[format "%%%.2X" [WPCharValue "\\&"]]} subarg]} {
683    set x [subst $subarg]
684    return $x
685  } else {
686    return $arg
687  }
688}
689
690proc WPJSQuote {l} {
691  regsub -all {([\\'])} $l {\\\1} l
692  return $l
693}
694
695proc WPurl {cmd cmdargs text explanation args} {
696    global _wp
697
698    lappend urlargs $text
699    lappend urlargs $cmd
700    if {[regexp "^java*" $cmd] == 0 && [string first . $cmd] < 0} {
701	append urlargs ".tcl"
702    }
703
704    if {[string length $cmdargs]} {
705	if {[set i [string first "?" $cmdargs]] >= 0} {
706	    append urlargs "[cgi_quote_url [string range $cmdargs 0 [expr {$i - 1}]]]?[cgi_quote_url [string range $cmdargs [incr i] end]]"
707	} else {
708	    append urlargs "?[cgi_quote_url $cmdargs]"
709	}
710    }
711
712    if {$_wp(statushelp)} {
713	lappend urlargs [WPmouseover $explanation]
714	lappend urlargs "onMouseOut=window.status=''"
715    }
716
717    return [eval "cgi_url $urlargs $args"]
718}
719
720proc WPMenuURL {cmd cmdargs text explanation args} {
721    return [WPurl $cmd $cmdargs $text $explanation class=menubar [join $args]]
722}
723
724proc WPGetTDFontSize {{ih 24}} {
725    if {$ih <= 20 } {return 12}
726    if {$ih >= 30 } {return 24}
727    return [expr {$ih - 8}]
728}
729
730proc WPGetviewFontSize {{ih 24}} {
731    if {$ih <= 20 } {return 8}
732    if {$ih >= 30 } {return 13}
733    return [expr {($ih / 2) - 2}]
734}
735
736proc WPIndexLineHeight {{ih 0}} {
737  global _wp
738
739  set ih [WPCmd PEInfo indexheight]
740  if {[string length $ih] == 0 ||  $ih <= 0} {
741    set ih $_wp(indexheight)
742  }
743
744  return [expr {($ih < 20) ? 20 : $ih}]
745}
746
747proc WPStyleSheets {{ih 0}} {
748  global _wp
749
750  cgi_stylesheet [file join / $_wp(urlprefix) $_wp(pubdir) standard.css]
751
752  if {$ih <= 0} {
753    set ih [WPIndexLineHeight]
754  }
755
756  cgi_puts "<style type='text/css'>\nTD { font-size: [WPGetTDFontSize $ih]px }\n.view {font-size: [WPGetviewFontSize $ih]pt }\n</style>"
757  return $ih
758}
759
760proc WPStdScripts {{ih 0}} {
761    global _wp
762
763    set ih [WPStyleSheets $ih]
764
765    cgi_script language="JavaScript" src="[file join / $_wp(urlprefix) $_wp(pubdir) standard.js]" {}
766    cgi_script language="JavaScript1.3" {cgi_put "js_version = '1.3';"}
767    cgi_javascript {
768	cgi_puts "function getIndexHeight(){return $ih}"
769    }
770}
771
772proc WPStdHttpHdrs {{ctype {}} {expires 0}} {
773    global _wp
774
775    # set date and expires headers the same to prevent caching
776    # Date: Tue, 15 Nov 1994 08:12:31 GMT
777    set doctime [clock seconds]
778
779    if {[string length $ctype]} {
780      cgi_content_type $ctype
781    } else {
782      cgi_content_type
783    }
784
785    cgi_puts "Date: [clock format $doctime -gmt true -format "%a, %d %b %Y %H:%M:%S GMT"]"
786    if {$expires == 0} {
787      set _wp(nocache) 1
788      cgi_puts "Cache-Control: no-cache"
789      cgi_puts "Expires: [clock format [expr {$doctime - 31536000}] -gmt true -format "%a, %d %b %Y %H:%M:%S GMT"]"
790    } elseif {$expires > 0} {
791      cgi_puts "Expires: [clock format [expr {$doctime + ($expires * 60)}] -gmt true -format "%a, %d %b %Y %H:%M:%S GMT"]"
792    }
793}
794
795proc WPStdHtmlHdr {pagetitle {pagescript ""} {newmail 0}} {
796    global _wp
797
798    if {0 && $newmail} {
799      set nm "* "
800    } else {
801      set nm ""
802    }
803
804    cgi_title "${nm}Web Alpine - $pagetitle"
805    # cgi_base "href=$_wp(serverpath)/"
806    if {[info exists _wp(nocache)]} {
807      cgi_http_equiv Pragma no-cache
808    }
809
810    # cgi_http_equiv Expires $_wp(docdate)
811    cgi_meta "name=Web Alpine" content=[clock format [file mtime [info script]] -format "%y%m%d/%H%M"]
812    if {[catch {WPCmd set nojs} nojs] || $nojs != 1} {
813      cgi_script  type="text/javascript" language="JavaScript" {
814	cgi_puts "if(self != top) top.location.href = location.href;"
815	cgi_puts "js_version = '1.0';"
816      }
817    }
818
819    cgi_put  "<link rel=\"icon\" href=\"[cgi_root]/favicon.ico\" type=\"image/x-icon\">"
820    cgi_put  "<link rel=\"shortcut icon\" href=\"[cgi_root]/favicon.ico\" type=\"image/x-icon\"> "
821}
822
823proc WPHtmlHdrReload {pagescript} {
824  global _wp
825
826  if {[regexp {\?} $pagescript]} {
827    set c "&"
828  } else {
829    set c "?"
830  }
831
832  cgi_http_equiv Refresh "$_wp(refresh); url=[cgi_root]/${pagescript}${c}reload=1"
833}
834
835proc WPNewMail {reload {viewpage msgview.tcl}} {
836
837    if {[catch {WPCmd PEMailbox newmail $reload} newmail]} {
838	return -code error $newmail
839    }
840
841    set newref ""
842
843    if {[set msgsnew [lindex $newmail 0]] > 0} {
844      if {[string length $viewpage]} {
845	if {[string first {?} $viewpage] < 0} {
846	  set delim ?
847	} else {
848	  set delim &
849	}
850
851	set newurl "${viewpage}${delim}uid=[lindex $newmail 1]"
852      } else {
853	set newurl [lindex $newmail 1]
854      }
855
856      set newicon "postmark"
857      set newtext [cgi_quote_html [WPCmd PEMailbox newmailstatmsg]]
858
859      if {[WPCmd PEInfo feature enable-newmail-sound]} {
860        #set audio sounds/mail_msg.wav
861	set audio /sounds/ding.wav
862	if {[isIE]} {
863	  set newsound "<bgsound src=\"$audio\" loop=\"1\" volume=\"100\">"
864	} else {
865	  set newsound "<embed src=\"$audio\" autostart=\"true\" hidden width=0 height=0 loop=\"false\"><noembed><bgsound src=\"$audio\" loop=\"1\"></noembed>"
866	}
867      } else {
868	set newsound {}
869      }
870
871      if {0 == [string length $newtext]} {
872	set newtext "You have $msgsnew new message[WPplural $msgsnew]"
873      }
874
875      lappend newref [list $newtext $newicon $newurl $newsound]
876    }
877
878    if {[set deleted [lindex $newmail 2]] > 0} {
879	set newtext "$deleted Message[WPplural $deleted] removed from folder"
880	lappend newref [list $newtext "" ""]
881    }
882
883    foreach statmsg [WPStatusMsgs] {
884      lappend newref [list $statmsg "" ""]
885      WPCmd PEInfo statmsg ""
886    }
887
888    if {!$reload} {
889	WPCmd PEMailbox newmailreset
890    }
891
892    return $newref
893}
894
895proc WPStatusMsgs {} {
896  set retmsgs ""
897  set lastmsg ""
898  if {[catch {WPCmd PEInfo statmsgs} statmsgs] == 0} {
899    foreach statmsg $statmsgs {
900      if {[string length $statmsg] > 0 && [string compare $statmsg $lastmsg]} {
901	if {[regexp "^Pinerc \(.+\) NOT saved$" $statmsg]} {
902	  lappend retmsgs "Another Pine/WebPine session may be running.  Settings cannot be saved."
903	} else {
904	  lappend retmsgs $statmsg
905	}
906
907	set lastmsg $statmsg
908      }
909    }
910  }
911
912  return $retmsgs
913}
914
915proc WPStatusIcon {uid {extension gif} {statbits ""}} {
916  global _wp
917
918  if {[string length $statbits] == 0} {
919    set statbits [WPCmd PEMessage $uid statusbits]
920  }
921
922  if {[string index $statbits 0]} {
923    append sicon "new"
924    set alt " N"
925    set fullalt "New "
926  } else {
927    append sicon "read"
928    set alt "  "
929    set fullalt "Viewed "
930  }
931
932  if {[string index $statbits 3]} {
933    append sicon "imp"
934    set alt "*[string range $alt 1 end]"
935    set fullaltend ", important message"
936  } elseif {([string index $statbits 4] || [string index $statbits 5])} {
937    append sicon "you"
938    set alt "+[string range $alt 1 end]"
939    set fullaltend " message to you"
940  }
941
942  if {[string index $statbits 2]} {
943    append sicon "ans"
944    set alt "[string range $alt 0 0]A"
945    append fullalt ", answered"
946  }
947
948  if {[string index $statbits 1]} {
949    append sicon "del"
950    set alt "[string range $alt 0 0]D"
951    append fullalt ", deleted"
952  }
953
954  if {[info exists fullaltend]} {
955    append fullalt $fullaltend
956  } else {
957    append fullalt message
958  }
959
960  regsub -all { } $alt {\&nbsp;} alt
961
962  return [list [file join $_wp(imagepath) $_wp(staticondir) ${sicon}.${extension}] i_${uid} $alt $fullalt]
963}
964
965proc WPStatusLabel {uid} {
966    global _wp
967
968    set statbits [WPCmd PEMessage $uid statusbits]
969
970    if {[string index $statbits 0]} {
971      set sl new
972    } else {
973      set sl read
974    }
975
976    if {[string index $statbits 3]} {
977      set sl important
978    }
979
980    if {[string index $statbits 1]} {
981      set sl deleted
982    }
983
984    if {[string index $statbits 2]} {
985      set sl answered
986    }
987
988    return $sl
989}
990
991proc WPStatusImg {uid} {
992    set sicon [WPStatusIcon $uid]
993    return [cgi_img [lindex $sicon 0] name=[lindex $sicon 1] id=[lindex $sicon 1] height=16 width=42 border=0 alt=[lindex $sicon 3]]
994}
995
996proc WPSessionState {args} {
997  switch [llength $args] {
998    1 -
999    2 {
1000      if {[catch {WPCmd PEInfo alpinestate} state_list] == 0} {
1001	array set state_array $state_list
1002	if {[llength $args] == 1} {
1003	  return $state_array([lindex $args 0])
1004	} else {
1005	  set state_array([lindex $args 0]) [lindex $args 1]
1006	  set state_list [array get state_array]
1007	  if {[catch {WPCmd PEInfo alpinestate $state_list} result]} {
1008	    error "Can't set session state : $result"
1009	  }
1010	}
1011      } else {
1012	error "Can't read session state"
1013      }
1014    }
1015    default {
1016      error "Unknown SessionState Parameters: $args"
1017    }
1018  }
1019}
1020
1021proc WPScriptVersion {tag {inc 0}} {
1022  if {[catch {WPCmd set wp_script_version} sv]} {
1023    set versions($tag) [expr int((1000 * rand()))]
1024    set sv [array get versions]
1025    catch {WPCmd set wp_script_version $sv}
1026  } else {
1027    array set versions $sv
1028
1029    if {![info exists versions($tag)]} {
1030      set versions($tag) [expr int((1000 * rand()))]
1031      set sv [array get versions]
1032      catch {WPCmd set wp_script_version $sv}
1033    } elseif {$inc} {
1034      incr versions($tag) $inc
1035      set sv [array get versions]
1036      catch {WPCmd set wp_script_version $sv}
1037    }
1038  }
1039
1040  return $versions($tag)
1041}
1042
1043proc WPplural {count} {
1044    if {$count > 1} {
1045	return "s"
1046    }
1047
1048    return ""
1049}
1050
1051proc WPcomma {number {dot ,}} {
1052    set x ""
1053
1054    while {[set n [string length $number]] > 3} {
1055	set x "${dot}[string range $number [incr n -3] end]$x"
1056	set number [string range $number 0 [incr n -1]]
1057    }
1058
1059    return "$number$x"
1060}
1061
1062proc isIE {} {
1063  global env
1064
1065  return [expr {[info exists env(HTTP_USER_AGENT)] == 1 && [string first MSIE $env(HTTP_USER_AGENT)] >= 0}]
1066}
1067
1068proc isW3C {} {
1069  global env
1070
1071  return [expr {[info exists env(HTTP_USER_AGENT)] && (([regexp {^Mozilla/([0-9]).[0-9]+} $env(HTTP_USER_AGENT) match majorversion] && $majorversion > 4) || ([regexp {Opera ([0-9])\.[0-9]+} $env(HTTP_USER_AGENT) match majorversion] && $majorversion > 5))}]
1072}
1073
1074proc WPdebug {args} {
1075    global _wp
1076
1077    switch [lindex $args 0] {
1078      level {
1079	if {[regexp {^([0-9])+$} [lindex $args 1]]} {
1080	  WPSend $_wp(sockname) [subst {PEDebug level [lindex $args 1]}]
1081	}
1082      }
1083      imap {
1084	switch [lindex $args 1] {
1085	  on {
1086	    WPSend $_wp(sockname) [subst {ePEDebug imap 4}]
1087	  }
1088	  off {
1089	    WPSend $_wp(sockname) [subst {PEDebug imap 0}]
1090	  }
1091	}
1092      }
1093      default {
1094	WPSend $_wp(sockname) "PEDebug write [list [list [file tail [info script]]: [lrange $args 0 end]]]"
1095      }
1096    }
1097}
1098
1099proc WPdebugstack {} {
1100    set stack {}
1101
1102    for {set n [expr {[info level] - 1}]} {$n > 0} {incr n -1} {
1103	append stack "$n) [info level $n]\n"
1104    }
1105    return $stack
1106}
1107
1108
1109##############################################################
1110# routines to improve integration with cgi.tcl
1111##############################################################
1112
1113# routine exposing some of cgi.tcl's innards.
1114# Should be exported by cgi.tcl package.
1115proc reset_cgi_state {} {
1116  global _cgi
1117
1118  catch {unset _cgi(http_head_in_progress)}
1119  catch {unset _cgi(http_head_done)}
1120  catch {unset _cgi(http_status_done)}
1121  catch {unset _cgi(html_in_progress)}
1122  catch {unset _cgi(head_in_progress)}
1123  catch {unset _cgi(head_done)}
1124  catch {unset _cgi(html_done)}
1125  catch {unset _cgi(head_suppress_tag)}
1126  catch {unset _cgi(body_in_progress)}
1127  catch {unset _cgi(tag_in_progress)}
1128  catch {unset _cgi(form_in_progress)}
1129  catch {unset _cgi(close_proc)}
1130
1131  if {[info exists _cgi(returnIndex)]} {
1132    while {[set _cgi(returnIndex)] > 0} {
1133      incr _cgi(returnIndex) -1
1134      rename cgi_puts ""
1135      rename cgi_puts$_cgi(returnIndex) cgi_puts
1136    }
1137  }
1138}
1139
1140###############################################################
1141# routines to process (and be called in) html template files
1142###############################################################
1143
1144proc html_readfile {file} {
1145    set x [open $file "r"]
1146    set result [read $x]
1147    close $x
1148    return $result
1149}
1150
1151proc html_eval {_vars_ _this_} {
1152    foreach {_i_ _j_} $_vars_ {
1153	if {$_i_ == "global"} {global $_j_} {set $_i_ $_j_}
1154    }
1155    unset _vars_ _i_ _j_
1156    return [subst $_this_]
1157}
1158
1159proc html_loop {varslist text} {
1160    set result ""
1161    foreach {vars} $varslist {
1162	append result [html_eval $vars $text]
1163    }
1164    return $result
1165}
1166