BlueHell mIRC Script — File Browser

BlueHellmIRCScriptv1.0 / BlueHellmIRCScriptv1.0 / BlueHellScript / System / tracker / tracker.mrc

tracker.mrc — 42.61 KB — Download this file

; mTracker BitTorrent Tracker by Scab
;
; Scrape: http://your.own.ip.number:6969/scrape
; Announce: http://your.own.ip.number:6969/announce
;
; TODO: Support for compact=0 requests
;       Client detection based on User-Agent, fall-back on peer_id
;       IPv6 support
;       &natmapped=1&localip=114.48.27.48&port_type=wan (some BC clients seem to send this; not sure how it's useful. localip commonly not local at all.)
;       timer that clears out dead torrents (last activity more than 93 days (3 months) ago or so, configurable)
;       dns-resolve ip's on info_hash web pages (set "dns" CGI data in peerdata table). configurable to avoid a ton of dns lookups?

menu menubar {
  -
  m&Tracker
  .$iif($sock(tracker),&Stop,&Start):if ($sock(tracker)) stoptracker | else starttracker
  .-
  .&Options
  ..Change &tracker port $+ $chr(9) $+ $iif($hget(trackerinfo,tracker_port),$+($chr(91),$v1,$chr(93))):trackerport tracker $$?="New port:"
  ..Change &httpd port $+ $chr(9) $+ $iif($hget(trackerinfo,httpd_port),$+($chr(91),$v1,$chr(93))):trackerport httpd $$?="New port:"
  ..Change h&ostname $+ $chr(9) $+ $+($chr(91),$iif($hget(trackerinfo,hostname_override),$v1,not set),$chr(93)):trackerhost $input(Enter the hostname to use: $+ $crlf $+ ("-unset" to unset current hostname),e,mTracker)
  ..Change webadmin &username $+ $chr(9) $+ $+($chr(91),$iif($hget(trackerinfo,httpd_u),$v1,not set),$chr(93)):trackerwebadmin user $$?="New webadmin username:"
  ..Change webadmin &password $+ $chr(9) $+ $+($chr(91),$iif($hget(trackerinfo,httpd_p),$v1,not set),$chr(93)):trackerwebadmin pass $$?="New webadmin password:"
  ..-
  ..&Uninstall:trackerunload
  .-
  .$iif($sock(trackerwww),&Web Interface):run http:// $+ $iif($hget(trackerinfo,hostname_override),$v1,$iif($hget(trackerinfo,wan_ip),$v1,$sock(tracker).ip)) $+ : $+ $iif($hget(trackerinfo,httpd_port),$v1,6970) $+ /
}
; alias to change webadmin user/pass
alias trackerwebadmin {
  if (($1 == user) || ($1 == pass)) && ($2) {
    hadd trackerinfo httpd_ [ $+ [ $replace($1,user,u,pass,p) ] ] $2
    echo -esa * Changed webadmin $1 $+ .
  }
}
; alias to change ports
alias trackerport {
  if (!$1) && ($isid) && ($hget(trackerinfo,tracker_port)) return $v1
  elseif (($1 == tracker) || ($1 == httpd)) && ($2) {
    if ($2 isnum 1-65536) {
      var %hash = $+($lower($1),_port), %sock = $replace($1,httpd,trackerwww)
      hadd trackerinfo %hash $2
      if ($show) echo -esa * Changed $lower($1) port to $2 $+ .
      if ($sock(%sock)) && ($sock(%sock).bindport != $2) {
        sockclose %sock
        socklisten %sock $2
        if ($show) echo -esa * Restarted $lower($1) $+ .
      }
    }
    elseif ($show) echo -esa * Port must be a number between 1 and 65536.
  }
}
; alias to toggle debugging
alias trackerdebug {
  if ($1 == on) {
    hadd trackerinfo debug 1
    if ($show) echo -esa * Turned on debugging.
  }
  elseif ($1 == off) {
    hadd trackerinfo debug 0
    if ($show) echo -esa * Turned off debugging.
  }
  else {
    var %x = $iif($hget(trackerinfo,debug),0,1)
    hadd trackerinfo debug %x
    if ($show) echo -esa * Turned $iif(%x,on,off) debugging.
  }
}
; alias to set tracker hostname
alias trackerhost {
  if ($1 == -unset) {
    if ($hget(trackerinfo,hostname_override)) {
      hdel trackerinfo hostname_override
      if ($show) echo -esa * Unset the tracker hostname.
    }
    elseif ($show) echo -esa * There is no tracker hostname set.
  }
  elseif ($1) {
    hadd trackerinfo hostname_override $1
    if ($show) echo -esa * Set the tracker hostname to $+(",$1,") $+ . Announce URL: http://  $+ $1 $+ : $+ $iif($hget(trackerinfo,tracker_port),$v1,6969) $+ /announce
  }
}
on *:load:{
  .loadhashes
  .loadclientlist
  hadd -m trackerinfo tracker_port $iif($hget(trackerinfo,tracker_port),$v1,6969)
  hadd -m trackerinfo httpd_port $iif($hget(trackerinfo,httpd_port),$v1,6970)
  hadd -m trackerinfo httpd_u admin
  hadd -m trackerinfo httpd_p $randstr
  echo -esa * mTracker was loaded successfully. A menubar popup menu has been added.
  echo -esa * It is highly recommended to create a hostname to use for your tracker URL before proceeding. Please read the first question and answer in the FAQ section of trackerreadme.txt.
}
alias trackerunload {
  if ($input(Are you sure you want to uninstall the tracker?,ywd,mTracker)) {
    unloadcleanup
    !unload -rs $qt($script)
  }
}
alias -l unloadcleanup {
  if ($sock(tracker)) sockclose tracker
  if ($sock(trackerwww)) sockclose trackerwww
  if ($sock(trackerdata.*,0) > 0) sockclose trackerdata.*
  if ($hget(torrents)) hfree torrents
  if ($hget(peers)) hfree peers
  if ($hget(peerdata)) hfree peerdata
  if ($hget(trackerinfo)) hfree trackerinfo
  if ($hget(torrentclients)) hfree torrentclients
  if ($hget(torrentclients_shadow)) hfree torrentclients_shadow
  if ($hget(torrentclients_bram)) hfree torrentclients_bram
}
on *:unload:{
  if ($hget(trackerinfo)) || ($hget(torrents)) {
    ; user is unloading the script with the /unload command; run the clean-up.
    unloadcleanup
  }
}
on *:start:{
  .loadhashes
  .loadclientlist
  if ($hget(trackerinfo,on_on_exit)) {
    starttracker
    hdel trackerinfo on_on_exit
  }
  .timertrackerbackup 0 3600 .savehashes
}
on *:exit:{
  if ($sock(tracker)) hadd trackerinfo on_on_exit 1
  savehashes
  saveclientlist
}
alias -l mtrackerversion return mTracker/0.2
alias starttracker {
  if ($sock(tracker)) {
    if ($show) echo -esa * Tracker is already running.
  }
  else starttrackerd
  starttrackerwww
}
alias -l starttrackerd {
  socklisten tracker $iif($hget(trackerinfo,tracker_port),$v1,6969)
  if (!$hget(peers)) hmake peers 1000
  if (!$hget(peerdata)) hmake peerdata 1000
  if (!$hget(torrents)) hmake torrents 100
  if ($show) {
    if ($hget(trackerinfo,hostname_override)) echo -esa * Tracker started (http://  $+ $v1 $+ : $+ $sock(tracker).bindport $+ /announce $+ ).
    else echo -esa * Tracker started (http://  $+ $iif($sock(tracker).ip,$v1,127.0.0.1) $+ : $+ $sock(tracker).bindport $+ /announce $+ ).
  }
  if (!$hget(trackerinfo,hostname_override)) wimiplookup
  return
  :error
  if ($gettok($error,3-4,32) == bind error) {
    echo -esa * Can't start tracker: port $iif($hget(trackerinfo,tracker_port),$v1,6969) is already in use.
    reseterror
  }
}
alias -l starttrackerwww {
  if (!$sock(trackerwww)) {
    socklisten trackerwww $iif($hget(trackerinfo,httpd_port),$v1,6970)
    return
    :error
    if ($gettok($error,3-4,32) == bind error) {
      echo -esa * Can't start tracker web interface: port $iif($hget(trackerinfo,httpd_port),$v1,6970) is already in use.
      reseterror
    }
  }
}
alias stoptracker {
  if (!$sock(tracker)) && ($show) echo -esa * Tracker is not running.
  else {
    sockclose tracker
    sockclose trackerdata.*
    if ($show) echo -esa * Tracker stopped.
  }
  if ($sock(trackerwww)) sockclose trackerwww
}
on *:socklisten:tracker:{
  var %unique = $+($r(1,999999),$ticks,$longip($sock($sockname).ip),$sock($sockname).port)
  sockaccept $+(trackerdata.,$sock(trackerdata.*,0),%unique)
}
on *:socklisten:trackerwww:{
  var %unique = $+($r(1,999999),$ticks,$longip($sock($sockname).ip),$sock($sockname).port), %sn = $+(trackerwww.,$sock(trackerdata.*,0),%unique)
  sockaccept %sn
  sockmark %sn state=1
}
on *:sockread:trackerdata.*:{
  if ($sockerr < 1) && ($sock(tracker)) {
    :read
    var %x
    sockread %x
    if ($sockbr > 0) && (%x) {
      tokenize 32 %x
      if ($1 == GET) && (HTTP/*.* iswm $3) {
        var %doc = $gettok($2,1,63), %parms = $gettok($2,2-,63) $+ &null=1
        if (%parms) {
          var %info_hash = $resolveinfohash($cgiparm(%parms).info_hash)
          var %peer_id = $+($resolvepeerid($cgiparm(%parms).peer_id),_,%info_hash)
          ; turn away peers that can't handle compact peerlists
          if ($cgiparm(%parms).compact == 0) {
            ; failure reason
            bset -t &nocomp 1 d14:failure reason43:This tracker only accepts compact responsese
            bset -t &nocompact 1 HTTP/1.0 200 OK $+ $crlf
            bset -t &nocompact $calc(1 + $bvar(&nocompact,0)) Content-Length: $bvar(&nocomp,0) $+ $crlf
            bset -t &nocompact $calc(1 + $bvar(&nocompact,0)) Connection: close $+ $crlf
            bset -t &nocompact $calc(1 + $bvar(&nocompact,0)) Content-Type: text/plain $+ $crlf $+ $crlf
            bcopy &nocompact $calc(1 + $bvar(&nocompact,0)) &nocomp 1 -1
            sw $sockname &nocompact
            if ($hget(trackerinfo,debug)) {
              bwrite trackerdebug.nocompact -1 $asctime($ctime) %peer $+ $crlf
              bwrite trackerdebug.nocompact -1 &nocompact
              bwrite trackerdebug.nocompact -1 $str($crlf,2)
            }
          }
        }
        if (%doc == /scrape) && (%info_hash) {
          ; scrape
          var %metainfo = $hget(torrents,%info_hash)
          if (%metainfo) {
            var %completed = $completed(%info_hash)
            var %downloaded = $cgiparm(%metainfo).downloaded
            var %incomplete = $incomplete(%info_hash)
            bset &binhash 1 $binhash($cgiparm(%parms).info_hash)
            bset -t &scrape 1 d5:filesd20:
            bcopy &scrape $calc(1 + $bvar(&scrape,0)) &binhash 1 -1
            bset -t &scrape $calc(1 + $bvar(&scrape,0)) d8:completei $+ %completed $+ e
            bset -t &scrape $calc(1 + $bvar(&scrape,0)) 10:downloadedi $+ %downloaded $+ e
            bset -t &scrape $calc(1 + $bvar(&scrape,0)) 10:incompletei $+ %incomplete $+ e
            bset -t &scrape $calc(1 + $bvar(&scrape,0)) eee
            ; reply
            bset -t &scrapesw 1 HTTP/1.0 200 OK $+ $crlf
            bset -t &scrapesw $calc(1 + $bvar(&scrapesw,0)) Content-Length: $bvar(&scrape,0) $+ $crlf
            bset -t &scrapesw $calc(1 + $bvar(&scrapesw,0)) Connection: close $+ $crlf
            bset -t &scrapesw $calc(1 + $bvar(&scrapesw,0)) Content-Type: text/plain $+ $crlf $+ $crlf
            bcopy &scrapesw $calc(1 + $bvar(&scrapesw,0)) &scrape 1 -1
            sw $sockname &scrapesw
            if ($hget(trackerinfo,debug)) {
              bwrite trackerdebug.scrape -1 &scrapesw
              bwrite trackerdebug.scrape -1 $str($crlf,2)
            }
          }
        }
        elseif (%doc == /announce) && (%info_hash) {
          ; announce
          if (!$hget(torrents,%info_hash)) {
            ; new torrent
            hadd torrents %info_hash downloaded=0 $+ &last_seen= $+ $ctime
          }
          hadd -u7200 peers %peer_id $modcgi($modcgi(%parms,info_hash,%info_hash),peer_id,%peer_id)
          ; blindly honor ip field for cases where client and tracker are both on same side of NAT gateway (RFC1918 ip in socket)
          ; clients will presumably not provide bad data seeing how that would make them non-routable...
          if (?*.?*.?*.?* iswm $cgiparm(%parms).ip) var %ip = $v2
          else var %ip = $sock($sockname).ip
          hadd -u7200 peerdata %peer_id $+(ip=,%ip)
          if ($cgiparm(%parms).event == completed) {
            if ($cgiparm($hget(torrents,%info_hash)).last_seen) hadd torrents %info_hash $calccgi($modcgi($hget(torrents,%info_hash),last_seen,$ctime),downloaded,+1)
            else hadd torrents %info_hash $calccgi($hget(torrents,%info_hash),downloaded,+1) $+ &last_seen= $+ $ctime
          }
          else {
            if ($cgiparm($hget(torrents,%info_hash)).last_seen) hadd torrents %info_hash $modcgi($hget(torrents,%info_hash),last_seen,$ctime)
            else hadd torrents %info_hash $hget(torrents,%info_hash) $+ &last_seen= $+ $ctime
          }
          if ($cgiparm(%parms).event == stopped) {
            hdel peers %peer_id
            hdel peerdata %peer_id
          }
          ; announce reply...
          var %completed = $+(d8:completei,$completed(%info_hash),e)
          var %downloaded = $+(10:downloadedi,$cgiparm($hget(torrents,%info_hash)).downloaded,e)
          var %incomplete = $+(10:incompletei,$incomplete(%info_hash),e)
          var %intervals = $+(8:intervali,1800,e,12:min intervali,900,e)
          var %intropeers = 5:peers
          ; Algo to select which peers to suggest (IMPORTANT!)
          ; loop through all peers
          var %x = 1, %peers = $hfind(peers,*info_hash= $+ %info_hash $+ &*,0,w).data, %peersrequested = 25
          var %wn = $+(@peers,%info_hash,$ticks), %wn2 = $+(%wn,D), %wn3 = $+(%wn,S)
          if ($cgiparm($hget(peers,%peer_id)).numwant isnum) var %peersrequested = $v1
          if (%peersrequested > %peers) var %peersrequested = %peers
          ; windows should really never exist, we rely on %wn producing a string unique to this request, but just in case..
          if ($window(%wn)) window -c %wn
          if ($window(%wn2)) window -c %wn
          if ($window(%wn3)) window -c %wn
          ; compile list of all known peers (this is mostly to avoid having to $hfind query the hash table repeatedly later on)
          window -h %wn
          while (%x <= %peers) {
            var %peer = $hfind(peers,*info_hash= $+ %info_hash $+ &*,%x,w).data
            aline %wn %peer
            inc %x
          }
          ; if requesting peer is a seeder, send it only leechers (should improve useful peer distribution in the swarm)
          ; NOTE: this client has already been hdel'd if this is the reply of an event=stopped call
          if ($cgiparm($hget(peers,%peer_id)).left == 0) || (!$hget(peers,%peer_id)) {
            ; compile list of leechers
            window -h %wn2
            var %x = 1
            while (%x <= $line(%wn,0)) {
              var %peer = $hget(peers,$line(%wn,%x)), %left = $cgiparm(%peer).left
              if (%left > 0) aline %wn2 $line(%wn,%x)
              inc %x
            }
            window -h %wn3
            ; make a random selection of them
            if ($line(%wn2,0) > 0) {
              var %x = 1, %linestoread = %peersrequested
              while (%x <= %linestoread) {
                var %rand = $r(1,$line(%wn2,0)), %peer = $line(%wn2,%rand)
                aline %wn3 %peer
                ; remove peer from list
                dline %wn2 %rand
                inc %x
                if ($line(%wn2,0) < 1) break
              }
            }
          }
          ; if requesting peer is not a seeder, proceed with regular peer selection algo
          else {
            ; compile list of peers ordered by completion
            window -hs %wn2
            var %x = 1
            while (%x <= $line(%wn,0)) {
              var %peer = $hget(peers,$line(%wn,%x)), %left = $cgiparm(%peer).left, %padded = $+($str(0,$calc(40 - $len(%left))),%left)
              aline %wn2 %padded $line(%wn,%x)
              inc %x
            }
            if ($line(%wn2,0) > 0) {
              ; pick out top 5% for selection to make sure presumptive seed(s) are suggested
              ; THIS IS A HIGH PERCENTAGE!
              ; Messing with the randomness of peer selection has a negative effect on the swarm!
              var %x = 1, %linestoread = $ceil($calc(%peersrequested / 20)), %linesmax = $line(%wn2,0)
              window -h %wn3
              while (%x <= %linestoread) {
                var %selected = $line(%wn2,%x), %curpeer = $gettok(%selected,2,32)
                aline %wn3 %curpeer
                ; remove peer from peer list
                dline %wn $fline(%wn,%curpeer,1)
                inc %x
                if (%x > %linesmax) break
              }
            }
            ; pad out with random peers
            var %x = 1, %selectedpeers = $line(%wn3,0), %linestoread = $calc(%peersrequested - %selectedpeers)
            while (%x <= %linestoread) {
              var %rand = $r(1,$line(%wn,0)), %peer = $line(%wn,%rand)
              aline %wn3 %peer
              ; remove peer from list
              dline %wn %rand
              inc %x
              if ($line(%wn,0) < 1) break
            }
          }
          ; %wn3 now has the peer selection
          var %x = 1
          while (%x <= $line(%wn3,0)) {
            var %peer = $line(%wn3,%x), %ip = $cgiparm($hget(peerdata,%peer)).ip, %port = $cgiparm($hget(peers,%peer)).port
            bset &peers $calc(1 + $bvar(&peers,0)) $binpeer(%ip,%port)
            inc %x
          }
          ; close windows
          window -c %wn
          window -c %wn2
          window -c %wn3

          ; send client's own data back if it's alone, rather than 0:e
          if ($bvar(&peers,0) == 0) && ($cgiparm(%parms).event != stopped) {
            var %ip = $sock($sockname).ip, %port = $cgiparm(%parms).port
            if (%ip) && (%port isnum) bset &peers 1 $binpeer(%ip,%port)
          }
          ; HTTP reply
          bset -t &peers $calc(1 + $bvar(&peers,0)) e
          bset -t &final 1 $+(%completed,%downloaded,%incomplete,%intervals,%intropeers)
          bset -t &final $calc(1 + $bvar(&final,0)) $+($calc($bvar(&peers,0) - 1),:)
          bcopy &final $calc(1 + $bvar(&final,0)) &peers 1 -1
          bset -t &sw 1 HTTP/1.0 200 OK $+ $crlf
          bset -t &sw $calc(1 + $bvar(&sw,0)) Content-Length: $bvar(&final,0) $+ $crlf
          bset -t &sw $calc(1 + $bvar(&sw,0)) Connection: close $+ $crlf
          bset -t &sw $calc(1 + $bvar(&sw,0)) Content-Type: text/plain $+ $crlf $+ $crlf
          bcopy &sw $calc(1 + $bvar(&sw,0)) &final 1 -1
          sw $sockname &sw
          if ($hget(trackerinfo,debug)) {
            bwrite trackerdebug.announce -1 &sw
            bwrite trackerdebug.announce -1 $str($crlf,2)
          }
        }
        else {
          ; HTTP permanently moved redirect to :httpd_port
          bset -t &moved 1 HTTP/1.0 301 Moved Permanently $+ $crlf
          bset -t &moved $calc(1 + $bvar(&moved,0)) Location: http:// $+ $iif($hget(trackerinfo,hostname_override),$v1,$iif($hget(trackerinfo,wan_ip),$v1,$sock(tracker).ip)) $+ : $+ $iif($sock(trackerwww).bindport,$v1,$iif($hget(trackerinfo,httpd_port),$v1,6970)) $+ $2 $+ $crlf
          bset -t &moved $calc(1 + $bvar(&moved,0)) Content-Length: 0 $+ $crlf
          bset -t &moved $calc(1 + $bvar(&moved,0)) Connection: close $+ $str($crlf,2)
          bwrite redirect 0 &moved
          sw $sockname &moved
        }
      }
      goto read
    }
  }
}
; Keeping the Web Interface in a separate socket for practical reasons
; (retaining ability to handle browser and torrent client requests differently if ever necessary)
on *:sockread:trackerwww.*:{
  if ($sockerr < 1) {
    :read
    var %x, %sm = $sock($sockname).mark
    sockread %x
    if ($sockbr) {
      tokenize 32 %x
      ; check for get request (only supported request atm)
      if ($1 === GET) && (/* iswm $2) && (HTTP/?* iswm $3) sockmark $sockname $modcgi(%sm,state,1) %x
      ; read headers
      elseif ($cgiparm($gettok(%sm,1,32)).state == 1) {
        if ($len(%x) == 0) {
          sockmark $sockname $modcgi(%sm,state,2)
          if ($hget(trackerinfo,debug)) {
            write $qt($+($scriptdir,trackerwww.log)) $sock($sockname).ip - - $+($chr(91),$asctime(dd/mmm/yyyy:HH:nn:ss zz),$chr(93)) %sm
          }
          ; provide reply
          var %doc = $gettok(%sm,3,32)
          if ($gettok(%doc,1,63) == /) {
            ; generate page content
            ; if "GET /?info_hash=somehash"
            if ($cgiparm($gettok(%doc,2,63)).info_hash) {
              var %req_hash = $v1, %hashpage = $qt($+($scriptdir,trackerwww.info_hash.html)), %lof = $lof(%hashpage)
              bread %hashpage 0 %lof &hashpage
              ; head
              var %startpos = $bfind(&hashpage,1,<!-- #head -->) + 15, %endpos = $bfind(&hashpage,%startpos,<!-- #end -->) - 1
              bcopy &htemplate 1 &hashpage %startpos $calc(%endpos - %startpos)
              bcopy &beforehead 1 &hashpage 1 $calc(%startpos - 16)
              bcopy &afterhead 1 &hashpage $calc(%endpos + 15) %lof
              bset -t &head 1 $replace($bvar(&htemplate,1,$bvar(&htemplate,0)).text,info_hash,%req_hash)
              bcopy &appliedhead 1 &beforehead 1 -1
              bcopy &appliedhead $calc($bvar(&appliedhead,0) + 1) &head 1 -1
              bcopy &appliedhead $calc($bvar(&appliedhead,0) + 1) &afterhead 1 -1
              ; peer list
              var %startpos = $bfind(&appliedhead,1,<!-- #list -->) + 15, %endpos = $bfind(&appliedhead,%startpos,<!-- #end -->) - 1
              bcopy &template 1 &appliedhead %startpos $calc(%endpos - %startpos)
              bcopy &beforelist 1 &appliedhead 1 $calc(%startpos - 16)
              bcopy &afterlist 1 &appliedhead $calc(%endpos + 15) %lof
              var %template = $bvar(&template,1,$bvar(&template,0)).text, %peers = $getpeer(%req_hash,0)
              var %x = 1
              while (%x <= %peers) {
                var %peer = $getpeer(%req_hash,%x)
                var %downloaded = $cgiparm($hget(peers,%peer)).downloaded
                var %uploaded = $cgiparm($hget(peers,%peer)).uploaded
                var %client = $getclientfrompeerid(%peer)
                if (!%client) {
                  var %client = <i>Unknown</i><!-- $deltok(%peer,-1,95) -->
                  if ($hget(trackerinfo,debug)) {
                    bwrite trackerdebug.peer_id -1 UNKNOWN %peer $+ $crlf
                  }
                }
                var %ratio = $round($calc(%uploaded / %downloaded),2)
                if (%ratio == 0) {
                  if (%uploaded > %downloaded) var %ratio = Inf.
                  elseif (%uploaded == %downloaded) var %ratio = 1.00
                  else var %ratio = 0.00
                }
                var %left = $cgiparm($hget(peers,%peer)).left
                ; insert into the template
                bset -t &peerlist $calc(1 + $bvar(&peerlist,0)) $replace(%template,peer_id,$cgiparm($hget(peerdata,%peer)).ip,client,%client,uploaded,$bytes(%uploaded).suf,downloaded,$bytes(%downloaded).suf,ratio,%ratio,left,$bytes(%left).suf) $+ $crlf
                inc %x
              }
              bcopy &finalpage 1 &beforelist 1 -1
              if ($bvar(&peerlist,0) == 0) bset -t &peerlist 1 <td colspan="6" scope="colgroup">n/a</td>
              bcopy &finalpage $calc($bvar(&finalpage,0) + 1) &peerlist 1 -1
              bcopy &finalpage $calc($bvar(&finalpage,0) + 1) &afterlist 1 -1
              ; version (god I hate this templating code..)
              ; NOTE: there's no version string in the default info_hash template.
              ;       'version' is also the only template string that can be left out; 'head' and 'list' MUST to be in the template.
              ;       I'm not bothering with checks to allow useless template designs..
              var %vstartpos = $bfind(&finalpage,1,<!-- #version -->) + 18, %vendpos = $bfind(&finalpage,%startpos,<!-- #end -->) - 1
              if (%vstartpos > 18) && (%vendpos isnum) {
                bcopy &vtemplate 1 &finalpage %vstartpos $calc(%vendpos - %vstartpos)
                bcopy &before 1 &finalpage 1 $calc(%vstartpos - 19)
                bcopy &after 1 &finalpage $calc(%vendpos + 15) -1
                bset -t &version 1 $replace($bvar(&vtemplate,1,$bvar(&vtemplate,0)).text,version,$mtrackerversion on $iif($hget(trackerinfo,hostname_override),$v1,$iif($hget(trackerinfo,wan_ip),$v1,$iif($sock(tracker).ip,$v1,localhost))))
                bcopy &sendout 1 &before 1 -1
                bcopy &sendout $calc($bvar(&sendout,0) + 1) &version 1 -1
                bcopy &sendout $calc($bvar(&sendout,0) + 1) &after 1 -1
              }
              else bcopy &sendout 1 &finalpage 1 -1
              ; serve page
              bset -t &www 1 HTTP/1.1 200 OK $+ $crlf
              bset -t &www $calc(1 + $bvar(&www,0)) Date: $asctime(ddd) $+ , $asctime(dd mmm yyyy HH:nn:ss) $gettok($asctime(zzz),2,32) $+ $crlf
              bset -t &www $calc(1 + $bvar(&www,0)) Content-Language: en-US $+ $crlf
              bset -t &www $calc(1 + $bvar(&www,0)) Content-Length: $bvar(&sendout,0) $+ $crlf
              bset -t &www $calc(1 + $bvar(&www,0)) Cache-Control: no-cache, must-revalidate $+ $crlf
              bset -t &www $calc(1 + $bvar(&www,0)) Pragma: no-cache $+ $crlf
              bset -t &www $calc(1 + $bvar(&www,0)) Connection: close $+ $crlf
              bset -t &www $calc(1 + $bvar(&www,0)) Content-Type: text/html; charset=iso-8859-1 $+ $str($crlf,2)
              bcopy &www $calc(1 + $bvar(&www,0)) &sendout 1 -1
              sw $sockname &www
            }
            ; if "GET /"
            else {
              var %index = $qt($+($scriptdir,trackerwww.index.html)), %lof = $lof(%index)
              bread %index 0 %lof &index
              var %startpos = $bfind(&index,1,<!-- #list -->) + 15, %endpos = $bfind(&index,%startpos,<!-- #end -->) - 1
              bcopy &template 1 &index %startpos $calc(%endpos - %startpos)
              bcopy &beforelist 1 &index 1 $calc(%startpos - 16)
              bcopy &afterlist 1 &index $calc(%endpos + 15) %lof
              var %template = $bvar(&template,1,$bvar(&template,0)).text
              if ($hget(torrents,0).item > 0) {
                var %x = 1
                while (%x <= $hget(torrents,0).item) {
                  ; data from torrents table
                  var %info_hash = $hget(torrents,%x).item, %downloaded = $cgiparm($hget(torrents,%info_hash)).downloaded, %last_seen = $cgiparm($hget(torrents,%info_hash)).last_seen
                  ; calculated data
                  var %peers = $getpeer(%info_hash,0), %seeds = $completed(%info_hash), %incomplete = $incomplete(%info_hash)
                  ; insert into the template
                  bset -t &torrents $calc(1 + $bvar(&torrents,0)) $replace(%template,info_hash,<a href="?info_hash= $+ %info_hash $+ "> $+ %info_hash $+ </a>,downloaded,%downloaded,peers,%peers,seeds,%seeds,incomplete,%incomplete,last_activity,$asctime(%last_seen)) $+ $crlf
                  inc %x
                }
              }
              else bset -t &torrents 1 <td colspan="6">n/a</td> $+ $crlf
              bcopy &finalindex 1 &beforelist 1 -1
              bcopy &finalindex $calc($bvar(&finalindex,0) + 1) &torrents 1 -1
              bcopy &finalindex $calc($bvar(&finalindex,0) + 1) &afterlist 1 -1
              ; version
              var %startpos = $bfind(&finalindex,1,<!-- #version -->) + 18, %endpos = $bfind(&finalindex,%startpos,<!-- #end -->) - 1
              bcopy &vtemplate 1 &finalindex %startpos $calc(%endpos - %startpos)
              bcopy &before 1 &finalindex 1 $calc(%startpos - 19)
              bcopy &after 1 &finalindex $calc(%endpos + 15) -1
              bset -t &version 1 $replace($bvar(&vtemplate,1,$bvar(&vtemplate,0)).text,version,$mtrackerversion on $iif($hget(trackerinfo,hostname_override),$v1,$iif($hget(trackerinfo,wan_ip),$v1,$iif($sock(tracker).ip,$v1,localhost))))
              bcopy &sendout 1 &before 1 -1
              bcopy &sendout $calc($bvar(&sendout,0) + 1) &version 1 -1
              bcopy &sendout $calc($bvar(&sendout,0) + 1) &after 1 -1
              ; serve page
              bset -t &www 1 HTTP/1.1 200 OK $+ $crlf
              bset -t &www $calc(1 + $bvar(&www,0)) Date: $asctime(ddd) $+ , $asctime(dd mmm yyyy HH:nn:ss) $gettok($asctime(zzz),2,32) $+ $crlf
              bset -t &www $calc(1 + $bvar(&www,0)) Content-Language: en-US $+ $crlf
              bset -t &www $calc(1 + $bvar(&www,0)) Content-Length: $bvar(&sendout,0) $+ $crlf
              bset -t &www $calc(1 + $bvar(&www,0)) Cache-Control: no-cache, must-revalidate $+ $crlf
              bset -t &www $calc(1 + $bvar(&www,0)) Pragma: no-cache $+ $crlf
              bset -t &www $calc(1 + $bvar(&www,0)) Connection: close $+ $crlf
              bset -t &www $calc(1 + $bvar(&www,0)) Content-Type: text/html; charset=iso-8859-1 $+ $str($crlf,2)
              bcopy &www $calc(1 + $bvar(&www,0)) &sendout 1 -1
              sw $sockname &www
            }
          }
          elseif ($gettok(%doc,1,63) == /admin/) || ($gettok(%doc,1,63) == /admin) {
            if ($cgiparm($sock($sockname).mark).authorization) {
              ; client is trying to log in
              var %str = $decode($gettok($decode($v1,m),2,32),m), %u = $gettok(%str,1,58), %p = $gettok(%str,2,58)
              var %hash_u = $hget(trackerinfo,httpd_u), %hash_p = $hget(trackerinfo,httpd_p)
              if (%hash_u == $null) || (%hash_p == $null) goto 401
              elseif (%u === %hash_u) && (%p === %hash_p) {
                ; authorized
                if ($cgiparm($gettok(%doc,2-,63)).starttracker) {
                  .starttracker
                }
                elseif ($cgiparm($gettok(%doc,2-,63)).stoptracker) {
                  if ($sock(tracker)) {
                    sockclose tracker
                    sockclose trackerdata.*
                  }
                }
                var %admindoc = $qt($+($scriptdir,trackerwww.admin.html)), %lof = $lof(%admindoc)
                bread %admindoc 0 %lof &admindoc
                ; status
                var %startpos = $bfind(&admindoc,1,<!-- #status -->) + 17, %endpos = $bfind(&admindoc,%startpos,<!-- #end -->) - 1
                bcopy &stemplate 1 &admindoc %startpos $calc(%endpos - %startpos)
                bcopy &sbefore 1 &admindoc 1 $calc(%startpos - 19)
                bcopy &safter 1 &admindoc $calc(%endpos + 15) -1
                bset -t &status 1 $replace($bvar(&stemplate,1,$bvar(&stemplate,0)).text,_status,$iif($sock(tracker),ON,OFF))
                bcopy &admin 1 &sbefore 1 -1
                bcopy &admin $calc($bvar(&admin,0) + 1) &status 1 -1
                bcopy &admin $calc($bvar(&admin,0) + 1) &safter 1 -1
                ; out
                bset -t &www 1 HTTP/1.1 200 OK $+ $crlf
                bset -t &www $calc(1 + $bvar(&www,0)) Date: $asctime(ddd) $+ , $asctime(dd mmm yyyy HH:nn:ss) $gettok($asctime(zzz),2,32) $+ $crlf
                bset -t &www $calc(1 + $bvar(&www,0)) Content-Length: $bvar(&admin,0) $+ $crlf
                bset -t &www $calc(1 + $bvar(&www,0)) Cache-Control: no-cache, must-revalidate $+ $crlf
                bset -t &www $calc(1 + $bvar(&www,0)) Pragma: no-cache $+ $crlf
                bset -t &www $calc(1 + $bvar(&www,0)) Connection: close $+ $crlf
                bset -t &www $calc(1 + $bvar(&www,0)) Content-Type: text/html; charset=iso-8859-1 $+ $str($crlf,2)
                bcopy &www $calc(1 + $bvar(&www,0)) &admin 1 -1
                sw $sockname &www
              }
              else goto 401
            }
            else {
              :401
              var %authdoc = $qt($+($scriptdir,trackerwww.401.html)), %lof = $lof(%authdoc)
              bread %authdoc 0 %lof &authdoc
              bset -t &www 1 HTTP/1.0 401 UNAUTHORIZED $+ $crlf
              bset -t &www $calc(1 + $bvar(&www,0)) Date: $asctime(ddd) $+ , $asctime(dd mmm yyyy HH:nn:ss) $gettok($asctime(zzz),2,32) $+ $crlf
              bset -t &www $calc(1 + $bvar(&www,0)) WWW-Authenticate: Basic realm="mTracker Administration Area" $+ $crlf
              bset -t &www $calc(1 + $bvar(&www,0)) Content-Length: $bvar(&authdoc,0) $+ $crlf
              bset -t &www $calc(1 + $bvar(&www,0)) Connection: close $+ $crlf
              bset -t &www $calc(1 + $bvar(&www,0)) Content-Type: text/html; charset=iso-8859-1 $+ $str($crlf,2)
              bcopy &www $calc(1 + $bvar(&www,0)) &authdoc 1 -1
              sw $sockname &www
            }
          }
          elseif (%doc == /favicon.ico) {
            var %favicon = $qt($+($scriptdir,trackerwww.favicon.ico)), %lof = $lof(%favicon)
            bread %favicon 0 %lof &favicon
            bset -t &fav 1 HTTP/1.1 200 OK $+ $crlf
            bset -t &fav $calc(1 + $bvar(&fav,0)) Date: $asctime(ddd) $+ , $asctime(dd mmm yyyy HH:nn:ss) $gettok($asctime(zzz),2,32) $+ $crlf
            bset -t &fav $calc(1 + $bvar(&fav,0)) Content-Length: $bvar(&favicon,0) $+ $crlf
            bset -t &fav $calc(1 + $bvar(&fav,0)) Accept-Ranges: none $+ $crlf
            bset -t &fav $calc(1 + $bvar(&fav,0)) Connection: close $+ $crlf
            bset -t &fav $calc(1 + $bvar(&fav,0)) Content-Type: image/x-icon $+ $str($crlf,2)
            bcopy &fav $calc(1 + $bvar(&fav,0)) &favicon 1 -1
            sw $sockname &fav
          }
          else {
            ; HTTP 404
            var %errordoc = $qt($+($scriptdir,trackerwww.404.html)), %lof = $lof(%errordoc)
            bread %errordoc 0 %lof &errordoc
            bset -t &notfound 1 HTTP/1.0 404 Not Found $+ $crlf
            bset -t &notfound $calc(1 + $bvar(&notfound,0)) Date: $asctime(ddd) $+ , $asctime(dd mmm yyyy HH:nn:ss) $gettok($asctime(zzz),2,32) $+ $crlf
            bset -t &notfound $calc(1 + $bvar(&notfound,0)) Content-Length: $bvar(&errordoc,0) $+ $crlf
            bset -t &notfound $calc(1 + $bvar(&notfound,0)) Connection: close $+ $crlf
            bset -t &notfound $calc(1 + $bvar(&notfound,0)) Content-Type: text/html; charset=iso-8859-1 $+ $str($crlf,2)
            bcopy &notfound $calc(1 + $bvar(&notfound,0)) &errordoc 1 -1
            sw $sockname &notfound
          }
        }
        else {
          tokenize 58 %x
          var %header = $1, %data = $2-
          ; this code may be a bit risky... sockmark can only hold so much data
          if (!$cgiparm($gettok(%sm,1,32)). [ $+ [ %header ] ]) {
            sockmark $sockname $gettok(%sm,1,32) $+ & $+ $replace($lower($1),$chr(32),_) $+ = $+ $encode(%data,m) $gettok(%sm,2-,32)
          }
        }        
      }
      ; this will trigger for any content data sent along with the headers.
      ; not useful right now, but maybe for some future feature..
      elseif ($cgiparm($gettok(%sm,1,32)).state == 2) {
        ; moo
      }
      goto read
    }
  }
}
; sockwrite shorthand
alias -l sw {
  sockwrite $1-
}
; alias to grab data from a RFC3875 query-string
alias -l cgiparm {
  if ($1) && ($prop) {
    var %a = $gettok($wildtok($1,$+($prop,=,*),1,38),2-,61)
    if (%a == $null) var %a = $gettok($wildtok($1,$+($prop,=,*),1,63),2-,61)
    return %a
  }
}
; alias to modify data in a RFC3875 query-string
alias -l modcgi {
  if ($3) {
    return $puttok($1,$+($2,=,$3-),$findtok($1,$wildtok($1,$+($2,=,*),1,38),38),38)
  }
}
; alias to run a basic arithmetic computation on the data in a RFC3875 query-string
alias -l calccgi {
  if ($3) {
    return $puttok($1,$+($2,=,$calc($gettok($wildtok($1,$+($2,=,*),1,38),2-,61) $3)),$findtok($1,$wildtok($1,$+($2,=,*),1,38),38),38)
  }
}
alias -l completed {
  var %x = $hfind(peers,*info_hash= $+ $1 $+ &*&left=0&*,0,w).data, %y = $hfind(peers,*left=0&*&info_hash= $+ $1 $+ &*,0,w).data, %z = %x + %y
  return %z
}
alias -l incomplete return $calc($hfind(peers,*info_hash= $+ $1 $+ &*,0,w).data - $completed($1))
alias -l getpeer return $hfind(peers,*info_hash= $+ $1 $+ &*,$2,w).data

; $binpeer(ip,port)
; $binpeer(127.0.0.1,80)
alias -l binpeer {
  var %x = 1, %in = $replace($1,.,$chr(32)) $2, %out
  while (%x <= $numtok(%in,32)) {
    var %hex = $base($gettok(%in,%x,32),10,16), %pos = 1
    while (%pos <= $len(%hex)) {
      var %out = %out $base($mid(%hex,%pos,2),16,10)
      inc %pos 2
    }
    inc %x
  }
  return %out
}

; horribly bloated code for something similar to php's urldecode()
alias -l resolveinfohash {
  var %pos = 1
  while (%pos <= $len($1)) {
    if ($asc($mid($1,%pos,1)) == 37) {
      bset &infohash $calc(1 + $bvar(&infohash,0)) $base($mid($1,$calc(%pos +1),2),16,10)
      inc %pos 3
    }
    else {
      bset -t &infohash $calc(1 + $bvar(&infohash,0)) $mid($1,%pos,1)
      inc %pos 1
    }
  }
  var %x = 1, %dec = $bvar(&infohash,1,$bvar(&infohash,0)), %out
  while (%x <= $numtok(%dec,32)) {
    var %hex = $base($gettok(%dec,%x,32),10,16)
    if ($len(%hex) < 2) var %hex = 0 $+ %hex
    var %out = %out $+ %hex
    inc %x
  }
  return %out
}

; 20 byte info_hash generation for scrape reply
alias -l binhash {
  var %pos = 1
  while (%pos <= $len($1)) {
    if ($asc($mid($1,%pos,1)) == 37) {
      bset &hash $calc(1 + $bvar(&hash,0)) $base($mid($1,$calc(%pos +1),2),16,10)
      inc %pos 3
    }
    else {
      bset -t &hash $calc(1 + $bvar(&hash,0)) $mid($1,%pos,1)
      inc %pos 1
    }
  }
  return $bvar(&hash,1,$bvar(&hash,0))
}

; slight variation of resolveinfohash code.
alias -l resolvepeerid {
  var %pos = 1
  while (%pos <= $len($1)) {
    if ($asc($mid($1,%pos,1)) == 37) {
      bset &peerid $calc(1 + $bvar(&peerid,0)) $base($mid($1,$calc(%pos +1),2),16,10)
      inc %pos 3
    }
    else {
      bset -t &peerid $calc(1 + $bvar(&peerid,0)) $mid($1,%pos,1)
      inc %pos 1
    }
  }
  var %x = 1, %dec = $bvar(&peerid,1,$bvar(&peerid,0)), %out
  while (%x <= $numtok(%dec,32)) {
    if ($gettok(%dec,%x,32) !isnum 33-122) {
      var %hex = $base($gettok(%dec,%x,32),10,16)
      if ($len(%hex) < 2) var %hex = 0 $+ %hex
      var %out = %out $+ %hex
    }
    else var %out = %out $+ $chr($gettok(%dec,%x,32))
    inc %x
  }
  return %out
}

; for TheSHAD0W's base64 version format. used in next alias below
alias -l base64version {
  var %digits = 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-, %x = 1
  while (%x <= $len($1)) {
    var %digit = $mid($1,%x,1), %y = 1
    while (%y <= $len(%digits)) {
      if ($mid(%digits,%y,1) === %digit) {
        var %out = $+(%out,$calc(%y - 1),.)
        break
      }
      inc %y
    }
    inc %x
  }
  var %trim = 1
  ; get rid of trailing 0's originating from unspecified alphabetical revision specifiers
  if ($right(%out,3) == .0.) var %trim = 3
  return $mid(%out,1,$calc($calc($len(%out))-%trim))
}

; it's probably wiser to go by the HTTP User-Agent header when available, but for client identification from the peer_id:
alias -l getclientfrompeerid {
  ; check for Bits on Wheels
  if ($left($1,1) == -) && ($mid($1,2,3) === BOW) {
    if ($mid($1,5,3) == A0C) var %v = 1.0.6
    return Bits on Wheels $+ $iif(%v,/ $+ %v)
  }
  ; check for Azureus-style encoding
  ;elseif (-??????-?* iswm $1) && ($mid($1,4,4) isalnum) {
  elseif (-????????* iswm $1) && ($mid($1,4,4) isalnum) {
    var %v = $v1, %c = $hget(torrentclients,$iif($mid($1,2,2) islower,$+($v1,(lower)),$v1)), %x = 1
    while (%x <= $len(%v)) {
      var %v2 = $+(%v2,$mid(%v,%x,1),.)
      inc %x
    }
    var %v = $mid(%v2,1,$calc($calc($len(%v2))-1))
    ; libTorrent exception
    if ($mid($1,2,2) === lt) var %v = $base64version($mid($1,4,4))
    if (%c) && (%v) return %c %v
  }
  ; check for Opera
  elseif ($left($1,2) === OP) && ($mid($1,3,4) isnum) return Opera build $v1
  ; check for Qvod
  elseif ($left($1,4) === QVOD) && ($mid($1,5,4) isnum) return Qvod build $v1
  ; check for Bram's new client style
  elseif ($left($1,1) isalpha) && ($mid($1,2,1) isnum) && ($gettok($1,2,45) isnum) && ($gettok($1,3,45) isnum) {
    var %c = $hget(torrentclients_bram,$left($1,1)), %v = $replace($mid($1,2,7),-,.)
    if (*. iswm %v) {
      var %x = 1
      while (%x <= $numtok(%v,46)) {
        var %v2 = $+(%v2,$gettok(%v,%x,46),.)
        inc %x
      }
      var %v = $mid(%v2,1,$calc($calc($len(%v2))-1))
    }
    if (%c) && (%v) return %c %v
    else return moo %c
  }
  ; check for Shadow's encoding (always last; check will at times match some other encodings)
  elseif ($left($1,1) isalpha) && ($remove($base64version($mid($gettok($1,1,45),2,$calc($len($gettok($1,1,45)) - 1))),.) isnum) && ($count($1,-) > 0) {
    var %v = $base64version($mid($gettok($1,1,45),2,$calc($len($gettok($1,1,45)) - 1))), %c = $hget(torrentclients_shadow,$left($1,1))
    if (%c) && (%v) return %c %v
  }
}
alias -l savehashes {
  if ($show) echo -esa * Saving tracker data...
  write -c $qt($+($scriptdir,tracker.dat))
  write -c $qt($+($scriptdir,trackerinfo.dat))
  hsave -b torrents $qt($+($scriptdir,tracker.dat))
  hsave -b trackerinfo $qt($+($scriptdir,trackerinfo.dat))
  if ($show) echo -esa * Done saving tracker data.
}
alias -l loadhashes {
  var %th = $qt($+($scriptdir,tracker.dat)), %trh = $qt($+($scriptdir,trackerinfo.dat))
  if ($show) echo -esa * Loading tracker data...
  if (!$hget(torrents)) hmake torrents 100
  if (!$hget(trackerinfo)) hmake trackerinfo 10
  if ($isfile(%th)) hload -b torrents %th
  if ($isfile(%trh)) hload -b trackerinfo %trh
  if ($show) echo -esa * Done loading tracker data.
}
alias -l saveclientlist {
  if ($show) echo -esa * Saving client list...
  write -c $qt($+($scriptdir,trackerclientlist1.dat))
  write -c $qt($+($scriptdir,trackerclientlist2.dat))
  write -c $qt($+($scriptdir,trackerclientlist3.dat))
  hsave -b torrentclients $qt($+($scriptdir,trackerclientlist1.dat))
  hsave -b torrentclients_shadow $qt($+($scriptdir,trackerclientlist2.dat))
  hsave -b torrentclients_bram $qt($+($scriptdir,trackerclientlist3.dat))
  if ($show) echo -esa * Done saving client list.
}
alias -l loadclientlist {
  if ($show) echo -esa * Loading client list...
  if (!$hget(torrentclients)) hmake torrentclients 10
  if (!$hget(torrentclients_shadow)) hmake torrentclients_shadow 10
  if (!$hget(torrentclients_bram)) hmake torrentclients_bram 10
  hload -b torrentclients $qt($+($scriptdir,trackerclientlist1.dat))
  hload -b torrentclients_shadow $qt($+($scriptdir,trackerclientlist2.dat))
  hload -b torrentclients_bram $qt($+($scriptdir,trackerclientlist3.dat))
  if ($show) echo -esa * Done loading client list.
}

; whatismyip.com automation for reliable WAN IP lookup
; http://www.whatismyip.com/automation/n09230945.asp
alias wimiplookup {
  if ($sock(wimiplookup)) sockclose wimiplookup
  sockopen wimiplookup www.whatismyip.com 80
  sockmark wimiplookup $iif($show,1,0) 1
}
alias -l ipsw sockwrite -tn $sockname $1-
on *:sockopen:wimiplookup:{
  ipsw GET /automation/n09230945.asp HTTP/1.1
  ipsw User-Agent: $mtrackerversion (compatible; MSIE 6.0; Windows NT 5.1)
  ipsw Host: www.whatismyip.com
  ipsw Connection: close
  ipsw $crlf
}
on *:sockread:wimiplookup:{
  if ($sockerr < 1) {
    :read
    var %x
    sockread -f %x
    if ($sockbr) {
      if ($gettok($sock($sockname).mark,2,32) == 1) && ($len(%x) == 0) sockmark $sockname $puttok($sock($sockname).mark,2,2,32)
      elseif ($gettok($sock($sockname).mark,2,32) == 2) {
        if (?*.?*.?*.?* iswm %x) && ($remove(%x,.) isnum) && ($longip(%x) isnum 1-4294967295) {
          hadd trackerinfo wan_ip %x
          if ($gettok($sock($sockname).mark,1,32) == 1) {
            if ($sock(tracker)) && ($sock(tracker).ip != %x) echo -esa * WAN announce URL: http://  $+ %x $+ : $+ $sock(tracker).bindport $+ /announce (if in doubt, use this and forward ports $iif($hget(trackerinfo,tracker_port),$v1,6969) and $iif($hget(trackerinfo,httpd_port),$v1,6969) in your gateway)
            else echo -esa * Your WAN IP is: %x
          }
        }
      }
      goto read
    }
  }
}
; rand
alias -l randstr {
  var %x = 1, %y = $iif($1 isnum,$1,8)
  while (%x <= %y) {
    var %z = $r(0,2), %out = %out $+ $replace(%z,0,$r(a,z),1,$r(A,Z),2,$r(1,9))
    inc %x
  }
  return %out
}


; EOF