# Web Alpine Config options # $Id: alpine.tcl 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $ # ======================================================================== # Copyright 2006-2008 University of Washington # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # ======================================================================== encoding system "utf-8" set _wp(appname) Alpine set _wp(admin) admin@sample-domain.edu set _wp(helpdesk) admin@sample-domain.edu set _wp(comments) help@sample-domain.edu # List of userid's allowed to request the monitor script output set _wp(monitors) {} # directory prefix web server uses for web alpine page requests # Note: set to {} if DocumentRoot set to the root of web alpine cgi scripts set _wp(urlprefix) webmail # file system path to CGI application files # directory containing web alpine application scripts and supporting tools set _wp(fileroot) /usr/local/libexec/alpine set _wp(tmpdir) /tmp # NOTE: Make SURE tclsh and alpine.tcl symlinks in this directory set _wp(cgipath) [file join $_wp(fileroot) cgi] # CGI scripts implementing U/I, session cookie scope set _wp(appdir) alpine # UI versions set _wp(ui1dir) 1.0 set _wp(ui2dir) 2.0 # place for CGI scripts not requiring session-key set _wp(pubdir) pub # place for binaries referenced by the CGI scripts set _wp(bin) [file join $_wp(fileroot) bin] set _wp(servlet) alpined set _wp(pc_servlet) pc_alpined set _wp(pldap) alpineldap # place for config files referenced by the CGI scripts set _wp(confdir) [file join $_wp(fileroot) config] set _wp(conffile) pine.conf set _wp(defconf) $_wp(conffile) # place for library files used by CGI scripts set _wp(lib) [file join $_wp(fileroot) lib] # directory used temporarily to stage attatched and detached files set _wp(detachpath) [file join $_wp(fileroot) detach] set _wp(imagepath) [file join / $_wp(urlprefix) images] set _wp(buttonpath) [file join $_wp(imagepath) buttons silver] set _wp(staticondir) env set _wp(servername) [info hostname] # MUST specify SSL/TLS connection set _wp(serverport) {} set _wp(serverpath) https://[file join [join [eval list $_wp(servername) $_wp(serverport)] :] $_wp(urlprefix)] # MAY specify a plaintext connection (comment out if plain support undesired) set _wp(plainport) {} set _wp(plainservpath) http://[file join [join [eval list $_wp(servername) $_wp(plainport)] :] $_wp(urlprefix)] # url of faq page(s) available from initial greeting page #set _wp(faq) "http://www.yourserver/faqs/alpine.html" # url of informational page accessible from initial greeting page set _wp(releaseblurb) "$_wp(plainservpath)/alpine/help/release.html" # url of previous version server to be accessible from initial greeting page #set _wp(oldserverpath) "https://previous.version.server.edu:444/" # session id length: make sure the integer count below matches what's built # into the pubcookie: "src/pubcookie/wp_uidmapper_lib.h:#define WP_KEY_LEN 6" set _wp(sessidlen) 6 # Where and what format the alpined comm socket should take set _wp(sockdir) $_wp(tmpdir) set _wp(sockpat) wp%s # skin settings set _wp(bordercolor) #FEFAC9 set _wp(menucolor) #3E2E6D set _wp(dialogcolor) #FEFAC9 set _wp(titlecolor) #000000 set _wp(logodir) alpine # various timerouts, dimensions and feature settings set _wp(refresh) 600 set _wp(timeout) 900 set _wp(autodraft) 300 set _wp(logoutpause) 60 set _wp(indexlines) 20 set _wp(indexlinesmax) 50 set _wp(indexheight) 24 set _wp(navheight) 28 set _wp(width) 80 set _wp(titleheight) 34 set _wp(titlesep) 4 set _wp(config) remote_pinerc set _wp(motd) motd set _wp(save_cache_max) 6 set _wp(fldr_cache_max) 20 set _wp(fldr_cache_def) 3 set _wp(statushelp) 0 set _wp(imgbuttons) 0 set _wp(keybindings) 1 set _wp(dictionary) 0 set _wp(debug) 0 set _wp(cmdtime) 0 set _wp(evaltime) 0 set _wp(menuargs) {width="112" nowrap valign=top} set _wp(ispell) /usr/local/bin/ispell # Yahoo! User Interface Library location #set _wp(yui) $_wp(serverpath)/$_wp(appdir)/$_wp(ui2dir)/lib/yui set _wp(yui) "http://yui.yahooapis.com/2.7.0" # usage reporter - input: username as first command line argument # output: space separated integers usage and total #set _wp(usage) $_wp(bin)/usage.tcl #set _wp(usage_link) "https://uwnetid.washington.edu/disk/" # limit uploads to 1 file at a time, maximum 20MB. set _wp(uplim_files) 1 set _wp(uplim_bytes) 20000000 # verify sessid from consistent REMOTE_ADDR (set to 0 for proxying clusters) set _wp(hostcheck) 0 # set to list of domains for which ssl is NOT required #set _wp(ssl_safe_domains) {} # set to list of address blocks or ranges for which ssl is NOT required #set _wp(ssl_safe_addrs) {} # set this value to zero to turn OFF ssl by default set _wp(ssl_default) 1 # allow connecting user to specify imap server on greeting page set _wp(flexserver) 1 # make sure tmp files and such are ours alone to read/write catch {exec umask 044} #fix up indexheight so it isn't too high or too low set _wp(indexheight) [expr {$_wp(indexheight) <= 20 ? 20 : $_wp(indexheight) >= 30 ? 30 : $_wp(indexheight)}] # SPAM reporting facility, if set "Report Spam" button appears at top of View Page #set _wp(spamaddr) spamaddr@sample-domain.edu #set _wp(spamfolder) junk-mail #set _wp(spamsubj) "ATTACHED SPAM" # external mail filter config link #set _wp(filter_link) http://delivery-filter.sample-domain.edu/filter/config # external vacation config link #set _wp(vacation_link) http://vacation.sample-domain.edu/vacation/config # # Nickname server bindings. If not present, prompt for the # destination of the default pinerc location. # set _wp(hosts) { { Deskmail $User.deskmail.washington.edu/ssl $_wp(confdir)/conf.deskmail } } # Everybody inherits the cgi, comm packages lappend auto_path $_wp(lib) package require cgi package require WPComm # Recipient of bad news bubbling up from cgi.tcl... cgi_admin_mail_addr $_wp(admin) cgi_sendmail {} #cgi_mail_relay localhost cgi_mail_relay smtpserver.sample-domain.edu # set permissions for owner-only handling cgi_tmpfile_permissions 0640 #set upload limits cgi_file_limit $_wp(uplim_files) $_wp(uplim_bytes) # universal body tag parameters cgi_body_args link=#0000FF vlink=#000080 alink=#FF0000 marginwidth=0 marginheight=0 topmargin=0 leftmargin=0 # Common Images Image definitions cgi_imglink logo [file join $_wp(imagepath) logo $_wp(logodir) big.gif] border=0 "alt=Web Alpine" cgi_imglink smalllogo [file join $_wp(imagepath) logo $_wp(logodir) small.gif] border=0 "alt=About Web Alpine" cgi_imglink background [file join $_wp(imagepath) logo $_wp(logodir) back.gif] border=0 align=top cgi_imglink dot [file join $_wp(imagepath) dot2.gif] border=0 align=top cgi_imglink increas [file join $_wp(imagepath) increas4.gif] border=0 align=absmiddle cgi_imglink decreas [file join $_wp(imagepath) decreas4.gif] border=0 align=absmiddle cgi_imglink expand [file join $_wp(imagepath) b_plus.gif] border=0 "alt=Expand" height=9 width=9 cgi_imglink contract [file join $_wp(imagepath) b_minus.gif] border=0 "alt=Collapse" height=9 width=9 cgi_imglink fullhdr [file join $_wp(imagepath) hdr.gif] border=0 "alt=Full Header" cgi_imglink nofullhdr [file join $_wp(imagepath) hdrnon.gif] border=0 "alt=Digested Header" cgi_imglink bang [file join $_wp(imagepath) caution.gif] border=0 "alt=!" cgi_imglink postmark [file join $_wp(imagepath) postmark.gif] border=0 "alt=New Mail" cgi_imglink gtab [file join $_wp(imagepath) tabs gtab.gif] border=0 align=top cgi_imglink gdtab [file join $_wp(imagepath) tabs gdtab.gif] border=0 align=top cgi_imglink abtab [file join $_wp(imagepath) tabs abtab.gif] border=0 align=top cgi_imglink abdtab [file join $_wp(imagepath) tabs abdtab.gif] border=0 align=top cgi_imglink ctab [file join $_wp(imagepath) tabs ctab.gif] border=0 align=top cgi_imglink cdtab [file join $_wp(imagepath) tabs cdtab.gif] border=0 align=top cgi_imglink ftab [file join $_wp(imagepath) tabs ftab.gif] border=0 align=top cgi_imglink fdtab [file join $_wp(imagepath) tabs fdtab.gif] border=0 align=top cgi_imglink mltab [file join $_wp(imagepath) tabs mltab.gif] border=0 align=top cgi_imglink mldtab [file join $_wp(imagepath) tabs mldtab.gif] border=0 align=top cgi_imglink mvtab [file join $_wp(imagepath) tabs mvtab.gif] border=0 align=top cgi_imglink mvdtab [file join $_wp(imagepath) tabs mvdtab.gif] border=0 align=top cgi_imglink rtab [file join $_wp(imagepath) tabs rtab.gif] border=0 align=top cgi_imglink rdtab [file join $_wp(imagepath) tabs rdtab.gif] border=0 align=top # Link definitions cgi_link Admin "Web Alpine Administrator" "mailto:$_wp(admin)" cgi_link Start "Web Alpine Home Page" "$_wp(serverpath)/session/greeting.tcl" target=_top # Internally referenced CGI directory root cgi_root $_wp(serverpath) cgi_suffix .tcl # have cgi.tcl convert eols in muiltipart/form-data set _cgi(no_binary_upload) 1 proc WPSocketName {sessid} { global _wp return [file join $_wp(sockdir) [format $_wp(sockpat) $sessid]] } proc WPValidId {{sessid {}}} { global _wp env if {[string length $sessid] == 0} { set created 1 # Session Handle: a bit reasonably random number. the format # is convenient for pubcookie auth'd support set rnum {} set rsrc /dev/urandom set idbytelength [expr {$_wp(sessidlen) * 4}] if {[file readable $rsrc] && [catch {open $rsrc r} fp] == 0} { while {1} { for {set i 0} {$i < $idbytelength} {incr i} { if {$i && ($i % 4) == 0} { append rnum "." } if {[catch {read $fp 1} n] == 0} { binary scan $n c x set x [expr ($x & 0xff)] append rnum [format {%02x} $x] } else { set rnum {} break } } if {[file exists [WPSocketName $rnum]]} { set rnum {} } else { break } } close $fp } # second choice for random numbers if {[string length $rnum] == 0} { expr srand([clock seconds]) for {set i 0} {$i < $idbytelength} {incr i 4} { if {$i && ($i % 4) == 0} { append rnum "." } append rnum [format {%08x} [expr int((100000000 * rand()))]] } } # generate a session ID set _wp(sessid) $rnum } else { set sessidparts [split $sessid {@}] switch [llength $sessidparts] { 1 { set _wp(sessid) $sessid } 2 { if {[string compare [string tolower [lindex $sessidparts 1]] [string tolower [info hostname]]]} { regexp {^([a-zA-Z]*://).*} [cgi_root] match proto error [list redirect "${proto}[lindex $sessidparts 1]:$env(SERVER_PORT)?$env(QUERY_STRING)"] } else { set _wp(sessid) [lindex $sessidparts 0] } } default { error "Malformed Session ID: $sessid" } } } set _wp(sockname) [WPSocketName $_wp(sessid)] if {[info exists _wp(cumulative)]} { rename WPCmd WPCmd.orig rename WPCmdTimed WPCmd } if {[info exists _wp(hostcheck)] && $_wp(hostcheck) == 1 && ![info exists created] && [catch {WPCmd set wp_client} client] == 0 && (([info exists env(REMOTE_ADDR)] && [string length $env(REMOTE_ADDR)] && [string compare $client $env(REMOTE_ADDR)]) || ([info exists env(REMOTE_HOST)] && [string length $env(REMOTE_HOST)] && [string compare $client $env(REMOTE_HOST)]))} { error "Request from unrecognized client" } } proc WPAbort {} { WPCleanup cgi_exit } proc WPCleanup {} { global _wp if {[info exists _wp(cleanup)]} { foreach item $_wp(cleanup) { catch {eval $item} } } } proc WPEval {vars cmd} { global _wp if {$_wp(cmdtime) || $_wp(evaltime)} { set _wp(cumulative) 0 } set _wp(cmd) $cmd set _wp(vars) [linsert $vars 0 [list sessid "Missing Session ID"]] uplevel 1 { cgi_eval { if {$_wp(debug) > 1} { cgi_debug -on } # Session id? if {[catch {WPGetInputAndID sessid}]} { return } foreach item $_wp(vars) { if {[catch {eval WPImport $item} errstr]} { WPInfoPage "Web Alpine Error" [font size=+2 $errstr] "Please close this window." return } } # evaluate the given script if {[catch {cgi_buffer $_wp(cmd)} result]} { reset_cgi_state if {[string index $result 0] == "_"} { switch -- [lindex $result 0] { _info { WPInfoPage [lindex $result 1] [font size=+2 [lindex $result 2]] [lindex $result 3] } _action { switch -regexp -- [lindex $result 2] { "[Ii]nactive [Ss]ession" { WPInactivePage } default { if {[string length [lindex $result 3]]} { set remedy [lindex $result 3] } else { set remedy " Click your browser's Back button to return to previous page." } WPInfoPage "[lindex $result 1] Error" [font size=+2 [lindex $result 2]] \ "Please report this to the [cgi_link Admin].$remedy" } } } _redirect { cgi_http_head { cgi_redirect [lindex $result 1] } cgi_html { cgi_body {} } } _close { if {[string length [lindex $result 1]] == 0} { set result "Indeterminate error" } WPInfoPage "Web Alpine Error" [font size=+2 [lindex $result 1]] "Please close this window." } default { if {[string length $result]} { WPInfoPage "Web Alpine Error" [font size=+2 "Eval Error: $result"] \ "Please complain to the [cgi_link Admin]. Click Back button to return to previous page." } else { WPInfoPage "Web Alpine Error" [font size=+2 "Indeterminate error response"] \ "Please complain to the [cgi_link Admin] and click Back button to return to previous page." } } } } else { if {[regexp {[Ii]nactive [Ss]ession} $result]} { WPInactivePage } else { WPInfoPage "Web Alpine Error" [font size=+2 "Error: $result"] \ "Please report this to the [cgi_link Admin]. Try clicking your browser's Back button to return to a working page." } } } else { catch {cgi_puts $result} } } } # cleanup here WPCleanup if {[info exists _wp(cumulative)]} { WPdebug "Cumulative Eval: $_wp(cumulative)" unset _wp(cumulative) } } proc WPGetInputAndID {_sessid} { global _wp upvar $_sessid sessid # Import data and validate it if {[catch {cgi_input "sessid=8543949466398&"} result]} { WPInfoPage "Web Alpine Error" [font size=+2 $result] "Please close this window." error "Cannot get CGI Input" } if {[catch {WPImport sessid "Missing Session ID"} errstr]} { if {[regexp {.*sessid.*no such.*} $errstr]} { WPInactivePage [list "Your browser may have failed to send the necessary cookie information. Please verify your browser configuration has cookies enabled."] } else { WPInfoPage "Web Alpine Error" [font size=+2 $errstr] "Please close this window." } error "Session ID Failure" } else { # initialization here if {[catch {WPValidId $sessid} result]} { if {[string compare [lindex $result 0] redirect]} { WPInfoPage "Web Alpine Error" [font size=+2 "$result"] \ "Please complain to the [cgi_link Admin] and visit the [cgi_link Start] later." } else { cgi_http_head { cgi_redirect [lindex $result 1] } } error "Unrecoverable Error" } elseif {$_wp(sessid) == 0} { WPInactivePage error "Inactive Session" } if {[catch {WPCmd set serverroot} serverroot] == 0} { cgi_root $serverroot } } } proc WPCmdEval {args} { return [eval $args] } proc WPCmd {args} { global _wp return [WPSend $_wp(sockname) $args] } proc WPCmdTimed {args} { global _wp set t [lindex [time {set r [WPSend $_wp(sockname) $args]}] 0] incr _wp(cumulative) $t if {$_wp(cmdtime)} { WPdebug "time $t : $args" } return $r } proc WPLoadCGIVar {_var} { upvar $_var var if {[catch {cgi_import_as $_var var} result] && [catch {WPCmd set $_var} var] && [catch {cgi_import_cookie_as $_var var} result]} { error [list _action "Import Cookie $_var" $result] } } proc WPLoadCGIVarAs {_var _varas} { upvar $_varas varas if {[catch {cgi_import_as $_var varas} result] && [catch {WPCmd set $_var} varas] && [catch {cgi_import_cookie_as $_var varas} result]} { set varas "" } } proc WPImport {valname {errstring ""} {default 0}} { upvar $valname val if {[catch {cgi_import_as $valname val} result]} { if {[catch {WPCmd set $valname} val]} { if {[catch {cgi_import_cookie_as $valname val} result]} { if {[string length $errstring] > 0} { error "$errstring: $result" } else { set val $default } } } } } proc WPExportCookie {name value {scope ""}} { global _wp cgi_cookie_set $name=$value "path=[file join / $_wp(urlprefix) $scope]" } # handle dynamic sizing of images showing thread relationships proc WPThreadImageLink {t h} { global _wp return "" } proc WPInactivePage {{reasons ""}} { set l {} foreach r $reasons { append l "
Please visit the [cgi_link Start] to start a new session."
}
proc WPInfoPage {title exp1 {exp2 ""} {imgurl {}} {exp3 ""}} {
global _wp
catch {
cgi_html {
cgi_head {
cgi_title $title
cgi_stylesheet [file join / $_wp(urlprefix) $_wp(pubdir) standard.css]
}
cgi_body {
cgi_table height="20%" {
cgi_table_row {
cgi_table_data {
cgi_puts [cgi_nbspace]
}
}
}
cgi_center {
cgi_table border=0 width=500 cellpadding=3 {
cgi_table_row {
cgi_table_data align=center rowspan=3 {
if {[string length $imgurl]} {
cgi_put [cgi_url [cgi_imglink logo] $imgurl]
} else {
cgi_put [cgi_imglink logo]
}
}
cgi_table_data rowspan=3 {
cgi_put [nbspace]
cgi_put [nbspace]
}
cgi_table_data {
cgi_puts $exp1
}
}
if {[string length $exp3]} {
cgi_table_row {
cgi_table_data "style=\"border: 1px solid red; background-color: pink\"" {
cgi_puts $exp3
}
}
}
if {[string length $exp2]} {
cgi_table_row {
cgi_table_data {
cgi_puts $exp2
}
}
}
}
}
}
}
}
}
proc WPimg {image {extension gif}} {
global _wp
return [file join $_wp(imagepath) ${image}.${extension}]
}
proc WPCharValue {c} {
scan "$c" %c n
return $n
}
proc WPPercentQuote {arg {exclude {}}} {
set t "\[^0-9a-zA-Z_${exclude}\]"
if {[regsub -all $t $arg {[format "%%%.2X" [WPCharValue "\\&"]]} subarg]} {
set x [subst $subarg]
return $x
} else {
return $arg
}
}
proc WPJSQuote {l} {
regsub -all {([\\'])} $l {\\\1} l
return $l
}
proc WPurl {cmd cmdargs text explanation args} {
global _wp
lappend urlargs $text
lappend urlargs $cmd
if {[regexp "^java*" $cmd] == 0 && [string first . $cmd] < 0} {
append urlargs ".tcl"
}
if {[string length $cmdargs]} {
if {[set i [string first "?" $cmdargs]] >= 0} {
append urlargs "[cgi_quote_url [string range $cmdargs 0 [expr {$i - 1}]]]?[cgi_quote_url [string range $cmdargs [incr i] end]]"
} else {
append urlargs "?[cgi_quote_url $cmdargs]"
}
}
if {$_wp(statushelp)} {
lappend urlargs [WPmouseover $explanation]
lappend urlargs "onMouseOut=window.status=''"
}
return [eval "cgi_url $urlargs $args"]
}
proc WPMenuURL {cmd cmdargs text explanation args} {
return [WPurl $cmd $cmdargs $text $explanation class=menubar [join $args]]
}
proc WPGetTDFontSize {{ih 24}} {
if {$ih <= 20 } {return 12}
if {$ih >= 30 } {return 24}
return [expr {$ih - 8}]
}
proc WPGetviewFontSize {{ih 24}} {
if {$ih <= 20 } {return 8}
if {$ih >= 30 } {return 13}
return [expr {($ih / 2) - 2}]
}
proc WPIndexLineHeight {{ih 0}} {
global _wp
set ih [WPCmd PEInfo indexheight]
if {[string length $ih] == 0 || $ih <= 0} {
set ih $_wp(indexheight)
}
return [expr {($ih < 20) ? 20 : $ih}]
}
proc WPStyleSheets {{ih 0}} {
global _wp
cgi_stylesheet [file join / $_wp(urlprefix) $_wp(pubdir) standard.css]
if {$ih <= 0} {
set ih [WPIndexLineHeight]
}
cgi_puts ""
return $ih
}
proc WPStdScripts {{ih 0}} {
global _wp
set ih [WPStyleSheets $ih]
cgi_script language="JavaScript" src="[file join / $_wp(urlprefix) $_wp(pubdir) standard.js]" {}
cgi_script language="JavaScript1.3" {cgi_put "js_version = '1.3';"}
cgi_javascript {
cgi_puts "function getIndexHeight(){return $ih}"
}
}
proc WPStdHttpHdrs {{ctype {}} {expires 0}} {
global _wp
# set date and expires headers the same to prevent caching
# Date: Tue, 15 Nov 1994 08:12:31 GMT
set doctime [clock seconds]
if {[string length $ctype]} {
cgi_content_type $ctype
} else {
cgi_content_type
}
cgi_puts "Date: [clock format $doctime -gmt true -format "%a, %d %b %Y %H:%M:%S GMT"]"
if {$expires == 0} {
set _wp(nocache) 1
cgi_puts "Cache-Control: no-cache"
cgi_puts "Expires: [clock format [expr {$doctime - 31536000}] -gmt true -format "%a, %d %b %Y %H:%M:%S GMT"]"
} elseif {$expires > 0} {
cgi_puts "Expires: [clock format [expr {$doctime + ($expires * 60)}] -gmt true -format "%a, %d %b %Y %H:%M:%S GMT"]"
}
}
proc WPStdHtmlHdr {pagetitle {pagescript ""} {newmail 0}} {
global _wp
if {0 && $newmail} {
set nm "* "
} else {
set nm ""
}
cgi_title "${nm}Web Alpine - $pagetitle"
# cgi_base "href=$_wp(serverpath)/"
if {[info exists _wp(nocache)]} {
cgi_http_equiv Pragma no-cache
}
# cgi_http_equiv Expires $_wp(docdate)
cgi_meta "name=Web Alpine" content=[clock format [file mtime [info script]] -format "%y%m%d/%H%M"]
if {[catch {WPCmd set nojs} nojs] || $nojs != 1} {
cgi_script type="text/javascript" language="JavaScript" {
cgi_puts "if(self != top) top.location.href = location.href;"
cgi_puts "js_version = '1.0';"
}
}
cgi_put ""
cgi_put " "
}
proc WPHtmlHdrReload {pagescript} {
global _wp
if {[regexp {\?} $pagescript]} {
set c "&"
} else {
set c "?"
}
cgi_http_equiv Refresh "$_wp(refresh); url=[cgi_root]/${pagescript}${c}reload=1"
}
proc WPNewMail {reload {viewpage msgview.tcl}} {
if {[catch {WPCmd PEMailbox newmail $reload} newmail]} {
return -code error $newmail
}
set newref ""
if {[set msgsnew [lindex $newmail 0]] > 0} {
if {[string length $viewpage]} {
if {[string first {?} $viewpage] < 0} {
set delim ?
} else {
set delim &
}
set newurl "${viewpage}${delim}uid=[lindex $newmail 1]"
} else {
set newurl [lindex $newmail 1]
}
set newicon "postmark"
set newtext [cgi_quote_html [WPCmd PEMailbox newmailstatmsg]]
if {[WPCmd PEInfo feature enable-newmail-sound]} {
#set audio sounds/mail_msg.wav
set audio /sounds/ding.wav
if {[isIE]} {
set newsound "