File: hv3_polipo.tcl

package info (click to toggle)
tk-html3 3.0~fossil20110109-6
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 6,644 kB
  • ctags: 5,882
  • sloc: ansic: 48,994; tcl: 26,030; sh: 1,190; yacc: 161; makefile: 24
file content (374 lines) | stat: -rw-r--r-- 10,182 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374

namespace eval hv3 { set {version($Id: hv3_polipo.tcl,v 1.17 2008/02/09 18:14:20 danielk1977 Exp $)} 1 }

# This file contains code to control a single instance of the 
# external program "hv3_polipo" that may be optionally used by
# hv3 as a web proxy.
#
# hv3_polipo is a slightly modified version of "polipo" by 
# (TODO: Juliusz full name) (TODO: polipo website).
#

namespace eval ::hv3::polipo {
  variable g

  # The port to listen on.
  set g(port) 8123

  # The path to the polipo binary.
  set g(binary) ""

  # The file-handle to the polipo's stdout. 
  set g(filehandle) ""

  # The file-handle to the polipo keepalive connection
  set g(keepalive) ""

  # Text of the log.
  set g(log) ""

  proc print {string} {
    variable g
    append g(log) $string
    if {[winfo exists .polipo]} {
      .polipo.text insert end $string
    }
  }

  # Initialise this sub-system. This proc sets the g(binary) variable -
  # the path to "polipo".
  #
  proc init {} {
    variable g

    set dir [file dirname [file normalize [info script]]]
    set prog hv3_polipo
    if {$::tcl_platform(platform) eq "windows"} {
      set prog hv3_polipo.exe
    }

    set locations [list [file dirname $dir] [pwd]]
    catch {
      set locations [concat $locations [split $::env(PATH) :]]
    }
    if {![info exists ::HV3_STARKIT]} {
      set locations [linsert $locations 0 $dir]
    }
    foreach loc $locations {
      set g(binary) [file join $loc $prog]
      if {[file executable $g(binary)]} {
        print "Using binary \"$g(binary)\"\n"
        break
      }
      set g(binary) ""
    }
    if {$g(binary) eq "" && [info exists ::HV3_STARKIT]} {
      set install_prog [file join $dir $prog]
      if {[file exists $install_prog]} {
        set g(binary) [install $install_prog]
      }
    }
  }

  # This proc is called when hv3 starts up if the following conditions
  # are met:
  #
  #     1. Hv3 is deployed as a starkit.
  #     2. There is no hv3_polipo[.exe] binary to be found.
  #     3. The starkit contains such a binary.
  #
  # We present the user with a dialog offering them the option to
  # copy the hv3_polipo binary out of the starkit to some location
  # on the disk, where we can execute it from.
  #
  # If the user does install the binary, the full path to it is 
  # returned. Otherwise an empty string is returned.
  #
  proc install {prog} {

    wm state . withdrawn

    set bin [file tail $prog]
    set path [file join [file dirname [file dirname $prog]] $bin]
    set radiooptions "-highlightthickness 0 -var ::hv3::polipo::c -bg white"

    # Document to
    set Template {
      <h1 style="text-align:center">Install hv3_polipo?</h1>
      <p>
	  Hv3 optionally uses an auxillary program, "hv3_polipo" to connect 
          to the internet. Currently, you do not have this program installed,
          but hv3 can install it now if you wish.
      <p>
	  Installing hv3_polipo does not require any further downloads, 
          requires less than 200KB disk space and greatly improves the
          performance of the hv3 web browser. Furthermore, once hv3_polipo
	  is installed, this irritating dialog box will stop appearing.The
          correct answer is to go ahead with the install.

      <table style="border:1px solid black;margin:1em auto; padding: 0 1em">
        <tr><td>
          <div class="widget" 
            cmd="radiobutton ${h}.r1 -val 1 $radiooptions">
            <td style="white-space:nowrap">Install hv3_polipo to $path
        <tr><td>
          <div class="widget" 
            cmd="radiobutton ${h}.r0 -val 0 $radiooptions">
            <td>Run without hv3_polipo.
      </table>

      <div class="widget" 
        style="width:90% ; margin:auto"
        cmd="button ${h}.button -text Ok -command ::hv3::polipo::install_ok">
    }

    set t [toplevel .polipo_install]
    wm title $t "Install hv3_polipo?"
    set h [html ${t}.html -width 800 -height 400 -shrink 1]
    pack $h -fill both -expand 1

    $h parse -final [subst -nocommands $Template]

    foreach node [$h search .widget] {
      $node replace [eval [$node attr cmd]]
    }
    set ::hv3::polipo::c 1

    bind .polipo_install <Destroy> ::hv3::polipo::install_cancel
    vwait ::hv3::polipo::signal
    destroy $t

    if {$::hv3::polipo::c} {
      if {[catch {
        file copy -force $prog $path
        if {$::tcl_platform(platform) eq "unix"} {
          file attributes $path -permissions rwxr-xr-x
        }
      }]} {
        tk_dialog .polipo_install "Installation failed" \
          "Installation of hv3_polipo failed (try running as root)" error 0 OK
      } else {
        wm state . normal
        return $path
      }
    }

    wm state . normal
    return ""
  }

  proc install_ok {} {
    bind .polipo_install <Destroy> ""
    set ::hv3::polipo::signal 1
  }

  proc install_cancel {} {
    exit
  }

  # Popup the gui log window.    
  proc popup {} {
    variable g
    if {![winfo exists .polipo]} {
      toplevel .polipo

      ::hv3::scrolled ::hv3::text .polipo.text -width 400 -height 250
      ::hv3::button .polipo.button -text "Restart Polipo" 

      pack .polipo.button -side bottom -fill x
      pack .polipo.text -fill both -expand true
      .polipo.button configure -command [namespace code restart]

      if {[string length $g(log)] > 10240} {
        set g(log) [string range $g(log) end-5120 end]
      }
      .polipo.text insert end $g(log)
    }
    raise .polipo
  }

  proc polipo_stdout {} {
    variable g
    if {[eof $g(filehandle)]} {
      set s "ERROR: Polipo failed."
      stop
      popup
    } else {
      set s [gets $g(filehandle)]
      if {$g(keepalive) eq "" && [scan $s "polipo port is %d" g(port)] == 1} {
        set g(keepalive) ""
        set g(keepalive) [socket localhost $g(port)]
      }
    }
    if {$s ne ""} { print "$s\n" }
  }

  # Stop any running polipo instance. If polipo is not running, this
  # proc is a no-op.
  proc stop {} {
    variable g
    catch {close $g(filehandle)}
    catch {close $g(keepalive)}
    set g(filehandle) ""
    set g(keepalive) ""
  }

  # (Re)start the polipo process. This proc blocks until polipo has
  # been successfully (re)started.
  proc restart {} {
    variable g

    # Make sure any previous polipo instance is finished.
    stop

    if {$g(binary) eq ""} {
      print "ERROR: No hv3_polipo binary found.\n"
      return
    }

    # Kick off polipo.
    set cmd "|{$g(binary)} dontCacheRedirects=true dontCacheCookies=true"
    append cmd " allowedPorts=1-65535"
    if {[info exists ::env(http_proxy)]} {
      append cmd " parentProxy=$::env(http_proxy)"
    }
    if {$::tcl_platform(platform) eq "unix"} {
      append cmd " |& cat"
      # set cmd "|{$g(binary)} diskCacheRoot=/home/dan/cache |& cat"
    }
    set fd [open $cmd r]
    fconfigure $fd -blocking 0
    fconfigure $fd -buffering none
    fileevent $fd readable [namespace code polipo_stdout]

    # Wait until the keepalive connection is established.
    set g(filehandle) $fd
    if {$g(keepalive) eq ""} {
      vwait ::hv3::polipo::g(keepalive)
    }

    # Log a fun and friendly message.
    if {$g(keepalive) ne ""} {
      print "INFO:  Polipo (re)started successfully.\n"
      catch {
        ::http::config -proxyhost 127.0.0.1
        ::http::config -proxyport $g(port)
      }
    } 
  }
}

namespace eval ::hv3::polipoclient {

  set N 0

  proc new {me args} {
    upvar #0 $me O

    set O(socket) [socket localhost [::http::config -proxyport]]

    fconfigure $O(socket)   \
        -blocking 0         \
        -encoding binary    \
        -translation binary \
        -buffering full     \
        -buffersize 8192

    fileevent $O(socket) readable [list $me ReadEvent]

    # State is always one of:
    # 
    #     "ready" 
    #     "waiting"
    #     "progress"
    #     "finished"
    #
    set O(state) ready

    set O(requesthandle) ""
    set O(protocol) ""

    set O(response) ""
    set O(headers) ""
    set O(data) ""

    incr ::hv3::polipoclient::N
    #puts "$::hv3::polipoclient::N requests outstanding"
  }

  proc destroy {me} {
    upvar #0 $me O
    catch {close $O(socket)}
    unset $me
    rename $me {}
    incr ::hv3::polipoclient::N -1
    #puts "$::hv3::polipoclient::N requests outstanding"
  }

  proc GET {me protocol requesthandle} {
    upvar #0 $me O
    if {$O(state) ne "ready"} { error "polipoclient state error" }

    set R $requesthandle
    set O(requesthandle) $R
    set O(protocol) $protocol

    # Make the GET request:
    #
    puts $O(socket)   "GET [$R cget -uri] HTTP/1.0"
    puts $O(socket)   "Accept: */*"
    puts $O(socket)   "User-Agent: [::http::config -useragent]"
    foreach {k v} [$R cget -requestheader] {
      puts $O(socket) "$k: $v"
    }
    puts $O(socket)   ""

    set O(state) "waiting"
    flush $O(socket)
  }

  proc ReadEvent {me args} {
    upvar #0 $me O

    append data [read $O(socket)]

    if {$O(state) eq "waiting"} {
      set iStart [expr { [string first "\x0D\x0A" $data]         + 2}]
      set iEnd   [expr { [string first "\x0D\x0A\x0D\x0A" $data] - 1}]
      if {$iStart>=0} {
        set O(response) [string range $data 0 [expr {$iStart - 3}]]
      }
      if {$iEnd>=0} {
        set O(state) progress
        set header ""
        foreach line [split [string range $data $iStart $iEnd] "\x0D\x0A"] {
          if {$line eq ""} continue
          set i [string first : $line]
          if {$i>=0} {
            lappend header [string trim [string range $line 0 [expr {$i-1}]]]
            lappend header [string trim [string range $line [expr {$i+1}] end]]
          }
        }
        $O(requesthandle) configure -header $header
        $O(protocol) AddToProgressList $O(requesthandle)

        set data [string range $data $iEnd+5 end]
      }
    }

    $O(requesthandle) append $data
    $O(protocol) BytesReceived $O(requesthandle) [string length $data]

    #puts "read [string length [read $O(socket)]] bytes"
    if {[eof $O(socket)]} {
      close $O(socket)
      $O(requesthandle) finish $data
    }
  }
}
::hv3::make_constructor ::hv3::polipoclient

::hv3::polipo::init
::hv3::polipo::restart
#::hv3::polipo::popup