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="¬(&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.
#!/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.
The changes in the configuration are 2 new objects:
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="¬(®exp('/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>
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="¬(®exp('/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.