~ubuntu-branches/debian/squeeze/alpine/squeeze

« back to all changes in this revision

Viewing changes to web/src/cgi.tcl-1.10/cgi.tcl.in

  • Committer: Bazaar Package Importer
  • Author(s): Asheesh Laroia
  • Date: 2007-02-17 13:17:42 UTC
  • Revision ID: james.westby@ubuntu.com-20070217131742-99x5c6cpg1pbkdhw
Tags: upstream-0.82+dfsg
ImportĀ upstreamĀ versionĀ 0.82+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
##################################################
 
2
#
 
3
# cgi.tcl - routines for writing CGI scripts in Tcl
 
4
# Author: Don Libes <libes@nist.gov>, January '95
 
5
#
 
6
# These routines implement the code described in the paper
 
7
# "Writing CGI scripts in Tcl" which appeared in the Tcl '96 conference.
 
8
# Please read the paper before using this code.  The paper is:
 
9
# http://expect.nist.gov/doc/cgi.pdf
 
10
#
 
11
##################################################
 
12
 
 
13
##################################################
 
14
# http header support
 
15
##################################################
 
16
 
 
17
proc cgi_http_head {args} {
 
18
    global _cgi env errorInfo
 
19
 
 
20
    if {[info exists _cgi(http_head_done)]} return
 
21
 
 
22
    set _cgi(http_head_in_progress) 1
 
23
 
 
24
    if {0 == [llength $args]} {
 
25
        cgi_content_type
 
26
    } else {
 
27
        if {[catch {uplevel 1 [lindex $args 0]} errMsg]} {
 
28
            set savedInfo $errorInfo
 
29
            cgi_content_type
 
30
        }
 
31
    }
 
32
    cgi_puts ""
 
33
 
 
34
    unset _cgi(http_head_in_progress)
 
35
    set _cgi(http_head_done) 1
 
36
 
 
37
    if {[info exists savedInfo]} {
 
38
        error $errMsg $savedInfo
 
39
    }
 
40
}
 
41
 
 
42
# avoid generating http head if not in CGI environment
 
43
# to allow generation of pure HTML files
 
44
proc _cgi_http_head_implicit {} {
 
45
    global env
 
46
 
 
47
    if {[info exists env(REQUEST_METHOD)]} cgi_http_head
 
48
}
 
49
 
 
50
proc cgi_status {num str} {
 
51
    global _cgi
 
52
 
 
53
    if {[info exists _cgi(http_status_done)]} return
 
54
    set _cgi(http_status_done) 1
 
55
    cgi_puts "Status: $num $str"
 
56
}
 
57
 
 
58
# If these are called manually, they automatically generate the extra newline
 
59
 
 
60
proc cgi_content_type {args} {
 
61
    global _cgi
 
62
 
 
63
    if {0==[llength $args]} {
 
64
        set t text/html
 
65
    } else {
 
66
        set t [lindex $args 0]
 
67
        if {[regexp ^multipart/ $t]} {
 
68
            set _cgi(multipart) 1
 
69
        }
 
70
    }
 
71
 
 
72
    if {[info exists _cgi(http_head_in_progress)]} {
 
73
        cgi_puts "Content-type: $t"
 
74
    } else {
 
75
        cgi_http_head [list cgi_content_type $t]
 
76
    }
 
77
}
 
78
 
 
79
proc cgi_redirect {t} {
 
80
    global _cgi
 
81
 
 
82
    if {[info exists _cgi(http_head_in_progress)]} {
 
83
        cgi_status 302 Redirected
 
84
        cgi_puts "Uri: $t"
 
85
        cgi_puts "Location: $t"
 
86
    } else {
 
87
        cgi_http_head {
 
88
            cgi_redirect $t
 
89
        }
 
90
    }
 
91
}
 
92
 
 
93
# deprecated, use cgi_redirect
 
94
proc cgi_location {t} {
 
95
    global _cgi
 
96
 
 
97
    if {[info exists _cgi(http_head_in_progress)]} {
 
98
        cgi_puts "Location: $t"
 
99
    } else {
 
100
        cgi_http_head "cgi_location $t"
 
101
    }
 
102
}
 
103
 
 
104
proc cgi_target {t} {
 
105
    global _cgi
 
106
 
 
107
    if {![info exists _cgi(http_head_in_progress)]} {
 
108
        error "cgi_target must be set from within cgi_http_head."
 
109
    }
 
110
    cgi_puts "Window-target: $t"
 
111
}
 
112
 
 
113
# Make client retrieve url in this many seconds ("client pull").
 
114
# With no 2nd arg, current url is retrieved.
 
115
proc cgi_refresh {seconds {url ""}} {
 
116
    global _cgi
 
117
 
 
118
    if {![info exists _cgi(http_head_in_progress)]} {
 
119
        error "cgi_refresh must be set from within cgi_http_head.  Try using cgi_http_equiv instead."
 
120
    }
 
121
    cgi_put "Refresh: $seconds"
 
122
 
 
123
    if {0!=[string compare $url ""]} {
 
124
        cgi_put "; $url"
 
125
    }
 
126
    cgi_puts ""
 
127
}
 
128
 
 
129
# Example: cgi_pragma no-cache
 
130
proc cgi_pragma {arg} {
 
131
    global _cgi
 
132
 
 
133
    if {![info exists _cgi(http_head_in_progress)]} {
 
134
        error "cgi_pragma must be set from within cgi_http_head."
 
135
    }
 
136
    cgi_puts "Pragma: $arg"
 
137
}
 
138
 
 
139
##################################################
 
140
# support for debugging or other crucial things we need immediately
 
141
##################################################
 
142
 
 
143
proc cgi_comment        {args}  {}      ;# need this asap
 
144
 
 
145
proc cgi_html_comment   {args}  {
 
146
    regsub -all {>} $args {\&gt;} args
 
147
    cgi_put "<!--[_cgi_list_to_string $args] -->"
 
148
}
 
149
 
 
150
set _cgi(debug) -off
 
151
proc cgi_debug {args} {
 
152
    global _cgi
 
153
 
 
154
    set old $_cgi(debug)
 
155
    set arg [lindex $args 0]
 
156
    if {$arg == "-on"} {
 
157
        set _cgi(debug) -on
 
158
        set args [lrange $args 1 end]
 
159
    } elseif {$arg == "-off"} {
 
160
        set _cgi(debug) -off
 
161
        set args [lrange $args 1 end]
 
162
    } elseif {[regexp "^-t" $arg]} {
 
163
        set temp 1
 
164
        set _cgi(debug) -on
 
165
        set args [lrange $args 1 end]
 
166
    } elseif {[regexp "^-noprint$" $arg]} {
 
167
        set noprint 1
 
168
        set args [lrange $args 1 end]
 
169
    }
 
170
 
 
171
    set arg [lindex $args 0]
 
172
    if {$arg == "--"} {
 
173
        set args [lrange $args 1 end]
 
174
    }
 
175
 
 
176
    if {[llength $args]} {
 
177
        if {$_cgi(debug) == "-on"} {
 
178
 
 
179
            _cgi_close_tag
 
180
            # force http head and open html, head, body
 
181
            catch {
 
182
                if {[info exists noprint]} {
 
183
                    uplevel 1 [lindex $args 0]
 
184
                } else {
 
185
                    cgi_html {
 
186
                        cgi_head {
 
187
                            cgi_title "debugging before complete HTML head"
 
188
                        }
 
189
                        # force body open and leave open
 
190
                        _cgi_body_start
 
191
                        uplevel 1 [lindex $args 0]
 
192
                        # bop back out to catch, so we don't close body
 
193
                        error "ignore"
 
194
                    }
 
195
                }
 
196
            }
 
197
        }
 
198
    }
 
199
 
 
200
    if {[info exists temp]} {
 
201
        set _cgi(debug) $old
 
202
    }
 
203
    return $old
 
204
}
 
205
 
 
206
proc cgi_uid_check {user} {
 
207
    global env
 
208
 
 
209
    # leave in so old scripts don't blow up
 
210
    if {[regexp "^-off$" $user]} return
 
211
 
 
212
    if {[info exists env(USER)]} {
 
213
        set whoami $env(USER)
 
214
    } elseif {0==[catch {exec whoami} whoami]} {
 
215
        # "who am i" on some Linux hosts returns "" so try whoami first
 
216
    } elseif {0==[catch {exec who am i} whoami]} {
 
217
        # skip over "host!"
 
218
        regexp "(.*!)?(\[^ \t]*)" $whoami dummy dummy whoami
 
219
    } elseif {0==[catch {package require registry}]} {
 
220
        set whoami [registry get HKEY_LOCAL_MACHINE\\Network\\Logon username]
 
221
    } else {
 
222
        set whoami $user  ;# give up and let go
 
223
    }
 
224
    if {$whoami != "$user"} {
 
225
        error "Warning: This CGI script expects to run with uid \"$user\".  However, this script is running as \"$whoami\"."
 
226
    }
 
227
}
 
228
 
 
229
# print out elements of an array
 
230
# like Tcl's parray, but formatted for browser
 
231
proc cgi_parray {a {pattern *}} {
 
232
    upvar 1 $a array
 
233
    if {![array exists array]} {
 
234
        error "\"$a\" isn't an array"
 
235
    }
 
236
 
 
237
    set maxl 0
 
238
    foreach name [lsort [array names array $pattern]] {
 
239
        if {[string length $name] > $maxl} {
 
240
            set maxl [string length $name]
 
241
        }
 
242
    }
 
243
    cgi_preformatted {
 
244
        set maxl [expr {$maxl + [string length $a] + 2}]
 
245
        foreach name [lsort [array names array $pattern]] {
 
246
            set nameString [format %s(%s) $a $name]
 
247
            cgi_puts [cgi_quote_html [format "%-*s = %s" $maxl $nameString $array($name)]]
 
248
        }
 
249
    }
 
250
}
 
251
 
 
252
proc cgi_eval {cmd} {
 
253
    global env _cgi
 
254
 
 
255
    # put cmd somewhere that uplevel can find it
 
256
    set _cgi(body) $cmd
 
257
 
 
258
    uplevel 1 {
 
259
        global env _cgi errorInfo
 
260
 
 
261
        if {1==[catch $_cgi(body) errMsg]} {
 
262
            # error occurred, handle it
 
263
            set _cgi(errorInfo) $errorInfo
 
264
 
 
265
            if {![info exists env(REQUEST_METHOD)]} {
 
266
                puts stderr $_cgi(errorInfo)
 
267
                return
 
268
            }
 
269
            # the following code is all to force browsers into a state
 
270
            # such that diagnostics can be reliably shown
 
271
 
 
272
            # close irrelevant things
 
273
            _cgi_close_procs
 
274
            # force http head and open html, head, body
 
275
            cgi_html {
 
276
                cgi_body {
 
277
                    if {[info exists _cgi(client_error)]} {
 
278
                        cgi_h3 "Client Error"
 
279
                        cgi_p "$errMsg  Report this to your system administrator or browser vendor."
 
280
                    } else {
 
281
                        cgi_put [cgi_anchor_name cgierror]
 
282
                        cgi_h3 "An internal error was detected in the service\
 
283
                                software.  The diagnostics are being emailed to\
 
284
                                the service system administrator ($_cgi(admin_email))."
 
285
 
 
286
                        if {$_cgi(debug) == "-on"} {
 
287
                            cgi_puts "Heck, since you're debugging, I'll show you the\
 
288
                                    errors right here:"
 
289
                            # suppress formatting
 
290
                            cgi_preformatted {
 
291
                                cgi_puts [cgi_quote_html $_cgi(errorInfo)]
 
292
                            }
 
293
                        } else {
 
294
                            cgi_mail_start $_cgi(admin_email)
 
295
                            cgi_mail_add "Subject: [cgi_name] CGI problem"
 
296
                            cgi_mail_add
 
297
                            cgi_mail_add "CGI environment:"
 
298
                            cgi_mail_add "REQUEST_METHOD: $env(REQUEST_METHOD)"
 
299
                            cgi_mail_add "SCRIPT_NAME: $env(SCRIPT_NAME)"
 
300
                            # this next few things probably don't need
 
301
                            # a catch but I'm not positive
 
302
                            catch {cgi_mail_add "HTTP_USER_AGENT: $env(HTTP_USER_AGENT)"}
 
303
                            catch {cgi_mail_add "HTTP_REFERER: $env(HTTP_REFERER)"}
 
304
                            catch {cgi_mail_add "HTTP_HOST: $env(HTTP_HOST)"}
 
305
                            catch {cgi_mail_add "REMOTE_HOST: $env(REMOTE_HOST)"}
 
306
                            catch {cgi_mail_add "REMOTE_ADDR: $env(REMOTE_ADDR)"}
 
307
                            cgi_mail_add "cgi.tcl version: @CGI_VERSION_FULL@"
 
308
                            cgi_mail_add "input:"
 
309
                            catch {cgi_mail_add $_cgi(input)}
 
310
                            cgi_mail_add "cookie:"
 
311
                            catch {cgi_mail_add $env(HTTP_COOKIE)}
 
312
                            cgi_mail_add "errorInfo:"
 
313
                            cgi_mail_add "$_cgi(errorInfo)"
 
314
                            cgi_mail_end
 
315
                        }
 
316
                    }
 
317
                } ;# end cgi_body
 
318
            } ;# end cgi_html
 
319
        } ;# end catch
 
320
    } ;# end uplevel
 
321
}
 
322
 
 
323
# return true if cgi_eval caught an error
 
324
proc cgi_error_occurred {} {
 
325
    global _cgi
 
326
 
 
327
    return [info exists _cgi(errorInfo)]
 
328
}
 
329
 
 
330
##################################################
 
331
# CGI URL creation
 
332
##################################################
 
333
 
 
334
# declare location of root of CGI files
 
335
# this allows all CGI references to be relative in the source
 
336
# making it easy to move everything in the future
 
337
# If you have multiple roots, just don't call this.
 
338
proc cgi_root {args} {
 
339
    global _cgi
 
340
 
 
341
    if {[llength $args]} {
 
342
        set _cgi(root) [lindex $args 0]
 
343
    } else {
 
344
        set _cgi(root)
 
345
    }
 
346
}
 
347
 
 
348
# make a URL for a CGI script
 
349
proc cgi_cgi {args} {
 
350
    global _cgi
 
351
 
 
352
    set root $_cgi(root)
 
353
    if {0!=[string compare $root ""]} {
 
354
        if {![regexp "/$" $root]} {
 
355
                append root "/"
 
356
        }
 
357
    }
 
358
                
 
359
    set suffix [cgi_suffix]
 
360
 
 
361
    set arg [lindex $args 0]
 
362
    if {0==[string compare $arg "-suffix"]} {
 
363
        set suffix [lindex $args 1]
 
364
        set args [lrange $args 2 end]
 
365
    }
 
366
 
 
367
    if {[llength $args]==1} {
 
368
        return $root[lindex $args 0]$suffix
 
369
    } else {
 
370
        return $root[lindex $args 0]$suffix?[join [lrange $args 1 end] &]
 
371
    }
 
372
}
 
373
 
 
374
proc cgi_suffix {args} {
 
375
    global _cgi
 
376
    if {[llength $args] > 0} {
 
377
        set _cgi(suffix) [lindex $args 0]
 
378
    }
 
379
    if {![info exists _cgi(suffix)]} {
 
380
        return .cgi
 
381
    } else {
 
382
        return $_cgi(suffix)
 
383
    }
 
384
}
 
385
 
 
386
proc cgi_cgi_set {variable value} {
 
387
    regsub -all {%}  $value "%25" value
 
388
    regsub -all {&}  $value "%26" value
 
389
    regsub -all {\+} $value "%2b" value
 
390
    regsub -all { }  $value "+"   value
 
391
    regsub -all {=}  $value "%3d" value
 
392
    regsub -all {#}  $value "%23" value
 
393
    regsub -all {/}  $value "%2f" value   ;# Added...
 
394
    return $variable=$value
 
395
}
 
396
 
 
397
##################################################
 
398
# URL dictionary support
 
399
##################################################
 
400
 
 
401
proc cgi_link {args} {
 
402
    global _cgi_link
 
403
 
 
404
    set tag [lindex $args 0]
 
405
    switch -- [llength $args] {
 
406
        1 {
 
407
            set label $_cgi_link($tag,label)
 
408
        } 2 {
 
409
            set label [lindex $args end]
 
410
        } default {
 
411
            set _cgi_link($tag,label) [set label [lindex $args 1]]
 
412
            set _cgi_link($tag,url) [lrange $args 2 end]
 
413
        }
 
414
    }
 
415
 
 
416
    return [eval cgi_url [list $label] $_cgi_link($tag,url)]
 
417
}
 
418
 
 
419
# same as above but for images
 
420
# note: uses different namespace
 
421
proc cgi_imglink {args} {
 
422
    global _cgi_imglink
 
423
 
 
424
    set tag [lindex $args 0]
 
425
    if {[llength $args] >= 2} {
 
426
        set _cgi_imglink($tag) [eval cgi_img [lrange $args 1 end]]
 
427
    }
 
428
    return $_cgi_imglink($tag)
 
429
}
 
430
 
 
431
proc cgi_link_label {tag} {
 
432
    global _cgi_link
 
433
    return $_cgi_link($tag,label)
 
434
}
 
435
 
 
436
proc cgi_link_url {tag} {
 
437
    global _cgi_link
 
438
    return $_cgi_link($tag,url)
 
439
}
 
440
 
 
441
##################################################
 
442
# hyperlink support
 
443
##################################################
 
444
 
 
445
# construct a hyperlink labeled "display"
 
446
# last arg is the link destination
 
447
# any other args are passed through into <a> display
 
448
proc cgi_url {display args} {
 
449
    global _cgi
 
450
 
 
451
    set buf "<a href=\"[lindex $args 0]\""
 
452
    foreach a [lrange $args 1 end] {
 
453
        if {[regexp $_cgi(attr,regexp) $a dummy attr str]} {
 
454
            append buf " $attr=\"$str\""
 
455
        } else {
 
456
            append buf " $a"
 
457
        }
 
458
    }
 
459
    return "$buf>$display</a>"
 
460
}
 
461
 
 
462
# generate an image reference (<img ...>)
 
463
# first arg is image url
 
464
# other args are passed through into <img> tag
 
465
proc cgi_img {args} {
 
466
    global _cgi
 
467
 
 
468
    set buf "<img src=\"[lindex $args 0]\""
 
469
    foreach a [lrange $args 1 end] {
 
470
        if {[regexp "^(alt|lowsrc|usemap)=(.*)" $a dummy attr str]} {
 
471
            append buf " $attr=[cgi_dquote_html $str]"
 
472
        } elseif {[regexp $_cgi(attr,regexp) $a dummy attr str]} {
 
473
            append buf " $attr=\"$str\""
 
474
        } else {
 
475
            append buf " $a"
 
476
        }
 
477
    }
 
478
    return "$buf />"
 
479
}
 
480
 
 
481
# names an anchor so that it can be linked to
 
482
proc cgi_anchor_name {name} {
 
483
    return "<a name=\"$name\"/>"
 
484
}
 
485
 
 
486
proc cgi_base {args} {
 
487
    global _cgi
 
488
 
 
489
    cgi_put "<base"
 
490
    foreach a $args {
 
491
        if {[regexp "^href=(.*)" $a dummy str]} {
 
492
            cgi_put " href=[cgi_dquote_html $str]"
 
493
        } elseif {[regexp $_cgi(attr,regexp) $a dummy attr str]} {
 
494
            cgi_put " $attr=\"$str\""
 
495
        } else {
 
496
            cgi_put " $a"
 
497
        }
 
498
    }
 
499
    cgi_puts " />"
 
500
}
 
501
 
 
502
##################################################
 
503
# quoting support
 
504
##################################################
 
505
 
 
506
if {[info tclversion] >= 8.2} {
 
507
    proc cgi_unquote_input buf {
 
508
        # rewrite "+" back to space
 
509
        # protect \ from quoting another \ and throwing off other things
 
510
        # replace line delimiters with newlines
 
511
        set buf [string map -nocase [list + { } "\\" "\\\\" %0d%0a \n] $buf]
 
512
 
 
513
        # prepare to process all %-escapes
 
514
        regsub -all -nocase {%([a-f0-9][a-f0-9])} $buf {\\u00\1} buf
 
515
 
 
516
        # process \u unicode mapped chars
 
517
        encoding convertfrom $::_cgi(queryencoding) \
 
518
                 [subst -novar -nocommand $buf]
 
519
    }
 
520
} elseif {[info tclversion] >= 8.1} {
 
521
    proc cgi_unquote_input buf {
 
522
        # rewrite "+" back to space
 
523
        regsub -all {\+} $buf { } buf
 
524
        # protect \ from quoting another \ and throwing off other things
 
525
        regsub -all {\\} $buf {\\\\} buf
 
526
 
 
527
        # replace line delimiters with newlines
 
528
        regsub -all -nocase "%0d%0a" $buf "\n" buf
 
529
 
 
530
        # prepare to process all %-escapes
 
531
        regsub -all -nocase {%([a-f0-9][a-f0-9])} $buf {\\u00\1} buf
 
532
        # process \u unicode mapped chars
 
533
        return [subst -novar -nocommand $buf]
 
534
    }
 
535
} else {
 
536
    proc cgi_unquote_input {buf} {
 
537
        # rewrite "+" back to space
 
538
        regsub -all {\+} $buf { } buf
 
539
        # protect \ from quoting another \ and throwing off other things first
 
540
        # protect $ from doing variable expansion
 
541
        # protect [ from doing evaluation
 
542
        # protect " from terminating string
 
543
        regsub -all {([\\["$])} $buf {\\\1} buf
 
544
        
 
545
        # replace line delimiters with newlines
 
546
        regsub -all -nocase "%0d%0a" $buf "\n" buf
 
547
        # Mosaic sends just %0A.  This is handled in the next command.
 
548
 
 
549
        # prepare to process all %-escapes 
 
550
        regsub -all -nocase {%([a-f0-9][a-f0-9])} $buf {[format %c 0x\1]} buf
 
551
        # process %-escapes and undo all protection
 
552
        eval return \"$buf\"
 
553
    }
 
554
}
 
555
 
 
556
# return string but with html-special characters escaped,
 
557
# necessary if you want to send unknown text to an html-formatted page.
 
558
proc cgi_quote_html {s} {
 
559
    regsub -all {&}     $s {\&amp;}     s       ;# must be first!
 
560
    regsub -all {"}     $s {\&quot;}    s
 
561
    regsub -all {<}     $s {\&lt;}      s
 
562
    regsub -all {>}     $s {\&gt;}      s
 
563
    return $s
 
564
}
 
565
 
 
566
proc cgi_dquote_html {s} {
 
567
    return \"[cgi_quote_html $s]\"
 
568
}
 
569
 
 
570
# return string quoted appropriately to appear in a url
 
571
proc cgi_quote_url {in} {
 
572
    regsub -all {%}  $in "%25" in
 
573
    regsub -all {\+} $in "%2b" in
 
574
    regsub -all { }  $in "%20" in
 
575
    regsub -all {"}  $in "%22" in
 
576
    regsub -all {\?} $in "%3f" in
 
577
    return $in
 
578
}
 
579
 
 
580
##################################################
 
581
# short or single paragraph support
 
582
##################################################
 
583
 
 
584
proc cgi_br {args} {
 
585
    cgi_put "<br"
 
586
    if {[llength $args]} {
 
587
        cgi_put "[_cgi_list_to_string $args]"
 
588
    }
 
589
    cgi_put " />"
 
590
}
 
591
 
 
592
# generate cgi_h1 and others
 
593
for {set _cgi(tmp) 1} {$_cgi(tmp)<8} {incr _cgi(tmp)} {
 
594
    proc cgi_h$_cgi(tmp) {{args}} "eval cgi_h $_cgi(tmp) \$args"
 
595
}
 
596
proc cgi_h {num args} {
 
597
    cgi_put "<h$num"
 
598
    if {[llength $args] > 1} {
 
599
        cgi_put "[_cgi_lrange $args 0 [expr [llength $args]-2]]"
 
600
        set args [lrange $args end end]
 
601
    }
 
602
    cgi_put ">[lindex $args 0]</h$num>"
 
603
}
 
604
 
 
605
proc cgi_p {args} {
 
606
    cgi_put "<p"
 
607
    if {[llength $args] > 1} {
 
608
        cgi_put "[_cgi_lrange $args 0 [expr [llength $args]-2]]"
 
609
        set args [lrange $args end end]
 
610
    }
 
611
    cgi_put ">[lindex $args 0]</p>"
 
612
}
 
613
 
 
614
proc cgi_address      {s} {cgi_put <address>$s</address>}
 
615
proc cgi_blockquote   {s} {cgi_puts <blockquote>$s</blockquote>}
 
616
 
 
617
##################################################
 
618
# long or multiple paragraph support
 
619
##################################################
 
620
 
 
621
# Shorthand for <div align=center>.  We used to use <center> tags but that
 
622
# is now officially unsupported.
 
623
proc cgi_center {cmd}   {
 
624
    uplevel 1 "cgi_division align=center [list $cmd]"
 
625
}
 
626
 
 
627
proc cgi_division {args} {
 
628
    cgi_put "<div"
 
629
    _cgi_close_proc_push "cgi_put </div>"
 
630
 
 
631
    if {[llength $args]} {
 
632
        cgi_put "[_cgi_lrange $args 0 [expr {[llength $args]-2}]]"
 
633
    }
 
634
    cgi_put ">"
 
635
    uplevel 1 [lindex $args end]
 
636
    _cgi_close_proc
 
637
}
 
638
 
 
639
proc cgi_preformatted {args} {
 
640
    cgi_put "<pre"
 
641
    _cgi_close_proc_push "cgi_put </pre>"
 
642
 
 
643
    if {[llength $args]} {
 
644
        cgi_put "[_cgi_lrange $args 0 [expr [llength $args]-2]]"
 
645
    }
 
646
    cgi_put ">"
 
647
    uplevel 1 [lindex $args end]
 
648
    _cgi_close_proc
 
649
}
 
650
 
 
651
##################################################
 
652
# list support
 
653
##################################################
 
654
 
 
655
proc cgi_li {args} {
 
656
    cgi_put <li
 
657
    if {[llength $args] > 1} {
 
658
        cgi_put "[_cgi_lrange $args 0 [expr [llength $args]-2]]"
 
659
    }
 
660
    cgi_put ">[lindex $args end]</li>"
 
661
}
 
662
 
 
663
proc cgi_number_list {args} {
 
664
    cgi_put "<ol"
 
665
    _cgi_close_proc_push "cgi_put </ol>"
 
666
 
 
667
    if {[llength $args] > 1} {
 
668
        cgi_put "[_cgi_lrange $args 0 [expr [llength $args]-2]]"
 
669
    }
 
670
    cgi_put ">"
 
671
    uplevel 1 [lindex $args end]
 
672
 
 
673
    _cgi_close_proc
 
674
}
 
675
 
 
676
proc cgi_bullet_list {args} {
 
677
    cgi_put "<ul"
 
678
    _cgi_close_proc_push "cgi_put </ul>"
 
679
 
 
680
    if {[llength $args] > 1} {
 
681
        cgi_put "[_cgi_lrange $args 0 [expr [llength $args]-2]]"
 
682
    }
 
683
    cgi_put ">"
 
684
    uplevel 1 [lindex $args end]
 
685
 
 
686
    _cgi_close_proc
 
687
}
 
688
 
 
689
# Following two are normally used from within definition lists
 
690
# but are actually paragraph types on their own.
 
691
proc cgi_term            {s} {cgi_put <dt>$s</dt>}
 
692
proc cgi_term_definition {s} {cgi_put <dd>$s</dd>}
 
693
 
 
694
proc cgi_definition_list {cmd} {
 
695
    cgi_put "<dl>"
 
696
    _cgi_close_proc_push "cgi_put </dl>"
 
697
 
 
698
    uplevel 1 $cmd
 
699
    _cgi_close_proc
 
700
}
 
701
 
 
702
proc cgi_menu_list {cmd} {
 
703
    cgi_put "<menu>"
 
704
    _cgi_close_proc_push "cgi_put </menu>"
 
705
 
 
706
    uplevel 1 $cmd
 
707
    _cgi_close_proc
 
708
}
 
709
proc cgi_directory_list {cmd} {
 
710
    cgi_put "<dir>"
 
711
    _cgi_close_proc_push "cgi_put </dir>"
 
712
 
 
713
    uplevel 1 $cmd
 
714
    _cgi_close_proc
 
715
}
 
716
 
 
717
##################################################
 
718
# text support
 
719
##################################################
 
720
 
 
721
proc cgi_put        {s} {cgi_puts -nonewline $s}
 
722
 
 
723
# some common special characters
 
724
proc cgi_lt          {}  {return "&lt;"}
 
725
proc cgi_gt          {}  {return "&gt;"}
 
726
proc cgi_amp         {}  {return "&amp;"}
 
727
proc cgi_quote       {}  {return "&quot;"}
 
728
proc cgi_enspace     {}  {return "&ensp;"}
 
729
proc cgi_emspace     {}  {return "&emsp;"}
 
730
proc cgi_nbspace     {}  {return "&#160;"} ;# nonbreaking space
 
731
proc cgi_tm          {}  {return "&#174;"} ;# registered trademark
 
732
proc cgi_copyright   {}  {return "&#169;"}
 
733
proc cgi_isochar     {n} {return "&#$n;"}
 
734
proc cgi_breakable   {}  {return "<wbr />"}
 
735
 
 
736
proc cgi_unbreakable_string {s} {return "<nobr>$s</nobr>"}
 
737
proc cgi_unbreakable {cmd} {
 
738
    cgi_put "<nobr>"
 
739
    _cgi_close_proc_push "cgi_put </nobr>"
 
740
    uplevel 1 $cmd
 
741
    _cgi_close_proc
 
742
}
 
743
 
 
744
proc cgi_nl          {args} {
 
745
    set buf "<br"
 
746
    if {[llength $args]} {
 
747
        append buf "[_cgi_list_to_string $args]"
 
748
    }
 
749
    return "$buf />"
 
750
}
 
751
 
 
752
proc cgi_bold       {s} {return "<b>$s</b>"}
 
753
proc cgi_italic     {s} {return "<i>$s</i>"}
 
754
proc cgi_underline  {s} {return "<u>$s</u>"}
 
755
proc cgi_strikeout  {s} {return "<s>$s</s>"}
 
756
proc cgi_subscript  {s} {return "<sub>$s</sub>"}
 
757
proc cgi_superscript {s} {return "<sup>$s</sup>"}
 
758
proc cgi_typewriter {s} {return "<tt>$s</tt>"}
 
759
proc cgi_blink      {s} {return "<blink>$s</blink>"}
 
760
proc cgi_emphasis   {s} {return "<em>$s</em>"}
 
761
proc cgi_strong     {s} {return "<strong>$s</strong>"}
 
762
proc cgi_cite       {s} {return "<cite>$s</cite>"}
 
763
proc cgi_sample     {s} {return "<samp>$s</samp>"}
 
764
proc cgi_keyboard   {s} {return "<kbd>$s</kbd>"}
 
765
proc cgi_variable   {s} {return "<var>$s</var>"}
 
766
proc cgi_definition {s} {return "<dfn>$s</dfn>"}
 
767
proc cgi_big        {s} {return "<big>$s</big>"}
 
768
proc cgi_small      {s} {return "<small>$s</small>"}
 
769
 
 
770
proc cgi_basefont   {size} {cgi_put "<basefont size=$size />"}
 
771
 
 
772
proc cgi_font {args} {
 
773
    global _cgi
 
774
 
 
775
    set buf "<font"
 
776
    foreach a [lrange $args 0 [expr [llength $args]-2]] {
 
777
        if {[regexp $_cgi(attr,regexp) $a dummy attr str]} {
 
778
            append buf " $attr=\"$str\""
 
779
        } else {
 
780
            append buf " $a"
 
781
        }
 
782
    }
 
783
    return "$buf>[lindex $args end]</font>"
 
784
}
 
785
 
 
786
# take a cgi func and have it return what would normally print
 
787
# This command is reentrant (that's why it's so complex).
 
788
proc cgi_buffer {cmd} {
 
789
    global _cgi
 
790
 
 
791
    if {0==[info exists _cgi(returnIndex)]} {
 
792
        set _cgi(returnIndex) 0
 
793
    }
 
794
 
 
795
    rename cgi_puts cgi_puts$_cgi(returnIndex)
 
796
    incr _cgi(returnIndex)
 
797
    set _cgi(return[set _cgi(returnIndex)]) ""
 
798
 
 
799
    proc cgi_puts args {
 
800
        global _cgi
 
801
        upvar #0 _cgi(return[set _cgi(returnIndex)]) buffer
 
802
 
 
803
        append buffer [lindex $args end]
 
804
        if {[llength $args] == 1} {
 
805
            append buffer $_cgi(buffer_nl)
 
806
        }
 
807
    }
 
808
 
 
809
    # must restore things before allowing the eval to fail
 
810
    # so catch here and rethrow later
 
811
    if {[catch {uplevel 1 $cmd} errMsg]} {
 
812
        global errorInfo
 
813
        set savedInfo $errorInfo
 
814
    }
 
815
 
 
816
    # not necessary to put remainder of code in close_proc_push since it's
 
817
    # all buffered anyway and hasn't yet put browser into a funky state.
 
818
 
 
819
    set buffer $_cgi(return[set _cgi(returnIndex)])
 
820
 
 
821
    incr _cgi(returnIndex) -1
 
822
    rename cgi_puts ""
 
823
    rename cgi_puts$_cgi(returnIndex) cgi_puts
 
824
 
 
825
    if {[info exists savedInfo]} {
 
826
        error $errMsg $savedInfo
 
827
    }
 
828
    return $buffer
 
829
}
 
830
 
 
831
set _cgi(buffer_nl) "\n"
 
832
proc cgi_buffer_nl {nl} {
 
833
    global _cgi
 
834
 
 
835
    set old $_cgi(buffer_nl)
 
836
    set _cgi(buffer_nl) $nl
 
837
    return $old
 
838
}
 
839
 
 
840
##################################################
 
841
# html and tags that can appear in html top-level
 
842
##################################################
 
843
 
 
844
proc cgi_html {args} {
 
845
    set html [lindex $args end]
 
846
    set argc [llength $args]
 
847
    if {$argc > 1} {
 
848
        eval _cgi_html_start [lrange $args 0 [expr {$argc-2}]]
 
849
    } else {
 
850
        _cgi_html_start
 
851
    }
 
852
    uplevel 1 $html
 
853
    _cgi_html_end
 
854
}
 
855
 
 
856
proc _cgi_html_start {args} {
 
857
    global _cgi
 
858
    
 
859
    if {[info exists _cgi(html_in_progress)]} return
 
860
    _cgi_http_head_implicit
 
861
 
 
862
    set _cgi(html_in_progress) 1
 
863
    cgi_doctype
 
864
 
 
865
    append buf "<html"
 
866
    foreach a $args {
 
867
        if {[regexp $_cgi(attr,regexp) $a dummy attr str]} {
 
868
            append buf " $attr=\"$str\""
 
869
        } else {
 
870
            append buf " $a"
 
871
        }
 
872
    }
 
873
    cgi_puts "$buf>"
 
874
}
 
875
 
 
876
proc _cgi_html_end {} {
 
877
    global _cgi
 
878
    unset _cgi(html_in_progress)
 
879
    set _cgi(html_done) 1
 
880
    cgi_puts "</html>"
 
881
}
 
882
 
 
883
# force closure of all tags and exit without going through normal returns.
 
884
# Very useful if you want to call exit from a deeply stacked CGI script
 
885
# and still have the HTML be correct.
 
886
proc cgi_exit {} {
 
887
    _cgi_close_procs
 
888
    cgi_html {cgi_body {}}
 
889
    exit
 
890
}
 
891
 
 
892
##################################################
 
893
# head support
 
894
##################################################
 
895
 
 
896
proc cgi_head {{head {}}} {
 
897
    global _cgi
 
898
 
 
899
    if {[info exists _cgi(head_done)]} {
 
900
        return
 
901
    }
 
902
 
 
903
    # allow us to be recalled so that we can display errors
 
904
    if {0 == [info exists _cgi(head_in_progress)]} {
 
905
        _cgi_http_head_implicit
 
906
        set _cgi(head_in_progress) 1
 
907
        cgi_puts "<head>"
 
908
    }
 
909
 
 
910
    # prevent cgi_html (during error handling) from generating html tags
 
911
    set _cgi(html_in_progress) 1
 
912
    # don't actually generate html tags since there's nothing to clean
 
913
    # them up
 
914
 
 
915
    if {0 == [string length $head]} {
 
916
        if {[catch {cgi_title}]} {
 
917
            set head "cgi_title untitled"
 
918
        }
 
919
    }
 
920
    uplevel 1 $head
 
921
    if {![info exists _cgi(head_suppress_tag)]} {
 
922
        cgi_puts "</head>"
 
923
    } else {
 
924
        unset _cgi(head_suppress_tag)
 
925
    }
 
926
 
 
927
    set _cgi(head_done) 1
 
928
 
 
929
    # debugging can unset this in the uplevel above
 
930
    catch {unset _cgi(head_in_progress)}
 
931
}
 
932
 
 
933
# with one arg: set, print, and return title
 
934
# with no args: return title
 
935
proc cgi_title {args} {
 
936
    global _cgi
 
937
 
 
938
    set title [lindex $args 0]
 
939
 
 
940
    if {[llength $args]} {
 
941
        _cgi_http_head_implicit
 
942
 
 
943
        # we could just generate <head></head> tags, but head-level commands
 
944
        # might follow so just suppress the head tags entirely
 
945
        if {![info exists _cgi(head_in_progress)]} {
 
946
            set _cgi(head_in_progress) 1
 
947
            set _cgi(head_suppress_tag) 1
 
948
        }
 
949
 
 
950
        set _cgi(title) $title
 
951
        cgi_puts "<title>$title</title>"
 
952
    }
 
953
    return $_cgi(title)
 
954
}
 
955
 
 
956
# This tag can only be called from with cgi_head.
 
957
# example: cgi_http_equiv Refresh 1
 
958
# There's really no reason to call this since it can be done directly
 
959
# from cgi_http_head.
 
960
proc cgi_http_equiv {type contents} {
 
961
    _cgi_http_head_implicit
 
962
    cgi_puts "<meta http-equiv=\"$type\" content=[cgi_dquote_html $contents]/>"
 
963
}
 
964
 
 
965
# Do whatever you want with meta tags.
 
966
# Example: <meta name="author" content="Don Libes">
 
967
proc cgi_meta {args} {
 
968
    cgi_put "<meta"
 
969
    foreach a $args {
 
970
        if {[regexp "^(name|content|http-equiv)=(.*)" $a dummy attr str]} {
 
971
            cgi_put " $attr=[cgi_dquote_html $str]"
 
972
        } else {
 
973
            cgi_put " $a"
 
974
        }
 
975
    }
 
976
    cgi_puts " />"
 
977
}
 
978
 
 
979
proc cgi_relationship {rel href args} {
 
980
    cgi_puts "<link rel=$rel href=\"$href\""
 
981
    foreach a $args {
 
982
        if {[regexp "^title=(.*)" $a dummy str]} {
 
983
            cgi_put " title=[cgi_dquote_html $str]"
 
984
        } elseif {[regexp "^type=(.*)" $a dummy str]} {
 
985
            cgi_put " type=[cgi_dquote_html $str]"
 
986
        } else {
 
987
            cgi_put " $a"
 
988
        }
 
989
    }
 
990
    cgi_puts "/>"
 
991
}
 
992
 
 
993
proc cgi_name {args} {
 
994
    global _cgi
 
995
 
 
996
    if {[llength $args]} {
 
997
        set _cgi(name) [lindex $args 0]
 
998
    }
 
999
    return $_cgi(name)
 
1000
}
 
1001
 
 
1002
##################################################
 
1003
# body and other top-level support
 
1004
##################################################
 
1005
 
 
1006
proc cgi_body {args} {
 
1007
    global errorInfo errorCode _cgi
 
1008
 
 
1009
    # allow user to "return" from the body without missing _cgi_body_end
 
1010
    if {1==[catch {
 
1011
        eval _cgi_body_start [lrange $args 0 [expr [llength $args]-2]]
 
1012
        uplevel 1 [lindex $args end]
 
1013
    } errMsg]} {
 
1014
        set savedInfo $errorInfo
 
1015
        set savedCode $errorCode
 
1016
        error $errMsg $savedInfo $savedCode
 
1017
    }
 
1018
    _cgi_body_end
 
1019
}
 
1020
 
 
1021
proc _cgi_body_start {args} {
 
1022
    global _cgi
 
1023
    if {[info exists _cgi(body_in_progress)]} return
 
1024
 
 
1025
    cgi_head
 
1026
 
 
1027
    set _cgi(body_in_progress) 1
 
1028
 
 
1029
    cgi_put "<body"
 
1030
    foreach a "$args $_cgi(body_args)" {
 
1031
        if {[regexp "^(background|bgcolor|text|link|vlink|alink|onLoad|onUnload)=(.*)" $a dummy attr str]} {
 
1032
            cgi_put " $attr=\"$str\""
 
1033
        } else {
 
1034
            cgi_put " $a"
 
1035
        }
 
1036
    }
 
1037
    cgi_puts ">"
 
1038
 
 
1039
    cgi_debug {
 
1040
        global env
 
1041
        catch {cgi_puts "Input: <pre>$_cgi(input)</pre>"}
 
1042
        catch {cgi_puts "Cookie: <pre>$env(HTTP_COOKIE)</pre>"}
 
1043
    }
 
1044
 
 
1045
    if {![info exists _cgi(errorInfo)]} {
 
1046
        uplevel 2 app_body_start
 
1047
    }
 
1048
}
 
1049
 
 
1050
proc _cgi_body_end {} {
 
1051
    global _cgi
 
1052
    if {![info exists _cgi(errorInfo)]} {
 
1053
        uplevel 2 app_body_end
 
1054
    }
 
1055
    unset _cgi(body_in_progress)
 
1056
    cgi_puts "</body>"
 
1057
 
 
1058
    if {[info exists _cgi(multipart)]} {
 
1059
        unset _cgi(http_head_done)
 
1060
        catch {unset _cgi(http_status_done)}
 
1061
        unset _cgi(head_done)
 
1062
        catch {unset _cgi(head_suppress_tag)}
 
1063
    }
 
1064
}
 
1065
 
 
1066
proc cgi_body_args {args} {
 
1067
    global _cgi
 
1068
 
 
1069
    set _cgi(body_args) $args
 
1070
}
 
1071
 
 
1072
proc cgi_script {args} {
 
1073
    cgi_puts "<script[_cgi_lrange $args 0 [expr [llength $args]-2]]>"
 
1074
    _cgi_close_proc_push "cgi_puts </script>"
 
1075
 
 
1076
    uplevel 1 [lindex $args end]
 
1077
 
 
1078
    _cgi_close_proc
 
1079
}
 
1080
 
 
1081
proc cgi_javascript {args} {
 
1082
    cgi_puts "<script[_cgi_lrange $args 0 [expr [llength $args]-2]]>"
 
1083
    cgi_puts "<!--- Hide script from browsers that don't understand JavaScript"
 
1084
    _cgi_close_proc_push {cgi_puts "// End hiding -->\n</script>"}
 
1085
 
 
1086
    uplevel 1 [lindex $args end]
 
1087
 
 
1088
    _cgi_close_proc
 
1089
}
 
1090
 
 
1091
proc cgi_noscript {args} {
 
1092
    cgi_puts "<noscript[_cgi_lrange $args 0 [expr [llength $args]-2]]>"
 
1093
    _cgi_close_proc_push {cgi_puts "</noscript>"}
 
1094
 
 
1095
    uplevel 1 [lindex $args end]
 
1096
 
 
1097
    _cgi_close_proc
 
1098
}
 
1099
 
 
1100
proc cgi_applet {args} {
 
1101
    cgi_puts "<applet[_cgi_lrange $args 0 [expr [llength $args]-2]]>"
 
1102
    _cgi_close_proc_push "cgi_puts </applet>"
 
1103
 
 
1104
    uplevel 1 [lindex $args end]
 
1105
    _cgi_close_proc
 
1106
}
 
1107
 
 
1108
proc cgi_param {nameval} {
 
1109
    regexp "(\[^=]*)(=?)(.*)" $nameval dummy name q value
 
1110
 
 
1111
    if {$q != "="} {
 
1112
        set value ""
 
1113
    }
 
1114
    cgi_puts "<param name=\"$name\" value=[cgi_dquote_html $value]/>"
 
1115
}
 
1116
 
 
1117
# record any proc's that must be called prior to displaying an error
 
1118
proc _cgi_close_proc_push {p} {
 
1119
    global _cgi
 
1120
    if {![info exists _cgi(close_proc)]} {
 
1121
        set _cgi(close_proc) ""
 
1122
    }
 
1123
    set _cgi(close_proc) "$p; $_cgi(close_proc)"
 
1124
}
 
1125
 
 
1126
proc _cgi_close_proc_pop {} {
 
1127
    global _cgi
 
1128
    regexp "^(\[^;]*);(.*)" $_cgi(close_proc) dummy lastproc _cgi(close_proc)
 
1129
    return $lastproc
 
1130
}
 
1131
 
 
1132
# generic proc to close whatever is on the top of the stack
 
1133
proc _cgi_close_proc {} {
 
1134
    eval [_cgi_close_proc_pop]
 
1135
}
 
1136
 
 
1137
proc _cgi_close_procs {} {
 
1138
    global _cgi
 
1139
 
 
1140
    _cgi_close_tag
 
1141
    if {[info exists _cgi(close_proc)]} {
 
1142
        uplevel #0 $_cgi(close_proc)
 
1143
    }
 
1144
}
 
1145
 
 
1146
proc _cgi_close_tag {} {
 
1147
    global _cgi
 
1148
 
 
1149
    if {[info exists _cgi(tag_in_progress)]} {
 
1150
        cgi_put ">"
 
1151
        unset _cgi(tag_in_progress)
 
1152
    }
 
1153
}
 
1154
 
 
1155
##################################################
 
1156
# hr support
 
1157
##################################################
 
1158
 
 
1159
proc cgi_hr {args} {
 
1160
    set buf "<hr"
 
1161
    foreach a $args {
 
1162
        if {[regexp "^width=(.*)" $a dummy str]} {
 
1163
            append buf " width=\"$str\""
 
1164
        } else {
 
1165
            append buf " $a"
 
1166
        }
 
1167
    }
 
1168
    cgi_put "$buf />"
 
1169
}
 
1170
 
 
1171
##################################################
 
1172
# form & isindex
 
1173
##################################################
 
1174
 
 
1175
proc cgi_form {action args} {
 
1176
    global _cgi
 
1177
 
 
1178
    _cgi_form_multiple_check
 
1179
    set _cgi(form_in_progress) 1
 
1180
 
 
1181
    _cgi_close_proc_push _cgi_form_end
 
1182
    cgi_put "<form action="
 
1183
    if {[regexp {^[a-z]*:} $action]} {
 
1184
        cgi_put "\"$action\""
 
1185
    } else {
 
1186
        cgi_put "\"[cgi_cgi $action]\""
 
1187
    }
 
1188
    set method "method=post"
 
1189
    foreach a [lrange $args 0 [expr [llength $args]-2]] {
 
1190
        if {[regexp "^method=" $a]} {
 
1191
            set method $a
 
1192
        } elseif {[regexp "^(target|onReset|onSubmit)=(.*)" $a dummy attr str]} {
 
1193
            cgi_put " $attr=\"$str\""
 
1194
        } elseif {[regexp "^enctype=(.*)" $a dummy str]} {
 
1195
            cgi_put " enctype=\"$str\""
 
1196
            set _cgi(form,enctype) $str
 
1197
        } else {
 
1198
            cgi_put " $a"
 
1199
        }
 
1200
    }
 
1201
    cgi_put " $method>"
 
1202
    uplevel 1 [lindex $args end]
 
1203
    catch {unset _cgi(form,enctype)}
 
1204
    _cgi_close_proc
 
1205
}
 
1206
 
 
1207
proc _cgi_form_end {} {
 
1208
    global _cgi
 
1209
    unset _cgi(form_in_progress)
 
1210
    cgi_put "</form>"
 
1211
}
 
1212
 
 
1213
proc _cgi_form_multiple_check {} {
 
1214
    global _cgi
 
1215
    if {[info exists _cgi(form_in_progress)]} {
 
1216
        error "Cannot create form (or isindex) with form already in progress."
 
1217
    }
 
1218
}
 
1219
 
 
1220
proc cgi_isindex {args} {
 
1221
    _cgi_form_multiple_check
 
1222
 
 
1223
    cgi_put "<isindex"
 
1224
    foreach a $args {
 
1225
        if {[regexp "^href=(.*)" $a dummy str]} {
 
1226
            cgi_put " href=\"$str\""
 
1227
        } elseif {[regexp "^prompt=(.*)" $a dummy str]} {
 
1228
            cgi_put " prompt=[cgi_dquote_html $str]"
 
1229
        } else {
 
1230
            cgi_put " $a"
 
1231
        }
 
1232
    }
 
1233
    cgi_put "/>"
 
1234
}
 
1235
 
 
1236
##################################################
 
1237
# argument handling
 
1238
##################################################
 
1239
 
 
1240
proc cgi_input {{fakeinput {}} {fakecookie {}}} {
 
1241
    global env _cgi _cgi_uservar _cgi_cookie _cgi_cookie_shadowed
 
1242
 
 
1243
    set _cgi(uservars) {}
 
1244
    set _cgi(uservars,autolist) {}
 
1245
 
 
1246
    if {[info exists env(CONTENT_TYPE)] && [regexp ^multipart/form-data $env(CONTENT_TYPE)]} {
 
1247
        if {![info exists env(REQUEST_METHOD)]} {
 
1248
            # running by hand
 
1249
            set fid [open $fakeinput]
 
1250
        } else {
 
1251
            set fid stdin
 
1252
        }
 
1253
        if {([info tclversion] >= 8.1) || [catch exp_version] || [info exists _cgi(no_binary_upload)]} {
 
1254
            _cgi_input_multipart $fid
 
1255
        } else {
 
1256
            _cgi_input_multipart_binary $fid
 
1257
        }
 
1258
    } else {
 
1259
        if {![info exists env(REQUEST_METHOD)]} {
 
1260
            set input $fakeinput
 
1261
            set env(HTTP_COOKIE) $fakecookie
 
1262
        } elseif { $env(REQUEST_METHOD) == "GET" } {
 
1263
            set input ""
 
1264
            catch {set input $env(QUERY_STRING)} ;# doesn't have to be set
 
1265
        } elseif { $env(REQUEST_METHOD) == "HEAD" } {
 
1266
            set input ""
 
1267
        } elseif {![info exists env(CONTENT_LENGTH)]} {
 
1268
            set _cgi(client_error) 1
 
1269
            error "Your browser failed to generate the content-length during a POST method."
 
1270
        } else {
 
1271
            set length $env(CONTENT_LENGTH)
 
1272
            if {0!=[string compare $length "-1"]} {
 
1273
                set input [read stdin $env(CONTENT_LENGTH)]             
 
1274
            } else {
 
1275
                set _cgi(client_error) 1
 
1276
                error "Your browser generated a content-length of -1 during a POST method."
 
1277
            }
 
1278
            if {[info tclversion] >= 8.1} {
 
1279
                # guess query encoding from Content-Type header
 
1280
                if {[info exists env(CONTENT_TYPE)] \
 
1281
                    && [regexp -nocase -- {charset=([^[:space:]]+)} $env(CONTENT_TYPE) m cs]} {
 
1282
                    if {[regexp -nocase -- {iso-?8859-([[:digit:]]+)} $cs m d]} {
 
1283
                        set _cgi(queryencoding) "iso8859-$d"
 
1284
                    } elseif {[regexp -nocase -- {windows-([[:digit:]]+)} $cs m d]} {
 
1285
                        set _cgi(queryencoding) "cp$d"
 
1286
                    } elseif {0==[string compare -nocase $cs "utf-8"]} {
 
1287
                        set _cgi(queryencoding) "utf-8"
 
1288
                    } elseif {0==[string compare -nocase $cs "utf-16"]} {
 
1289
                        set _cgi(queryencoding) "unicode"
 
1290
                    }
 
1291
                } else {
 
1292
                    set _cgi(queryencoding) [encoding system]
 
1293
                }
 
1294
            }
 
1295
        }
 
1296
        # save input for possible diagnostics later
 
1297
        set _cgi(input) $input
 
1298
 
 
1299
        set pairs [split $input &]
 
1300
        foreach pair $pairs {
 
1301
            if {0 == [regexp "^(\[^=]*)=(.*)$" $pair dummy varname val]} {
 
1302
                # if no match, unquote and leave it at that
 
1303
                # this is typical of <isindex>-style queries
 
1304
                set varname anonymous
 
1305
                set val $pair
 
1306
            }
 
1307
 
 
1308
            set varname [cgi_unquote_input $varname]
 
1309
            set val [cgi_unquote_input $val]
 
1310
            _cgi_set_uservar $varname $val
 
1311
        }
 
1312
    }
 
1313
 
 
1314
    # O'Reilly's web server incorrectly uses COOKIE
 
1315
    catch {set env(HTTP_COOKIE) $env(COOKIE)}
 
1316
    if {![info exists env(HTTP_COOKIE)]} return
 
1317
    foreach pair [split $env(HTTP_COOKIE) ";"] {
 
1318
        # pairs are actually split by "; ", sigh
 
1319
        set pair [string trimleft $pair " "]
 
1320
        # spec is not clear but seems to allow = unencoded
 
1321
        # only sensible interpretation is to assume no = in var names
 
1322
        # appears MS IE can omit "=val"
 
1323
        set val ""
 
1324
        regexp (\[^=]*)=?(.*) $pair dummy varname val
 
1325
 
 
1326
        set varname [cgi_unquote_input $varname]
 
1327
        set val [cgi_unquote_input $val]
 
1328
 
 
1329
        if {[info exists _cgi_cookie($varname)]} {
 
1330
            lappend _cgi_cookie_shadowed($varname) $val
 
1331
        } else {
 
1332
            set _cgi_cookie($varname) $val
 
1333
        }
 
1334
    }
 
1335
}
 
1336
 
 
1337
proc _cgi_input_multipart {fin} {
 
1338
    global env _cgi _cgi_uservar _cgi_userfile
 
1339
 
 
1340
    cgi_debug -noprint {
 
1341
        # save file for debugging purposes
 
1342
        set dbg_filename [file join $_cgi(tmpdir) CGIdbg.[pid]]
 
1343
        # explicitly flush all writes to fout, because sometimes the writer
 
1344
        # can hang and we won't get to the termination code
 
1345
        set dbg_fout [open $dbg_filename w $_cgi(tmpperms)]
 
1346
        set _cgi(input) $dbg_filename
 
1347
        catch {fconfigure $dbg_fout -translation binary}
 
1348
    }
 
1349
 
 
1350
    # figure out boundary
 
1351
    if {0==[regexp boundary=(.*) $env(CONTENT_TYPE) dummy boundary]} {
 
1352
        set _cgi(client_error) 1
 
1353
        error "Your browser failed to generate a \"boundary=\" line in a multipart response (CONTENT_TYPE: $env(CONTENT_TYPE)).  Please upgrade (or fix) your browser."
 
1354
    }
 
1355
 
 
1356
    # make boundary into a legal regsub pattern by protecting #
 
1357
    # legal boundary characters include ()+.? (among others)
 
1358
    regsub -all "\\(" $boundary "\\(" boundary
 
1359
    regsub -all "\\)" $boundary "\\)" boundary
 
1360
    regsub -all "\\+" $boundary "\\+" boundary
 
1361
    regsub -all "\\." $boundary "\\." boundary
 
1362
    regsub -all "\\?" $boundary "\\?" boundary
 
1363
 
 
1364
    set boundary --$boundary
 
1365
 
 
1366
    # don't corrupt or modify uploads yet allow Tcl 7.4 to work
 
1367
    catch {fconfigure $fin -translation binary}
 
1368
 
 
1369
    # get first boundary line
 
1370
    gets $fin buf
 
1371
    if {[info exists dbg_fout]} {puts $dbg_fout $buf; flush $dbg_fout}
 
1372
 
 
1373
    set _cgi(file,filecount) 0
 
1374
 
 
1375
    while {1} {
 
1376
        # process Content-Disposition:
 
1377
        if {-1 == [gets $fin buf]} break
 
1378
        if {[info exists dbg_fout]} {puts $dbg_fout $buf; flush $dbg_fout}
 
1379
        catch {unset filename}
 
1380
        regexp {name="([^"]*)"} $buf dummy varname
 
1381
        if {0==[info exists varname]} {
 
1382
            # lynx violates spec and doesn't use quotes, so try again but
 
1383
            # assume space is delimiter
 
1384
            regexp {name=([^ ]*)} $buf dummy varname
 
1385
            if {0==[info exists varname]} {
 
1386
                set _cgi(client_error) 1
 
1387
                error "In response to a request for a multipart form, your browser generated a part header without a name field.  Please upgrade (or fix) your browser."
 
1388
            }
 
1389
        }           
 
1390
        # Lame-o encoding (on Netscape at least) doesn't escape field
 
1391
        # delimiters (like quotes)!!  Since all we've ever seen is filename=
 
1392
        # at end of line, assuming nothing follows.  Sigh.
 
1393
        regexp {filename="(.*)"} $buf dummy filename
 
1394
 
 
1395
        # Skip remaining headers until blank line.
 
1396
        # Content-Type: can appear here.
 
1397
        set conttype ""
 
1398
        while {1} {
 
1399
            if {-1 == [gets $fin buf]} break
 
1400
            if {[info exists dbg_fout]} {puts $dbg_fout $buf; flush $dbg_fout}
 
1401
            if {0==[string compare $buf "\r"]} break
 
1402
            regexp -nocase "^Content-Type:\[ \t]+(.*)\r" $buf x conttype
 
1403
        }
 
1404
 
 
1405
        if {[info exists filename]} {
 
1406
            if {$_cgi(file,filecount) > $_cgi(file,filelimit)} {
 
1407
                error "Too many files submitted.  Max files allowed: $_cgi(file,filelimit)"
 
1408
            }
 
1409
 
 
1410
            # read the part into a file
 
1411
            set foutname [file join $_cgi(tmpdir) CGI[pid].[incr _cgi(file,filecount)]]
 
1412
            set fout [open $foutname w $_cgi(tmpperms)]
 
1413
            # "catch" permits this to work with Tcl 7.4
 
1414
            catch {fconfigure $fout -translation binary}
 
1415
            _cgi_set_uservar $varname [list $foutname $filename $conttype]
 
1416
            set _cgi_userfile($varname) [list $foutname $filename $conttype]
 
1417
 
 
1418
            #
 
1419
            # Look for a boundary line preceded by \r\n.
 
1420
            #
 
1421
            # To do this, we buffer line terminators that might
 
1422
            # be the start of the special \r\n$boundary sequence.
 
1423
            # The buffer is called "leftover" and is just inserted
 
1424
            # into the front of the next output (assuming it's
 
1425
            # not a boundary line).
 
1426
 
 
1427
            set leftover ""
 
1428
            while {1} {
 
1429
                if {-1 == [gets $fin buf]} break
 
1430
                if {[info exists dbg_fout]} {puts $dbg_fout $buf; flush $dbg_fout}
 
1431
 
 
1432
                if {0 == [string compare "\r\n" $leftover]} {
 
1433
                    if {[regexp ^[set boundary](--)?\r?$ $buf dummy dashdash]} {
 
1434
                        if {$dashdash == "--"} {set eof 1}
 
1435
                        break
 
1436
                    }
 
1437
                }
 
1438
                if {[regexp (.*)\r$ $buf x data]} {
 
1439
                    puts -nonewline $fout $leftover$data
 
1440
                    set leftover "\r\n"
 
1441
                } else {
 
1442
                    puts -nonewline $fout $leftover$buf
 
1443
                    set leftover "\n"
 
1444
                }
 
1445
                if {[file size $foutname] > $_cgi(file,charlimit)} {
 
1446
                    error "File size exceeded.  Max file size allowed: $_cgi(file,charlimit)"
 
1447
                }
 
1448
            }
 
1449
 
 
1450
            close $fout
 
1451
            unset fout
 
1452
        } else {
 
1453
            # read the part into a variable
 
1454
            set val ""
 
1455
            set blanks 0
 
1456
            while {1} {
 
1457
                if {-1 == [gets $fin buf]} break
 
1458
                if {[info exists dbg_fout]} {puts $dbg_fout $buf; flush $dbg_fout}
 
1459
                if {[regexp ^[set boundary](--)?\r?$ $buf dummy dashdash]} {
 
1460
                    if {$dashdash == "--"} {set eof 1}
 
1461
                    break
 
1462
                }
 
1463
                if {0!=[string compare $val ""]} {
 
1464
                    append val \n
 
1465
                }
 
1466
                regexp (.*)\r$ $buf dummy buf
 
1467
                if {[info exists blanks]} {
 
1468
                    if {0!=[string compare $buf ""]} {
 
1469
                        if {$blanks} {
 
1470
                            append val [string repeat \n [incr blanks]]
 
1471
                        }
 
1472
                        unset blanks
 
1473
                    } else {
 
1474
                        incr blanks
 
1475
                    }
 
1476
                }
 
1477
                append val $buf
 
1478
            }
 
1479
            _cgi_set_uservar $varname $val
 
1480
        }
 
1481
        if {[info exists eof]} break
 
1482
    }
 
1483
    if {[info exists dbg_fout]} {close $dbg_fout}
 
1484
}
 
1485
 
 
1486
proc _cgi_input_multipart_binary {fin} {
 
1487
    global env _cgi _cgi_uservar _cgi_userfile
 
1488
 
 
1489
    log_user 0
 
1490
    set timeout -1
 
1491
 
 
1492
    cgi_debug -noprint {
 
1493
        # save file for debugging purposes
 
1494
        set dbg_filename [file join $_cgi(tmpdir) CGIdbg.[pid]]
 
1495
        set _cgi(input) $dbg_filename
 
1496
        spawn -open [open $dbg_filename w $_cgi(tmpperms)]
 
1497
        set dbg_sid $spawn_id
 
1498
    }
 
1499
    spawn -open $fin
 
1500
    set fin_sid $spawn_id
 
1501
    remove_nulls 0
 
1502
 
 
1503
    if {0} {
 
1504
        # dump input to screen
 
1505
        cgi_debug {
 
1506
            puts "<xmp>"
 
1507
            expect {
 
1508
                -i $fin_sid
 
1509
                -re ^\r {puts -nonewline "CR"; exp_continue}
 
1510
                -re ^\n {puts "NL"; exp_continue}
 
1511
                -re . {puts -nonewline $expect_out(buffer); exp_continue}
 
1512
            }
 
1513
            puts "</xmp>"
 
1514
            exit
 
1515
        }
 
1516
    }
 
1517
 
 
1518
    # figure out boundary
 
1519
    if {0==[regexp boundary=(.*) $env(CONTENT_TYPE) dummy boundary]} {
 
1520
        set _cgi(client_error) 1
 
1521
        error "Your browser failed to generate a \"boundary=\" definition in a multipart response (CONTENT_TYPE: $env(CONTENT_TYPE)).  Please upgrade (or fix) your browser."
 
1522
    }
 
1523
 
 
1524
    # make boundary into a legal regsub pattern by protecting #
 
1525
    # legal boundary characters include ()+.? (among others)
 
1526
    regsub -all "\\(" $boundary "\\(" boundary
 
1527
    regsub -all "\\)" $boundary "\\)" boundary
 
1528
    regsub -all "\\+" $boundary "\\+" boundary
 
1529
    regsub -all "\\." $boundary "\\." boundary
 
1530
    regsub -all "\\?" $boundary "\\?" boundary
 
1531
 
 
1532
    set boundary --$boundary
 
1533
    set linepat "(\[^\r]*)\r\n"
 
1534
 
 
1535
    # get first boundary line
 
1536
    expect {
 
1537
        -i $fin_sid
 
1538
        -re $linepat {
 
1539
            set buf $expect_out(1,string)
 
1540
            if {[info exists dbg_sid]} {send -i $dbg_sid -- $buf\n}
 
1541
        }
 
1542
        eof {
 
1543
            set _cgi(client_error) 1
 
1544
            error "Your browser failed to provide an initial boundary ($boundary) in a multipart response.  Please upgrade (or fix) your browser."
 
1545
        }
 
1546
    }
 
1547
 
 
1548
    set _cgi(file,filecount) 0
 
1549
 
 
1550
    while {1} {
 
1551
        # process Content-Disposition:
 
1552
        expect {
 
1553
            -i $fin_sid
 
1554
            -re $linepat {
 
1555
                set buf $expect_out(1,string)
 
1556
                if {[info exists dbg_sid]} {send -i $dbg_sid -- $buf\n}
 
1557
            }
 
1558
            eof break
 
1559
        }
 
1560
        catch {unset filename}
 
1561
        regexp {name="([^"]*)"} $buf dummy varname
 
1562
        if {0==[info exists varname]} {
 
1563
            set _cgi(client_error) 1
 
1564
            error "In response to a request for a multipart form, your browser generated a part header without a name field.  Please upgrade (or fix) your browser."
 
1565
        }           
 
1566
 
 
1567
        # Lame-o encoding (on Netscape at least) doesn't escape field
 
1568
        # delimiters (like quotes)!!  Since all we've ever seen is filename=
 
1569
        # at end of line, assuming nothing follows.  Sigh.
 
1570
        regexp {filename="(.*)"} $buf dummy filename
 
1571
 
 
1572
        # Skip remaining headers until blank line.
 
1573
        # Content-Type: can appear here.
 
1574
        set conttype ""
 
1575
        expect {
 
1576
            -i $fin_sid
 
1577
            -re $linepat {
 
1578
                set buf $expect_out(1,string)
 
1579
                if {[info exists dbg_sid]} {send -i $dbg_sid -- $buf\n}
 
1580
                if {0!=[string compare $buf ""]} exp_continue
 
1581
                regexp -nocase "^Content-Type:\[ \t]+(.*)\r" $buf x conttype
 
1582
            }
 
1583
            eof break
 
1584
        }
 
1585
 
 
1586
        if {[info exists filename]} {
 
1587
            if {$_cgi(file,filecount) > $_cgi(file,filelimit)} {
 
1588
                error "Too many files submitted.  Max files allowed: $_cgi(file,filelimit)"
 
1589
            }
 
1590
 
 
1591
            # read the part into a file
 
1592
            set foutname [file join $_cgi(tmpdir) CGI[pid].[incr _cgi(file,filecount)]]
 
1593
            spawn -open [open $foutname w $_cgi(tmpperms)]
 
1594
            set fout_sid $spawn_id
 
1595
 
 
1596
            _cgi_set_uservar $varname [list $foutname $filename $conttype]
 
1597
            set _cgi_userfile($varname) [list $foutname $filename $conttype]
 
1598
 
 
1599
            # This is tricky stuff - be very careful changing anything here!
 
1600
            # In theory, all we have to is record everything up to
 
1601
            # \r\n$boundary\r\n.  Unfortunately, we can't simply wait on
 
1602
            # such a pattern because the input can overflow any possible
 
1603
            # buffer we might choose.  We can't simply catch buffer_full
 
1604
            # because the boundary might straddle a buffer.  I doubt that
 
1605
            # doing my own buffering would be any faster than taking the
 
1606
            # approach I've done here.
 
1607
            #
 
1608
            # The code below basically implements a simple scanner that
 
1609
            # keeps track of whether it's seen crlfs or pieces of them.
 
1610
            # The idea is that we look for crlf pairs, separated by
 
1611
            # things that aren't crlfs (or pieces of them).  As we encounter
 
1612
            # things that aren't crlfs (or pieces of them), or when we decide
 
1613
            # they can't be, we mark them for output and resume scanning for
 
1614
            # new pairs.
 
1615
            #
 
1616
            # The scanner runs tolerably fast because the [...]+ pattern picks
 
1617
            # up most things.  The \r and \n are ^-anchored so the pattern
 
1618
            # match is pretty fast and these don't happen that often so the
 
1619
            # huge \n action is executed rarely (once per line on text files).
 
1620
            # The null pattern is, of course, only used when everything
 
1621
            # else fails.
 
1622
 
 
1623
            # crlf      == "\r\n" if we've seen one, else == ""
 
1624
            # cr        == "\r" if we JUST saw one, else == ""
 
1625
            #           Yes, strange, but so much more efficient
 
1626
            #           that I'm willing to sacrifice readability, sigh.
 
1627
            # buf       accumulated data between crlf pairs
 
1628
 
 
1629
            set buf ""
 
1630
            set cr ""
 
1631
            set crlf ""
 
1632
 
 
1633
            expect {
 
1634
                -i $fin_sid
 
1635
                -re "^\r" {
 
1636
                    if {$cr == "\r"} {
 
1637
                        append buf "\r"
 
1638
                    }
 
1639
                    set cr \r
 
1640
                    exp_continue
 
1641
                } -re "^\n" {
 
1642
                    if {$cr == "\r"} {
 
1643
                        if {$crlf == "\r\n"} {
 
1644
                            # do boundary test
 
1645
                            if {[regexp ^[set boundary](--)?$ $buf dummy dashdash]} {
 
1646
                                if {$dashdash == "--"} {
 
1647
                                    set eof 1
 
1648
                                }
 
1649
                            } else {
 
1650
                                # boundary test failed
 
1651
                                if {[info exists dbg_sid]} {send -i $dbg_sid -- \r\n$buf}
 
1652
                                send -i $fout_sid \r\n$buf ; set buf ""
 
1653
                                set cr ""
 
1654
                                exp_continue
 
1655
                            }
 
1656
                        } else {
 
1657
                            set crlf "\r\n"
 
1658
                            set cr ""
 
1659
                            if {[info exists dbg_sid]} {send -i $dbg_sid -- $buf}
 
1660
                            send -i $fout_sid -- $buf ; set buf ""
 
1661
                            exp_continue
 
1662
                        }
 
1663
                    } else {
 
1664
                        if {[info exists dbg_sid]} {send -i $dbg_sid -- $crlf$buf\n}
 
1665
                        send -i $fout_sid -- $crlf$buf\n ; set buf ""
 
1666
                        set crlf ""
 
1667
                        exp_continue
 
1668
                    }
 
1669
                } -re "\[^\r\n]+" {
 
1670
                    if {$cr == "\r"} {
 
1671
                        set buf $crlf$buf\r$expect_out(buffer)
 
1672
                        set crlf ""
 
1673
                        set cr ""
 
1674
                    } else {
 
1675
                        append buf $expect_out(buffer)
 
1676
                    }
 
1677
                    exp_continue
 
1678
                } null {
 
1679
                    if {[info exists dbg_sid]} {
 
1680
                        send -i $dbg_sid -- $crlf$buf$cr
 
1681
                        send -i $dbg_sid -null
 
1682
                    }
 
1683
                    send -i $fout_sid -- $crlf$buf$cr ; set buf ""
 
1684
                    send -i $fout_sid -null
 
1685
                    set cr ""
 
1686
                    set crlf ""
 
1687
                    exp_continue
 
1688
                } eof {
 
1689
                    set _cgi(client_error) 1
 
1690
                    error "Your browser failed to provide an ending boundary ($boundary) in a multipart response.  Please upgrade (or fix) your browser."
 
1691
                }
 
1692
            }
 
1693
            exp_close -i $fout_sid    ;# implicitly closes fout
 
1694
            exp_wait -i $fout_sid
 
1695
            unset fout_sid
 
1696
        } else {
 
1697
            # read the part into a variable
 
1698
            set val ""
 
1699
            expect {
 
1700
                -i $fin_sid
 
1701
                -re $linepat {
 
1702
                    set buf $expect_out(1,string)
 
1703
                    if {[info exists dbg_sid]} {send -i $dbg_sid -- $buf\n}
 
1704
                    if {[regexp ^[set boundary](--)?$ $buf dummy dashdash]} {
 
1705
                        if {$dashdash == "--"} {set eof 1}
 
1706
                    } else {
 
1707
                        regexp (.*)\r$ $buf dummy buf
 
1708
                        if {0!=[string compare $val ""]} {
 
1709
                            append val \n
 
1710
                        }
 
1711
                        append val $buf
 
1712
                        exp_continue
 
1713
                    }
 
1714
                }
 
1715
            }
 
1716
            _cgi_set_uservar $varname $val
 
1717
        }           
 
1718
        if {[info exists eof]} break
 
1719
    }
 
1720
    if {[info exists fout]} {
 
1721
        exp_close -i $dbg_sid
 
1722
        exp_wait -i $dbg_sid
 
1723
    }
 
1724
 
 
1725
    # no need to close fin, fin_sid, or dbg_sid
 
1726
}
 
1727
 
 
1728
# internal routine for defining user variables
 
1729
proc _cgi_set_uservar {varname val} {
 
1730
    global _cgi _cgi_uservar
 
1731
 
 
1732
    set exists [info exists _cgi_uservar($varname)]
 
1733
    set isList $exists
 
1734
    # anything we've seen before and is being set yet again necessarily
 
1735
    # has to be (or become a list)
 
1736
 
 
1737
    if {!$exists} {
 
1738
        lappend _cgi(uservars) $varname
 
1739
    }
 
1740
 
 
1741
    if {[regexp List$ $varname]} {
 
1742
        set isList 1
 
1743
    } elseif {$exists} {
 
1744
        # vars that we've seen before but aren't marked as lists
 
1745
        # need to be "listified" so we can do appends later
 
1746
        if {-1 == [lsearch $_cgi(uservars,autolist) $varname]} {
 
1747
            # remember that we've listified it
 
1748
            lappend _cgi(uservars,autolist) $varname
 
1749
            set _cgi_uservar($varname) [list $_cgi_uservar($varname)]
 
1750
        }
 
1751
    }
 
1752
    if {$isList} {
 
1753
        lappend _cgi_uservar($varname) $val
 
1754
    } else {
 
1755
        set _cgi_uservar($varname) $val
 
1756
    }
 
1757
}
 
1758
 
 
1759
# export named variable
 
1760
proc cgi_export {nameval} {
 
1761
    regexp "(\[^=]*)(=?)(.*)" $nameval dummy name q value
 
1762
 
 
1763
    if {$q != "="} {
 
1764
        set value [uplevel 1 set [list $name]]
 
1765
    }
 
1766
 
 
1767
    cgi_put "<input type=hidden name=\"$name\" value=[cgi_dquote_html $value]/>"
 
1768
}
 
1769
 
 
1770
proc cgi_export_cookie {name args} {
 
1771
    upvar 1 $name x
 
1772
    eval cgi_cookie_set [list $name=$x] $args
 
1773
}
 
1774
 
 
1775
# return list of variables available for import
 
1776
# Explicit list is used to keep items in order originally found in form.
 
1777
proc cgi_import_list {} {
 
1778
    global _cgi
 
1779
 
 
1780
    return $_cgi(uservars)
 
1781
}
 
1782
 
 
1783
# import named variable
 
1784
proc cgi_import {name} {
 
1785
    global _cgi_uservar
 
1786
    upvar 1 $name var
 
1787
 
 
1788
    set var $_cgi_uservar($name)
 
1789
}
 
1790
 
 
1791
proc cgi_import_as {name tclvar} {
 
1792
    global _cgi_uservar
 
1793
    upvar 1 $tclvar var
 
1794
 
 
1795
    set var $_cgi_uservar($name)
 
1796
}
 
1797
 
 
1798
# like cgi_import but if not available, try cookie
 
1799
proc cgi_import_cookie {name} {
 
1800
    global _cgi_uservar
 
1801
    upvar 1 $name var
 
1802
 
 
1803
    if {0==[catch {set var $_cgi_uservar($name)}]} return
 
1804
    set var [cgi_cookie_get $name]
 
1805
}
 
1806
 
 
1807
# like cgi_import but if not available, try cookie
 
1808
proc cgi_import_cookie_as {name tclvar} {
 
1809
    global _cgi_uservar
 
1810
    upvar 1 $tclvar var
 
1811
 
 
1812
    if {0==[catch {set var $_cgi_uservar($name)}]} return
 
1813
    set var [cgi_cookie_get $name]
 
1814
}
 
1815
 
 
1816
proc cgi_import_file {type name} {
 
1817
    global _cgi_userfile
 
1818
    upvar 1 $name var
 
1819
 
 
1820
    set var $_cgi_userfile($name)
 
1821
    switch -- $type {
 
1822
        "-server" {
 
1823
            lindex $var 0
 
1824
        } "-client" {
 
1825
            lindex $var 1
 
1826
        } "-type" {
 
1827
            lindex $var 2
 
1828
        }
 
1829
    }
 
1830
}
 
1831
 
 
1832
# deprecated, use cgi_import_file
 
1833
proc cgi_import_filename {type name} {
 
1834
    global _cgi_userfile
 
1835
    upvar 1 $name var
 
1836
 
 
1837
    set var $_cgi_userfile($name)
 
1838
    if {$type == "-server" || $type == "-local"} {
 
1839
        # -local is deprecated
 
1840
        lindex $var 0
 
1841
    } else {
 
1842
        lindex $var 1
 
1843
    }
 
1844
}
 
1845
 
 
1846
# set the urlencoding
 
1847
proc cgi_urlencoding {{encoding ""}} {
 
1848
    global _cgi 
 
1849
    
 
1850
    set result [expr {[info exists _cgi(queryencoding)]
 
1851
                      ? $_cgi(queryencoding)
 
1852
                      : ""}]
 
1853
 
 
1854
    # check if the encoding is available 
 
1855
    if {[info tclversion] >= 8.1
 
1856
        && [lsearch -exact [encoding names] $encoding] != -1 } {        
 
1857
        set _cgi(queryencoding) $encoding
 
1858
    }
 
1859
 
 
1860
    return $result
 
1861
}
 
1862
 
 
1863
##################################################
 
1864
# button support
 
1865
##################################################
 
1866
 
 
1867
# not sure about arg handling, do we need to support "name="?
 
1868
proc cgi_button {value args} {
 
1869
    cgi_put "<input type=button value=[cgi_dquote_html $value]"
 
1870
    foreach a $args {
 
1871
        if {[regexp "^onClick=(.*)" $a dummy str]} {
 
1872
            cgi_put " onClick=\"$str\""
 
1873
        } else {
 
1874
            cgi_put " $a"
 
1875
        }
 
1876
    }
 
1877
    cgi_put "/>"
 
1878
}
 
1879
 
 
1880
# Derive a button from a link predefined by cgi_link
 
1881
proc cgi_button_link {args} {
 
1882
    global _cgi_link
 
1883
 
 
1884
    set tag [lindex $args 0]
 
1885
    if {[llength $args] == 2} {
 
1886
        set label [lindex $args end]
 
1887
    } else {
 
1888
        set label $_cgi_link($tag,label)
 
1889
    }
 
1890
    
 
1891
    cgi_button $label onClick=$_cgi_link($tag,url)
 
1892
}
 
1893
 
 
1894
proc cgi_submit_button {{nameval {=Submit Query}} args} {
 
1895
    regexp "(\[^=]*)=(.*)" $nameval dummy name value
 
1896
    cgi_put "<input type=submit"
 
1897
    if {0!=[string compare "" $name]} {
 
1898
        cgi_put " name=\"$name\""
 
1899
    }
 
1900
    cgi_put " value=[cgi_dquote_html $value]"
 
1901
    foreach a $args {
 
1902
        if {[regexp "^onClick=(.*)" $a dummy str]} {
 
1903
            cgi_put " onClick=\"$str\""
 
1904
        } else {
 
1905
            cgi_put " $a"
 
1906
        }
 
1907
    }
 
1908
    cgi_put "/>"
 
1909
}
 
1910
 
 
1911
 
 
1912
proc cgi_reset_button {{value Reset} args} {
 
1913
    cgi_put "<input type=reset value=[cgi_dquote_html $value]"
 
1914
 
 
1915
    foreach a $args {
 
1916
        if {[regexp "^onClick=(.*)" $a dummy str]} {
 
1917
            cgi_put " onClick=\"$str\""
 
1918
        } else {
 
1919
            cgi_put " $a"
 
1920
        }
 
1921
    }
 
1922
    cgi_put "/>"
 
1923
}
 
1924
 
 
1925
proc cgi_radio_button {nameval args} {
 
1926
    regexp "(\[^=]*)=(.*)" $nameval dummy name value
 
1927
 
 
1928
    cgi_put "<input type=radio name=\"$name\" value=[cgi_dquote_html $value]"
 
1929
 
 
1930
    foreach a $args {
 
1931
        if {[regexp "^checked_if_equal=(.*)" $a dummy default]} {
 
1932
            if {0==[string compare $default $value]} {
 
1933
                cgi_put " checked"
 
1934
            }
 
1935
        } elseif {[regexp "^checked=(.*)" $a dummy checked]} {
 
1936
            # test explicitly to avoid forcing user eval
 
1937
            if {$checked} {
 
1938
                cgi_put " checked"
 
1939
            }
 
1940
        } elseif {[regexp "^onClick=(.*)" $a dummy str]} {
 
1941
            cgi_put " onClick=\"$str\""
 
1942
        } else {
 
1943
            cgi_put " $a"
 
1944
        }
 
1945
    }
 
1946
    cgi_put "/>"
 
1947
}
 
1948
 
 
1949
proc cgi_image_button {nameval args} {
 
1950
    regexp "(\[^=]*)=(.*)" $nameval dummy name value
 
1951
    cgi_put "<input type=image"
 
1952
    if {0!=[string compare "" $name]} {
 
1953
        cgi_put " name=\"$name\""
 
1954
    }
 
1955
    cgi_put " src=\"$value\""
 
1956
    foreach a $args {
 
1957
        if {[regexp "^onClick=(.*)" $a dummy str]} {
 
1958
            cgi_put " onClick=\"$str\""
 
1959
        } else {
 
1960
            cgi_put " $a"
 
1961
        }
 
1962
    }
 
1963
    cgi_put "/>"
 
1964
}
 
1965
 
 
1966
# map/area implement client-side image maps
 
1967
proc cgi_map {name cmd} {
 
1968
    cgi_put "<map name=\"$name\">"
 
1969
    _cgi_close_proc_push "cgi_put </map>"
 
1970
 
 
1971
    uplevel 1 $cmd
 
1972
    _cgi_close_proc
 
1973
}
 
1974
 
 
1975
proc cgi_area {args} {
 
1976
    cgi_put "<area"
 
1977
    foreach a $args {
 
1978
        if {[regexp "^(coords|shape|href|target|onMouseOut|alt)=(.*)" $a dummy attr str]} {
 
1979
            cgi_put " $attr=\"$str\""
 
1980
        } else {
 
1981
            cgi_put " $a"
 
1982
        }
 
1983
    }
 
1984
    cgi_put "/>"
 
1985
}
 
1986
 
 
1987
##################################################
 
1988
# checkbox support
 
1989
##################################################
 
1990
 
 
1991
proc cgi_checkbox {nameval args} {
 
1992
    regexp "(\[^=]*)(=?)(.*)" $nameval dummy name q value
 
1993
    cgi_put "<input type=checkbox name=\"$name\""
 
1994
 
 
1995
    if {0!=[string compare "" $value]} {
 
1996
        cgi_put " value=[cgi_dquote_html $value]"
 
1997
    }
 
1998
 
 
1999
    foreach a $args {
 
2000
        if {[regexp "^checked_if_equal=(.*)" $a dummy default]} {
 
2001
            if {0==[string compare $default $value]} {
 
2002
                cgi_put " checked"
 
2003
            }
 
2004
        } elseif {[regexp "^checked=(.*)" $a dummy checked]} {
 
2005
            # test explicitly to avoid forcing user eval
 
2006
            if {$checked} {
 
2007
                cgi_put " checked"
 
2008
            }
 
2009
        } elseif {[regexp "^onClick=(.*)" $a dummy str]} {
 
2010
            cgi_put " onClick=\"$str\""
 
2011
        } else {
 
2012
            cgi_put " $a"
 
2013
        }
 
2014
    }
 
2015
    cgi_put "/>"
 
2016
}
 
2017
 
 
2018
##################################################
 
2019
# textentry support
 
2020
##################################################
 
2021
 
 
2022
proc cgi_text {nameval args} {
 
2023
    regexp "(\[^=]*)(=?)(.*)" $nameval dummy name q value
 
2024
 
 
2025
    cgi_put "<input name=\"$name\""
 
2026
 
 
2027
    if {$q != "="} {
 
2028
        set value [uplevel 1 set [list $name]]
 
2029
    }
 
2030
    cgi_put " value=[cgi_dquote_html $value]"
 
2031
 
 
2032
    foreach a $args {
 
2033
        if {[regexp "^on(Select|Focus|Blur|Change)=(.*)" $a dummy event str]} {
 
2034
            cgi_put " on$event=\"$str\""
 
2035
        } else {
 
2036
            cgi_put " $a"
 
2037
        }
 
2038
    }
 
2039
    cgi_put "/>"
 
2040
}
 
2041
 
 
2042
##################################################
 
2043
# textarea support
 
2044
##################################################
 
2045
 
 
2046
proc cgi_textarea {nameval args} {
 
2047
    regexp "(\[^=]*)(=?)(.*)" $nameval dummy name q value
 
2048
 
 
2049
    cgi_put "<textarea name=\"$name\""
 
2050
    foreach a $args {
 
2051
        if {[regexp "^on(Select|Focus|Blur|Change)=(.*)" $a dummy event str]} {
 
2052
            cgi_put " on$event=\"$str\""
 
2053
        } else {
 
2054
            cgi_put " $a"
 
2055
        }
 
2056
    }
 
2057
    cgi_put ">"
 
2058
 
 
2059
    if {$q != "="} {
 
2060
        set value [uplevel 1 set [list $name]]
 
2061
    }
 
2062
    cgi_put "[cgi_quote_html $value]</textarea>"
 
2063
}
 
2064
 
 
2065
##################################################
 
2066
# file upload support
 
2067
##################################################
 
2068
 
 
2069
# for this to work, pass enctype=multipart/form-data to cgi_form
 
2070
proc cgi_file_button {name args} {
 
2071
    global _cgi
 
2072
    if {[info exists _cgi(formtype)] && ("multipart/form-data" != $_cgi(form,enctype))} {
 
2073
        error "cgi_file_button requires that cgi_form have the argument enctype=multipart/form-data"
 
2074
    }
 
2075
    cgi_put "<input type=file name=\"$name\"[_cgi_list_to_string $args]/>"
 
2076
}
 
2077
 
 
2078
# establish a per-file limit for uploads
 
2079
 
 
2080
proc cgi_file_limit {files chars} {
 
2081
    global _cgi
 
2082
 
 
2083
    set _cgi(file,filelimit) $files
 
2084
    set _cgi(file,charlimit) $chars
 
2085
}
 
2086
 
 
2087
##################################################
 
2088
# select support
 
2089
##################################################
 
2090
 
 
2091
proc cgi_select {name args} {
 
2092
    cgi_put "<select name=\"$name\""
 
2093
    _cgi_close_proc_push "cgi_put </select>"
 
2094
    foreach a [lrange $args 0 [expr [llength $args]-2]] {
 
2095
        if {[regexp "^on(Focus|Blur|Change)=(.*)" $a dummy event str]} {
 
2096
            cgi_put " on$event=\"$str\""
 
2097
        } else {
 
2098
            if {0==[string compare multiple $a]} {
 
2099
                ;# sanity check
 
2100
                if {![regexp "List$" $name]} {
 
2101
                    cgi_puts ">" ;# prevent error from being absorbed
 
2102
                    error "When selecting multiple options, select variable \
 
2103
                            must end in \"List\" to allow the value to be \
 
2104
                            recognized as a list when it is processed later."
 
2105
                }
 
2106
            }
 
2107
            cgi_put " $a"
 
2108
        }
 
2109
    }
 
2110
    cgi_put ">"
 
2111
    uplevel 1 [lindex $args end]
 
2112
    _cgi_close_proc
 
2113
}
 
2114
 
 
2115
proc cgi_option {o args} {
 
2116
    cgi_put "<option"
 
2117
    set value $o
 
2118
    set selected 0
 
2119
    foreach a $args {
 
2120
        if {[regexp "^selected_if_equal=(.*)" $a dummy selected_if_equal]} {
 
2121
        } elseif {[regexp "^value=(.*)" $a dummy value]} {
 
2122
            cgi_put " value=[cgi_dquote_html $value]"
 
2123
        } else {
 
2124
            cgi_put " $a"
 
2125
        }
 
2126
    }
 
2127
    if {[info exists selected_if_equal]} {
 
2128
        if {0 == [string compare $selected_if_equal $value]} {
 
2129
            cgi_put " selected"
 
2130
        }
 
2131
    }
 
2132
    cgi_puts ">[cgi_quote_html $o]</option>"
 
2133
}
 
2134
 
 
2135
##################################################
 
2136
# plug-in support
 
2137
##################################################
 
2138
 
 
2139
proc cgi_embed {src wh args} {
 
2140
    regexp (.*)x(.*) $wh dummy width height
 
2141
    cgi_put "<embed src=[cgi_dquote_html $src] width=\"$width\" height=\"$height\""
 
2142
    foreach a $args {
 
2143
        if {[regexp "^palette=(.*)" $a dummy str]} {
 
2144
            cgi_put " palette=\"$str\""
 
2145
        } elseif {[regexp -- "-quote" $a]} {
 
2146
            set quote 1
 
2147
        } else {
 
2148
            if {[info exists quote]} {
 
2149
                regexp "(\[^=]*)=(.*)" $a dummy var val
 
2150
                cgi_put " var=[cgi_dquote_html $var]"
 
2151
            } else {
 
2152
                cgi_put " $a"
 
2153
            }
 
2154
        }
 
2155
    }
 
2156
    cgi_put "/>"
 
2157
}
 
2158
 
 
2159
##################################################
 
2160
# mail support
 
2161
##################################################
 
2162
 
 
2163
# mail to/from the service itself
 
2164
proc cgi_mail_addr {args} {
 
2165
    global _cgi
 
2166
 
 
2167
    if {[llength $args]} {
 
2168
        set _cgi(email) [lindex $args 0]
 
2169
    }
 
2170
    return $_cgi(email)
 
2171
}
 
2172
 
 
2173
proc cgi_mail_start {to} {
 
2174
    global _cgi
 
2175
 
 
2176
    set _cgi(mailfile) [file join $_cgi(tmpdir) cgimail.[pid]]
 
2177
    set _cgi(mailfid) [open $_cgi(mailfile) w+]
 
2178
    set _cgi(mailto) $to
 
2179
 
 
2180
    # mail is actually sent by "nobody".  To force bounce messages
 
2181
    # back to us, override the default return-path.
 
2182
    cgi_mail_add "Return-Path: <$_cgi(email)>"
 
2183
    cgi_mail_add "From: [cgi_name] <$_cgi(email)>"
 
2184
    cgi_mail_add "To: $to"
 
2185
}
 
2186
 
 
2187
# add another line to outgoing mail
 
2188
# if no arg, add a blank line
 
2189
proc cgi_mail_add {{arg {}}} {
 
2190
    global _cgi
 
2191
 
 
2192
    puts $_cgi(mailfid) $arg
 
2193
}       
 
2194
 
 
2195
# end the outgoing mail and send it
 
2196
proc cgi_mail_end {} {
 
2197
    global _cgi
 
2198
 
 
2199
    flush $_cgi(mailfid)
 
2200
 
 
2201
    foreach sendmail in $_cgi(sendmail) {
 
2202
        if {[file executable $sendmail]} {
 
2203
            exec $sendmail -t -odb < $_cgi(mailfile)
 
2204
            # Explanation:
 
2205
            # -t   means: pick up recipient from body
 
2206
            # -odb means: deliver in background
 
2207
            # note: bogus local address cause sendmail to fail immediately
 
2208
            set sent 1
 
2209
        }
 
2210
    }
 
2211
 
 
2212
    if {0==[info exists sent]} {
 
2213
        # fallback for sites without sendmail
 
2214
 
 
2215
        if {0==[info exists _cgi(mail_relay)]} {
 
2216
            regexp "@(.*)" $_cgi(mailto) dummy _cgi(mail_relay)
 
2217
        }
 
2218
 
 
2219
        set s [socket $_cgi(mail_relay) 25]
 
2220
        gets $s answer
 
2221
        if {[lindex $answer 0] != 220} {error $answer} 
 
2222
 
 
2223
        puts $s "HELO [info host]";flush $s
 
2224
        gets $s answer
 
2225
        if {[lindex $answer 0] != 250} {error $answer}  
 
2226
 
 
2227
        puts $s "MAIL FROM:<$_cgi(email)>";flush $s
 
2228
        gets $s answer
 
2229
        if {[lindex $answer 0] != 250} {error $answer}  
 
2230
 
 
2231
        puts $s "RCPT TO:<$_cgi(mailto)>";flush $s
 
2232
        gets $s answer
 
2233
        if {[lindex $answer 0] != 250} {error $answer}  
 
2234
 
 
2235
        puts $s DATA;flush $s
 
2236
        gets $s answer
 
2237
        if {[lindex $answer 0] != 354} {error $answer}  
 
2238
 
 
2239
        seek $_cgi(mailfid) 0 start
 
2240
        puts $s [read $_cgi(mailfid)];flush $s
 
2241
        puts $s .;flush $s
 
2242
        gets $s answer
 
2243
        if {[lindex $answer 0] != 250} {error $answer}  
 
2244
 
 
2245
        close $s
 
2246
    }
 
2247
    close $_cgi(mailfid)
 
2248
    file delete -force $_cgi(mailfile)
 
2249
}
 
2250
 
 
2251
proc cgi_mail_relay {host} {
 
2252
    global _cgi
 
2253
 
 
2254
    set _cgi(mail_relay) $host
 
2255
}
 
2256
 
 
2257
proc cgi_sendmail {path} {
 
2258
    global _cgi
 
2259
 
 
2260
    set _cgi(sendmail) $path
 
2261
}
 
2262
 
 
2263
##################################################
 
2264
# cookie support
 
2265
##################################################
 
2266
 
 
2267
# calls to cookie_set look like this:
 
2268
#   cgi_cookie_set user=don domain=nist.gov expires=never
 
2269
#   cgi_cookie_set user=don domain=nist.gov expires=now
 
2270
#   cgi_cookie_set user=don domain=nist.gov expires=...actual date...
 
2271
 
 
2272
proc cgi_cookie_set {nameval args} {
 
2273
    global _cgi
 
2274
 
 
2275
    if {![info exists _cgi(http_head_in_progress)]} {
 
2276
        error "Cookies must be set from within cgi_http_head."
 
2277
    }
 
2278
    cgi_puts -nonewline "Set-Cookie: [cgi_cookie_encode $nameval];"
 
2279
 
 
2280
    foreach a $args {
 
2281
        if {[regexp "^expires=(.*)" $a dummy expiration]} {
 
2282
            if {0==[string compare $expiration "never"]} {
 
2283
                set expiration "Friday, 11-Jan-2038 23:59:59 GMT"
 
2284
            } elseif {0==[string compare $expiration "now"]} {
 
2285
                set expiration "Friday, 31-Dec-1990 23:59:59 GMT"
 
2286
            }
 
2287
            cgi_puts -nonewline " expires=$expiration;"
 
2288
        } elseif {[regexp "^(domain|path)=(.*)" $a dummy attr str]} {
 
2289
            cgi_puts -nonewline " $attr=[cgi_cookie_encode $str];"
 
2290
        } elseif {[regexp "^secure$" $a]} {
 
2291
            cgi_puts -nonewline " secure;"
 
2292
        }
 
2293
    }
 
2294
    cgi_puts ""
 
2295
}
 
2296
 
 
2297
# return list of cookies available for import
 
2298
proc cgi_cookie_list {} {
 
2299
    global _cgi_cookie
 
2300
 
 
2301
    array names _cgi_cookie
 
2302
}
 
2303
 
 
2304
proc cgi_cookie_get {args} {
 
2305
    global _cgi_cookie
 
2306
 
 
2307
    set all 0
 
2308
 
 
2309
    set flag [lindex $args 0]
 
2310
    if {$flag == "-all"} {
 
2311
        set args [lrange $args 1 end]
 
2312
        set all 1
 
2313
    }
 
2314
    set name [lindex $args 0]
 
2315
 
 
2316
    if {$all} {
 
2317
        global _cgi_cookie_shadowed
 
2318
 
 
2319
        if {[info exists _cgi_cookie_shadowed($name)]} {
 
2320
            return [concat $_cgi_cookie($name) $_cgi_cookie_shadowed($name)]
 
2321
        } else {
 
2322
            return [concat $_cgi_cookie($name)]
 
2323
        }
 
2324
    }
 
2325
    return $_cgi_cookie($name)
 
2326
}
 
2327
 
 
2328
proc cgi_cookie_encode {in} {
 
2329
    regsub -all " " $in "+" in
 
2330
    regsub -all "%" $in "%25" in   ;# must preceed other subs that produce %
 
2331
    regsub -all ";" $in "%3B" in
 
2332
    regsub -all "," $in "%2C" in
 
2333
    regsub -all "\n" $in "%0D%0A" in
 
2334
    return $in
 
2335
}
 
2336
 
 
2337
##################################################
 
2338
# table support
 
2339
##################################################
 
2340
 
 
2341
proc cgi_table {args} {
 
2342
    cgi_put "<table"
 
2343
    _cgi_close_proc_push "cgi_put </table>"
 
2344
 
 
2345
    if {[llength $args]} {
 
2346
        cgi_put "[_cgi_lrange $args 0 [expr [llength $args]-2]]"
 
2347
    }
 
2348
    cgi_put ">"
 
2349
    uplevel 1 [lindex $args end]
 
2350
    _cgi_close_proc
 
2351
}
 
2352
 
 
2353
proc cgi_caption {args} {
 
2354
    cgi_put "<caption"
 
2355
    _cgi_close_proc_push "cgi_put </caption>"
 
2356
 
 
2357
    if {[llength $args]} {
 
2358
        cgi_put "[_cgi_lrange $args 0 [expr [llength $args]-2]]"
 
2359
    }
 
2360
    cgi_put ">"
 
2361
    uplevel 1 [lindex $args end]
 
2362
    _cgi_close_proc
 
2363
}
 
2364
 
 
2365
proc cgi_table_row {args} {
 
2366
    cgi_put "<tr"
 
2367
    _cgi_close_proc_push "cgi_put </tr>"
 
2368
    if {[llength $args]} {
 
2369
        cgi_put "[_cgi_lrange $args 0 [expr [llength $args]-2]]"
 
2370
    }
 
2371
    cgi_put ">"
 
2372
    uplevel 1 [lindex $args end]
 
2373
    _cgi_close_proc
 
2374
}
 
2375
 
 
2376
# like table_row but without eval
 
2377
proc cgi_tr {args} {
 
2378
    cgi_put <tr
 
2379
    if {[llength $args] > 1} {
 
2380
        cgi_put "[_cgi_lrange $args 0 [expr [llength $args]-2]]"
 
2381
    }
 
2382
    cgi_put ">"
 
2383
    foreach i [lindex $args end] {
 
2384
        cgi_td $i
 
2385
    }
 
2386
    cgi_put </tr>
 
2387
}
 
2388
 
 
2389
proc cgi_table_head {args} {
 
2390
    cgi_put "<th"
 
2391
    _cgi_close_proc_push "cgi_put </th>"
 
2392
 
 
2393
    if {[llength $args]} {
 
2394
        cgi_put "[_cgi_lrange $args 0 [expr [llength $args]-2]]"
 
2395
    }
 
2396
    cgi_put ">"
 
2397
    uplevel 1 [lindex $args end]
 
2398
    _cgi_close_proc
 
2399
}
 
2400
 
 
2401
# like table_head but without eval
 
2402
proc cgi_th {args} {
 
2403
    cgi_put "<th"
 
2404
 
 
2405
    if {[llength $args] > 1} {
 
2406
        cgi_put "[_cgi_lrange $args 0 [expr [llength $args]-2]]"
 
2407
    }
 
2408
    cgi_put ">[lindex $args end]</th>"
 
2409
}
 
2410
 
 
2411
proc cgi_table_data {args} {
 
2412
    cgi_put "<td"
 
2413
    _cgi_close_proc_push "cgi_put </td>"
 
2414
 
 
2415
    if {[llength $args]} {
 
2416
        cgi_put "[_cgi_lrange $args 0 [expr [llength $args]-2]]"
 
2417
    }
 
2418
    cgi_put ">"
 
2419
    uplevel 1 [lindex $args end]
 
2420
    _cgi_close_proc
 
2421
}
 
2422
 
 
2423
# like table_data but without eval
 
2424
proc cgi_td {args} {
 
2425
    cgi_put "<td"
 
2426
 
 
2427
    if {[llength $args] > 1} {
 
2428
        cgi_put "[_cgi_lrange $args 0 [expr [llength $args]-2]]"
 
2429
    }
 
2430
    cgi_put ">[lindex $args end]</td>"
 
2431
}
 
2432
 
 
2433
##################################################
 
2434
# stylesheets - not yet documented
 
2435
##################################################
 
2436
 
 
2437
proc cgi_stylesheet {href} {
 
2438
    cgi_puts "<link rel=stylesheet href=\"$href\" type=\"text/css\"/>"
 
2439
}
 
2440
 
 
2441
proc cgi_span {args} {
 
2442
    set buf "<span"
 
2443
    foreach a [lrange $args 0 [expr [llength $args]-2]] {
 
2444
        if {[regexp "style=(.*)" $a dummy str]} {
 
2445
            append buf " style=\"$str\""
 
2446
        } elseif {[regexp "class=(.*)" $a dummy str]} {
 
2447
            append buf " class=\"$str\""
 
2448
        } else {
 
2449
            append buf " $a"
 
2450
        }
 
2451
    }
 
2452
    return "$buf>[lindex $args end]</span>"
 
2453
}
 
2454
 
 
2455
##################################################
 
2456
# frames
 
2457
##################################################
 
2458
 
 
2459
proc cgi_frameset {args} {
 
2460
    cgi_head ;# force it out, just in case none
 
2461
 
 
2462
    cgi_put "<frameset"
 
2463
    _cgi_close_proc_push "cgi_puts </frameset>"
 
2464
 
 
2465
    foreach a [lrange $args 0 [expr [llength $args]-2]] {
 
2466
        if {[regexp "^(rows|cols|onUnload|onLoad|onBlur)=(.*)" $a dummy attr str]} {
 
2467
            cgi_put " $attr=\"$str\""
 
2468
        } else {
 
2469
            cgi_put " $a"
 
2470
        }
 
2471
    }
 
2472
    cgi_puts ">"
 
2473
    uplevel 1 [lindex $args end]
 
2474
    _cgi_close_proc
 
2475
}
 
2476
 
 
2477
proc cgi_frame {namesrc args} {
 
2478
    cgi_put "<frame"
 
2479
 
 
2480
    regexp "(\[^=]*)(=?)(.*)" $namesrc dummy name q src
 
2481
 
 
2482
    if {$name != ""} {
 
2483
        cgi_put " name=\"$name\""
 
2484
    }
 
2485
 
 
2486
    if {$src != ""} {
 
2487
        cgi_put " src=\"$src\""
 
2488
    }
 
2489
 
 
2490
    foreach a $args {
 
2491
        if {[regexp "^(marginwidth|marginheight|scrolling|onFocus)=(.*)" $a dummy attr str]} {
 
2492
            cgi_put " $attr=\"$str\""
 
2493
        } else {
 
2494
            cgi_put " $a"
 
2495
        }
 
2496
    }
 
2497
    cgi_puts "/>"
 
2498
}
 
2499
 
 
2500
proc cgi_noframes {args} {
 
2501
    cgi_puts "<noframes>"
 
2502
    _cgi_close_proc_push "cgi_puts </noframes>"
 
2503
    uplevel 1 [lindex $args end]
 
2504
    _cgi_close_proc
 
2505
}
 
2506
 
 
2507
##################################################
 
2508
# admin support
 
2509
##################################################
 
2510
 
 
2511
# mail address of the administrator
 
2512
proc cgi_admin_mail_addr {args} {
 
2513
    global _cgi
 
2514
 
 
2515
    if {[llength $args]} {
 
2516
        set _cgi(admin_email) [lindex $args 0]
 
2517
    }
 
2518
    return $_cgi(admin_email)
 
2519
}
 
2520
 
 
2521
##################################################
 
2522
# if possible, make each cmd available without cgi_ prefix
 
2523
##################################################
 
2524
 
 
2525
if {[info tclversion] >= 7.5} {
 
2526
    foreach _cgi(old) [info procs cgi_*] {
 
2527
        regexp "^cgi_(.*)" $_cgi(old) _cgi(dummy) _cgi(new)
 
2528
        if {[llength [info commands $_cgi(new)]]} continue
 
2529
        interp alias {} $_cgi(new) {} $_cgi(old)
 
2530
    }
 
2531
} else {
 
2532
    foreach _cgi(old) [info procs cgi_*] {
 
2533
        regexp "^cgi_(.*)" $_cgi(old) _cgi(dummy) _cgi(new)
 
2534
        if {[llength [info commands $_cgi(new)]]} continue
 
2535
        proc $_cgi(new) {args} "uplevel 1 $_cgi(old) \$args"
 
2536
    }
 
2537
}
 
2538
 
 
2539
##################################################
 
2540
# internal utilities
 
2541
##################################################
 
2542
 
 
2543
# undo Tcl's quoting due to list protection
 
2544
# This leaves a space at the beginning if the string is non-null
 
2545
# but this is always desirable in the HTML context in which it is called
 
2546
# and the resulting HTML looks more readable.
 
2547
# (It makes the Tcl callers a little less readable - however, there aren't
 
2548
# more than a handful and they're all right here, so we'll live with it.)
 
2549
proc _cgi_list_to_string {list} {
 
2550
    set string ""
 
2551
    foreach l $list {
 
2552
        append string " $l"
 
2553
    }
 
2554
    # remove first space if possible
 
2555
    # regexp "^ ?(.*)" $string dummy string
 
2556
    return $string
 
2557
}
 
2558
 
 
2559
# do lrange but return as string
 
2560
# needed for stuff like: cgi_puts "[_cgi_lrange $args ...]
 
2561
# Like _cgi_list_to_string, also returns string with initial blank if non-null
 
2562
proc _cgi_lrange {list i1 i2} {
 
2563
    _cgi_list_to_string [lrange $list $i1 $i2]
 
2564
}
 
2565
 
 
2566
##################################################
 
2567
# temporary file procedures
 
2568
##################################################
 
2569
 
 
2570
# set appropriate temporary file modes
 
2571
proc cgi_tmpfile_permissions {{mode ""}} {
 
2572
    global _cgi
 
2573
 
 
2574
    if {[string length $mode]} {
 
2575
        set _cgi(tmpperms) $mode
 
2576
    }
 
2577
 
 
2578
    return $_cgi(tmpperms)
 
2579
}
 
2580
 
 
2581
##################################################
 
2582
# user-defined procedures
 
2583
##################################################
 
2584
 
 
2585
# User-defined procedure called immediately after <body>
 
2586
# Good mechanism for controlling things such as if all of your pages
 
2587
# start with the same graphic or other boilerplate.
 
2588
proc app_body_start {} {}
 
2589
 
 
2590
# User-defined procedure called just before </body>
 
2591
# Good place to generate signature lines, last-updated-by, etc.
 
2592
proc app_body_end {} {}
 
2593
 
 
2594
proc cgi_puts {args} {
 
2595
    eval puts $args
 
2596
}
 
2597
 
 
2598
# User-defined procedure to generate DOCTYPE declaration
 
2599
proc cgi_doctype {} {}
 
2600
 
 
2601
##################################################
 
2602
# do some initialization
 
2603
##################################################
 
2604
 
 
2605
# cgi_init initializes to a known state.
 
2606
 
 
2607
proc cgi_init {} {
 
2608
    global _cgi
 
2609
    unset _cgi
 
2610
 
 
2611
    # set explicitly for speed
 
2612
    set _cgi(debug) -off
 
2613
    set _cgi(buffer_nl) "\n"
 
2614
 
 
2615
    cgi_name ""
 
2616
    cgi_root ""
 
2617
    cgi_body_args ""
 
2618
    cgi_file_limit 10 100000000
 
2619
 
 
2620
    if {[info tclversion] >= 8.1} {
 
2621
        # set initial urlencoding
 
2622
        if { [lsearch -exact [encoding names] "utf-8"] != -1} {
 
2623
            cgi_urlencoding "utf-8"
 
2624
        } else {
 
2625
            cgi_urlencoding [encoding system]
 
2626
        }
 
2627
    }
 
2628
 
 
2629
    # email addr of person responsible for this service
 
2630
    cgi_admin_mail_addr "root"  ;# you should override this!
 
2631
 
 
2632
    # most services won't have an actual email addr
 
2633
    cgi_mail_addr "CGI script - do not reply"
 
2634
}
 
2635
cgi_init
 
2636
 
 
2637
# deduce tmp directory
 
2638
switch $tcl_platform(platform) {
 
2639
    unix {
 
2640
        set _cgi(tmpdir) /tmp
 
2641
        set _cgi(tmpperms)      0644
 
2642
        set _cgi(sendmail) [list /usr/lib/sendmail /usr/sbin/sendmail]
 
2643
    } macintosh {
 
2644
        set _cgi(tmpdir) [pwd]
 
2645
        set _cgi(tmpperms)      {}
 
2646
        set _cgi(sendmail) {}
 
2647
    } default {
 
2648
        set _cgi(tmpdir) [pwd]
 
2649
        catch {set _cgi(tmpdir) $env(TMP)}
 
2650
        catch {set _cgi(tmpdir) $env(TEMP)}
 
2651
        set _cgi(tmpperms)      {}
 
2652
        set _cgi(sendmail) {}
 
2653
    }
 
2654
}
 
2655
 
 
2656
# regexp for matching attr=val
 
2657
set _cgi(attr,regexp) "^(\[^=]*)=(\[^\"].*)"
 
2658
 
 
2659
package provide cgi @CGI_VERSION_FULL@