Next Previous Contents

1. Implementation of PUT request handler

The standard Pi3Web server configuration raises a 501 error if request method handled by the default handler (passed the CGI- and ISAPI-handlers) is another than method GET or HEAD.


CheckType Condition="&not(&or(&cmpi($m,GET),&cmpi($m,HEAD)))" \ 
        StatusCode StatusCode="501"

The task is to build a handler for the PUT request method and to redirect PUT requests to this handler. It is possible to build this handler as a true server plugin with C++ using Pi3API function calls but I tried a CGI Perl script.

1.1 CGI program


#!/usr/bin/perl
if ((-e @ARGV[0]) && !(-w @ARGV[0])) {
   print "Content-type: text/plain\n\nPermission denied.";
}
else {
   open(FILE, "+>@ARGV[0]") || die "Can't open!";
   binmode FILE;
   binmode STDIN;
   read(STDIN,$in,$ENV{CONTENT_LENGTH});
   print FILE $in || die "Can't write!";
   close(FILE) || die "Can't close!";
   print "Content-type: text/plain\n\nUpload success.";
}; 

This script is a simple one. The stumble stone is not to forget to switch the file handles into binary mode if you are using Windows. Otherwise the STDIN is only read until the first EOF char occurs and the server resets the connection due to this incomplete read. A preference is to hand over the filename in the first command line argument.

1.2 Configuration changes

The changes in the configuration are 2 new objects:

  1. A flexible handler object named Put
  2. A CGIClass object named PutCGI

There are some changes in the mapping configuration to make PUT requests secure with authentication and in the http dispatcher configuration to raise the handler. The description of the in existence objects is abbreviated. The PutCGI object has comments for some configuration options.


<Object> 
        Name Start
        Class FlexibleHandlerClass
        ...
        Mapping Condition="&cmpi($m,PUT)" \
        PathMapper From="/upload/" To="Upload\" \
Action="&dbreplace(response,string,AuthenticationRealm,User)"
        Mapping Condition="&cmpi($m,PUT)" \
PathMapper From="/cgi-bin/" To="Cgi-Bin\" \
Action="&dbreplace(response,string,AuthenticationRealm,Administration)"
        Mapping Condition="&cmpi($m,PUT)" \
PathMapper From="/icons/" To="Icons\" \
Action="&dbreplace(response,string,AuthenticationRealm,Administration)"
       Mapping Condition="&cmpi($m,PUT)" \
PathMapper From="/" To="Webroot\" \
Action="&dbreplace(response,string,AuthenticationRealm,Administration)"
        ...
        # Default Mappings
</Object>

<Object>
        Name HTTPLogicObject
        Class HTTPDispatcherClass
       ...
       Handlers Start Scripts WinScripts FastCGIScripts ISAPI Put Default
       ...
 </Object>

<Object>
        Name Put
        Class FlexibleHandlerClass
        CheckAuth Authenticate
        CheckAuth ReturnCode ReturnCode=COMPLETED
        # No path checking 'cause also non existing files can be PUTted
        CheckPath ReturnCode ReturnCode=COMPLETED
        # Here I could place an additional filter to allow
        # only URL's beginning with allowed path 
        # CheckType Condition="&not(&regexp('/upload/*',$z))" \
        # StatusCode StatusCode="403"
        CheckType ReturnCode ReturnCode=COMPLETED
        CheckAccess ReturnCode ReturnCode="COMPLETED"
        # No access checking since fail for non existing files
        # CheckAccess AccessByFile RequirePermissions="W"
        Condition "&cmpi($m,PUT)"
        Handle PutCGI
</Object>

<Object>
       Name PutCGI
       Class CGIClass
       ...
       DefaultCommandLine "perl c:\\pi3web\\cgi-bin\\put.pl %p%q"
       ...
</Object> 

1.3 Security

Another current problem is that in existence mappings are not filtered for the request method. I corrected this very simple with adding a conditional mapping if request method is PUT and an authentication for this case.


        Mapping Condition="&cmpi($m,PUT)" \
PathMapper From="/upload/" To="Upload\" \
Action="&dbreplace(response,string,AuthenticationRealm,Administration)"
        Mapping Condition="&cmpi($m,PUT)" \
PathMapper From="/cgi-bin/" To="Cgi-Bin\" \
Action="&dbreplace(response,string,AuthenticationRealm,Administration)"
       Mapping Condition="&cmpi($m,PUT)" \
PathMapper From="/icons/" To="Icons\" \
Action="&dbreplace(response,string,AuthenticationRealm,Administration)"
       Mapping Condition="&cmpi($m,PUT)" \
PathMapper From="/" To="Webroot\" \
Action="&dbreplace(response,string,AuthenticationRealm,Administration)"

To be sure to allow uploads only for one single directory remove comments from the following lines within the Put object:


# CheckType Condition="&not(&regexp('/upload/*',$z))" \
#   StatusCode StatusCode="403"

A current problem is the access checking for write access rights. The RefuseFileByMask and CheckAccess functions could not be used since the upload of not existing files isn't allowed then. Besides this the file attribute mechanism of the operating system is working if the target file is read only etc.


Next Previous Contents