MotionWS 2 — File Browser

MotionWS2v2.0 / MotionWS2v2.0 / MotionWS2.mrc

MotionWS2.mrc — 79.55 KB — Download this file

/*
**
**
**  MotionWS 2 
**
**  You break it, you buy it :)
**
**
*/

;######################################## Menus ########################################


Menu menubar,status,@motionws {
  MotionWS-2
  . $iif($mws.isrunning,$style(2)) Start:mws.start
  . $iif(!$mws.isrunning,$style(2)) Stop:mws.stop
  . $iif(!$mws.isrunning,$style(2)) Restart:mws.restart
  .-
  .Help: run $mws.quote($scriptdirdocs\help.html)
  .-
  .Configure
  ..Main:mws.config
  ..Virtual folders:mws.vfolders
  ..CGI:mws.managecgi
  .Connections:mws.connections
  .Unload:mws.unload
  .-
  .Check for updates:mws.updatecheck

}


;######################################## Public aliases ########################################

;## Returns version info
Alias MWS.Version return 2.0

;## Returns BETA if this is a beta build
Alias MWS.Beta return BETA

;## Returns build number
Alias MWS.build { 
  var %f = $+(",$scriptdir,\build,")
  return $gettok($read(%f,n,$lines(%f)),1,32) 
}

;## Saves hashtables and unloads the webserver
Alias MWS.Unload {
  mws.stop
  mws.savetables
  mws.echo MotionWS build $mws.build have been unloaded. To remove this addon completely, delete the directory $scriptdir
  mws.echo Your files and settings will still be intact if you wish to reload MotionWS
  hfree -w mws.*
  .timer -m 1 1 unload -rs $mws.quote($script)
}

;## Starts the webserver
Alias MWS.Start {
  if ($mws.isrunning) { mws.echo MWS is already running | return }
  if ($version < 6.12) {
    mws.echo This build only works with mIRC v6.12 and above. You are currently using $version $+ .
    return
  }
  ;## Load hashtables
  mws.pbar 1 100 Loading hashtables
  mws.loadtables

  ;## Check some initial settings, and use default settings if they are invalid/don't exist.
  mws.pbar 10 100 Checking log files
  if (!$mws.gs(logfile)) { .disable #mws.debuglog | mws.dbg No logfile specified, debug logging disabled }
  if (!$mws.gs(httplogfile)) { .disable #mws.httplog | mws.dbg No HTTP logfile specified, HTTP logging disabled }
  if (!$mws.gs(htdocs)) { mws.ps htdocs $+($scriptdir,htdocs) | mws.dbg No Htdocs directory specified, using default }

  ;## Check temp, data and htdocs directory
  mws.pbar 20 100 Checking directories
  MWS.Checkdirs

  ;## Check previous exit
  mws.pbar 30 100 Looking for exit
  MWS.CheckExit
  mws.ps Exit bad

  ;## Save the hashtables
  mws.pbar 40 100 Temporary saving hashtables
  mws.savetables

  ;## Check CGI executables
  mws.pbar 50 100 Checking CGI executables
  mws.checkcgi

  ;## Echo some info, map the vfolder for icons/errorpages/css, and start socklistening
  mws.pbar 60 100 Starting webserver
  mws.dbg Starting webserver..
  mws.dbg Version info: $+($mws.version,/,$mws.build,/,$version,/Win-,$os) $mws.paran(Now sporting $lines($script) lines of code)
  mws.dbg Webserver using $nopath($mws.ht.filename($mws.ht.settings)) $mws.paran($file($mws.ht.filename($mws.ht.settings)).size $+ b)
  mws.dbg Mapping needed virtual folders...
  MWS.CheckVirtual
  mws.dbg DLL: $mid($dll($mws.dll.zip,DLLInfo, .),5)
  mws.dbg Attempting socklistening..
  if (!$mws.socklisten) {
    ;## Socklistening successfull, webserver online
    mws.dbg Webserver ready!
    mws.pd isrunning $true
  }
  else {
    ;## Socklistening failed, webserver not online
    mws.dbg -e Webserver startup failed!
    mws.pd isrunning $false
  }
  mws.pbar 100 100 Done
  if ($1 == -c) {
    mws.updatecheck
  }
  elseif ($mws.gs(checkforupdateonstart) == true) {
    mws.updatecheck
  }
  mws.pbar -c
}

;## Stops the webserver
Alias MWS.Stop {
  if (!$mws.isrunning) { mws.echo MWS Webserver is not running | return }

  ;## Loop through open file streams to count them
  var %i = 1, %t = $fopen(0), %tfs = 0, %tf = 0, %ts = 0
  while (%i <= %t) {
    if (mws.* iswm $fopen(%i)) { inc %tfs }
    inc %i
  }

  ;## Count temporary files and sockets
  var %tf = $findfile($mws.tempdir,*,0,0,.remove $mws.quote($1-)), %ts = $sock(mws.*,0)

  ;## Close sockets
  sockclose mws.*
  MWS.ps Exit ok

  ;## Save hashtables
  MWS.savetables

  ;## Echo some info before file streams are closed
  MWS.Dbg Webserver stopped $mws.paran(TotalFilestreams= $+ %tfs $+ ; TotalTempfiles= $+ %tf $+ ; TotalSockets= $+ %ts $+ ;)

  ;## Close filestreams and timers
  .fclose mws.*
  .timermws* off
  mws.pd isrunning $false
}

;## Restarts the webserver
Alias MWS.Restart {
  if ($mws.isrunning) {
    MWS.Stop
    MWS.Start
  }
  else {
    mws.echo MWS Webserver is not running, can't stop
    mws.start
  }
}

;## Stops the webserver, saves hashtables, and outputs "mIRC exiting" to debug buffer
Alias MWS.Exit {
  MWS.Stop
  MWS.Dbg mIRC exiting
}

;## Opens the configuration dialog
Alias MWS.setup { mws.config }
Alias MWS.config {
  if (!$dialog(mws.config)) { dialog -m mws.config mws.config }
}

Alias mws.vfolders {
  if (!$dialog(mws.vfolders)) { dialog -m mws.vfolders mws.vfolders }
}
Alias MWS.managecgi {
  if (!$dialog(mws.cgi)) { dialog -m mws.cgi mws.cgi }
}



;## Settings manipulation commands

;## /MWS.ChangeHtdocs <dir>
;## Changes the htdocs directory, also in run-time
Alias Mws.ChangeHtdocs {
  var %dir = $1-
  if (!$mws.isrunning) {
    mws.loadtables
  }
  mws.ps Htdocs %dir
  if (!$mws.isrunning) {
    mws.savetables
  }
}

;## /MWS.Changeport <port number>
;## Changes listening port to the specified port number, also in run-time
Alias MWS.ChangePort {
  var %port = $1, %sockname = $mws.listenname
  if (%port !isnum) {
    mws.dbg -e Invalid port number, could not change port
    return
  }
  if (!$mws.isrunning) {
    ;## If MWS is not running, we load the tables, put the new port number into the hashtable, and save the tables again
    mws.loadtables
    mws.ps ListenPort %port
    mws.savetables
    return $true
  }
  else {
    mws.ps ListenPort %port
    var %oldport = $sock(%sockname).port
    sockclose %sockname
    if ($portfree(%port)) {
      socklisten %sockname %port
      mws.dbg Port changed to %port
      return $true
    }
    else {
      mws.dbg -e Could not change port, port %port not free. Going back to using port %oldport
      socklisten %sockname %oldport
    }
  }
}

;## /MWS.ChangeDebugLog <filename>
;## Changes the debug logging file to the filename specified
Alias MWS.ChangeDebugLog { mws.ps Logfile $1- }

;## /MWS.ChangeHtaccess <filename>
;## Changes the htaccess filename to the filename specified
Alias MWS.ChangeHtaccess { mws.ps Htaccess $1 }

;## /MWS.ChangeDefGet <true|false>
;## Changes the default folder download property
Alias MWS.ChangeDefGet { mws.ps DefGet $1 }

;## /MWS.ChangeDefList <true|false>
;## Change the default folder listing property
Alias MWS.ChangeDefList { mws.ps DefList $1 }

;## /MWS.SetMaxCPS <Maximum characters/bytes per second>
;## Sets the maximum upload bandwidth usage in CPS (bytes/sec)
Alias MWS.SetMaxCPS { mws.ps MaxCPS $1 }

;## /MWS.ChangeDateFormat <dateformat>
;## Changes the date format used in directory listing
Alias MWS.ChangeDateFormat { mws.ps DateFormat $1 }


;######################################## Casual Events ########################################
On *:Exit: {
  MWS.Exit
}
On *:Load: {
  var %wasrunning = $mws.gd(wasrunning), %updated = $mws.gd(updated)
  if (%updated) {
    mws.echo MWS has been updated! Now using build $mws.build
    if (%wasrunning) { mws.start }
    else {
      mws.echo Use /mws.start to start MWS
    }
    mws.pd wasrunning $false
    mws.pd updated $false

  }
  else {
    mws.echo -s Use /mws.config to configure MotionWS v $+ $mws.version $+ .
  }
}

;######################################## Socket events ########################################

On *:socklisten:$($mws.listenname): {
  var %socknameok = $true, %sockname

  ;## Check if sockname exists
  while (%socknameok) {
    %sockname = $MWS.CreateSockname
    if (!$sock(%sockname)) { %socknameok = $false }
  }

  ;## Accept socket
  sockaccept %sockname

  ;## Check for errors
  if ($sockerr) {
    mws.dbg -e Recieved socket error from %sockname $mws.paran(Sockerr: $sockerr -  Winsock msg: $sock(%sockname).wsmsg) - Type: Listen/Connect
    return
  }

  ;## Output debug info and wait for data
  mws.dbg Connection recieved, using socket name %sockname $+ , IP: $sock(%sockname).ip
}
On *:sockread:mws.*: {

  ;## Error check
  if ($sockerr) {
    mws.dbg -e Recieved socket error from %sockname $mws.paran(Sockerr: $sockerr -  Winsock msg: $sock(%sockname).wsmsg) - Type: Sockread
    return
  }
  var %sockname = $sockname, %read = $true, %handle, %filename

  ;## Loop and read data from socket
  while (%read) {
    sockread &data

    ;## Set filenames
    %filename = $mws.quote($mws.gettempfilename(%sockname,-nRecvd).filename)
    %handle = $mws.gettempfilename(%sockname,-nRecvd).name

    ;## Check if file handle is already open. If not, open it.
    if (!$mws.isfileopen(%handle)) { .fopen $iif(!$exists($mws.quote(%filename)),-n) %handle $mws.quote(%filename) }

    ;## Set file pointer to end of file, write incoming data
    .fseek %handle $file(%filename).size
    .fwrite -b %handle &data
    .fclose %handle    

    ;## Avoid duplicate header checks, and consequently, responses
    if ($mws.gd($sockname $+ .gotheader) != $true) {

      ;## If header have been recieved, parse it
      if ($mws.headercheck(%sockname)) { 
        mws.pd $sockname $+ .gotheader $true
        mws.rawparse %sockname
        return
      }
    }
    if ($sockbr == 0) { %read = $false }
  }
}

;## Executes cleanup alias
On *:sockclose:mws.*: {
  mws.sockclose $sockname 
}

;######################################## Local aliases ########################################

;## This alias executes the cleanup alias whenever a mws socket is closed manually
Alias -l sockclose {
  if ($1) {
    mws.sockclose $1
    sockclose $1
  }
}

;######################################## Core aliases ########################################


;## Loads hashtables
Alias MWS.LoadTables {
  if (!$hget($mws.ht.settings)) { hmake $mws.ht.settings 100 }
  hload -oi $mws.ht.settings $mws.quote($mws.ht.filename($mws.ht.settings)) Settings
  if (!$hget($mws.ht.vfolders)) { hmake $mws.ht.vfolders 100 }
  hload -oi $mws.ht.vfolders $mws.quote($mws.ht.filename($mws.ht.vfolders)) Folders
  if (!$hget($mws.ht.cgi)) { hmake $mws.ht.cgi 100 }
  hload -oi $mws.ht.cgi $mws.quote($mws.ht.filename($mws.ht.cgi)) CGI
  if (!$hget($mws.ht.inHeaders)) { hmake $mws.ht.inHeaders 100 }
  if (!$hget($mws.ht.outHeaders)) { hmake $mws.ht.outHeaders 100 }
  if (!$hget($mws.ht.Dynamic)) { hmake $mws.ht.Dynamic 100 }
  if (!$hget($mws.ht.Mime)) { hmake $mws.ht.Mime 100 }
  hload -oi $mws.ht.mime $mws.quote($mws.ht.filename($mws.ht.mime)) Mime-Types
}

;## Saves hashtables
Alias MWS.SaveTables {
  if ($hget($mws.ht.settings)) {
    hsave -oi $mws.ht.settings $mws.quote($mws.ht.filename($mws.ht.settings)) Settings
    hsave -oi $mws.ht.vfolders $mws.quote($mws.ht.filename($mws.ht.vfolders)) Folders
    hsave -oi $mws.ht.cgi $mws.quote($mws.ht.filename($mws.ht.cgi)) CGI
  }
}

;## Checks if the previous exit was forceful
Alias MWS.CheckExit {
  if ($mws.gs(Exit) == bad) {
    mws.dbg -w MotionWS was not shut down properly last time, some settings might have been lost
  }
}

;## Checks virtual folders
Alias MWS.CheckVirtual {
  if (!$mws.getvirtualfolder(/mws2_data)) {
    mws.addvirtualfolder -g /mws2_data $mws.datadir $+ \ $+ webdata
  }
  if (!$exists($mws.getvirtualfolder(/mws2_data))) {
    mws.addvirtualfolder -g /mws2_data $mws.datadir $+ \ $+ webdata
  }
}

;## Starts listening on the configured socket
Alias MWS.Socklisten {
  var %port = $mws.gs(Listenport)

  ;## Close the socket in case it already exists
  sockclose $MWS.ListenName

  ;## Check for port availability
  if (!$portfree(%port)) { mws.dbg -f Port %port not free, unable to listen for connections | return err }

  ;## Start listening
  socklisten $MWS.ListenName $mws.gs(Listenport)
  mws.dbg Now listening for connections on port %port
}

;## Checks that all needed directories exists, should be self-explainatory
Alias MWS.CheckDirs {
  mws.dbg Checking directories...
  var %basedir = $scriptdir $+ \
  if (!$isdir($mws.datadir)) { mws.dbg -f Data directory does not exist. Reinstall required. Errors might follow | return }
  else { mws.dbg > Data directory OK }
  if (!$isdir($mws.tempdir)) {
    mws.dbg > Temp directory does not exist, creating..
    .mkdir $mws.quote($mws.tempdir)
    mws.dbg > Temp directory created, directory OK
  }
  else { mws.dbg > Temp directory OK } 
  if (!$isdir($mws.htdocsdir)) {
    mws.dbg > Htdocs directory does not exist, creating..
    .mkdir $mws.quote($mws.htdocsdir)
    mws.dbg > Htdocs directory created, directory OK
  }
  else { mws.dbg > Htdocs directory OK } 
}

;## Checks all cgi executables as given in the CGI hashtable
Alias MWS.CheckCGI {
  mws.dbg Check CGI executables...
  var %ht = $mws.ht.cgi, %i = 1, %t = $hget(%ht,0).item, %item, %data, %execs
  while (%i <= %t) {
    %item = $hget(%ht,%i).item
    %data = $hget(%ht,%item)
    if (%data !isin %execs) {
      %execs = %execs %data
      if (!$exists($mws.quote(%data))) {
        mws.dbg -w CGI Executable $mws.quote(%data) does not exists
      }
      else {
        mws.dbg > $nopath(%data) OK
      }
    }
    inc %i
  }
}

/*
* This alias parses the raw data sent from the client. Each header is loaded into the InHeader hashtable, and
* a secondary parsing alias is called when this has been completed. 
*/
Alias MWS.RawParse {
  var %sockname = $1, %filename = $mws.quote($mws.gettempfilename(%sockname,-nRecvd).filename), %len = $file(%filename).size, %handle = $mws.gettempfilename(%sockname,-nRecvd).name

  if (!$mws.isfileopen(%handle)) {
    ;## File does not have a stream opened, open a stream
    .fopen $iif(!$exists($mws.quote(%filename)),-n) %handle $mws.quote(%filename)
  }

  ;## Loop through file
  var %i = 1, %t = $lines(%filename), %line
  while (%i <= %t) {
    .fseek -l %handle %i
    %line = $fread(%handle)
    if (!%line) { 

      ;## Blank line, all headers have been loaded, secondary parsing can be done
      mws.dbg Loaded headers from %sockname $+ , parsing..
      .fclose %handle
      mws.parse %sockname
      break
    }

    ;## Load headers into header hashtable here
    ;echo REQUEST -> %sockname %line
    mws.inheader %sockname %line
    inc %i
  }
  if ($mws.isfileopen(%handle)) { .fclose %handle }
}

Alias MWS.Parse {
  ;## First of all, load headers into a hashtable
  var %sockname = $1, %filename = $mws.quote($mws.gettempfilename(%sockname,-nRecvd).filename), %handle = $mws.gettempfilename(%sockname,-nRecvd).name  

  ;## All headers are now loaded into a hashtable, and can be effectively parsed
  ;## First, get the requested url and translate it
  var %req = $mws.inheader(%sockname,_request_)

  if (GET*HTTP/1.? iswm %req) {
    ;## It's a GET request, get url
    var %url = $gettok(%req,2,32)
    mws.dbg Got url -> %url Method: Get
    mws.pd %sockname $+ .method Get
    mws.translate %sockname %url
  }

  elseif (HEAD*HTTP/1.? iswm %req) {
    ;## It's a HEAD request, get url
    var %url = $gettok(%req,2,32)
    mws.dbg Got url -> %url Method: Get
    mws.pd %sockname $+ .method Head
    mws.translate %sockname %url
  }

  elseif (POST*HTTP/1.? iswm %req) {
    ;## It's a POST request, get url
    var %url = $gettok(%req,2,32)
    mws.dbg Got url -> %url Method: Post
    mws.pd %sockname $+ .method Post
    mws.translate %sockname %url
  }

  else {
    ;## Error in HTTP request
    mws.dbg Client sendt a bad request, or no request at all $mws.paran(%req)
    mws.response %sockname 400
  }
}

;## Checks to see if the header have been recieved, and that all of the message-body have been recieved
alias MWS.headercheck {
  var %sockname = $1, %filename = $mws.quote($mws.gettempfilename(%sockname,-nRecvd).filename), %len = $file(%filename).size, %handle = $mws.gettempfilename(%sockname,-nRecvd).name
  ;## Load file into a binary variable and scan for a double $crlf, which means "end-of-header", which again
  ;## means that the header can be parsed
  bread %filename 0 %len &data

  ;## Scan for the double crlf offset
  var %offset = $bfind(&data,1,13 10 13 10)
  if (%offset) { 

    ;## All the headers have been recieved, let's see if we got the whole message body (if there is a message-body)
    if (!$mws.isfileopen(%handle)) {
      ;## File does not have a stream opened, open a stream
      .fopen $iif(!$exists($mws.quote(%filename)),-n) %handle $mws.quote(%filename)
    }
    var %i = 1, %t = $lines(%filename), %line, %lof, %conlen
    while (%i <= %t) {

      ;## Get the content-length value
      .fseek -l %handle %i
      %line = $fread(%handle)
      if ($gettok(%line,1,58) == Content-length) {
        ;## Do stuff to determine if message body have been recieved or not, then return either $true or $false
        var %content-length = $gettok(%line,2,32)
        %offset = $calc(%offset + 3)
        %lof = $file(%filename).size
        %conlen = $calc(%lof - %offset)
        if (%conlen >= %content-length) { 
          mws.dbg POST data applies, stripping from recieved data and moving into new file

          ;## copy the message body into another file
          ;## Create write filename
          var %wfilename = $mws.quote($mws.gettempfilename(%sockname,-nPostData).filename)
          write -c %wfilename

          ;## Read post data into variable
          bread %filename %offset %conlen &postdata

          ;## Cut off the original file
          btrunc %filename %offset

          ;## Write post data to new file
          bwrite %wfilename -1 -1 &postdata
          return $true
        }
        else { break }
      }
      inc %i
    }
    ;## Close filestream
    .fclose %handle
    return $true
  }
  return $false
}

Alias MWS.ListFolder {
  /*
  * def: MWS.ListFolder(%sockname,-n%N)
  * %N:
  * - Additional naming for the file, can be either alphanumerical or numerical values
  * %sockname:
  * - The sockname of the current connection 
  */

  ;## Set initial variables
  var %sockname = $1, %vfolder = $2, %folder = $mws.quote($3-), %win = $+(@,%sockname), %ticks = $ticks, %name, %filename, %indexof = $+(/,$mws.fixslashes($nopath(%vfolder)))
  ;## Create window
  window -c %win
  window -lsh %win 0 0 0 0

  ;## Search directory
  var %x = $findfile(%folder,*,0,1,aline %win $nopath($1-) : $bytes($file($1-).size).suf : $file($1-).mtime), %x = $finddir(%folder,*,0,1,aline %win $+($chr(33),dir,$chr(33),$nopath($1-)))
  var %i = 1, %t = $line(%win,0), %curfile, %curdata, %fullpath, %filesize, %modified, %isdir, %dateformat = $mws.gs(DateFormat)

  ;## Get filenames
  %name = $MWS.GetTempFilename(%sockname,-nDirList).name
  %filename = $MWS.Quote($MWS.GetTempFilename(%sockname,-nDirList).filename)

  ;## Check if filehandle already exists (not likely) and close it 
  if ($MWS.IsFileOpen(%name)) { .fclose %name }

  ;## Open file stream
  .fopen -n %name %filename

  ;## Create html/table beginning
  .fwrite -tn %name <html><head><title>Index of $mws.rawurldecode(%indexof) $+ </title></head><link rel="stylesheet" type="text/css" href="/mws2_data/Stylesheet.css"><body><h2>Index of $mws.rawurldecode(%indexof) $+ </h2><table>
  .fwrite -tn %name <tr><td></td><td class="DirListFilename">File</td><td class="DirListFileSize">Size</td><td>&nbsp;&nbsp;</td><td class="DirListFileModified">Modified</td></tr>
  .fwrite -tn %name <tr><td colspan="5"><hr width="100%"></td></tr>
  if (%vfolder != /) { .fwrite -tn %name $+(<tr><td><img src="/mws2_data/icons/back.jpg"></td><td class="DirListFilename" width="60%"><a href=",$nofile(%vfolder),"><b>Parent Folder</b></a></td><td></td><td></td></tr>) }
  ;## Check if it's the htaccess file
  var %afile = $mws.gs(AccessFile), %line
  ;## Loop and dump html into binvar &data
  while (%i <= %t) {
    %isdir = $false | %line = $line(%win,%i) | %curfile = $gettok(%line,1,58)
    if (%curfile == %afile) { inc %i | continue }
    %filesize = $gettok(%line,2,58) | %modified = $asctime($remove($gettok(%line,3,58),$chr(32)), %dateformat)
    if ($gettok(%line,1,33) == dir) { var %curfile = $gettok(%line,2,33), %isdir = $true }
    if (%isdir) { .fwrite -tn %name $+(<tr><td><img src="/mws2_data/icons/folder.jpg"></td><td class="DirListFilename" width="60%"><a href=",%vfolder,$iif(%vfolder != /,/),%curfile,/">,%curfile,/</a></td><td>-</td><td></td><td>-</td></tr>) }
    else {  .fwrite -tn %name $+(<tr><td><img src="/mws2_data/icons/file.jpg"></td><td class="DirListFilename" width="60%"><a href=",%vfolder,$iif(%vfolder != /,/),%curfile,">,%curfile,</a></td><td class="DirListFileSize">,%filesize,</td><td>&nbsp;</td><td class="DirListFileModified" align="right" nowrap>,%modified,</td></tr>)  }
    inc %i
  }
  var %ttime = $calc(($ticks - %ticks) / 1000)
  .fwrite -tn %name <tr><td colspan="5"><hr width="100%"></td></tr>
  .fwrite -tn %name <tr><td colspan="5" class="DirListServerInfo"> $mws.serverreply on port $mws.gs(ListenPort) </td></tr>
  .fwrite -tn %name <tr><td colspan="5" class="DirListServerInfo">Page built in %ttime $+ s</td></tr>
  .fwrite -tn %name </table></body></html>
  mws.dbg Directory list time: %ttime Objects: %t

  ;## Pass on arguments to the MWS.SendTempFile alias which will pass it further on to the socket sending code
  MWS.SendTempFile %sockname -nDirList

  ;## Echo debug info and finish up
  window -c %win
}

Alias MWS.WriteTempFile {
  /*
  * def: $MWS.WriteTempFile(%sockname, [-n%N], %data)
  * %N:
  * - Additional naming for the file, can be either alphanumerical or numerical values
  * %sockname:
  * - The sockname of the current connection 
  * %data
  * - The data to be stored in the file
  */

  ;## Set some initial variables
  var %sockname = $1, %N = $2, %data, %name, %filename
  if (-n* iswm %N) { %data = $3- } | else { %data = $2- }

  ;## Generate filenames
  %name = $mws.gettempfilename(%sockname,%N).name
  %filename = $mws.gettempfilename(%sockname,%N).filename

  ;## Check parameters
  if (!$2) { mws.dbg -f Not enough parameters for temporary file writing | return $false } 

  ;## Quote filename for spaced directory compatability
  %filename = $mws.quote(%filename)

  ;## Check if the file handle is already active, and if the filename matches
  if (!$mws.isfileopen(%name)) { 
    if ($exists(%filename)) {
      mws.dbg Temporary file already exists with no handle, trying deletion (not likely to work, file probably locked by error)
      .remove %filename
      mws.dbg File removed, continuing..
    }
    mws.dbg Opening temporary file %filename $mws.paran(%name) for writing..
    .fopen -n %name %filename
  }
  else {

    ;## Here's the filename matching check
    if ($mws.quote($fopen(%name).fname) != %filename) { mws.dbg -f File handle already in use for a different file | return }
    else { mws.dbg File already open, continuing.. }
  }

  ;## We're set to go, write data, check $ferr, and return $true or $false, depending on success or failure
  mws.dbg File ready, writing data..
  .fwrite %name %data
  if ($ferr) { mws.dbg -e File writing for temporary file %filename $mws.paran(%name) failed $mws.paran($ferr) $+ , Closing file handle and returning script processing | .fclose %name | return $false }
  else { return $true } 
  ;## The MWS.SendTempFile alias will close the file handler, for security precautions.
}

Alias MWS.SendTempFile {
  ;## The SocketSending alias must take care of temp file deletion, as this alias does NOT do so
  ;## Set some initial variables
  var %sockname = $1, %N = $2, %name, %filename

  ;## Generate filenames
  %name = $mws.gettempfilename(%sockname,%N).name
  %filename = $mws.gettempfilename(%sockname,%N).filename

  ;## Quote filename for spaced directory compatability
  %filename = $mws.quote(%filename)

  ;## Check if file handle exists
  if (!$mws.isfileopen(%name)) {
    mws.dbg -e Could not send temporary file, file handle %name does not exists
  }
  else {
    mws.dbg Sending temporary file %filename to socket $mws.quote(%sockname) using handle $mws.quote(%name)
    .fclose %name
    mws.sendfile %sockname %filename
  }
}
Alias MWS.GetTempFilename {

  ;## Set some initial variables
  var %sockname = $1, %N = $2

  ;## Generate filenames
  if (-n* iswm %N) { %N = $mid(%N,3) | var %name = $+(%sockname,.,%N,.tmp), %filename = $+($mws.tempdir,\,%sockname,.,%N,.tmp) }
  else { var %name = $+(%sockname,.tmp), %filename = $+($mws.tempdir,\,%sockname,.tmp) }

  ;## Return values
  if ($prop == name) { return %name }
  if ($prop == filename) { return %filename }
}

Alias MWS.Debug { mws.dbg $1- }
Alias MWS.Dbg {
  var %d, %data
  if ($1 == -f) { %d = Debug/FATAL ERROR: | %data = $2- }
  elseif ($1 == -e) { %d = Debug/ERROR: | %data = $2- }
  elseif ($1 == -w) { %d = Debug/WARNING: | %data = $2- }
  else { %d = Debug: | %data = $1- }
  if ($isalias(mws._dbg)) { mws._dbg %d %data }
  if ($isalias(mws.dbg.log)) { mws.dbg.log %d %data }
}

;## Turn this group on to enable debugging
#MWS.Debug on
Alias -l mws._dbg {
  window @MotionWS
  echo @MotionWS $1-
}
#MWS.Debug end

;## Turn this group on to enable debug logging
#MWS.DebugLog off
Alias -l mws.dbg.log {
  var %file = $MWS.gs(logfile), %handle = mws.logfile
  if (!%file) { return }
  if (!$mws.isfileopen(%handle)) { .fopen $iif(!$exists($mws.quote(%file)),-n) %handle $mws.quote(%file) }
  .fseek -l %handle $calc($lines(%file) + 1) 
  .fwrite -n %handle $1-
  .timermwslog 1 2 .fclose %handle
}
#MWS.DebugLog end

#MWS.HttpLog off
Alias -l mws.http.log {
  var %file = $MWS.gs(httplogfile), %handle = mws.httplogfile
  if (!$mws.isfileopen(%handle)) { .fopen $iif(!$exists(%file),-n) %handle $mws.quote(%file) }
  .fseek -l %handle $calc($lines(%file) + 1) 
  .fwrite -n %handle $1-
  .timermwshttplog 1 2 .fclose %handle
}
#MWS.DebugLog end


;## Checks if a file handle already exists
Alias MWS.IsFileOpen {
  if ($fopen($1)) { return $true }
  else { return $false }
}

Alias MWS.Method { return $mws.gd($1 $+ .method)  }
Alias MWS.ServerReply { return $+(MotionWS/,$mws.version)  }
Alias MWS.DateReply { return $asctime($ctime, ddd $+ $chr(44) dd mmm yyyy HH:nn:ss) GMT }
Alias MWS.Quote { var %r = $remove($1-,") | return $+(",%r,") }
Alias MWS.Paran { return $+($chr(40),$1-,$chr(41)) }
Alias MWS.Datadir return $+($scriptdir,data)
Alias MWS.Htdocsdir return $mws.gs(htdocs)
Alias MWS.Tempdir return $+($scriptdir,temp)
Alias MWS.Docsdir return $+($scriptdir,docs)
Alias MWS.Webdir return $+($scriptdir,data\webdata)
Alias MWS.Ht.Settings { return mws.settings }
Alias MWS.Ht.CGI { return mws.cgi }
Alias MWS.Ht.Vfolders { return mws.vfolders }
Alias MWS.Ht.InHeaders { return mws.inHeaders }
Alias MWS.Ht.OutHeaders { return mws.outHeaders }
Alias MWS.Ht.Dynamic { return mws.Dynamic }
Alias MWS.Ht.Mime { return mws.Mime }
Alias MWS.Ht.All { return mws.* }
Alias MWS.dll.zip { return $+($scriptdir,dll\zip\munzip.dll) }

Alias MWS.isrunning {
  if ($mws.gd(isrunning) == $true) && ($sock($mws.listenname).status == listening) { return $true }
  else { return $false }
}
Alias MWS.echo { echo -s $+(MWS/,$mws.version,:) $1- }
;## Get settings
Alias MWS.gs { return $hget($mws.ht.settings,$1-) }
Alias MWS.ps { hadd $mws.ht.settings $1 $2- }

;## Get dynamic
Alias MWS.gd { return $hget($mws.ht.dynamic,$1-) }
Alias MWS.pd { hadd $mws.ht.dynamic $1 $2- }

;## Get vfolder
Alias MWS.gf { return $hget($mws.ht.vfolders,$1-) }
Alias MWS.pf { hadd $mws.ht.vfolders $1 $2- }

;## Get cgi
Alias MWS.gc { return $hget($mws.ht.cgi,$1-) }
Alias MWS.pc { hadd $mws.ht.cgi $1 $2- }


;## Get mime-type
Alias MWS.gm { 
  var %d = $1-, %mime, %return
  if ($left(%d,1) != .) { %mime = $+(.,%d) }
  else { %mime = %d }
  %return = $hget($mws.ht.mime,%mime) 
  if (!%return) { %return = text/plain }
  return %return
}

;## Save query string
Alias MWS.pq {
  var %s = $1, %qs = $2-
  mws.pd $+(%s,querystring) %qs
}

;## Get query string or data
Alias MWS.gq {
  var %s = $1, %d = $2-, %qs, %item, %part, %data
  %qs = $mws.gd($+(%s,querystring))
  if (!$prop) { return %qs }
  var %i = 1, %t = $numtok(%qs,38)
  while (%i <= %t) {
    %part = $gettok(%qs,%i,38)
    %item = $gettok(%part,1,61)
    %data = $gettok(%part,2,61)
    if ($prop == item) && (%d == %item) {
      return %data
    }
    inc %i
  }
}


Alias MWS.ListenName { return MWS.Listen }
Alias MWS.CreateSockname { var %sockname = $+(mws.,$r(1,9),$r(1,9),$r(1,9),$r(1,9),$r(1,9)) | return %sockname }
Alias MWS.Htdocs { return $mws.gs(htdocs) }
Alias MWS.ht.filename { return $+($scriptdir,\,Data\,$1-,.ini) }
Alias MWS.FixSlashes {
  var %d = $1-, %i = 1, %t = 20
  while (%i <= %t) {
    %d = $replace(%d,$str(\,%t),/)
    %d = $replace(%d,$str(/,%t),/)
    dec %t
  }
  while ($right(%d,1) == /) {
    %d = $mid(%d,1,-1)
  }
  return %d
}

Alias MWS.GetVirtualFolder {
  ;## $MWS.GetVirtualFolder(vfolder)
  ;## Returns the folder to which the virtual folder is mapped
  ;## Properties:
  ;## .Get - returns true if the folder can be downloaded from
  ;## .List - returns true if the folder contents can be listed
  ;## .Username - returns username if the folder is password protected
  ;## .Password - returns password if the folder is password protected
  ;## .IsPwd - returns true if the folder is password protected

  var %f = $1-, %data = $MWS.gf(%f), %i = 1, %t = $numtok(%data,124), %item, %ispwd, %username, %password
  while (%i <= %t) {
    %item = $gettok(%data,%i,124)
    var % $+ $gettok(%item,1,61) $gettok(%item,2,61)
    inc %i
  }
  if (%username) { %ispwd = true }
  if ($prop == list) { return %list }
  if ($prop == get) { return %get }
  if ($prop == ispwd) { return %ispwd }
  if ($prop == username) { return %username }
  if ($prop == password) { return %password }
  elseif (!$prop) { return %rfolder }
}

Alias MWS.DeleteVirtualFolder {
  var %tbl = $mws.ht.vfolders
  if ($hget(%tbl,$1)) {
    hdel %tbl $1
    mws.dbg Removed folder $1 from the virtual folder database
  }
  else {
    mws.dbg -e Could not remove folder $1 from the virtual folder database, did not exist
  }
}

Alias MWS.AddVirtualFolder {
  ;## /mws.addvirtual folder -lg <virtual folder> <system folder>
  ;## -l: Folder can be listed
  ;## -g: Files can be downloaded from this folder
  ;## Replace any spaces with %20 to add spaced directories
  ;## Set variables
  if ($1 == /?) || ($1 == -?) || ($1 == ?) { echo Usage: /mws.addvirtual folder -lg <virtual folder> <system folder> | return }

  if (-* iswm $1) {
    var %switch = $1, %vfolder = $2, %rfolder = $3-
  }
  else {
    var %vfolder = $1, %rfolder = $2-
  }
  var %get, %list, %pprotect, %username, %password
  ;## Sorting the switches out..
  if (-*l* iswm %switch) { %list = true }
  else { %list = false }
  if (-*g* iswm %switch) { %get = true }
  else { %get = false }
  if (-*p* iswm %switch) {
    %pprotect = true
    %username = $2
    %password = $3
    %vfolder = $4
    %rfolder = $5-
    if (!$4) && (!$5-) { mws.dbg -e Username and password not given, virtual folder could not be added | return }
  }

  ;## Check for illegal characters
  if ($chr(32) isin %vfolder) || ($chr(58) isin %vfolder) || ($chr(42) isin %vfolder) || ($chr(63) isin %vfolder) || ($chr(34) isin %vfolder) || ($chr(60) isin %vfolder)  || ($chr(62) isin %vfolder) || ($chr(124) isin %vfolder) {    
    mws.dbg -e Virtual folder contained one or more illegal characters, folder could not be added (Ascii $chr(35) $+ $asc($ifmatch) $+ )
    return
  }

  ;## Check for existing rfolder
  if (!$isdir(%rfolder)) {
    mws.dbg Warning: Given system folder does not exist (will continue)
  }

  ;## Check if vfolder is /, that cannot be added
  if (%vfolder == /) {
    mws.dbg -e You can not map / as a virtual folder
    return
  }

  ;## Fix slash errors
  %vfolder = / $+ %vfolder
  while ($right(%vfolder,1) == /) {
    %vfolder = $mid(%vfolder,1,-1)
  }
  %vfolder = $mws.fixslashes(%vfolder)

  ;## Check if vfolder already exists
  if ($mws.getvirtualfolder(%vfolder)) {
    mws.dbg Warning: Virtual folder already exists, previous entry will be overwritten $mws.paran($+($mws.getvirtualfolder(%vfolder).list,:,$mws.getvirtualfolder(%vfolder).get,:,%vfolder,-->,$mws.getvirtualfolder(%vfolder)))
  }

  ;## Add the folder in the hashtable
  mws.dbg Mapping virtual folder %vfolder to physical folder %rfolder with permissions: list= $+ %list get= $+ %get $iif(%pprotect,$mws.paran(Using password: yes),$mws.paran(Using password: no))
  mws.pf %vfolder $+(list=,%list,|get=,%get,$iif(%pprotect,|username= $+ %username $+ |password= $+ %password),|rfolder=,%rfolder)
}

;## Adds and retrieves headers from the client into a hashtable
;## /mws.inheader %sockname %header
;## Use $mws.header(%sockname,_request_) to retrieve the request-line
Alias MWS.InHeader {
  var %sockname = $1, %tbl = $mws.ht.inheaders, %key = $+(%sockname,.,$2), %data = $3-, %all = $2-
  if (!$isid) {
    if (GET*HTTP/1.? iswm %all) {
      hadd %tbl $+(%sockname,.,_request_) %all
    }
    if (HEAD*HTTP/1.? iswm %all) {
      hadd %tbl $+(%sockname,.,_request_) %all
    }
    if (POST*HTTP/1.? iswm %all) {
      hadd %tbl $+(%sockname,.,_request_) %all
    }

    else {
      hadd %tbl %key %data
    }
  }
  else {
    return $hget(%tbl,%key)
  }
}

;## Adds/retrieves a header to the response header that should be returned to the client
;## /mws.outheader %sockname %header %header data
;## $mws.outheader(%sockname,header/_response_)
Alias MWS.OutHeader {
  var %sockname = $1, %tbl = $mws.ht.outheaders, %key = $+(%sockname,.,$2), %data = $3-, %all = $2-
  if (!$isid) {
    if (HTTP/1.? * iswm %all) {
      hadd %tbl $+(%sockname,.,_response_) %all
    }
    else {
      hadd %tbl %key %data
    }
  }
  else {
    return $hget(%tbl,%key)
  }
}


;## Aliases for encoding and decoding urls. Written by Techster.
;## These aliases can be found on mircscripts.org.
;## Mws.rawurldecode have been altered to deal with "%24", "%5C" and "+".
;## Taken from MotionWS1
Alias Mws.rawurlencode { var %encode = $1- | while ($regex($eval(%encode,1), /([^a-zA-Z0-9_\-\.\\\/])/)) { var %t = $regsub($eval(%encode,1), /([^a-zA-Z0-9_\-\.\\\/])/, $+(,$base($asc($regml(1)),10,16,2)), %encode) } | return $replace(%encode, , %) }
Alias Mws.rawurldecode { var %decode = $replace($eval($1-,1), +, $eval(%20,0)), %decode = $replace($eval(%decode,1), $eval(%5C,0), ), %decode = $replace($eval(%decode,1), $eval(%24,0), $eval($,0)) | while ($regex($eval(%decode,1), /\%([a-fA-F0-9]{2})/)) { var %t = $regsub($eval(%decode,1), /\%([a-fA-F0-9]{2})/, $chr($base($regml(1),16,10)), %decode) } | return $replace(%decode, $eval(%20,0), +,,\) }


Alias mws.sockclose {
  ;## Clean up files, hashtables, file streams

  ;## Set some variables
  var %sockname = $1, %x, %wc, %i, %t, %tf = 0, %ticks = $ticks, %script

  ;## Check if the socket is related to MotionWS
  if (mws.* iswm %sockname) && (%sockname != $mws.listenname) && (%sockname != mws.*) && ($sock(%sockname)) {
    %wc = $+(*,%sockname,*)
    mws.dbg Cleaning up after socket %sockname $+ ..

    ;## Delete hashtable entries
    hdel -w $mws.ht.inheaders %wc
    hdel -w $mws.ht.outheaders %wc
    hdel -w $mws.ht.dynamic %wc

    ;## Loop through file streams and get the total number, and close any that's related to the socket
    %i = 1, %t = $fopen(0)
    while (%i <= %t) {
      if (%wc iswm $fopen(%i)) { inc %tf }
      inc %i
    }
    .fclose %wc

    ;## Loop through scripts and search for mws.*
    %i = 1 | %t = $script(0)
    while (%i <= %t) {
      %script = $script(%i)
      if (mws.*.tmp iswm $nopath(%script)) {
        .unload -rs $mws.quote(%script)
      }
      inc %i
    }
    ;## Shut down CGI wait timer
    .timermws.cgi. $+ %sockname off

    ;## Delete temporary files related to the socket
    %x = $findfile($mws.tempdir,%wc,0,1,.timer 1 1 .remove $mws.quote($1-))
    mws.dbg Cleanup finished for socket %sockname $mws.paran(Filestreams= $+ %tf $+ ; Tempfiles= $+ %x) in $calc(($ticks - %ticks) / 1000) $+ s
  }
}

Alias MWS.Translate {
  ;## Translate a requested folder into a physical folder or file that can be listed or sent.
  ;## If none of these options are available, an error-message should be output

  ;## Set some initial variables
  var %s = $1, %ovfolder = $2, %vfolder = $mws.rqstring($2), %qs = $mws.gqstring($2), %file, %rfolder, %sockname = %s, %username, %password, %realm, %get, %list
  mws.pq %s %qs
  ;## Format url
  %vfolder = $mws.fixslashes(%vfolder)
  if (!%vfolder) { %vfolder = / }

  ;## Loop through %vfolder, and find first mapped folder.
  var %t = 1, %i = $numtok(%vfolder,47), %checkfolder, %extrafolder, %ismapped = $false
  while (%i >= %t) {
    %checkfolder = $mws.fixslashes(/ $+ $gettok(%vfolder,1- $+ %i,47)) | %extrafolder = $gettok(%vfolder,$calc(%i + 1) $+ -,47)
    if ($mws.GetVirtualFolder(%checkfolder)) {
      ;## We found a mapped folder, set %rfolder to the appropriate values
      %rfolder = $mws.GetVirtualFolder(%checkfolder) $+ \ $+ %extrafolder
      %ismapped = $true
      break
    }
    dec %i
  }

  if (!%ismapped) {
    ;## Could not find a mapped folder, add the whole schebang to the htdocs
    %rfolder = $+($mws.htdocs,\,%vfolder)
  }

  ;## If the requested folder is "/", then set the %rfolder to $mws.htdocs
  if (%vfolder == /) { %rfolder = $mws.htdocs }

  ;## Fix any slashes in the physical folder string
  %rfolder = $mws.fixbackslashes(%rfolder)

  ;## If the rfolder starts with a \, then it's a LAN computer, and we must add another \ at the beginning
  if ($mid(%rfolder,1,1) == \) { %rfolder = $+(\,%rfolder) }

  ;## Fix %XX's
  %rfolder = $mws.rawurldecode(%rfolder)

  ;## Get folder properties, listing/downloading/username/password
  if (%ismapped) {
    %list = $MWS.GetVirtualFolder(%checkfolder).list
    %get = $MWS.GetVirtualFolder(%checkfolder).get
    %username = $MWS.GetVirtualFolder(%checkfolder).username
    %password = $MWS.GetVirtualFolder(%checkfolder).password
  }

  ;## The folder is a subfolder within the htdocs folder, check for htaccess
  else {
    %list = $MWS.GetHtaccess(%rfolder).list
    %get = $MWS.GetHtaccess(%rfolder).get
    %username = $MWS.GetHtaccess(%rfolder).username
    %password = $MWS.GetHtaccess(%rfolder).password
  }

  ;## Set default values
  var %deflist = false, %defget = false
  if (!%list) { %list = $mws.gs(DefList) | %deflist = true }
  if (!%get) { %get = $mws.gs(DefGet) | %defget = true }

  mws.dbg Extracted properties: Vfolder: %vfolder - Rfolder: %rfolder - $mws.paran($+(List=,%list,;,Get=,%get,;,User=,%username,;,Realm=,%realm) - Default values: $+(%deflist,/,%defget))
  mws.dbg Vfolder type: $iif(%ismapped,map/virtual,phys/real)

  ;## Exploit fix
  if (%vfolder == /..) || (%vfolder == ../) || (%vfolder == ..) || (/.. isin %vfolder) {
    mws.dbg Possible exploit detected, giving 401 error
    mws.response %s 401
    return
  }

  ;## Exploit fix 2
  if (%rfolder == /..) || (%rfolder == ../) || (%rfolder == ..) || (/.. isin %rfolder) {
    mws.dbg Possible exploit detected, giving 401 error
    mws.response %s 401
    return
  }


  ;## Check the requested url
  ;## If it's a directory, a / must be appended to the end if it doesn't exist
  ;## Then the client must be redirected to the new url
  if ($isdir(%rfolder)) {
    if ($mid(%ovfolder,-1) != /) {
      %ovfolder = $+(%ovfolder,/)
      mws.outheader %s Location: %ovfolder
      mws.response %s 302
      return
    }
  }

  ;## Check for directoryindex
  if ($isdir(%rfolder)) {
    var %di = $mws.gs(DirectoryIndex), %i = 1, %t = $numtok(%di,44), %cdi
    while (%i <= %t) {
      %cdi = $mws.fixbackslashes($+(%rfolder,\,$gettok(%di,%i,44)))
      if ($exists(%cdi)) { %rfolder = %cdi | break }
      inc %i
    }
  }
  if (%username) {
    mws.dbg Authentication required for the requested file/folder...
    var %auth = $mws.inheader(%s,Authorization:)
    var %usrpass = $decode($gettok(%auth,2,32),m)
    var %susername = $gettok(%usrpass,1,58), %spassword = $gettok(%usrpass,2,58)
    if (%susername != %username) || (%spassword != %password) {
      mws.dbg Authentication failed!
      mws.outheader %s WWW-Authenticate: Basic realm="MotionWS $+ $mws.version $+ "
      mws.response %s 401
      return
    }
    mws.dbg Authentication successfull, continuing...
  }
  if ($isdir(%rfolder)) {
    ;## The requested url is a directory
    mws.dbg Requested URL -> directory (Exists: yes)
    if (%list == true) {
      mws.response %s 200
      mws.outheader %s Content-Type: text/html
      mws.listfolder %sockname %vfolder %rfolder
    }
    elseif (%list != true) {
      mws.response %s 401
    }
  }
  elseif ($isfile(%rfolder)) {
    ;## The requested url is a file
    mws.dbg Requested URL -> file (Exists: yes)
    if (%get == true) {
      if ($nopath(%rfolder) == $mws.gs(AccessFile)) {
        mws.response %s 401 %rfolder
      }
      else {
        mws.response %s 200 %rfolder 
        ;## Check if it's mhtml
        if ($gettok($remove(%rfolder,"),-1,46) == mhtml) {
          var %ticks = $ticks
          %rfolder = $Mws.mhtml(%s,%rfolder)
          .fclose mws.mhtml*
          if (%rfolder == $false) {
            mws.response %s 500
          }
          mws.dbg Mhtml executed in $calc(($ticks - %ticks) / 1000) $+ s
        }
        ;## Check if it's a extension to send through CGI
        elseif (($mws.gc($gettok($remove(%rfolder,"),-1,46))) && ($mws.gs(CGI) == true)) {
          mws.cgi %s $gettok($remove(%rfolder,"),-1,46) %ovfolder %rfolder
          return
          ;## /mws.cgi <sockname> <type> <vfolder> <rfolder>
        }
        else {
          mws.sendfile %s %rfolder 
        }
      }
    }
    elseif (%get != true) {
      mws.response %s 401
    }
  }
  else {
    ;## The requested url does not exist
    mws.dbg Requested URL -> unknown (Exists: no)
    mws.response %s 404
  }
}

Alias MWS.Response {
  ;## /Mws.response <sockname> <code> [filename]
  var %sockname = $1, %code = $2, %filename = $3-,%ext = $gettok(%filename,$numtok(%filename,46),46), %resp = $mws.codetable(%code)
  mws.pd $+(%sockname,respcode) %code
  mws.dbg Response: %resp
  if (%code == 400) {
    mws.outheader %sockname _response_ %resp
    mws.outheader %sockname Content-type: text/html
    mws.sendfile %sockname $+($mws.webdir,\errorpages\400.html)
  }
  if (%code == 401) {
    mws.outheader %sockname _response_ %resp
    mws.outheader %sockname Content-type: text/html
    mws.sendfile %sockname $+($mws.webdir,\errorpages\401.html)
  }
  if (%code == 403) {
    mws.outheader %sockname _response_ %resp
    mws.outheader %sockname Content-type: text/html
    mws.sendfile %sockname $+($mws.webdir,\errorpages\403.html)
  }
  if (%code == 404) {
    mws.outheader %sockname _response_ %resp
    mws.outheader %sockname Content-type: text/html
    mws.sendfile %sockname $+($mws.webdir,\errorpages\404.html)
  }
  if (%code == 500) {
    mws.outheader %sockname _response_ %resp
    mws.outheader %sockname Content-type: text/html
    mws.sendfile %sockname $+($mws.webdir,\errorpages\500.html)
  }

  if (%code == 200) {
    mws.outheader %sockname _response_ %resp
    mws.outheader %sockname Content-type: $mws.gm(%ext)
    if ($isfile(%rfolder)) { mws.outheader %sockname Content-length: $file(%rfolder).size }
  }
  if (%code == 302) {
    mws.outheader %sockname _response_ %resp
    mws.outheader %sockname Content-type: text/html
    mws.sendfile %sockname $+($mws.webdir,\errorpages\302.html)
  }
}

;## Returns http response code
Alias MWS.CodeTable {
  var %j = $1
  goto $1
  :200 | return HTTP/1.1 200 Ok
  :302 | return HTTP/1.1 302 Found
  :400 | return HTTP/1.1 400 Bad request
  :401 | return HTTP/1.1 401 Unauthorized
  :403 | return HTTP/1.1 403 Forbidden
  :404 | return HTTP/1.1 404 Not found
  :500 | return HTTP/1.1 500 Internal server error
  :%j | return Unknown
}

;## Returns info from htaccess files
Alias MWS.GetHtaccess {
  var %f = $1-, %f2, %line, %1, %2

  ;## Check if htaccess can be appended
  if ($isfile($+(%f,\htaccess))) {
    %f = $+(%f,\htaccess)
  }

  ;## Try removing files, then append htaccess
  elseif ($isfile($+($nofile(%f),\htaccess))) {
    %f = $+($nofile(%f),\htaccess)
  }

  ;## htaccess didn't exist
  else { return $false }

  %f = $mws.quote(%f)

  ;## htaccess exists, open file stream
  .fclose HTAccess*
  .fopen HTAccess %f

  ;## Loop and read lines
  var %i = 1, %t = $lines(%f)
  while (%i <= %t) {
    %line = $fread(HTAccess)
    if ($mid(%line,1,1) == $chr(59)) { continue }
    %1 = $gettok(%line,1,32) | %2 = $gettok(%line,2,32)
    if ($prop == %1) { .fclose HTAccess | return %2 } 
    inc %i
  }
  .fclose HTAccess
  return
}

;## Fixes backslashes
Alias MWS.FixBackSlashes {
  var %d = $1-, %i = 1, %t = 20
  %d = $replace(%d,/,\)
  while (%i <= %t) {
    %d = $replace(%d,$str(/,%t),\)
    %d = $replace(%d,$str(\,%t),\)
    dec %t
  }
  while ($right(%d,1) == \) {
    %d = $mid(%d,1,-1)
  }
  return %d
}

;## Returns url without query string
Alias MWS.rqstring {
  return $gettok($1-,1,63)
}

;## Returns query string, without url
Alias MWS.gqstring {
  return $gettok($1-,2,63)
}

Alias MWS.SendFile {
  ;## /mws.sendfile %sockname [-bN] filename
  var %filename, %sockname = $1, %offset, %chunk = 8192, %maxbuf = 16384, %filesize, %maxcps = $mws.gs(MaxCPS), %curcps

  if (-b* iswm $2) {
    %offset = $mid($2,3)
    %filename = $mws.quote($3-)
  }
  else {
    %offset = 0
    %filename = $mws.quote($2-)

    ;## Add some headers
    mws.outheader %sockname Content-length: $file(%filename).size
    mws.outheader %sockname Date: $mws.datereply
    mws.outheader %sockname Server: $mws.serverreply
    mws.outheader %sockname Connection: Close
    mws.outheader %sockname Accept-Ranges: bytes

    if ($mws.gs(GZIP) == true) {
      ;## Check for Gzip acceptance
      var %gclient = $mws.inheader(%sockname,Accept-Encoding:), %gserver = $mws.outheader(%sockname, Content-Encoding:)
      if (gzip isin %gclient) && (!%gserver) {
        var %gkey = %sockname $+ .gzip
        mws.pd %gkey true
        ;mws.outheader %sockname Content-encoding: gzip
      }
    }

    /*

    if ($mws.inheader(%sockname,Range:)) {
      ;## Check for content-range
      var %rline = $gettok($mws.inheader(%sockname,Range:),2,61), %rstart = $gettok(%rline,1,45), %rlen = $gettok(%rline,2,45)
      if (!%rlen) { %rlen = $calc($file(%filename).size - 1) }
      mws.dbg User-Agent requesting specific range, starts at %rstart bytes, length %rlen bytes
      if (%rstart > 0) { %offset = %rstart }
      if (%rlen > 0) { mws.pd Rangelen. $+ %sockname $calc(%rlen + 1) }
      mws.outheader %sockname Content-Range: bytes $+($iif(%rstart,%rstart,0),-,$iif(%rlen,%rlen,0),/,$file(%filename).size)
      mws.outheader %sockname _response_ HTTP/1.1 206 Partial content
      hdel -w $mws.ht.outheaders $+(%sockname,.Content-length:*)
    }
    */

    ;## Send the headers
    mws.SendHeaders %sockname %filename
    ;## Output to http log
    if ($isalias(mws.http.log)) {
      mws.http.log $sock(%sockname).ip - - $asctime($ctime,[dd/mmm/yyyy:HH:nn:ss zz]) $mws.quote($mws.inheader(%sockname,_request_)) $mws.gd($+(%sockname,respcode)) $file(%filename).size
    }

    ;## Stop here if it was a head request
    if ($mws.method(%sockname) == head) { 
      .timer -m 1 20 sockclose %sockname
      return
    }
  }
  %filesize = $file(%filename).size
  if (!$exists(%filename)) { mws.dbg -e Filename for sockwriting did not exist (likely not a problem) File: %filename Sock: $sock(%sockname) | sockclose %sockname | return }
  var %sending = $true

  ;## var %gkey = %sockname $+ .gzip
  ;## Some vars for gzip encoding
  ;## if ($mws.gd(%gkey) == true) {
  ;##   var %usegzip = 1, %greturn
  ;## }
  while (%sending) {
    ;## CPS check
    %curcps = $mws.totcps
    var %rlen = $mws.gd(Rangelen. $+ %sockname)
    if (%curcps > %maxcps) && (%maxcps > 0) {
      %sending = $false
      break
    }
    if (!$sock(%sockname)) { mws.dbg -e Socket %sockname does not exist anymore, aborting filesend (No problem) | return }
    if ((%offset >= %filesize) || (%offset >= %rlen)) { Mws.FinishSend %sockname | return }
    if ((%rlen > 0) && (%rlen != $null)) {
      ;## %rlen represents where to stop reading data
      if ($calc(%offset + %chunk) > %rlen) {
        %chunk = $calc(%rlen - %offset)
      }
    }
    if ($calc($sock(%sockname).sq + %chunk) > %maxbuf) {
      %sending = $false 
      break
    }
    else {
      bread %filename %offset %chunk &data
      sockwrite %sockname &data
      %offset = $calc(%offset + %chunk)
    }
  }
  sockmark %sockname $+(-b,%offset) %filename
  ;timer $+ %sockname -m 1 30 Mws.sendfile %sockname $+(-b,%offset) %filename
}

On *:sockwrite:mws.*: {
  if ($sockerr) || ($sockname == $mws.ListenName) { mws.dbg File sending / sockwrite error $sockerr (Most likely no problem) | return }
  if ($sock($sockname).sq == 0) {
    ;timer $+ %sockname off
    Mws.sendfile $sockname $sock($sockname).mark
  }
}

;## Sends http headers to the given socket
Alias MWS.SendHeaders {
  var %sockname = $1, %filename = $2-, %item
  if (!$sock(%sockname)) { mws.dbg -e Socket %sockname did not exist when trying to send headers | return }
  var %i = 1, %t = $hget($mws.ht.outheaders,0).item, %sheader, %sdata
  sockwrite -n %sockname $mws.outheader(%sockname,_response_)
  hdel -w $mws.ht.outheaders $+(%sockname,._response_*)
  while (%i <= %t) {
    %item = $hget($mws.ht.outheaders,%i).item
    if (%sockname isin %item) {
      %sheader = $mid(%item,$calc($len(%sockname) + 2))
      %sdata = $hget($mws.ht.outheaders,%item)
      if (Set-cookie:.* iswm %sheader) {
        %sheader = $gettok(%sheader,1,46)
      }
      sockwrite -n %sockname %sheader %sdata
    }
    inc %i
  }
  sockwrite -n %sockname
}

;## Loop while the buffer is not empty and then close the connection
Alias MWS.FinishSend {
  var %sockname = $1
  if ($sock(%sockname).sq > 0) {
    .timer -m 1 1 mws.finishsend %sockname
  }
  else {
    sockclose %sockname
  }
}

;## Returns total CPS in use by MotionWS
alias mws.totcps {
  var %i = 1, %t = $sock(mws.*,0), %sock, %tcps
  while (%i <= %t) {
    %sock = $sock(mws.*,%i)
    if (%sock == $mws.listenname) { inc %i | continue }
    %tcps = $calc(%tcps + ($sock(%sock).sent / $sock(%sock).to))
    inc %i
  }
  return %tcps
}

Alias MWS.rb { return $chr(125) }
Alias MWS.lb { return $chr(123) }

;## Parses an mhtml file
Alias MWS.Mhtml {
  var %rec = $false, %s, %file, %reclevel

  ;## Check for recursion
  if ($1 == -r) { %rec = $true |  %s = $2 | %reclevel = $3 | inc %reclevel | %file = $4- }
  else { %s = $1 | %file = $2- | %reclevel = 1 }
  var %mhandle = MWS.Mhtml, %trhandle = $mws.gettempfilename(%s,-nToRun).name, %tshandle = $mws.gettempfilename(%s,-nToSend).name
  var %torun = $mws.gettempfilename(%s,-nToRun).filename, %tosend = $mws.gettempfilename(%s,-nToSend).filename
  %torun = $mws.quote(%torun) | %tosend = $mws.quote(%tosend)

  ;## Add quotes to file
  %file = $mws.quote(%file)

  ;## If the previous source file handle was open, close it, as we're going to open another one
  ;## The other file streams can stay open
  if ($mws.isfileopen(%mhandle)) { .fclose %mhandle }

  ;## Open file streams
  if (%rec) {
    ;## We are in recursive parsing mode, close previous and open new one
    if ($mws.isfileopen(%mhandle)) {
      .fclose %mhandle
    }
    .fopen %mhandle %file
  }
  else {
    .fopen %mhandle %file
  }
  if (!$mws.isfileopen(%trhandle)) { .fopen $iif(!$exists(%torun),-n) %trhandle $mws.quote(%torun) }
  if (!$mws.isfileopen(%tshandle)) { .fopen $iif(!$exists(%tosend),-n) %tshandle $mws.quote(%tosend) }
  if (!%rec) {
    ;## Write header aliases
    .fwrite -n %trhandle alias -l mhtml.print $mws.lb if ((!$1) || ($1 == -n && !$2)) $mws.lb return $mws.rb $chr(124) if ($1 == -n) $mws.lb .fwrite -n %tshandle $eval($2-,0) $mws.rb $chr(124) else $mws.lb .fwrite %tshandle $eval($1-,0) $mws.rb $mws.rb
    .fwrite -n %trhandle alias -l mhtml.isset $MWS.lb if ($get($1)) $mws.lb return $eval($true,0) $mws.rb $chr(124) else $mws.lb return $eval($false,0) $mws.rb $MWS.rb
    .fwrite -n %trhandle alias -l mhtml.get $MWS.lb return $eval($,0) $+ mws.gq( $+ %s $+ ,$1).item $MWS.rb
    ;## .fwrite -n %trhandle alias -l mhtml.post $MWS.lb return $eval($,0) $+ mws.gp( $+ %s $+ ,$1).item $MWS.rb
    .fwrite -n %trhandle alias -l mhtml.rootdir $MWS.lb return $nofile(%file) $MWS.rb $+ $crlf
    .fwrite -n %trhandle alias -___runme $MWS.lb
  }
  var %i = 1, %t = $lines(%file), %line, %inscript = $false, %amatch = alias $+(*,$MWS.lb,*), %numlb, %numrb, %il,%aline, %incendpos, %returncode, %window = $+(@,%s)
  if (!$window(%window)) {
    window -lh %window 0 0 0 0
  }
  while (%i <= %t) {
    .fseek -l %mhandle %i
    %line = $fread(%mhandle)

    ;## check for script start
    if (%line == <?mhtml) {
      %inscript = $true
      inc %i | continue
    }

    ;## check for script end
    if (%line == ?>) {
      %inscript = $false
      inc %i | continue
    }

    ;## Check if we're currently inside script code, check for aliases, copy it to the ToRun file
    if (%inscript == $true) {
      if (%amatch iswm %line) {
        ;## Got an alias, copy it to the top of the ToRun file
        %numlb = 0 | %numrb = 0 | %aline = %i
        ;## Loop while there's an uneven number of curly brackets
        %il = 1
        while (%i <= %t) {
          .fseek -l %mhandle %i
          %line = $fread(%mhandle)
          inc %numlb $count(%line,$MWS.lb) | inc %numrb $count(%line,$MWS.rb)
          ;## Copy line to window
          if (%line) { aline %window %line }
          inc %il | inc %i
          if (%numlb == %numrb) { break }
          if (%i > %t) { mws.dbg Bracket mismatch error in mhtml file %file on line %aline $+ , aborting execution | return $false }
        }
        continue
      }
      if ($mid(%line,1,14) == $+(mhtml.include,$chr(32))) {
        ;## Include another file
        %returncode = $mws.mhtml.rec(%s,%reclevel,$+($nofile(%file),$mid(%line,15)))
        if (%returncode == $false) { return $false }
        .fclose %mhandle
        .fopen %mhandle %file

      }
      else {
        ;## Usual script code, write directly to file
        if (%line) {
          .fseek -l %trhandle $lines(%torun) | .fseek -n %trhandle
          .fwrite -n %trhandle %line
        }
      }
    }
    else {
      ;## Check if it's a special include directive
      if ($mid(%line,1,12) == $+(<?mhtml.inc,$chr(32))) {
        %returncode = $mws.mhtml.rec(%s,%reclevel,$+($nofile(%file),$mid(%line,13,-2)))
        if (%returncode == $false) { return $false }
        .fclose %mhandle
        .fopen %mhandle %file

      }
      else {
        ;## Normal html code, write print command + html with parsed characters
        .fseek -l %trhandle $lines(%torun) | .fseek -n %trhandle
        .fwrite -n %trhandle mhtml.print -n $mws.mhtml.parsechars(%line)
      }
    }
    inc %i
  }
  if (!%rec) {
    ;## Write final curly bracket
    write %torun $MWS.rb
    ;## Save aliases from alias window
    savebuf -a %window %torun
    ;## Load script
    .load -rs %torun
    -___runme
    .unload -rs %torun
    .timer -m 1 100 .window -c %window
    .fclose %trhandle | .fclose %mhandle
    ;## Send the output
    mws.sendtempfile %s -nToSend
    return $true
  }
}

;## Recurses an mhtml file when it's included into another mhtml file
Alias MWS.Mhtml.Rec {
  if ($2 == 100) { return $false }
  return $mws.mhtml(-r,$1,$2,$3-)
}

;## Replaces curly brackets with $chr counterparts to avoid problems with the mIRC parser
Alias MWS.Mhtml.parsechars {
  var %in = $1-
  var %m1 = $+($chr(32),$MWS.lb,$chr(32))
  var %m2 = $+($chr(32),$MWS.rb,$chr(32))
  var %m3 = $+($chr(32),$chr(124),$chr(32))
  var %r1 = $+($chr(32),$eval($chr(123),0),$chr(32))
  var %r2 = $+($chr(32),$eval($chr(125),0),$chr(32))
  var %r3 = $+($chr(32),$eval($chr(124),0),$chr(32))
  %in = $replace(%in,%m1,%r1,%m2,%r2,%m3,%r3)
  if (%in == $chr(123)) { %in = $eval($chr(123),0) }
  if (%in == $chr(125)) { %in = $eval($chr(125),0) }
  return %in
}



;######################################## AutoUpdate ########################################
Alias mws.gotfile {
  if ($right($1-,20) == motionws_update.zip") {
    mws.echo Updating MotionWS..
    var %u = $1-
    var %s = $dll($mws.quote($mws.dll.zip), Unzip, -od %u $mws.quote($scriptdir))
    if ($gettok(%s,1,32) != S_OK) { mws.echo Error while applying update: %s }
    if ($exists(%u)) { .remove %u }
    mws.echo Please wait..
    mws.loadtables
    if ($mws.isrunning) { mws.stop | mws.pd wasrunning $true }
    else { mws.pd wasrunning $false }
    mws.pd updated $true
    .load -rs $mws.quote($script)
  }
  if ($right($1-,13) == versions.txt") {
    ;## Check for newer version
    var %build = $read($1-,n,1), %time = $gettok(%build,2,95), %curtime = $gettok($mws.build,2,95)
    if ($calc(%curtime - %time) < 0) {
      ;## New update available
      mws.echo New version available!
      var %dname = mws.newversion
      if (!$dialog(%dname)) { dialog -m %dname %dname }
      var %i = 1, %f = $mws.quote($1-), %t = $lines(%f)
      did -a %dname 1 Version %build $+ : $+ $crlf $+ $crlf
      loadbuf $+(2-,%t) -o %dname 1 $mws.quote($1-)
    }
    else {
      mws.echo No new version
    }
    if ($exists($1-)) { .remove $1- } 
  }
}

;## Get's the latest update, regardless of version
Alias mws.update { if (%mwsdev == true) { echo -s This is the dev build, stupid | return } | mws.stop | mws.getfile mws.ileet.net 80 /updates/motionws_update.zip }

;## Checks if there is a new STABLE release available for update
Alias mws.updatecheck { mws.getfile mws.ileet.net 80 /updates/versions.txt }

;## Downloads a file
Alias mws.getfile {
  var %addr = $1, %port = $2, %s = mwsupdate, %getfile = $3-
  sockclose %s
  sockopen %s %addr %port
  sockmark %s %getfile %addr
  mws.echo Connecting to $+(%addr,:,%port) $+ ..
}

On *:sockopen:mwsupdate: {
  if ($sockerr) { mws.echo Socket error $sockerr (sockopen), aborting | return }
  var %s = $sockname, %getfile = $gettok($sock(%s).mark,1,32), %addr = $gettok($sock(%s).mark,2,32), %of = $mws.quote($+($scriptdir,$nopath(%getfile)))
  write -c %of
  %getfile = $mws.rawurlencode(%getfile)
  mws.echo Connected to $sock(%s).ip $+ , sending request..
  sockwrite %s $+(GET %getfile HTTP/1.0,$crlf,Host: %addr,$crlf,Connection: Close,$crlf,User-agent: MWS,$crlf,Accept: */*,$crlf,$crlf)

}
On *:sockread:mwsupdate: {
  if ($sockerr) { mws.echo Socket error $sockerr (sockread), aborting | return }
  var %s = $sockname, %of = $mws.quote($+($scriptdir,$nopath($gettok($sock(%s).mark,1,32)))), %read = $true
  while (%read) {
    sockread &data
    if ($bvar(&data,0)) { bwrite %of -1 -1 &data }
    if (!$sockbr) { %read = $false }
  }
}

On *:sockclose:mwsupdate: {
  var %s = $sockname, %of, %offset
  mws.echo Connection to $+($sock(%s).ip,:,$sock(%s).port) closed, file recieved
  %of = $mws.quote($+($scriptdir,$nopath($gettok($sock(%s).mark,1,32))))
  bread  %of 0 $file(%of).size &file
  %offset = $calc($bfind(&file, 1, 13 10 13 10) + 4)
  bcopy &file2 1 &file %offset -1
  write -c %of
  bwrite %of -1 -1 &file2
  mws.gotfile %of
}


;######################################## Dialogs below ########################################



dialog mws.config {
  title "MotionWS-2 Configuration"
  size -1 -1 477 270
  option pixels
  icon 1, 0 0 476 60
  tab "Main", 2, 2 65 471 174
  box "Sockets/Files", 7, 9 94 238 135, tab 2
  text "Listen on port:", 8, 20 108 168 16, tab 2 right
  edit "", 9, 191 107 49 21, tab 2 multi autohs autovs
  text "HTdocs:", 10, 19 133 49 16, tab 2 right
  edit "", 11, 71 131 139 21, tab 2 multi autohs autovs
  button "...", 12, 213 132 25 20, tab 2
  text "Directory index:", 13, 19 158 97 16, tab 2 right
  edit "", 14, 118 156 121 21, tab 2 multi autohs autovs
  text "Htaccess filename:", 15, 18 183 97 16, tab 2 right
  edit "", 16, 118 180 121 21, tab 2 multi autohs autovs
  box "Logging/Debugging", 17, 252 94 215 135, tab 2
  check "Echo debug info", 18, 259 116 100 20, tab 2
  check "Debug log", 19, 259 141 71 20, tab 2
  edit "", 20, 332 141 99 21, tab 2 multi autohs autovs
  button "...", 21, 434 142 25 20, tab 2
  check "Http log", 22, 259 166 64 20, tab 2
  edit "", 23, 332 167 99 21, tab 2 multi autohs autovs
  button "...", 24, 434 167 25 20, tab 2
  text "('Http log' uses the standard http log format)", 25, 299 192 156 29, tab 2
  text "Server name:", 45, 18 206 97 16, tab 2 right
  edit "", 46, 118 204 121 21, tab 2 multi autohs autovs
  tab "Advanced", 3
  box "Sockets", 26, 9 94 181 132, tab 3
  text "Max upload speed:", 27, 15 116 108 16, tab 3 right
  edit "", 28, 126 113 36 21, tab 3 multi autohs autovs
  text "KB/s", 29, 164 116 23 16, tab 3
  text "Note: Set the value to 0 for unlimited bandwidth usage.", 127, 25 146 138 40, tab 3
  box "Folder properties", 30, 196 94 271 132, tab 3
  check "Folders can be listed by default", 31, 205 112 174 20, tab 3
  check "Folders can be downloaded from by default", 32, 205 135 233 20, tab 3
  text "Date format:", 33, 206 160 70 16, tab 3 right
  edit "", 34, 278 157 130 21, tab 3 multi autohs autovs
  tab "Misc", 40
  check "Check for updates on webserver start", 41, 11 96 217 20, tab 40
  check "Use GZIP compression where available", 42, 11 120 208 20, disable tab 40
  check "Enable CGI module", 43, 11 144 118 20, tab 40
  tab "About", 4
  text "MotionWS2", 35, 12 98 454 16, tab 4 center
  text "Created by oracel", 36, 375 214 92 16, tab 4
  edit "", 37, 7 118 461 91, tab 4 read multi return vsbar
  link "Visit the MotionWS2 webpage", 38, 9 214 155 16, tab 4
  button "Ok", 5, 317 241 75 25, default ok
  button "Cancel", 6, 398 241 75 25, cancel
  text "Note: Many of these settings require that you restart MotionWS2 for them to take effect.", 39, 13 239 249 28
  link "Need help?", 44, 409 65 60 16
}

dialog mws.newversion {
  title "MotionWS  - New version available!"
  size -1 -1 209 139
  option dbu
  edit "", 1, 1 12 207 111, read multi vsbar
  text "New version available! Check out what's new:", 2, 2 3 204 8
  button "Download", 3, 128 125 37 12, ok default
  button "Cancel", 4, 170 125 37 12, cancel
}

dialog mws.vfolders {
  title "Virtual folders managment"
  size -1 -1 108 130
  option dbu
  list 1, 1 1 68 128, sort size vsbar hsbar
  button "Add", 2, 70 2 37 12
  button "Remove", 3, 70 16 37 12, disabled
  button "Edit", 4, 70 30 37 12, disabled
  button "Close", 5, 70 117 37 12, ok
}


;######################################## Dialog events & commands ########################################
On *:Dialog:MWS.Config:*:*: {
  var %dname = mws.config
  if ($devent == init) {
    if (!$mws.isrunning) { mws.loadtables }
    loadbuf -o %dname 37 $mws.quote($+($mws.docsdir,\about.txt))
    did -g %dname 1 $mws.quote($+($mws.datadir,\top.png))
    did -a %dname 9 $mws.gs(ListenPort)
    did -a %dname 11 $mws.gs(HTdocs)
    did -a %dname 46 $mws.gs(ServerName)
    did -a %dname 14 $mws.gs(DirectoryIndex)
    did -a %dname 16 $mws.gs(AccessFile)
    if ($group(#mws.debug).status == on) { did -c %dname 18 }
    if ($group(#mws.debuglog).status == on) { did -c %dname 19 }
    if ($group(#mws.httplog).status == on) { did -c %dname 22 }
    did -a %dname 20 $mws.gs(Logfile)
    did -a %dname 23 $mws.gs(HttpLogFile)
    did -a %dname 28 $int($calc($mws.gs(MaxCPS) / 1024))
    if ($mws.gs(deflist) == true) { did -c %dname 31 }
    if ($mws.gs(defget) == true) { did -c %dname 32 }
    if ($mws.gs(cgi) == true) { did -c %dname 43 }
    if ($mws.gs(CheckForUpdateOnStart) == true) { did -c %dname 41 }
    did -a %dname 34 $mws.gs(DateFormat)
    if ($did(19).state == 1) { did -e $dname 20,21 }
    else { did -b $dname 20,21 }
    if ($did(22).state == 1) { did -e $dname 23,24 }
    else { did -b $dname 23,24 }

  }
  if ($devent == sclick) && ($did == 5) {

    mws.ps Listenport $did(9).text
    mws.ps Htdocs $did(11).text
    mws.ps DirectoryIndex $did(14).text
    mws.ps AccessFile $did(16).text
    if ($did(18).state == 1) { .enable #mws.debug } | else { .disable #mws.debug }
    if ($did(19).state == 1) { .enable #mws.debuglog } | else { .disable #mws.debuglog }
    if ($did(22).state == 1) { .enable #mws.httplog } | else { .disable #mws.httplog }
    mws.ps LogFile $did(20).text
    mws.ps HttpLogFile $did(23).text
    mws.ps ServerName $did(46).text
    mws.ps MaxCPS $calc($did(28).text * 1024)
    mws.ps DefList $replace($did(31).state,1,True,0,False)
    mws.ps DefGet $replace($did(32).state,1,True,0,False)
    mws.ps CGI $replace($did(43).state,1,True,0,False)
    mws.ps CheckForUpdateOnStart $replace($did(41).state,1,True,0,False)
    mws.ps DateFormat $did(34).text
    if (!$mws.isrunning) { mws.savetables }
  }
  if ($devent == sclick) {
    if ($did == 19) {
      if ($did(19).state == 1) { did -e $dname 20,21 }
      else { did -b $dname 20,21 }
    }
    if ($did == 22) {
      if ($did(22).state == 1) { did -e $dname 23,24 }
      else { did -b $dname 23,24 }
    }
    if ($did == 12) {
      did -ra $dname 11 $$sdir($mircdir)
    }
    if ($did == 21) {
      did -ra $dname 20 $$sfile($mircdir)
    }
    if ($did == 24) {
      did -ra $dname 23 $$sfile($mircdir)
    }

    if ($did == 38) {
      run http://mws.ileet.net
    }
    if ($did == 44) {
      run $mws.quote($scriptdirdocs\help.html)
    }

  }
}
On *:Dialog:MWS.Newversion:sclick:3: {
  mws.update
}


on *:dialog:mws.vfolders:*:*: {
  var %tbl = $mws.ht.vfolders
  if ($devent == init) {
    if (!$mws.isrunning) { mws.loadtables }
    mws.addvfitems $dname
  }
  if ($devent == sclick) {
    if ($did(1).seltext) { did -e $dname 3,4 }
    else { did -b $dname 3,4 }

    if ($did == 2) {
      if (!$dialog(mws.addvfolder)) { var %x = $dialog(mws.addvfolder,mws.addvfolder,-4) }
    }
    if ($did == 4) {
      mws.pd vfolders.edit true
      mws.pd vfolders.vfolder $mws.rawurlencode($did(1).seltext)
      if (!$dialog(mws.addvfolder)) { var %x = $dialog(mws.addvfolder,mws.addvfolder,-4) }
      mws.pd vfolders.edit false
      mws.pd vfolders.vfolder
    }
    if ($did == 3) {
      var %f = $mws.rawurlencode($did(1).seltext), %ln = $did(1,1).sel
      mws.deletevirtualfolder %f
      mws.addvfitems $dname
      did -c $dname 1 %ln
    }
  }
  if ($devent == close) {
    if (!$mws.isrunning) { mws.savetables }
  }
}
alias mws.addvfitems {
  var %tbl = $mws.ht.vfolders, %i = 1, %t = $hget(%tbl,0).item
  did -r $1 1 
  while (%i <= %t) {
    did -a $1 1 $mws.rawurldecode($hget(%tbl,%i).item)
    inc %i
  }
}

dialog mws.addvfolder {
  title "Virtual folder mapping"
  size -1 -1 132 91
  option dbu
  edit "/foldername", 1, 52 1 79 10, multi autohs autovs
  edit "", 2, 52 12 64 10, multi autohs autovs
  button "...", 3, 118 12 13 10
  check "The contents of this folder can be listed", 4, 1 24 107 10
  check "The contents of this folder can be downloaded", 5, 1 33 122 10
  text "Virtual folder name:", 6, 0 2 50 8, right
  text "Physical folder:", 7, 0 13 50 8, right
  edit "", 8, 42 45 50 10
  text "Username:", 9, 7 46 34 8, right
  text "Password:", 10, 7 58 34 8, right
  edit "", 11, 42 57 50 10, pass
  box "", 12, 1 70 130 4
  button "Ok", 13, 53 77 37 12, ok
  button "Cancel", 14, 93 77 37 12, cancel
}

on *:dialog:mws.addvfolder:*:*: {
  if ($devent == init) {
    did -c $dname 4,5
    if ($mws.gd(vfolders.edit) == true) {
      var %vfolder = $mws.gd(vfolders.vfolder), %rfolder = $mws.getvirtualfolder(%vfolder), %get = $mws.getvirtualfolder(%vfolder).get, %list = $mws.getvirtualfolder(%vfolder).list, %username = $mws.getvirtualfolder(%vfolder).username, %password = $mws.getvirtualfolder(%vfolder).password
      did -ra $dname 1 %vfolder
      did -a $dname 2 %rfolder
      if (%get != true) { did -u $dname 5 }
      if (%list != true) { did -u $dname 4 }
      did -a $dname 8 %username
      did -a $dname 11 %password
    }
  }
  if ($devent == sclick) {
    if ($did == 13) {
      var %vfolder = $did(1).text, %rfolder = $did(2).text, %get = $did(5).state, %list = $did(4).state, %username = $did(8).text, %password = $did(11).text

      if (!%vfolder) { mws.errmsg No virtual folder specified | halt }
      if (!%rfolder) { mws.errmsg No physical folder specified | halt }
      if (%username && !%password) { mws.errmsg No password specified | halt }
      if (%vfolder == /) { mws.errmsg You can not map "/" as a virtual folder. Please use your Htdocs directory as root directory | halt }
      ;var %sw = $iif(%get || %list || %username, $+(-,$iif(%list,l),$iif(%get,g),$iif(%username,p)))
      var %sw
      %sw = -
      if (%get == 1) { %sw = $+(%sw,g) }
      if (%list == 1) { %sw = $+(%sw,l) }
      if (%username) { %sw = $+(%sw,p) }

      if (%sw == -) { var %sw }
      mws.addvirtualfolder %sw %username %password $mws.rawurlencode(%vfolder) %rfolder
      mws.addvfitems mws.vfolders
    }
    if ($did == 3) {
      did -ra $dname 2 $sdir($mircdir)
    }
  }
}
dialog mws.cgi {
  title "CGI Setup"
  size -1 -1 174 48
  option dbu
  combo 1, 43 2 91 120, size drop
  button "Add..", 2, 137 2 37 12
  button "Remove", 3, 137 18 37 12
  button "Close", 4, 137 34 37 12, ok
  text "CGI executable:", 5, 1 16 40 8, right
  edit "", 6, 42 15 79 10, multi autohs
  button "...", 7, 122 15 12 10
  link "Need help?", 10, 52 37 30 8
  text "File extension:", 8, 2 4 39 8, right
}

on *:dialog:mws.cgi:*:*: {
  var %tbl = $mws.ht.cgi, %dlg = mws.cgi
  if ($devent == init) {
    mws.loadtables
    var %i = 1, %t = $hget(%tbl,0).item
    while (%i <= %t) {
      did -a %dlg 1 $hget(%tbl,%i).item
      inc %i
    }
  }
  if ($devent == edit) {
    var %item = $did(1).seltext
    if (%item) {
      mws.pc %item $did(6).text
    }
  }
  if ($devent == sclick) {
    if ($did == 3) {
      hdel $mws.ht.cgi $did(1).seltext
      did -r %dlg 1,6
      var %i = 1, %t = $hget(%tbl,0).item
      while (%i <= %t) {
        did -a %dlg 1 $hget(%tbl,%i).item
        inc %i
      }
    }
    if ($did == 1) {
      did -ra %dlg 6 $mws.gc($did(1).seltext)
    }
    if ($did == 7) {
      did -ra %dlg 6 $$sfile($mircdir, Select CGI executable, Ok)
      ;## Change hash value here
      var %item = $did(1).seltext
      if (%item) {
        mws.pc %item $did(6).text
      }
    }
    if ($did == 2) {
      var %item = $$input(Enter new file extension (no periods):,eq,New CGI file extension)
      mws.pc %item
      did -r %dlg 1,6
      var %i = 1, %t = $hget(%tbl,0).item
      while (%i <= %t) {
        did -a %dlg 1 $hget(%tbl,%i).item
        inc %i
      }
    }
    if ($did == 10) {
      run $mws.quote($scriptdirdocs\help.html)
    }
    if ($did == 4) {
      mws.savetables
    }
  }
}
dialog mws.connections {
  title "MotionWS Connections"
  size -1 -1 209 102
  option dbu
  list 1, 0 1 209 86, size
  button "Close", 2, 171 89 37 12, ok
}
alias mws.connections {
  var %tbl = mws.connections
  if (!$dialog(%tbl)) {
    dialog -m %tbl %tbl
  }
  .timermwsconnections -m 0 100 $!mws.connections.add
}

alias mws.connections.add {
  var %tbl = mws.connections
  if (!$dialog(%tbl)) {
    .timermwsconnections off
    return  
  }
  did -r %tbl 1
  var %i = 1, %t = $sock(mws.*,0), %sck, %totcps = 0, %totsockets = 0
  while (%i <= %t) {
    %sck = $sock(mws.*,%i)
    if (%sck == $MWS.ListenName) || (mws.* !iswm %sck) { inc %i | continue }
    inc %totsockets
    var %timeopen = $iif($sock(%sck).to == 0,1,$sock(%sck).to), %cps = $calc($sock(%sck).sent / %timeopen)
    inc %totcps %cps
    did -a %tbl 1 Sock %totsockets $+ : %sck $+ /IP: $sock(%sck).ip $+ /Open: $duration($sock(%sck).to) $+ /Upload: $round($calc(%cps  / 1024),2) $+ KB/sec
    inc %i
  }
  did -a %tbl 1 ---
  var %maxcps = $mws.gs(MaxCPS)  
  if (%maxcps > 0) {
    did -a %tbl 1 Currently %totsockets sockets, using $round($calc(%totcps / 1024),2) $+ KB/sec (Maximum $calc($mws.gs(MaxCPS) / 1024) $+ KB/sec allowed) 
  }
  else { 
    did -a %tbl 1 Currently %totsockets sockets, using $round($calc(%totcps / 1024),2) $+ KB/sec (No bandwidth limit) 
  }
}

Alias MWS.Errmsg {
  var %x = $input($1-,oh,Error)
}

;######################################## CGI ########################################

Alias mws.cgi {
  if (!$1-) { mws.managecgi | return }
  ;## /mws.cgi <sockname> <type> <vfolder> <rfolder>
  var %ticks = $ticks
  ;## Set some initial variables
  var %s = $1, %type = $2, %vfolder = $3, %rfolder = $4-, %exec = $shortfn($mws.gc(%type)), %reqm = $gettok($mws.inheader(%s,_request_),1,32), %qstring1 = $gettok(%vfolder,2,63), %qstring2 = $+(?,%qstring1)
  mws.dbg CGI: Generating filenames for output

  ;## Generate a primary output filename
  var %outpri = $mws.gettempfilename(%s,-nCGI.Primary).filename, %hpri = $mws.gettempfilename(%s,-nCGI.Primary).name, %cgioutput = $mws.gettempfilename(%s,-nCGI.Output).filename, %wfilename = $mws.gettempfilename(%s,-nPostData).filename
  %outpri = $mid(%outpri,1,-3) $+ bat

  var %vbfile = $+($scriptdir,temp\,%s,.Env.vbs), %vb = mws.vbscript
  var %w = .fwrite

  .fopen -n %vb $mws.quote(%vbfile)
  .fopen -n %hpri $mws.quote(%outpri)

  %w -n %vb Set WshShell = WScript.CreateObject("WScript.Shell") $+ $crlf $+ Set WshProcessEnv = WshShell.Environment("PROCESS")

  ;## Generate a secondary output file
  var %outsec = $mws.gettempfilename(%s,-nCGI.Secondary).filename

  ;## Write standard CGI headers to the batch file
  mws.dbg CGI: Setting Environmental variables
  %w -n %vb WshProcessEnv("GATEWAY_INTERFACE") = $mws.quote(CGI/1.1)
  %w -n %vb WshProcessEnv("SERVER_SOFTWARE") = $mws.quote($mws.serverreply)
  %w -n %vb WshProcessEnv("SERVER_PROTOCOL") = $mws.quote(HTTP/1.1)
  %w -n %vb WshProcessEnv("REDIRECT_STATUS") = $mws.quote(200)
  %w -n %vb WshProcessEnv("REQUEST_METHOD") = $mws.quote(%reqm)
  %w -n %vb WshProcessEnv("REQUEST_URI") = $mws.quote(%vfolder)
  %w -n %vb WshProcessEnv("PATH_INFO") = $mws.quote(%vfolder)
  %w -n %vb WshProcessEnv("PATH_TRANSLATED") = $mws.quote($longfn(%rfolder))
  %w -n %vb WshProcessEnv("SCRIPT_FILENAME") = $mws.quote($longfn(%rfolder))
  %w -n %vb WshProcessEnv("DOCUMENT_ROOT") = $mws.quote($nofile($longfn(%rfolder)))
  %w -n %vb WshProcessEnv("SCRIPT_NAME") = $mws.quote($gettok(%vfolder,1,63))
  %w -n %vb WshProcessEnv("REMOTE_ADDR") = $mws.quote($sock(%s).ip)
  %w -n %vb WshProcessEnv("CONTENT_TYPE") = $mws.quote($mws.inheader(%s,Content-Type:))
  %w -n %vb WshProcessEnv("CONTENT_LENGTH") = $mws.quote($mws.inheader(%s,Content-Length:))
  %w -n %vb WshProcessEnv("REDIRECT_URL") = $mws.quote($gettok(%vfolder,1,63))
  %w -n %vb WshProcessEnv("SERVER_NAME") = $mws.quote($mws.gs(ServerName))
  %w -n %vb WshProcessEnv("SERVER_PORT") = $mws.quote($mws.gs(listenport))

  ;## If it's a GET or HEAD request, add querystring
  ;if (%reqm == GET) || (%reqm == HEAD) {
  %w -n %vb WshProcessEnv("QUERY_STRING") = $mws.quote(%qstring1)
  %w -n %vb WshProcessEnv("REDIRECT_QUERY_STRING") = $mws.quote(%qstring1)
  ;}

  ;## Loop to add additional headers from the incoming hashtable
  var %ht = $mws.ht.inheaders, %match = $+(%s,.*), %i = 1, %t = $hfind(%ht,%match,0,w), %hitem, %hdata, %hdr
  while (%i <= %t) {
    %hitem = $hfind(%ht,%match,%i,w)
    %hdata = $hget(%ht,%hitem)

    ;## All extra headers should be added as HTTP_ $+ <header>, with all -'s converted to underscore
    %hdr = $upper(HTTP_ $+ $replace($gettok(%hitem,3,46),-,_,:,))
    if (%hdr != HTTP_GET) && (%hdr != HTTP_POST) && (%hdr != HTTP_HEAD) && (%hdr != HTTP__REQUEST_) {
      %w -n %vb WshProcessEnv(" $+ %hdr $+ ") = $mws.quote(%hdata)
    }
    inc %i
  }

  ;## Append POST data if it applies
  if ($exists(%wfilename)) {
    ;## POST data applies
    var %postfile = < $mws.quote(%wfilename)
  }

  %w -n %vb Return = WshShell.Run(" $+ CMD /C "" $+ %outpri $+ "" $+ ",0)


  ;## Write exection line to batch file
  %w -n %hpri %exec $mws.quote($longfn(%rfolder)) %postfile > $mws.quote(%cgioutput)

  ;## Write echoline to batch file, needed to verify when the script has finished processing
  %w -n %hpri echo Done > $mws.quote(%outsec)
  .fclose %hpri
  .fclose %vb

  ;## Execute the batchfile through COM using Online's /xrun alias
  mws.dbg CGI: Processing done in $calc(($ticks - %ticks) / 1000) $+ s, Running WSH file and waiting for output..
  run -n $mws.quote(%vbfile)
  ;## Wait for the CGI executable to finish processing
  mws.cgi.wait %s
}
Alias MWS.vbw {
  var %s = $1, %var = $2, %val = $3-
  ;## Generate a enviromental setting vbscript filename
  var %vbfile = $+($scriptdir,temp\,%s,.Env.vbs)
  write $mws.quote(%vbfile) WshProcessEnv(" $+ $2 $+ ") = $mws.quote($3-)
}

Alias mws.cgi.wait {
  var %s = $1, %file = $mws.gettempfilename(%s,-nCGI.Secondary).filename
  if (!$exists($mws.quote(%file))) {
    ;## Delayed recursion
    .timermws.cgi. $+ %s -m 1 2 mws.cgi.wait %s
    return
  }

  ;## Get output filename
  var %outfile = $mws.gettempfilename(%s,-nCGI.Output).filename, %ohandle = $mws.gettempfilename(%s,-nCGI.Output).name

  ;## Open stream to file
  .fopen %ohandle $mws.quote(%outfile)

  ;## Loop while there's headers
  var %done = $false, %header, %len = 0, %recode, %cookies, %cookiedata
  while (!%done) {

    ;## Get the header lines
    %header = $fread(%ohandle)
    if (%header) {
      ;## If it's a status reply, reply to the client with 302 Found
      if ($gettok(%header,1,32) == Status:) {
        %recode = $gettok(%header,2,32)
      }
      else {
        if ($gettok(%header,1,32) == Set-Cookie:) {
          ;## Multiple cookie hack   
          %cookiedata = $mid(%header,13)
          %header = Set-cookie:. $+ %cookies %cookiedata
          inc %cookies
        }
        ;## Add header to the outheader hashtable
        mws.outheader %s %header
      }
    }
    else {
      ;## Empty line, calculate offset for messagebody data start
      var %offset = $fopen(%ohandle).pos
      %done = $true
      break
    }
  }
  if (%recode == 302) {
    ;## No point in continuing if it's a 302
    mws.response %s 302
    return
  }
  ;## Get filesize and load messagebody into a binary variable
  var %size = $file($mws.quote(%outfile)).size, %x = $fread(%ohandle,$calc(%size - %offset),&data)
  ;## Close the filehandle and empty the file
  .fclose %ohandle
  write -c $mws.quote(%outfile)
  ;## Reopen file, write data, close file
  .fopen %ohandle $mws.quote(%outfile)
  .fwrite -b %ohandle &data
  .fclose %ohandle
  ;## Send the file
  mws.sendfile %s %outfile
}

Alias MWS.Pbar {
  ;## Thanks to Eric^^ for this one
  ; /pbar -c [pos] [max] [text]
  ; the -c switch close the progress bar
  if ($1 == -c) { if ($window(@pbar)) window -c @pbar | return }
  if (!$window(@pbar))  window -BhaCdfopk +Ldf @pbar -1 -1 300 60  
  drawrect -rfn @pbar $rgb(220,220,220) 1 0 0 300 60
  drawline -rn @pbar $rgb(255,255,255) 2 0 60 0 0 300 0
  drawline -rn @pbar $rgb(128,128,128) 2 0 60 300 60 300 0
  if ($3 != $null) { 
    var %width = $calc(150 - ($width($3-,Verdana,11) /2))
    drawtext -rnc @pbar $rgb(170,170,170) Verdana 11 $calc(%width + 1) 10 290 20 $3-
    drawtext -rnc @pbar $rgb(text) Verdana 11 %width 9 290 20 $3-
  }
  drawrect -rn @pbar $rgb(frame) 1 10 29 280 20
  drawrect -rnf @pbar $rgb(134,187,246) 1 11 30 $calc(($1 / $2) *278) 18
  var %width = $width($+($calc(($1 / $2) *100),%),tahoma,11)
  drawdot @pbar
}