HTTPd — File Browser

HTTPdv1.0 / HTTPdv1.0 / HTTPd / HTTPd pages.mrc

HTTPd pages.mrc — 12.21 KB — Download this file

/*
Custom HTTPd mSL pages


Here are some useful examples and aliases for creating your own mSL pages!


TO AUTHORIZE POST BODIES:
You need to authorize POST body data with "alias httpd.post.<ALIAS NAME>" alias.
Check "httpd.page./post" for an example.

TO AUTHORIZE UPLOADS:
You need to authorize multipart/form-data with "alias httpd.upload.<ALIAS name>" alias.
Check "httpd.page./upload" for an example.


WARNING: NEVER TRUST ANYTHING THE CLIENT SENDS TO THE SERVER.
Always assume the client is trying to screw you, always check and sanitize.
*/


;$httpd.cookie(<hash table>,<cookie>) - Returns the content of the cookie or $null
alias httpd.cookie return $replacex($utfdecode($regsubex($gettok($wildtok($hget($1,Cookie:),$2 $+ =?*,1,32),2,61),/%(\w\w)/g,$chr($base(\t,16,10)))),+,$chr(32))

;$httpd.safe(<text>) - Returns HTML safe version of the input text.
alias httpd.safe return $replacex($utfdecode($1),&,&amp;,",&quot;,',&#039;,<,&lt;,>,&gt;)

;$httpd.unsafe(<text>) - Returns unsafe/normal version of the input text.
alias httpd.unsafe return $replacex($utfdecode($1),&amp;,&,&quot;,",&#039;,',&lt;,<,&gt;,>)

;$httpd.urldecode(<text>) - Returns plain text version of the given text.
alias httpd.urldecode return $regsubex($1,/%(\w\w)/g,$chr($base(\t,16,10)))

;$httpd.urlencode(<link>) - Returns percent encoded version of the given text.
alias httpd.urlencode return $regsubex($1,/([^-.~\w])/g,% $+ $base($asc(\t),10,16))





/*
http://127.0.0.1/mypage

Example of a simple mSL page.
*/
alias httpd.page./mypage {

  ;"200 OK" is the default response, so you don't necessarily need to include this line.
  ;This is here to point out that you can change the response code if you want.
  hadd $sockname Response 200 OK

  ;"text/html; charset=utf-8" is the default content-type, so you don't necessarily need to include this line.
  ;This is here to point out that you can change the content-type if you want.
  hadd $sockname Content-Type text/html; charset=utf-8

  ;Here's the HTML code itself.
  bset -t &h -1 <!DOCTYPE html>
  bset -t &h -1 <html>
  bset -t &h -1 <head>
  bset -t &h -1 <title>My first mSL page!</title>
  bset -t &h -1 </head>
  bset -t &h -1 <body>
  bset -t &h -1 <h2>My first mSL page!</h2>
  bset -t &h -1 <p>This is my first mSL page</p>
  bset -t &h -1 </body>
  bset -t &h -1 </html>

  ;When you've filled &h binary variable with whatever data you want, call "httpd.send" alias once.
  httpd.send $sockname

  ;The end.
}





/*
http://127.0.0.1/ip

Example of a bit more complicated mSL page.
*/
alias httpd.page./ip {

  ;Check for a transparent proxy.
  if ($hget($sockname,via:)) || ($hget($sockname,forwarded:)) || ($hget($sockname,x-forwarded-for:)) || ($hget($sockname,client-ip:)) set -l %ip $httpd.safe($v1)
  else set -l %ip

  ;Start of the HTML code
  bset -t &h -1 <!DOCTYPE html><html><head>

  ;If the client is behind a transparent proxy, form the output accordingly.
  if (%ip) bset -t &h -1 $+(<title>,%ip ( $+ $sock($sockname).ip $+ ),</title></head><body><p>Your IP address is: %ip,</p><p>Your proxy's IP address is: $sock($sockname).ip,</p>)
  else bset -t &h -1 $+(<title>,$sock($sockname).ip,</title></head><body><p>Your IP address is: $sock($sockname).ip,</p>)

  ;Client headers.
  bset -t &h -1 <p>Full header:</p><table><tr><th>Item</th><th>Data</th></tr>
  noop $hfind($sockname,*?:,0,w,bset -t &h -1 $+(<tr><td>,$httpd.safe($1),</td><td>,$httpd.safe($hget($sockname,$1)),</td></tr>))
  bset -t &h -1 </table></body></html>

  ;Everything done, send it to the client.
  httpd.send $sockname
}





/*
http://127.0.0.1/post

Example of POST body mSL page.

To enable POST bodies, include "alias httpd.post.<PAGE NAME HERE>" alias.
The server checks if this alias exists before proceeding with a POST request that has a body.
*/
alias httpd.post./post

alias httpd.page./post {

  ;Set string item value to %string variable.
  set -l %string $hget($sockname,item.string)

  ;Start of the HTML code.
  bset -t &h -1 <!DOCTYPE html><html>
  bset -t &h -1 <head>
  bset -t &h -1 <title>Binary tool</title>
  bset -t &h -1 </head>
  bset -t &h -1 </body>
  bset -t &h -1 <form action="/post" method="post">
  bset -t &h -1 <fieldset>
  bset -t &h -1 <legend>Enter your string</legend>
  bset -t &h -1 $+(<p><input type="text" name="string" autofocus autocomplete="off" value=",$httpd.safe(%string),"></p>)
  bset -t &h -1 <p><input type="submit" name="type" value="BIN2ASC"> <input type="submit" name="type" value="ASC2BIN"></p>
  bset -t &h -1 </fieldset>
  bset -t &h -1 </form>

  ;Check if string exists.
  if (%string != $null) {
    bset -t &h -1 <fieldset>

    ;Check what kind of conversion should be done.
    if ($hget($sockname,item.type) == bin2asc) {
      bset -t &h -1 <legend>BIN2ASC</legend>

      ;Check if the input is valid.
      if ($remove(%string,0,1)) bset -t &h -1 <p>Invalid input.</p>
      else {

        ;Convert the binary string to ASCII.
        bset -t &h -1 $+(<p>,$qt($httpd.safe(%string)) converts into:</p>)
        bset -t &h -1 $+(<p>,$httpd.safe($regsubex(%string,/(.{8})/g,$chr($base(\t,2,10)))),</p>)
      }
    }
    else {

      ;Convert the string to binary.
      bset -t &h -1 <legend>ASC2BIN</legend>
      bset -t &h -1 $+(<p>,$qt($httpd.safe(%string)) converts into:</p>)
      bset -t &h -1 $+(<p>,$httpd.safe($regsubex(%string,/(.)/g,$base($asc(\t),10,2,8))),</p>)
    }
    bset -t &h -1 </fieldset>
  }

  ;End of the HTML code.
  bset -t &h -1 </body>
  bset -t &h -1 </html>

  ;Everything done, send it to the client.
  httpd.send $sockname
}





/*
http://127.0.0.1/upload

Example of multipart upload mSL page.

To enable multipart/form-data uploads, include "alias httpd.upload.<PAGE NAME HERE>" alias.
The server checks if this alias exists before proceeding with multipart request.
*/
alias httpd.upload./upload

alias httpd.page./upload {

  ;Allowed file type, separated by a comma. Note: include dot in front of the file type.
  set -l %accept .mp4,.mkv,.webm,.png,.jpg,.jpeg,.gif,.mp3,.flac,.ogg

  ;Start of the page.
  bset -t &h -1 <!DOCTYPE html><html><head><title>File upload</title></head><body>
  bset -t &h -1 <form action="upload" enctype="multipart/form-data" method="post">

  ;Check if uploads are allowed on server side or not.
  if ($hget(httpd.config,AllowMultipartFormData)) && ($istok($hget(httpd.config,AllowedHTTPMethods),POST,32)) && ($isalias(httpd.upload./upload)) {
    bset -t &h -1 <fieldset>
    bset -t &h -1 <legend>File upload</legend>
  }
  else {
    bset -t &h -1 <fieldset disabled>
    bset -t &h -1 <legend>File upload (feature disabled on server side)</legend>
  }

  ;HTML file input with accepted file types.
  bset -t &h -1 $+(<p><input type="file" name="file" accept=",%accept," autofocus multiple required></p>)

  bset -t &h -1 <p><input type="submit" value="Upload"></p>
  bset -t &h -1 $+(<p><i>You can upload up to $hget(httpd.config,MaximumMultiPartItems) file(s) totaling $bytes($hget(httpd.config,MaximumMultipartSize)).suf at once.</i></p>)
  bset -t &h -1 </fieldset>
  bset -t &h -1 </form>

  ;Check if any files were uploaded.
  ;httpd.uploadedfiles item contains all the hashes, separated by an empty space.
  ;Each has is its own item on the $sockname hash table, and a file the cache directory.
  if ($hget($sockname,httpd.uploadedfiles)) {
    tokenize 32 $v1

    ;Path to your uploads folder in the root directory.
    set -l %folder $hget(httpd.config,RootDirectory) $+ uploads\

    ;If the uploads folder doesn't exist, create it.
    if (!$isdir(%folder)) mkdir $qt(%folder)

    bset -t &h -1 <fieldset>
    bset -t &h -1 <legend>Upload successful</legend>

    ;Go through each of the hashes in httpd.uploadedfiles.
    while ($1) {

      ;Original filename in sanitized form, but the length has not been checked.
      ;Eg. My "upload" file.exe --> My__upload__file.exe
      set -l %filename $gettok($hget($sockname,$1),1,32)

      ;Make sure the filename is not over 100 characters long.
      ;Must have enough room for the path and possible SHA1 hash.
      if ($len(%filename) > 100) %filename = $+($left(%filename,90),.,$left($gettok(%filename,-1,46),9))

      ;Checking if the file type is in the accepted file type list.
      if ($istok(%accept,. $+ $gettok(%filename,-1,46),44)) {

        ;Full path to the file, including quotation marks.
        ;Note: this fill will be deleted by the server when your page script finishes.
        set -l %tempfile $gettok($hget($sockname,$1),2-,32)

        ;Checking if %file exists and if it does, add random SHA1 hash to the name.
        while ($isfile($+(",%folder,%filename,"))) %filename = $+($gettok(%filename,-2--,46),_,$sha1(%filename $r(10000,99999)),.,$gettok(%filename,-1,46))

        ;Add file entry.
        bset -t &h -1 $+(<p><a target="_blank" href="uploads/,$httpd.urlencode(%filename),">,$httpd.safe(%filename),</a> ( $+ $bytes($file(%tempfile).size).suf $+ )</p>)

        ;Rename and move the temporary file to the uploads folder.
        .rename -o %tempfile $+(",%folder,%filename,")
      }
      else {

        ;File type wasn't in the accepted file type list.
        bset -t &h -1 $+(<p><s>,$httpd.safe(%filename),</s> (disallowed filet type)</p>)
      }

      ;Next hash.
      tokenize 32 $2-
    }

    bset -t &h -1 </fieldset>
  }

  ;Link to the uploads folder.
  bset -t &h -1 <p><a target="_blank" href="uploads\">Browse uploads</a></p>
  bset -t &h -1 </body></html>

  ;And now, let's send it all to the client.
  httpd.send $sockname
}





/*
http://127.0.0.1/chunked

Example of chunked transfer-encoding.

"$httpd.chunkedheader" identifier must be checked at the start of the page.
The identifier returns 1 for success, and 0 for failure. Failure can occur when
the client sends a HEAD request, in which case only the header is sent.

Call "httpd.sendchunked <sockname>" alias whenever you have some data to send.
Call "httpd.stopchunked <sockname>" alias when you want to end the stream.

Always make sure the socket exists before you call send/stop/reset commands.

Note: the socket will automatically be closed after a set amount of time.
Check "Keep-Alive time" in "Limitations" section in settings. (/HTTPD LIMITATIONS)
You can reset the idle time by calling "httpd.resetchunk <sockname>" command.
*/
alias httpd.page./chunked {

  ;Checking if chunked transfer can continue.
  if ($httpd.chunkedheader) {

    ;Change the content-type header to text/html.
    hadd $sockname Content-Type text/html; charset=utf-8

    ;Start of the HTML code.
    bset -t &h -1 <!DOCTYPE html><html><head><title>Chunked test</title></head><body>
    bset -t &h -1 <h2>A mighty short story</h2>

    ;Let's send t he start of the HTML code.
    ;Note: at this point of the process, the socket will always exist.
    httpd.sendchunked $sockname

    ;Set the story token number to 0.
    hadd %c.httpd.name storypart 0

    ;Start a timer to play out rest of the story.
    .timerhttpd.chunked. $+ $sockname 0 2 chunked.part $sockname
  }
}

alias -l chunked.part {

  ;Check if the socket still exists.
  if ($sock($1)) {

    ;Increase story token number by one.
    hinc $1 storypart

    ;Set the full story to %story variable.
    set -l %story Once upon a time...|There was a little HTTPd...|Written in mSL...|One day it decided to go on a little journey...|The little journey took it to chunked land...|In the chunked land the little HTTPd chunked chunks...|When it finally ran out of chunks...|It said goodbye and farewell.

    ;Check if the current story token exists.
    if ($gettok(%story,$hget($1,storypart),124)) {

      ;Set the story token to &h bvar.
      bset -t &h -1 $+(<p>,$v1,</p>)

      ;Send the data to the client.
      httpd.sendchunked $1
    }
    else {

      ;No more tokens left to send, end the story.
      bset -t &h -1 The end.</body></html>

      ;Send the end.
      httpd.sendchunked $1

      ;Stop the chunked transfer.
      httpd.stopchunked $1

      ;Stop the timer.
      .timerhttpd.chunked. $+ $1 off
    }
  }
  ;Turn the timer off if the socket doesn't exist.
  else .timerhttpd.chunked. $+ $1 off
}