#!/tvbin/tivosh

# TiVo web server written by Stephen Rothwell (sfr@linuxcare.com.au)
# SendKeys Tivo Remote Mod by Jon Squire (jsquire@justice.loyola.edu)
# Note: SendKeys TiVo remote has currently only been tested on a 
# DirecTiVo, but should be fine on others, if a key doesn't work
# look at your sendkeys.tcl file and makesure that the proper key
# is being sent.
#
# Remount added 11/10/2000 1:44am EST but Jon Squire
#
source $tcl_library/tv/log.tcl
source $tcl_library/tv/mfslib.tcl
source $tcl_library/tv/sendkey.tcl

proc html_start {title} {
    set ret "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\n"
    append ret "<HTML><HEAD>"
    if {$title != ""} {
	append ret "<TITLE>$title</TITLE>"
    }
    append ret "</HEAD><BODY>"
}

proc html_end {} {
    return "</BODY></HTML>"
}

proc html_link {link anchor} {
    return "<A HREF=\"$link\">$anchor</A>"
}

proc html_table_start {tattr cap capattr} {
    set ret "<TABLE"
    if {$tattr != ""} {
	append ret " $tattr"
    }
    append ret ">"
    if {$cap != ""} {
	append ret "<CAPTION"
	if {$capattr != ""} {
	    append ret " $capattr"
	}
	append ret ">$cap</CAPTION>"
    }
    return $ret
}

proc html_table_end {} {
    return "</TABLE>"
}

proc DumpObject_html {db obj} {
    # Show type and open bracket
    append ret [dbobj $obj type] " " [dbobj $obj fsid] "/" [dbobj $obj subobjid] " {\n"

    # Show the construction status
    if { [dbobj $obj construction] } {
	append ret "  UNDER CONSTRUCTION\n"
    }

    # dump the body of the object	
    foreach attr [dbobj $obj attrs] {
	append ret [format {  %-14s =} $attr]
	if { [dbobj $obj attrtype $attr] == "object" } {
	    foreach subObj [dbobj $obj gettarget $attr] {
		append ret " " [html_link "/object/$subObj" $subObj]
	    }
	} else {
	    append ret " " [dbobj $obj get $attr]
	}
	append ret "\n"
    }

    # close bracket
    append ret "}"
}

proc do_dir {chan path} {
    puts $chan [html_start "Directory listing of $path"]
    puts $chan [html_table_start "" "Directory listing of $path" "ALIGN=TOP"]
    puts $chan "<TR ALIGN=LEFT><TH>Name</TH><TH>Type</TH><TH>Id</TH><TH>Date      Time</TH><TH>Size</TH></TR>"
    ForeachMfsFile fsid name type $path "" {
	transaction {
	    if {[catch {set size [FileSize $type $fsid]}] != 0} {
	        set size "N/A"
	    }
	    if {[catch {set date [FileDate $fsid]}] != 0} {
	        set date "N/A"
	    }
	}
	if {[string range $path end end] != "/"} {
	    append path "/"
	}
	puts -nonewline $chan "<TR><TD>"
	puts -nonewline $chan [html_link "/mfs$path$name" $name]
	puts -nonewline $chan "</TD><TD>$type</TD><TD>"
	if {$type == "tyDb"} {
	    puts -nonewline $chan [html_link "/object/$fsid" $fsid]
	} else {
	    puts -nonewline $chan $fsid
	}
	puts $chan "</TD><TD>$date</TD><TD>$size</TD></TR>"
    }
    puts -nonewline $chan [html_table_end]
    puts $chan [html_end]
}

proc do_object {chan path objectid} {
    puts $chan [html_start $path]
    puts $chan "<PRE>"

    set db [dbopen]
    transaction {
	if { [regexp {([0-9]*)/(.*)} $objectid junk fsid subobjid] } {
	    set obj [db $db openidconstruction $fsid $subobjid]
	} else {
	    set obj [db $db openid $objectid]
	}
	puts $chan [DumpObject_html $db $obj]
    }
    puts $chan "</PRE>"
    puts $chan [html_end]
}

proc action_object {chan objectid} {
    if {[string index $objectid 0] == "/"} {
	set objectid [string range $objectid 1 end]
    }
    do_object $chan "" $objectid
}

proc action_mfs {chan path} {
    if {$path == "" } {
	set path "/"
    }
    if {[catch {transaction {mfs find $path}} l] != 0} {
	puts $chan [html_start ""]
	puts $chan $l
	puts $chan [html_end]
    } else {
	set type [lindex $l 1]
	if {$type == "tyDir"} {
	    do_dir $chan $path
	} elseif {$type == "tyDb"} {
	    do_object $chan $path [lindex $l 0]
	}
    }
}


proc action_NowShowing {chan path} {
    puts $chan  [html_start "Now Showing"]
    
    ForeachMfsFile fsid name type  "/Recording/NowShowing" "" { 
	if {$type == "tyDb"} {
	    set db [dbopen]
	    transaction { 
		set tystreams ""
		set foundnotshow true
		set foundnotpart true
		if {[catch {set obj [db $db openidconstruction $fsid 10]}] == 0} {
		    foreach attr [dbobj $obj attrs] {
			if {$attr == "Showing" && $foundnotshow} {
			    set foundnotshow false
			    set foundnotprog true
			    if { [dbobj $obj attrtype $attr] == "object" } {
				foreach obj2 [dbobj $obj gettarget $attr] {
				    regexp {([0-9]*)/(.*)} $obj2 junk part1 part2
				    set obj3 [db $db openidconstruction $part1 $part2]
				    foreach attr2 [dbobj $obj3 attrs] {
					if {$attr2 == "Program" && $foundnotprog} {
					    set foundnotprog false
					    if { [dbobj $obj3 attrtype $attr2] == "object" } { 
						foreach obj4 [dbobj $obj3 gettarget $attr2] { 
						    regexp {([0-9]*)/(.*)} $obj4 junk part1 part2
						    set obj5 [db $db openidconstruction $part1 $part2]
						    puts $chan "<BR>"
						    foreach attr3 [dbobj $obj5 attrs] {
							if {$attr3 == "Title" || 
							$attr3 == "Description" || 
							$attr3 == "EpisodeTitle" } {
							    foreach obj6 [dbobj $obj5 get $attr3] { 
								puts -nonewline $chan $attr3 
								puts -nonewline $chan ": " 
								puts -nonewline $chan $obj6
								puts $chan "<BR>"
								if {$attr3 == "Title"} {
								    set title $obj6
								}
							    }
							}
						    }
						}
					    }
					}
				    }
				}
			    }
			}
			if {$attr == "Part" && $foundnotpart} {
			    set foundnotpart false
			    if { [dbobj $obj attrtype $attr] == "object" } {
				foreach obj2 [dbobj $obj gettarget $attr] {
				    regexp {([0-9]*)/(.*)} $obj2 junk part1 part2
				    set obj3 [db $db openidconstruction $part1 $part2]
				    foreach attr2 [dbobj $obj3 attrs] {
					if {$attr2 == "File"} {
					    foreach obj4 [dbobj $obj3 get $attr2] { 
						append tystreams "/$obj4"
					    }
					}
				    }
				}
			    }
			}
			if {$attr == "RecordQuality"} {
			    if { [dbobj $obj get $attr] == 0 } {
				set quality "TiVoBasic"
			    } else {
				set quality "TiVoBest"
			    }
			}
		    }
		}
		regsub -all " " $title "%20" newtitle
		puts -nonewline $chan [html_link "/tystream$tystreams/$newtitle.$quality.mp2v" "Play TyStream: $tystreams"]
		puts $chan "<BR><BR>"
	    }
	}
    }
    puts $chan  [html_end]
}

proc action_tystream {chan path} {
    set second ""
    set quality ""
    while { 0 < [regexp {^/([0-9]+)(.*)$} $path dummy first path]}  {
	set second "$second $first"
    }
    if { 0 < [regexp {.*TiVoBest.*} $path dummy dummy2 path]} {
	set quality "-B\r\n"
    }
    exec /bin/bash -c "/hack/bin/doehtml $second" &
    set ifc [exec /sbin/ifconfig]
    regexp {.*inet addr:([0-9.]*).*} $ifc junk ipaddr
    puts -nonewline $chan "TIVOIP=$ipaddr\r\nBEST=$quality" 
}

proc action_remount {chan path} {
    if {[string index $path 0] == "/"} {
	set path [string range $path 1 end]
    }
    puts $chan [html_start ""]
    puts $chan "TiVoHack Web Root Remount $path<BR>"
  
  	if {$path == "ro" } {
	puts $chan "/ Remounted Read Only"
	exec mount -oremount,ro /
    } elseif {$path == "rw" } {
	puts $chan "/ remounted Read Write"
	exec mount -oremount,rw /
    }
    puts $chan "Remount /<BR>"
    puts -nonewline $chan [html_link "ro" "Read Only"]
    puts $chan "<BR>"
    puts -nonewline $chan [html_link "rw" "Read Write"]
    puts $chan [html_end]
}

proc action_sendkey {chan path} {
    if {[string index $path 0] == "/"} {
	set path [string range $path 1 end]
    }
    puts $chan [html_start "TivoHack Web Remote"]
    if {$path != "" } {
	SendKey $path
	puts $chan "Last Key Sent $path<HR>"
    }
    puts $chan [html_table_start "BORDER=1" "" ""]
    puts -nonewline $chan "<TR ALIGN=center><TD WIDTH=33%>"
    puts -nonewline $chan [html_link "power" "Power"]
    puts -nonewline $chan "</TD><TD WIDTH=33%>"
    puts -nonewline $chan [html_link "tivo" "TiVo"]
    puts -nonewline $chan "</TD><TD WIDTH=33%>"
    puts -nonewline $chan [html_link "livetv" "Live TV/Guide"]
    puts $chan "</TD></TR>"
    puts -nonewline $chan "<TR><TD ALIGN=center COLSPAN=3>"
    puts $chan [html_table_start "BORDER=1" "" ""]
    puts -nonewline $chan "<TR><TD COLSPAN=2 ALIGN=CENTER>"
    puts -nonewline $chan [html_link "up" "Up"]
    puts $chan "</TD></TR>"
    puts -nonewline $chan "<TR><TD>"
    puts -nonewline $chan [html_link "left" "Left"]
    puts -nonewline $chan "</TD><TD>"
    puts -nonewline $chan [html_link "right" "Right"]
    puts $chan "</TD></TR>"
    puts -nonewline $chan "<TR><TD COLSPAN=2 ALIGN=CENTER>"
    puts -nonewline $chan [html_link "down" "Down"]
    puts -nonewline $chan "</TD></TR>"
    puts -nonewline $chan [html_table_end]
    puts $chan "</TD></TR>"
    puts -nonewline $chan "<TR ALIGN=center><TD>"
    puts -nonewline $chan [html_link "thumbsdown" "Thumbs Down"]
    puts -nonewline $chan "</TD><TD>"
    puts -nonewline $chan [html_link "select" "Select"]
    puts -nonewline $chan "</TD><TD>"
    puts -nonewline $chan [html_link "thumbsup" "Thumbs Up"]
    puts $chan "</TD></TR>"
    puts -nonewline $chan "<TR ALIGN=center><TD>"
    puts -nonewline $chan [html_link "volumeUp" "volumeUp"]
    puts -nonewline $chan "</TD><TD>"
    puts -nonewline $chan [html_link "mute" "mute"]
    puts -nonewline $chan "</TD><TD>"
    puts -nonewline $chan [html_link "surfup" "surfup"]
    puts $chan "</TD></TR>"
    puts -nonewline $chan "<TR ALIGN=center><TD>"
    puts -nonewline $chan [html_link "volumeDown" "volumeDown"]
    puts -nonewline $chan "</TD><TD>"
    puts -nonewline $chan [html_link "save" "save"]
    puts -nonewline $chan "</TD><TD>"
    puts -nonewline $chan [html_link "surfdown" "surfdown"]
    puts $chan "</TD></TR>"
    puts -nonewline $chan "<TR ALIGN=center><TD COLSPAN=3>"
    puts -nonewline $chan [html_link "play" "play"]
    puts $chan "</TD></TR>"
    puts -nonewline $chan "<TR ALIGN=center><TD>"
    puts -nonewline $chan [html_link "reverse" "reverse"]
    puts -nonewline $chan "</TD><TD>"
    puts -nonewline $chan [html_link "pause" "pause"]
    puts -nonewline $chan "</TD><TD>"
    puts -nonewline $chan [html_link "forward" "forward"]
    puts $chan "</TD></TR>"
    puts -nonewline $chan "<TR ALIGN=center><TD>"
    puts -nonewline $chan [html_link "replay" "replay"]
    puts -nonewline $chan "</TD><TD>"
    puts -nonewline $chan [html_link "playslow" "playslow"]
    puts -nonewline $chan "</TD><TD>"
    puts -nonewline $chan [html_link "catchup" "catchup"]
    puts $chan "</TD></TR>"
    puts -nonewline $chan "<TR ALIGN=CENTER><TD WIDTH=33%>"
    puts -nonewline $chan [html_link 1 1]
    puts -nonewline $chan "</TD><TD WIDTH=33%>"
    puts -nonewline $chan [html_link 2 2]
    puts -nonewline $chan "</TD><TD WIDTH=33%>"
    puts -nonewline $chan [html_link 3 3]
    puts $chan "</TD></TR>"
    puts -nonewline $chan "<TR ALIGN=CENTER><TD>"
    puts -nonewline $chan [html_link 4 4]
    puts -nonewline $chan "</TD><TD>"
    puts -nonewline $chan [html_link 5 5]
    puts -nonewline $chan "</TD><TD>"
    puts -nonewline $chan [html_link 6 6]
    puts $chan "</TD></TR>"
    puts -nonewline $chan "<TR ALIGN=CENTER><TD>"
    puts -nonewline $chan [html_link 7 7]
    puts -nonewline $chan "</TD><TD>"
    puts -nonewline $chan [html_link 8 8]
    puts -nonewline $chan "</TD><TD>"
    puts -nonewline $chan [html_link 9 9]
    puts $chan "</TD></TR>"
    puts -nonewline $chan "<TR ALIGN=CENTER><TD>"
    puts -nonewline $chan [html_link "clear" "Clear"]
    puts -nonewline $chan "</TD><TD>"
    puts -nonewline $chan [html_link 0 0]
    puts -nonewline $chan "</TD><TD>"
    puts -nonewline $chan [html_link "enter" "Enter"]
    puts $chan "</TD></TR>"
    puts -nonewline $chan [html_table_end]
    puts $chan "<P>"
    puts -nonewline $chan [html_link "bookmark" "bookmark"]
    puts $chan "<BR>"
    puts -nonewline $chan [html_link "display" "display"]
    puts $chan "<BR>"
    puts -nonewline $chan [html_link "directv" "directv"]
    puts $chan "<BR>"
    puts -nonewline $chan [html_link "surfCommit" "surfCommit"]
    puts $chan "<BR>"
    puts -nonewline $chan [html_link "standby" "standby"]
    puts $chan "<BR>"
    puts -nonewline $chan [html_link "nowShowing" "nowShowing"]
    puts $chan "<BR>"
    puts -nonewline $chan [html_link "replay" "replay"]
    puts $chan "<BR>"
    puts -nonewline $chan [html_link "delimiter" "delimiter"]
    puts $chan "<BR>"
    puts -nonewline $chan [html_link "guide" "guide"]
    puts $chan "<BR>"
    puts -nonewline $chan [html_link "displayAndLeft" "displayAndLeft"]
    puts $chan " (System Test on Combo)<BR>"
    puts -nonewline $chan [html_link "displayAndRight" "displayAndRight"]
    puts $chan " (System Diagnostics on Combo))<BR>"
    puts -nonewline $chan [html_link "selectAndDown" "selectAndDown"]
    puts $chan " (Clear Program Data on Combo)<BR>"
    puts -nonewline $chan [html_link "selectAndUp" "selectAndUp"]
    puts $chan " (System Information on Combo)<BR>"
    puts -nonewline $chan [html_link "power" "power"]
    puts $chan "<BR>"
    puts -nonewline $chan [html_link "dumpState" "dumpState"]
    puts $chan "<BR>"
    puts $chan [html_end]
}

proc action_quit {chan dummy} {
    global quit

    puts $chan [html_start ""]
    puts $chan "Server has terminated."
    puts $chan [html_end]
    set quit 1
}

proc action_ {chan dummy} {
    puts $chan [html_start ""]
    puts -nonewline $chan [html_link "NowShowing" "Now Showing"]
    puts $chan "<BR>"
    puts -nonewline $chan [html_link "mfs/" "mfs"]
    puts $chan "<BR>"
    puts -nonewline $chan [html_link "sendkey/" "TiVo Web Remote"]
    puts $chan "<BR>"
    puts -nonewline $chan [html_link "remount/" "Remount Root Filesystem"]
    puts $chan "<BR><HR>"
    puts -nonewline $chan [html_link "quit" "Terminate the web server"]
    puts $chan [html_end]
}

proc session {chan addr port} {
    set head_req 0
    while {[gets $chan line] >= 0} {
    	if {$line == "\r"} break
	if {$line == ""} break
	if {[regexp -nocase {^get +(.*) +http/[0-9]+\.[0-9]+.?$} $line dummy path] == 1} {
	    continue
	}
	if {[regexp -nocase {^head +(.*) +http/[0-9]+\.[0-9]+.?$} $line dummy path] == 1} {
	    set head_req 1
	    continue
	}
    }
    if {$path == ""} {
	close $chan
	return
    }

    if {[regexp {^/([-_A-Za-z0-9]*)(.*)} $path dummy action part] == 1} {
	if {[info procs "action_$action"] == "action_$action"} {
	    if {$action != "tystream"} {
		puts $chan "HTTP/1.0 200 OK\r"
		puts $chan [format "Date: %s GMT\r" [clock format [clock seconds] -format "%a, %d %b %Y %T" -gmt true]]
		puts $chan "Connection: close\r"
		puts $chan "Content-Type: text/html; charset=iso-8859-1\r"
		puts $chan "\r"
		if {$head_req == 1} {
		    close $chan
		    return
		}
	    }
	    catch [action_$action $chan $part]
	} else {
	    puts $chan "HTTP/1.0 200 OK\r"
	    puts $chan [format "Date: %s GMT\r" [clock format [clock seconds] -format "%a, %d %b %Y %T" -gmt true]]
	    puts $chan "Connection: close\r"
	    puts $chan "Content-Type: text/html; charset=iso-8859-1\r"
	    puts $chan "\r"
	    if {$head_req == 1} {
		close $chan
		return
	    }
	    action_ $chan $part
	}
    } else {
	puts $chan "HTTP/1.0 200 OK\r"
	puts $chan [format "Date: %s GMT\r" [clock format [clock seconds] -format "%a, %d %b %Y %T" -gmt true]]
	puts $chan "Connection: close\r"
	puts $chan "Content-Type: text/html; charset=iso-8859-1\r"
	puts $chan "\r"
	if {$head_req == 1} {
	    close $chan
	    return
	}
    	action_ $chan ""
    }

    close $chan
}

global quit

set quit 0
setpri fifo 1
socket -server session 80
vwait quit
