b655bf2004-06-30Martin Stjernholm // This is a roxen module. Copyright © 1996 - 2004, Roxen IS.
57f45e1996-11-27Per Hedbor 
b1fca01996-11-12Per Hedbor // This is a virtual "file-system". // It will be located somewhere in the name-space of the server.
57f45e1996-11-27Per Hedbor // Also inherited by some of the other filesystems.
f6d62d1997-03-26Per Hedbor 
1909011998-02-24Henrik Grubbström (Grubba) inherit "module"; inherit "socket";
8997762006-12-14Henrik Grubbström (Grubba) constant cvs_version= "$Id: filesystem.pike,v 1.154 2006/12/14 13:29:30 grubba Exp $";
1909011998-02-24Henrik Grubbström (Grubba) constant thread_safe=1;
6682b91997-08-31Peter Bortas 
b1fca01996-11-12Per Hedbor #include <module.h>
48fa361997-04-05Per Hedbor #include <roxen.h>
14179b1997-01-29Per Hedbor #include <stat.h>
c5e0961999-10-04Per Hedbor #include <request_trace.h>
b1fca01996-11-12Per Hedbor 
79ca872000-11-24Per Hedbor  //<locale-token project="mod_filesystem">LOCALE</locale-token> #define LOCALE(X,Y) _DEF_LOCALE("mod_filesystem",X,Y) // end of the locale related stuff
ac69b51999-12-28Martin Nilsson #ifdef FILESYSTEM_DEBUG # define FILESYSTEM_WERR(X) werror("Filesystem: "+X+"\n") #else # define FILESYSTEM_WERR(X) #endif #ifdef QUOTA_DEBUG # define QUOTA_WERR(X) werror("QUOTA: "+X+"\n") #else # define QUOTA_WERR(X) #endif
f53aae2004-05-13Martin Stjernholm #if constant(System.normalize_path) #define NORMALIZE_PATH(X) System.normalize_path(X)
c7ec632001-08-15Henrik Grubbström (Grubba) #else /* !constant(system.normalize_path) */ #define NORMALIZE_PATH(X) (X) #endif /* constant(system.normalize_path) */
c5e0961999-10-04Per Hedbor constant module_type = MODULE_LOCATION;
f2e3482001-05-20Martin Nilsson LocaleString module_name = LOCALE(51,"File systems: Normal File system");
f0d6942001-01-29Per Hedbor LocaleString module_doc =
de9ca82000-11-27Per Hedbor LOCALE(2,"This is the basic file system module that makes it possible "
79ca872000-11-24Per Hedbor  "to mount a directory structure in the virtual file system of " "your site.");
c5e0961999-10-04Per Hedbor constant module_unique = 0;
6396111997-02-14Per Hedbor 
b1fca01996-11-12Per Hedbor int redirects, accesses, errors, dirlists;
6cec001998-05-14David Hedbor int puts, deletes, mkdirs, moves, chmods;
b1fca01996-11-12Per Hedbor 
1358d51999-05-06Henrik Grubbström (Grubba) static mapping http_low_answer(int errno, string data, string|void desc) {
7f59662000-05-16Henrik Grubbström (Grubba)  mapping res = Roxen.http_low_answer(errno, data);
1358d51999-05-06Henrik Grubbström (Grubba)  if (desc) { res->rettext = desc; } return res; }
f53aae2004-05-13Martin Stjernholm // Note: This does a TRACE_LEAVE. static mapping(string:mixed) errno_to_status (int err, int(0..1) create, RequestID id) { switch (err) { case System.ENOENT: if (!create) { SIMPLE_TRACE_LEAVE ("File not found"); id->misc->error_code = Protocols.HTTP.HTTP_NOT_FOUND; return 0; } // Fall through. case System.ENOTDIR: TRACE_LEAVE(sprintf("%s: Missing intermediate.", id->method)); id->misc->error_code = Protocols.HTTP.HTTP_CONFLICT; return 0; case System.ENOSPC: SIMPLE_TRACE_LEAVE("%s: Insufficient space", id->method); return Roxen.http_status (Protocols.HTTP.DAV_STORAGE_FULL); case System.EPERM: TRACE_LEAVE(sprintf("%s: Permission denied", id->method)); return Roxen.http_status(Protocols.HTTP.HTTP_FORBIDDEN, "Permission denied."); case System.EEXIST: TRACE_LEAVE(sprintf("%s failed. Directory name already exists. ", id->method)); // FIXME: More methods are probably allowed, but what use is // that header anyway? return Roxen.http_method_not_allowed("GET, HEAD", "Collection already exists."); #if constant(System.ENAMETOOLONG) case System.ENAMETOOLONG: SIMPLE_TRACE_LEAVE ("Path too long"); return Roxen.http_status (Protocols.HTTP.HTTP_URI_TOO_LONG); #endif default: SIMPLE_TRACE_LEAVE ("Unexpected I/O error: %s", strerror (err)); return Roxen.http_status (Protocols.HTTP.HTTP_INTERNAL_ERR, "Unexpected I/O error: %s", strerror (err)); } }
b1fca01996-11-12Per Hedbor static int do_stat = 1; string status() {
de9ca82000-11-27Per Hedbor  return "<h2>"+LOCALE(3,"Accesses to this filesystem")+"</h2>"+ (redirects?"<b>"+LOCALE(4,"Redirects")+"</b>: "+redirects+"<br>":"")+ (accesses?"<b>"+LOCALE(5,"Normal files")+"</b>: "+accesses+"<br>"
cc56022002-07-01Anders Johansson  :LOCALE(6,"No file accesses")+"<br>")+
ed540b2001-01-13Martin Nilsson  (query("put")&&puts?"<b>"+LOCALE(7,"PUTs")+"</b>: "+puts+"<br>":"")+ (query("put")&&mkdirs?"<b>"+LOCALE(8,"MKDIRs")+"</b>: "+mkdirs+"<br>":"")+ (query("put")&&query("delete")&&moves?
de9ca82000-11-27Per Hedbor  "<b>"+LOCALE(9,"Moved files")+"</b>: "+moves+"<br>":"")+
ed540b2001-01-13Martin Nilsson  (query("put")&&chmods?"<b>"+LOCALE(10,"CHMODs")+"</b>: "+chmods+"<br>":"")+ (query("delete")&&deletes?"<b>"+LOCALE(11,"Deletes")+"</b>: "+deletes+"<br>":"")+
de9ca82000-11-27Per Hedbor  (errors?"<b>"+LOCALE(12,"Permission denied")+"</b>: "+errors +" ("+LOCALE(13,"not counting .htaccess")+")<br>":"")+ (dirlists?"<b>"+LOCALE(14,"Directories")+"</b>:"+dirlists+"<br>":"");
b1fca01996-11-12Per Hedbor } void create() {
b1e8672001-08-01Per Hedbor  defvar("mountpoint", "/", LOCALE(15,"Mount point"), TYPE_LOCATION|VAR_INITIAL|VAR_NO_DEFAULT,
de9ca82000-11-27Per Hedbor  LOCALE(16,"Where the module will be mounted in the site's virtual "
79ca872000-11-24Per Hedbor  "file system."));
b1e8672001-08-01Per Hedbor  defvar("searchpath", "NONE", LOCALE(17,"Search path"), TYPE_DIR|VAR_INITIAL|VAR_NO_DEFAULT,
de9ca82000-11-27Per Hedbor  LOCALE(18,"The directory that contains the files."));
79ca872000-11-24Per Hedbor 
de9ca82000-11-27Per Hedbor  defvar(".files", 0, LOCALE(19,"Show hidden files"), TYPE_FLAG|VAR_MORE, LOCALE(20,"If set, hidden files, ie files that begin with a '.', "
79ca872000-11-24Per Hedbor  "will be shown in directory listings." ));
de9ca82000-11-27Per Hedbor  defvar("dir", 1, LOCALE(21,"Enable directory listings per default"),
79ca872000-11-24Per Hedbor  TYPE_FLAG|VAR_MORE,
de9ca82000-11-27Per Hedbor  LOCALE(22,"If set, it will be possible to get a directory listings "
79ca872000-11-24Per Hedbor  "from directories in this file system. It is possible to " "force a directory to never be browsable by putting a " "<tt>.www_not_browsable</tt> or a <tt>.nodiraccess</tt> file " "in it. Similarly it is possible to let a directory be " "browsable, even if the file system is not, by putting a " "<tt>.www_browsable</tt> file in it.\n"));
b1fca01996-11-12Per Hedbor 
c4908f2000-05-06Martin Nilsson  defvar("nobrowse", ({ ".www_not_browsable", ".nodiraccess" }),
de9ca82000-11-27Per Hedbor  LOCALE(23,"List prevention files"), TYPE_STRING_LIST|VAR_MORE, LOCALE(24,"All directories containing any of these files will not be "
79ca872000-11-24Per Hedbor  "browsable."));
c4908f2000-05-06Martin Nilsson 
11ad7e2006-12-13Henrik Grubbström (Grubba)  defvar("no-parse", 0, LOCALE(0, "Raw files"), TYPE_FLAG|VAR_MORE, LOCALE(0, "If set files from this filesystem will be returned " "without any further processing. This disables eg RXML " "parsing of files."));
c4908f2000-05-06Martin Nilsson 
de9ca82000-11-27Per Hedbor  defvar("tilde", 0, LOCALE(25,"Show backup files"), TYPE_FLAG|VAR_MORE, LOCALE(26,"If set, files ending with '~', '#' or '.bak' will "+
79ca872000-11-24Per Hedbor  "be shown in directory listings"));
b1fca01996-11-12Per Hedbor 
de9ca82000-11-27Per Hedbor  defvar("put", 0, LOCALE(27,"Handle the PUT method"), TYPE_FLAG, LOCALE(28,"If set, it will be possible to upload files with the HTTP "
79ca872000-11-24Per Hedbor  "method PUT, or through FTP."));
b1fca01996-11-12Per Hedbor 
de9ca82000-11-27Per Hedbor  defvar("delete", 0, LOCALE(29,"Handle the DELETE method"), TYPE_FLAG, LOCALE(30,"If set, it will be possible to delete files with the HTTP "
79ca872000-11-24Per Hedbor  "method DELETE, or through FTP."));
b1fca01996-11-12Per Hedbor 
de9ca82000-11-27Per Hedbor  defvar("check_auth", 1, LOCALE(31,"Require authentication for modification"),
b1fca01996-11-12Per Hedbor  TYPE_FLAG,
de9ca82000-11-27Per Hedbor  LOCALE(32,"Only allow users authenticated by a authentication module "
79ca872000-11-24Per Hedbor  "to use methods that can modify the files, such as PUT or " "DELETE. If this is not set the file system will be a " "<b>very</b> public one since anyone will be able to edit " "files."));
14179b1997-01-29Per Hedbor 
de9ca82000-11-27Per Hedbor  defvar("stat_cache", 0, LOCALE(33,"Cache the results of stat(2)"),
79ca872000-11-24Per Hedbor  TYPE_FLAG|VAR_MORE,
de9ca82000-11-27Per Hedbor  LOCALE(34,"A performace option that can speed up retrieval of files "
79ca872000-11-24Per Hedbor  "from NFS with up to 50%. In turn it uses some memory and the " "file system will not notice that files have changed unless " "it gets a pragma no-cache request (produced e.g. by " "Alt-Ctrl-Reload in Netscape). Therefore this option should " "not be used on file systems that change a lot."));
527dc32001-09-21Per Hedbor  defvar("access_as_user", 0, LOCALE(35,"Access files as the logged in user"),
9b9f701997-08-12Per Hedbor  TYPE_FLAG|VAR_MORE,
de9ca82000-11-27Per Hedbor  LOCALE(36,"If set, the module will access files as the authenticated "
79ca872000-11-24Per Hedbor  "user. This assumes that a authentication module which imports" " the users from the operating systems, such as the <i>User " "database</i> module is used. This option is very useful for " "named FTP sites, but it will have severe performance impacts " "since all threads will be locked for each access."));
527dc32001-09-21Per Hedbor  defvar("access_as_user_db", Variable.UserDBChoice( " all", VAR_MORE,
80c5ae2001-09-27Martin Nilsson  LOCALE(53,"Authentication database to use"), LOCALE(54,"The User database module to use "
527dc32001-09-21Per Hedbor  "when authenticating users for the " "access file as the logged in user " "feature."), my_configuration())); defvar( "access_as_user_throw", 0,
80c5ae2001-09-27Martin Nilsson  LOCALE(55,"Access files as the logged in user forces login"),
527dc32001-09-21Per Hedbor  TYPE_FLAG|VAR_MORE,
80c5ae2001-09-27Martin Nilsson  LOCALE(56,"If true, a user will have to be logged in to access files in "
527dc32001-09-21Per Hedbor  "this filesystem") );
de9ca82000-11-27Per Hedbor  defvar("no_symlinks", 0, LOCALE(37,"Forbid access to symlinks"),
9b9f701997-08-12Per Hedbor  TYPE_FLAG|VAR_MORE,
de9ca82000-11-27Per Hedbor  LOCALE(38,"It set, the file system will not follow symbolic links. "
79ca872000-11-24Per Hedbor  "This option can lower performace by a lot." ));
de9ca82000-11-27Per Hedbor  defvar("charset", "iso-8859-1", LOCALE(39,"File contents charset"),
79ca872000-11-24Per Hedbor  TYPE_STRING,
de9ca82000-11-27Per Hedbor  LOCALE(40,"The charset of the contents of the files on this file "
79ca872000-11-24Per Hedbor  "system. This variable makes it possible for Roxen to use " "any text file, no matter what charset it is written in. If" " necessary, Roxen will convert the file to Unicode before " "processing the file."));
de9ca82000-11-27Per Hedbor  defvar("path_encoding", "iso-8859-1", LOCALE(41,"Filename charset"),
79ca872000-11-24Per Hedbor  TYPE_STRING,
de9ca82000-11-27Per Hedbor  LOCALE(42,"The charset of the file names of the files on this file "
79ca872000-11-24Per Hedbor  "system. Unlike the <i>File contents charset</i> variable, " "this might not work for all charsets simply because not " "all browsers support anything except ISO-8859-1 " "in URLs."));
de9ca82000-11-27Per Hedbor  defvar("internal_files", ({}), LOCALE(43,"Internal files"), TYPE_STRING_LIST, LOCALE(44,"A list of glob patterns that matches files which should be "
79ca872000-11-24Per Hedbor  "considered internal. Internal files cannot be requested " "directly from a browser, won't show up in directory listings " "and can never be uploaded, moved or deleted by a browser." "They can only be accessed internally, e.g. with the RXML tags" " <tt>&lt;insert&gt;</tt> and <tt>&lt;use&gt;</tt>."));
54b4802000-03-07Martin Stjernholm }
c7ec632001-08-15Henrik Grubbström (Grubba) string path, mountpoint, charset, path_encoding, normalized_path;
a2f3c72000-08-13Per Hedbor int stat_cache, dotfiles, access_as_user, no_symlinks, tilde; array(string) internal_files;
527dc32001-09-21Per Hedbor UserDB access_as_user_db; int access_as_user_throw;
b1fca01996-11-12Per Hedbor void start() {
ed540b2001-01-13Martin Nilsson  tilde = query("tilde"); charset = query("charset"); path_encoding = query("path_encoding"); no_symlinks = query("no_symlinks"); access_as_user = query("access_as_user");
527dc32001-09-21Per Hedbor  access_as_user_throw = query("access_as_user_throw"); access_as_user_db = my_configuration()->find_user_database( query("access_as_user_db") );
ed540b2001-01-13Martin Nilsson  dotfiles = query(".files"); path = query("searchpath"); mountpoint = query("mountpoint"); stat_cache = query("stat_cache"); internal_files = query("internal_files");
527dc32001-09-21Per Hedbor 
c7ec632001-08-15Henrik Grubbström (Grubba) #if constant(system.normalize_path) if (catch { if ((<'/','\\'>)[path[-1]]) { normalized_path = system.normalize_path(path + "."); } else { normalized_path = system.normalize_path(path); } #ifdef __NT__ normalized_path += "\\"; #else /* !__NT__ */ normalized_path += "/"; #endif /* __NT__ */ }) {
191ff32001-08-24Per Hedbor  report_error(LOCALE(1, "Path verification of %s failed.\n"), mountpoint);
c7ec632001-08-15Henrik Grubbström (Grubba)  normalized_path = path; } #else /* !constant(system.normalize_path) */ normalized_path = path; #endif /* constant(system.normalize_path) */
ed540b2001-01-13Martin Nilsson  FILESYSTEM_WERR("Online at "+query("mountpoint")+" (path="+path+")");
8d10df2000-01-06Martin Stjernholm  cache_expire("stat_cache");
b1fca01996-11-12Per Hedbor } string query_location() {
a2f3c72000-08-13Per Hedbor  return mountpoint;
b1fca01996-11-12Per Hedbor }
54b4802000-03-07Martin Stjernholm #define FILTER_INTERNAL_FILE(f, id) \
a2f3c72000-08-13Per Hedbor  (!id->misc->internal_get && sizeof (filter (internal_files, glob, (f/"/")[-1])))
54b4802000-03-07Martin Stjernholm 
4bc95b2001-09-11Per Hedbor #define SETUID(X) \
31eeb52001-12-21Henrik Grubbström (Grubba)  if( access_as_user && !id->misc->internal_get) \
4bc95b2001-09-11Per Hedbor  { \
527dc32001-09-21Per Hedbor  User uid = id->conf->authenticate( id,access_as_user_db ); \ if( access_as_user_throw && !uid ) \ return id->conf->authenticate_throw( id, "User",access_as_user_db);\ if( uid && uid->uid() ) \ privs=Privs(X, uid->uid(), uid->gid() ); \ }
4dea322001-12-21Henrik Grubbström (Grubba) #define SETUID_TRACE(X,LEVELS) \
31eeb52001-12-21Henrik Grubbström (Grubba)  if( access_as_user && !id->misc->internal_get) \
4dea322001-12-21Henrik Grubbström (Grubba)  { \ User uid = id->conf->authenticate( id,access_as_user_db ); \ if( access_as_user_throw && !uid ) { \ int levels = (LEVELS); \ while(levels--) TRACE_LEAVE(""); \ TRACE_LEAVE(X ": Auth required."); \ return id->conf->authenticate_throw( id, "User",access_as_user_db);\ } \ if( uid && uid->uid() ) \ privs=Privs(X, uid->uid(), uid->gid() ); \ }
527dc32001-09-21Per Hedbor #define SETUID_NT(X) \
31eeb52001-12-21Henrik Grubbström (Grubba)  if( access_as_user && !id->misc->internal_get ) \
527dc32001-09-21Per Hedbor  { \ User uid = id->conf->authenticate( id,access_as_user_db ); \
4bc95b2001-09-11Per Hedbor  if( uid && uid->uid() ) \ privs=Privs(X, uid->uid(), uid->gid() ); \ }
54b4802000-03-07Martin Stjernholm mixed stat_file( string f, RequestID id )
b1fca01996-11-12Per Hedbor {
1f4a6c2000-08-28Per Hedbor  Stat fs;
54b4802000-03-07Martin Stjernholm 
cdf0532000-06-23Martin Stjernholm  FILESYSTEM_WERR("stat_file for \""+f+"\"" + (id->misc->internal_get ? " (internal)" : ""));
54b4802000-03-07Martin Stjernholm 
2c0a3e2001-08-13Per Hedbor  f = path+f;
ae46202001-06-27Per Hedbor  if (FILTER_INTERNAL_FILE (f, id)) return 0;
54b4802000-03-07Martin Stjernholm 
64c51c1997-10-11Henrik Grubbström (Grubba)  if(stat_cache && !id->pragma["no-cache"] &&
2c0a3e2001-08-13Per Hedbor  (fs=cache_lookup("stat_cache",f)))
1b8f6b1998-02-24Per Hedbor  return fs[0]; object privs;
527dc32001-09-21Per Hedbor  SETUID_NT("Statting file");
8afc811998-02-04Per Hedbor 
ec0dab2000-04-03Per Hedbor  /* No security currently in this function */
2c0a3e2001-08-13Per Hedbor  fs = file_stat(decode_path(f));
64c51c1997-10-11Henrik Grubbström (Grubba)  privs = 0;
a2f3c72000-08-13Per Hedbor  if(!stat_cache) return fs;
2c0a3e2001-08-13Per Hedbor  cache_set("stat_cache", f, ({fs}));
14179b1997-01-29Per Hedbor  return fs;
b1fca01996-11-12Per Hedbor }
0681342004-04-20Henrik Grubbström (Grubba) string decode_path( string p ) { if( path_encoding != "iso-8859-1" ) p = Locale.Charset.encoder( path_encoding )->feed( p )->drain(); #ifndef __NT__ if( String.width( p ) != 8 ) p = string_to_utf8( p ); #else while( strlen(p) && p[-1] == '/' ) p = p[..strlen(p)-2]; #endif return p; } string real_path(string f, RequestID id) { f = path + f; if (FILTER_INTERNAL_FILE(f, id)) return 0; catch { f = NORMALIZE_PATH(decode_path(f)); if (has_prefix(f, normalized_path) || #ifdef __NT__ (f+"\\" == normalized_path) #else /* !__NT__ */ (f+"/" == normalized_path) #endif /* __NT__ */ ) { return f; } }; return 0; }
54b4802000-03-07Martin Stjernholm string real_file( string f, RequestID id )
b1fca01996-11-12Per Hedbor {
c7ec632001-08-15Henrik Grubbström (Grubba)  if(stat_file( f, id )) {
0681342004-04-20Henrik Grubbström (Grubba)  return real_path(f, id);
c7ec632001-08-15Henrik Grubbström (Grubba)  }
b1fca01996-11-12Per Hedbor }
699fa72004-05-06Henrik Grubbström (Grubba) // We support locking if put is enabled. mapping(string:mixed) lock_file(string path, DAVLock lock, RequestID id) { if (!query("put")) return 0;
9040d52004-05-15Henrik Grubbström (Grubba)  if (query("check_auth") && (!id->conf->authenticate( id )) ) { TRACE_LEAVE("LOCK: Permission denied");
699fa72004-05-06Henrik Grubbström (Grubba)  return
5c27782004-05-12Martin Stjernholm  // FIXME: Sane realm.
699fa72004-05-06Henrik Grubbström (Grubba)  Roxen.http_auth_required("foo",
fafdea2005-12-13Anders Johansson  "<h1>Permission to 'LOCK' files denied</h1>", id);
699fa72004-05-06Henrik Grubbström (Grubba)  } register_lock(path, lock, id); return 0; }
2045292004-05-14Martin Stjernholm mapping(string:mixed) unlock_file(string path, DAVLock lock, RequestID|int(0..0) id) { if (!query("put")) return 0;
9040d52004-05-15Henrik Grubbström (Grubba)  if (id && query("check_auth") && (!id->conf->authenticate( id )) ) { TRACE_LEAVE("UNLOCK: Permission denied");
2045292004-05-14Martin Stjernholm  return // FIXME: Sane realm. Roxen.http_auth_required("foo",
fafdea2005-12-13Anders Johansson  "<h1>Permission to 'UNLOCK' files denied</h1>", id);
2045292004-05-14Martin Stjernholm  } unregister_lock(path, lock, id); return 0; }
54b4802000-03-07Martin Stjernholm int dir_filter_function(string f, RequestID id)
b1fca01996-11-12Per Hedbor {
a2f3c72000-08-13Per Hedbor  if(f[0]=='.' && !dotfiles) return 0; if(!tilde && Roxen.backup_extension(f)) return 0;
b1fca01996-11-12Per Hedbor  return 1; }
c4908f2000-05-06Martin Nilsson array(string) list_lock_files() {
ed540b2001-01-13Martin Nilsson  return query("nobrowse");
c4908f2000-05-06Martin Nilsson }
556b832004-05-13Henrik Grubbström (Grubba) static mapping(string:mixed)|int(0..1) write_access(string path, int(0..1) recursive, RequestID id) { SIMPLE_TRACE_ENTER(this, "write_access(%O, %O, %O)\n", path, recursive, id); if(query("check_auth") && (!id->conf->authenticate( id ) ) ) { SIMPLE_TRACE_LEAVE("%s: Authentication required.", id->method); // FIXME: Sane realm. // FIXME: Recursion and htaccess? return Roxen.http_auth_required("foo", sprintf("<h1>Permission to '%s' denied</h1>",
fafdea2005-12-13Anders Johansson  id->method), id);
556b832004-05-13Henrik Grubbström (Grubba)  } TRACE_LEAVE("Fall back to the default write access checks."); return ::write_access(path, recursive, id); }
54b4802000-03-07Martin Stjernholm array find_dir( string f, RequestID id )
b1fca01996-11-12Per Hedbor { array dir;
cdf0532000-06-23Martin Stjernholm  FILESYSTEM_WERR("find_dir for \""+f+"\"" + (id->misc->internal_get ? " (internal)" : ""));
54b4802000-03-07Martin Stjernholm 
0216e21997-08-12Henrik Grubbström (Grubba)  object privs;
527dc32001-09-21Per Hedbor  SETUID_NT("Read dir");
0216e21997-08-12Henrik Grubbström (Grubba) 
c7ec632001-08-15Henrik Grubbström (Grubba)  if (catch { f = NORMALIZE_PATH(decode_path(path + f)); } || !(dir = get_dir(f))) {
64c51c1997-10-11Henrik Grubbström (Grubba)  privs = 0;
b1fca01996-11-12Per Hedbor  return 0;
64c51c1997-10-11Henrik Grubbström (Grubba)  }
0216e21997-08-12Henrik Grubbström (Grubba)  privs = 0;
ed540b2001-01-13Martin Nilsson  if(!query("dir"))
b1fca01996-11-12Per Hedbor  // Access to this dir is allowed.
c4908f2000-05-06Martin Nilsson  if(! has_value(dir, ".www_browsable"))
b1fca01996-11-12Per Hedbor  { errors++; return 0; } // Access to this dir is not allowed.
ed540b2001-01-13Martin Nilsson  if( sizeof(dir & query("nobrowse")) )
b1fca01996-11-12Per Hedbor  { errors++; return 0; } dirlists++; // Pass _all_ files, hide none.
a2f3c72000-08-13Per Hedbor  if(tilde && dotfiles && (!sizeof( internal_files ) || id->misc->internal_get))
b1fca01996-11-12Per Hedbor  return dir;
3bf15e2000-03-08Martin Stjernholm  dir = Array.filter(dir, dir_filter_function, id); if (!id->misc->internal_get)
a2f3c72000-08-13Per Hedbor  foreach (internal_files, string globstr)
3bf15e2000-03-08Martin Stjernholm  dir -= glob (globstr, dir); return dir;
b1fca01996-11-12Per Hedbor }
39edd92004-05-05Martin Stjernholm void recursive_rm(string real_dir, string virt_dir,
40cae52004-05-09Henrik Grubbström (Grubba)  int(0..1) check_status_needed, RequestID id)
a4d1ad2003-06-11Henrik Grubbström (Grubba) {
40cae52004-05-09Henrik Grubbström (Grubba)  SIMPLE_TRACE_ENTER(this, "Deleting all files in directory %O...", real_dir);
a4d1ad2003-06-11Henrik Grubbström (Grubba)  foreach(get_dir(real_dir) || ({}), string fname) { string real_fname = combine_path(real_dir, fname);
39edd92004-05-05Martin Stjernholm  string virt_fname = virt_dir + "/" + fname;
40cae52004-05-09Henrik Grubbström (Grubba) 
a4d1ad2003-06-11Henrik Grubbström (Grubba)  Stat stat = file_stat(real_fname); if (!stat) {
39edd92004-05-05Martin Stjernholm  id->set_status_for_path(virt_fname, 404);
a4d1ad2003-06-11Henrik Grubbström (Grubba)  TRACE_LEAVE("File not found."); continue; }
40cae52004-05-09Henrik Grubbström (Grubba)  SIMPLE_TRACE_ENTER(this, "Deleting %s %O.", stat->isdir?"directory":"file", real_fname); int(0..1)|mapping sub_status; if (check_status_needed && mappingp(sub_status = write_access(virt_fname, 1, id))) { id->set_status_for_path(virt_fname, sub_status->error); TRACE_LEAVE("Write access denied."); continue; }
0c8b5b2003-12-23Henrik Grubbström (Grubba)  if (stat->isdir) {
40cae52004-05-09Henrik Grubbström (Grubba)  recursive_rm(real_fname, virt_fname, sub_status, id);
a4d1ad2003-06-11Henrik Grubbström (Grubba)  } /* Clear the stat-cache for this file */ if (stat_cache) { cache_set("stat_cache", real_fname, 0); } if (!rm(real_fname)) { #if constant(System.EEXIST) if (errno() != System.EEXIST) #endif {
39edd92004-05-05Martin Stjernholm  id->set_status_for_path(virt_fname, 403);
a4d1ad2003-06-11Henrik Grubbström (Grubba)  TRACE_LEAVE("Deletion failed."); } #if constant(System.EEXIST) else { TRACE_LEAVE("Directory not empty."); } #endif } else { deletes++; if (id->misc->quota_obj && stat->isreg()) {
39edd92004-05-05Martin Stjernholm  id->misc->quota_obj->deallocate(virt_fname,
a4d1ad2003-06-11Henrik Grubbström (Grubba)  stat->size()); } TRACE_LEAVE("Ok."); } } TRACE_LEAVE("Done."); }
b1fca01996-11-12Per Hedbor  mapping putting = ([]);
0c8b5b2003-12-23Henrik Grubbström (Grubba) void done_with_put( array(object|string|int) id_arr )
b1fca01996-11-12Per Hedbor {
b3ef2d1999-12-18Martin Nilsson // werror("Done with put.\n");
2f83081999-05-05Henrik Grubbström (Grubba)  object to; object from; object id; string oldf;
0c8b5b2003-12-23Henrik Grubbström (Grubba)  int size;
2f83081999-05-05Henrik Grubbström (Grubba) 
0c8b5b2003-12-23Henrik Grubbström (Grubba)  [to, from, id, oldf, size] = id_arr;
2f83081999-05-05Henrik Grubbström (Grubba) 
ac69b51999-12-28Martin Nilsson  FILESYSTEM_WERR(sprintf("done_with_put(%O)\n" "from: %O\n", id_arr, mkmapping(indices(from), values(from))));
1358d51999-05-06Henrik Grubbström (Grubba) 
2f83081999-05-05Henrik Grubbström (Grubba)  to->close(); from->set_blocking(); m_delete(putting, from); if (putting[from] && (putting[from] != 0x7fffffff)) {
0163161999-04-21Henrik Grubbström (Grubba)  // Truncated!
5c27782004-05-12Martin Stjernholm  id->send_result(Roxen.http_status(400, "Bad Request - " "Expected more data."));
0163161999-04-21Henrik Grubbström (Grubba)  } else {
5c27782004-05-12Martin Stjernholm  id->send_result(Roxen.http_status((size < 0)?201:200, "Transfer Complete."));
0163161999-04-21Henrik Grubbström (Grubba)  }
b1fca01996-11-12Per Hedbor }
0c8b5b2003-12-23Henrik Grubbström (Grubba) void got_put_data( array(object|string|int) id_arr, string data )
b1fca01996-11-12Per Hedbor {
b3ef2d1999-12-18Martin Nilsson // werror(strlen(data)+" .. ");
2f83081999-05-05Henrik Grubbström (Grubba)  object to; object from; object id; string oldf;
0c8b5b2003-12-23Henrik Grubbström (Grubba)  int size;
2f83081999-05-05Henrik Grubbström (Grubba) 
0c8b5b2003-12-23Henrik Grubbström (Grubba)  [to, from, id, oldf, size] = id_arr;
2f83081999-05-05Henrik Grubbström (Grubba) 
0163161999-04-21Henrik Grubbström (Grubba)  // Truncate at end.
2f83081999-05-05Henrik Grubbström (Grubba)  data = data[..putting[from]]; if (id->misc->quota_obj && !id->misc->quota_obj->check_quota(oldf, sizeof(data))) { to->close(); from->set_blocking(); m_delete(putting, from);
f53aae2004-05-13Martin Stjernholm  id->send_result(Roxen.http_status(507, "Out of disk quota."));
2f83081999-05-05Henrik Grubbström (Grubba)  return; }
0163161999-04-21Henrik Grubbström (Grubba) 
2f83081999-05-05Henrik Grubbström (Grubba)  int bytes = to->write( data );
0163161999-04-21Henrik Grubbström (Grubba)  if (bytes < sizeof(data)) { // Out of disk!
2f83081999-05-05Henrik Grubbström (Grubba)  to->close(); from->set_blocking(); m_delete(putting, from);
f53aae2004-05-13Martin Stjernholm  id->send_result(Roxen.http_status(507, "Disk full."));
2f83081999-05-05Henrik Grubbström (Grubba)  return;
0163161999-04-21Henrik Grubbström (Grubba)  } else {
2f83081999-05-05Henrik Grubbström (Grubba)  if (id->misc->quota_obj && !id->misc->quota_obj->allocate(oldf, bytes)) { to->close(); from->set_blocking(); m_delete(putting, from);
f53aae2004-05-13Martin Stjernholm  id->send_result(Roxen.http_status(507, "Out of disk quota."));
2f83081999-05-05Henrik Grubbström (Grubba)  return; } if (putting[from] != 0x7fffffff) { putting[from] -= bytes;
32b4ba1999-04-21Henrik Grubbström (Grubba)  }
2f83081999-05-05Henrik Grubbström (Grubba)  if(putting[from] <= 0) { putting[from] = 0; // Paranoia done_with_put( id_arr );
0163161999-04-21Henrik Grubbström (Grubba)  } }
b1fca01996-11-12Per Hedbor }
c9e0362000-08-28Johan Sundström int _file_size(string X, RequestID id)
14179b1997-01-29Per Hedbor {
1f4a6c2000-08-28Per Hedbor  Stat fs;
a2f3c72000-08-13Per Hedbor  if( stat_cache )
14179b1997-01-29Per Hedbor  {
c9e0362000-08-28Johan Sundström  array(Stat) cached_fs; if(!id->pragma["no-cache"] && (cached_fs = cache_lookup("stat_cache", X)))
a2f3c72000-08-13Per Hedbor  {
c9e0362000-08-28Johan Sundström  id->misc->stat = cached_fs[0]; return cached_fs[0] ? cached_fs[0][ST_SIZE] : -1;
a2f3c72000-08-13Per Hedbor  }
14179b1997-01-29Per Hedbor  }
ec0dab2000-04-03Per Hedbor  if(fs = file_stat(decode_path(X)))
14179b1997-01-29Per Hedbor  { id->misc->stat = fs;
a2f3c72000-08-13Per Hedbor  if( stat_cache ) cache_set("stat_cache",(X),({fs}));
14179b1997-01-29Per Hedbor  return fs[ST_SIZE];
a2f3c72000-08-13Per Hedbor  } else if( stat_cache )
1b8f6b1998-02-24Per Hedbor  cache_set("stat_cache",(X),({0}));
14179b1997-01-29Per Hedbor  return -1; }
afb1581997-07-06Henrik Grubbström (Grubba) int contains_symlinks(string root, string path) {
f819222004-05-08Henrik Grubbström (Grubba)  foreach(path/"/" - ({ "" }), path) {
afb1581997-07-06Henrik Grubbström (Grubba)  root += "/" + path;
f819222004-05-08Henrik Grubbström (Grubba)  Stat rr;
1f4a6c2000-08-28Per Hedbor  if (rr = file_stat(decode_path(root), 1)) { if (rr[1] == -3) {
afb1581997-07-06Henrik Grubbström (Grubba)  return(1); } } else { return(0); } } return(0); }
9aea232005-10-19Henrik Grubbström (Grubba) //! @[chmod()] that doesn't throw errors. string safe_chmod(string path, int mask) { return describe_error(catch { chmod(path, mask); return 0; }); }
0681342004-04-20Henrik Grubbström (Grubba) mapping make_collection(string coll, RequestID id) { TRACE_ENTER(sprintf("make_collection(%O)", coll), this_object()); string norm_f = real_path(coll, id); if (!norm_f) { TRACE_LEAVE(sprintf("%s: Bad path", id->method));
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(405, "Bad path.");
0681342004-04-20Henrik Grubbström (Grubba)  } if(!query("put")) { TRACE_LEAVE(sprintf("%s disallowed (since PUT is disallowed)", id->method));
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(405, "Disallowed.");
0681342004-04-20Henrik Grubbström (Grubba)  } // FIXME: Is this the correct filename? int size = _file_size(norm_f, id); if (size != -1) { TRACE_LEAVE(sprintf("%s failed. Directory name already exists. ", id->method)); if (id->method == "MKCOL") {
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(405, "Collection already exists.");
0681342004-04-20Henrik Grubbström (Grubba)  } return 0; }
699fa72004-05-06Henrik Grubbström (Grubba)  // Disallow if the name is locked, or if the parent directory is locked. mapping(string:mixed) ret = write_access(coll, 0, id) || write_access(combine_path(coll, ".."), 0, id); if (ret) return ret;
0681342004-04-20Henrik Grubbström (Grubba)  mkdirs++; object privs; SETUID_TRACE("Creating directory/collection", 0); if (query("no_symlinks") && (contains_symlinks(path, coll))) { privs = 0; errors++;
40cae52004-05-09Henrik Grubbström (Grubba)  report_error(LOCALE(46,"Creation of %O failed. Permission denied.\n"),
0681342004-04-20Henrik Grubbström (Grubba)  coll); TRACE_LEAVE(sprintf("%s: Contains symlinks. Permission denied", id->method));
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(403, "Permission denied.");
0681342004-04-20Henrik Grubbström (Grubba)  } int code = mkdir(norm_f); int err_code = errno(); TRACE_ENTER(sprintf("%s: Accepted", id->method), 0); if (code) {
9aea232005-10-19Henrik Grubbström (Grubba)  string msg = safe_chmod(norm_f, 0777 & ~(id->misc->umask || 022)); privs = 0; if (msg) { TRACE_LEAVE(sprintf("%s: chmod %O failed: %s", id->method, norm_f, msg)); } else { TRACE_LEAVE(sprintf("%s: chmod ok", id->method, msg)); }
0681342004-04-20Henrik Grubbström (Grubba)  TRACE_LEAVE(sprintf("%s: Success", id->method));
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(201, "Created");
0681342004-04-20Henrik Grubbström (Grubba)  }
9aea232005-10-19Henrik Grubbström (Grubba)  privs = 0;
0681342004-04-20Henrik Grubbström (Grubba)  TRACE_LEAVE(sprintf("%s: Failed", id->method));
f53aae2004-05-13Martin Stjernholm  return errno_to_status (err_code, 1, id);
0681342004-04-20Henrik Grubbström (Grubba) }
756d3c2005-07-19Henrik Grubbström (Grubba) class CacheCallback(string f, int orig_size) {
5347f52005-09-02Martin Stjernholm  int(0..1) `()(RequestID id, mixed key)
756d3c2005-07-19Henrik Grubbström (Grubba)  { return _file_size(f, id) == orig_size; } }
54b4802000-03-07Martin Stjernholm mixed find_file( string f, RequestID id )
b1fca01996-11-12Per Hedbor {
8afb501998-04-15Henrik Grubbström (Grubba)  TRACE_ENTER("find_file(\""+f+"\")", 0);
b1fca01996-11-12Per Hedbor  object o; int size; string tmp;
afb1581997-07-06Henrik Grubbström (Grubba)  string oldf = f;
7aa8982004-04-20Martin Stjernholm  object privs; int code;
01128d1998-04-27Henrik Grubbström (Grubba) 
cdf0532000-06-23Martin Stjernholm  FILESYSTEM_WERR("Request for \""+f+"\"" + (id->misc->internal_get ? " (internal)" : ""));
01128d1998-04-27Henrik Grubbström (Grubba) 
a2f3c72000-08-13Per Hedbor  /* only used for the quota system, thus rather unessesary to do for each request.... */
c7ec632001-08-15Henrik Grubbström (Grubba) #define URI combine_path(mountpoint + "/" + oldf, ".")
1358d51999-05-06Henrik Grubbström (Grubba) 
c7ec632001-08-15Henrik Grubbström (Grubba)  string norm_f; catch { /* NOTE: NORMALIZE_PATH() may throw errors. */ f = norm_f = NORMALIZE_PATH(f = decode_path(path + f)); #if constant(system.normalize_path)
bd25082001-08-16Henrik Grubbström (Grubba)  if (!has_prefix(norm_f, normalized_path) &&
b0f68e2001-08-16Henrik Grubbström (Grubba) #ifdef __NT__
3282772001-08-16Henrik Grubbström (Grubba)  (norm_f+"\\" != normalized_path)
b0f68e2001-08-16Henrik Grubbström (Grubba) #else /* !__NT__ */ (norm_f+"/" != normalized_path) #endif /* __NT__ */ ) {
c7ec632001-08-15Henrik Grubbström (Grubba)  errors++;
191ff32001-08-24Per Hedbor  report_error(LOCALE(52, "Path verification of %O failed:\n"
b0f68e2001-08-16Henrik Grubbström (Grubba)  "%O is not a prefix of %O\n" ), oldf, normalized_path, norm_f);
c7ec632001-08-15Henrik Grubbström (Grubba)  TRACE_LEAVE(""); TRACE_LEAVE("Permission denied.");
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(403, "File exists, but access forbidden " "by user");
c7ec632001-08-15Henrik Grubbström (Grubba)  }
b0f68e2001-08-16Henrik Grubbström (Grubba)  /* Adjust not_query */
c7ec632001-08-15Henrik Grubbström (Grubba)  id->not_query = mountpoint + replace(norm_f[sizeof(normalized_path)..], "\\", "/");
bd25082001-08-16Henrik Grubbström (Grubba)  if (sizeof(oldf) && (oldf[-1] == '/')) { id->not_query += "/"; }
c7ec632001-08-15Henrik Grubbström (Grubba) #endif /* constant(system.normalize_path) */ };
a2f3c72000-08-13Per Hedbor 
f819222004-05-08Henrik Grubbström (Grubba)  // NOTE: Sets id->misc->stat.
a2f3c72000-08-13Per Hedbor  size = _file_size( f, id );
b1fca01996-11-12Per Hedbor 
9e759c2001-09-11Henrik Grubbström (Grubba)  FILESYSTEM_WERR(sprintf("_file_size(%O, %O) ==> %d\n", f, id, size));
465eb61998-08-26Henrik Grubbström (Grubba)  /* * FIXME: Should probably move path-info extraction here. * /grubba 1998-08-26 */
b04ec82000-01-31Per Hedbor 
b1fca01996-11-12Per Hedbor  switch(id->method) { case "GET": case "HEAD": case "POST":
b04ec82000-01-31Per Hedbor 
b1fca01996-11-12Per Hedbor  switch(-size) { case 1:
8afb501998-04-15Henrik Grubbström (Grubba)  case 3: case 4: TRACE_LEAVE("No file");
b1fca01996-11-12Per Hedbor  return 0; /* Is no-file */ case 2:
8afb501998-04-15Henrik Grubbström (Grubba)  TRACE_LEAVE("Is directory");
b1fca01996-11-12Per Hedbor  return -1; /* Is dir */ default:
c7ec632001-08-15Henrik Grubbström (Grubba)  if( oldf[ -1 ] == '/' || /* Trying to access file with '/' appended */ !norm_f) { /* Or a file that is not normalizable. */
b04ec82000-01-31Per Hedbor  return 0;
c7ec632001-08-15Henrik Grubbström (Grubba)  }
b1fca01996-11-12Per Hedbor 
a2f3c72000-08-13Per Hedbor  if(!id->misc->internal_get) { if (!dotfiles
0fb1b12000-03-26Martin Stjernholm  && sizeof (tmp = (id->not_query/"/")[-1])
a2f3c72000-08-13Per Hedbor  && tmp[0] == '.') {
54b4802000-03-07Martin Stjernholm  TRACE_LEAVE("Is .-file"); return 0; }
a2f3c72000-08-13Per Hedbor  if (FILTER_INTERNAL_FILE (f, id)) {
54b4802000-03-07Martin Stjernholm  TRACE_LEAVE ("Is internal file"); return 0; }
8afb501998-04-15Henrik Grubbström (Grubba)  }
5e5fd62000-03-07Martin Stjernholm  TRACE_ENTER("Opening file \"" + f + "\"", 0);
4dea322001-12-21Henrik Grubbström (Grubba)  SETUID_TRACE("Open file", 1);
a2f3c72000-08-13Per Hedbor  o = Stdio.File( );
c7ec632001-08-15Henrik Grubbström (Grubba)  if(!o->open(norm_f, "r" )) o = 0;
2e7c231997-06-10Henrik Grubbström (Grubba)  privs = 0;
a2f3c72000-08-13Per Hedbor  if(!o || (no_symlinks && (contains_symlinks(path, oldf))))
b1fca01996-11-12Per Hedbor  { errors++;
de9ca82000-11-27Per Hedbor  report_error(LOCALE(45,"Open of %s failed. Permission denied.\n"),f);
b04ec82000-01-31Per Hedbor 
8afb501998-04-15Henrik Grubbström (Grubba)  TRACE_LEAVE(""); TRACE_LEAVE("Permission denied.");
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(403, "File exists, but access forbidden " "by user");
b1fca01996-11-12Per Hedbor  }
756d3c2005-07-19Henrik Grubbström (Grubba)  // Add a cache callback. id->misc->_cachecallbacks += ({ CacheCallback(f, size) });
c7ec632001-08-15Henrik Grubbström (Grubba)  id->realfile = norm_f;
8afb501998-04-15Henrik Grubbström (Grubba)  TRACE_LEAVE("");
b1fca01996-11-12Per Hedbor  accesses++;
a2f3c72000-08-13Per Hedbor  if( charset != "iso-8859-1" ) {
6b3cb12002-07-23Martin Stjernholm  if( id->set_output_charset ) id->set_output_charset( charset, 2 );
a2f3c72000-08-13Per Hedbor  id->misc->input_charset = charset; }
11ad7e2006-12-13Henrik Grubbström (Grubba)  if (query("no-parse")) {
8997762006-12-14Henrik Grubbström (Grubba)  TRACE_ENTER("Content-type mapping module", conf->types_module);
11ad7e2006-12-13Henrik Grubbström (Grubba)  array(string) tmp = conf->type_from_filename(norm_f, 1); TRACE_LEAVE(tmp?sprintf("Returned type %s %s.", tmp[0], tmp[1]||"") : "Missing type."); TRACE_LEAVE("No parse return"); return Roxen.http_file_answer(o, tmp[0]) + ([ "encoding":tmp[1] ]); } TRACE_LEAVE("Normal return");
b1fca01996-11-12Per Hedbor  return o; } break;
b04ec82000-01-31Per Hedbor 
a4d1ad2003-06-11Henrik Grubbström (Grubba)  case "MKCOL":
534e292004-05-10Martin Stjernholm  if (id->request_headers["content-type"] || sizeof (id->data)) {
a4d1ad2003-06-11Henrik Grubbström (Grubba)  // RFC 2518 8.3.1: // If a server receives a MKCOL request entity type it does not support // or understand it MUST respond with a 415 (Unsupported Media Type) // status code.
534e292004-05-10Martin Stjernholm  SIMPLE_TRACE_LEAVE ("MKCOL failed since the request has content."); return Roxen.http_status(415, "Unsupported media type.");
a4d1ad2003-06-11Henrik Grubbström (Grubba)  } /* FALL_THROUGH */
8764c01998-05-01Henrik Grubbström (Grubba)  case "MKDIR":
0681342004-04-20Henrik Grubbström (Grubba) #if 1 return make_collection(oldf, id); #else /* !1 */
ed540b2001-01-13Martin Nilsson  if(!query("put"))
8764c01998-05-01Henrik Grubbström (Grubba)  { id->misc->error_code = 405;
a4d1ad2003-06-11Henrik Grubbström (Grubba)  TRACE_LEAVE(sprintf("%s disallowed (since PUT is disallowed)", id->method));
8764c01998-05-01Henrik Grubbström (Grubba)  return 0;
b04ec82000-01-31Per Hedbor  }
8764c01998-05-01Henrik Grubbström (Grubba) 
54b4802000-03-07Martin Stjernholm  if (FILTER_INTERNAL_FILE (f, id)) { id->misc->error_code = 405;
a4d1ad2003-06-11Henrik Grubbström (Grubba)  TRACE_LEAVE(sprintf("%s disallowed (since the dir name matches internal file glob)", id->method));
54b4802000-03-07Martin Stjernholm  return 0; }
9e759c2001-09-11Henrik Grubbström (Grubba)  if (size != -1) {
a4d1ad2003-06-11Henrik Grubbström (Grubba)  TRACE_LEAVE(sprintf("%s failed. Directory name already exists. ", id->method)); if (id->method == "MKCOL") {
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(405, "Collection already exists.");
a4d1ad2003-06-11Henrik Grubbström (Grubba)  }
c7ec632001-08-15Henrik Grubbström (Grubba)  return 0; }
556b832004-05-13Henrik Grubbström (Grubba)  if (mapping(string:mixed) ret = write_access(oldf, 0, id)) { TRACE_LEAVE("MKCOL: Write access denied."); return ret;
8764c01998-05-01Henrik Grubbström (Grubba)  }
556b832004-05-13Henrik Grubbström (Grubba) 
8764c01998-05-01Henrik Grubbström (Grubba)  mkdirs++;
a4d1ad2003-06-11Henrik Grubbström (Grubba)  SETUID_TRACE("Creating directory/collection", 0);
8764c01998-05-01Henrik Grubbström (Grubba) 
ed540b2001-01-13Martin Nilsson  if (query("no_symlinks") && (contains_symlinks(path, oldf))) {
8764c01998-05-01Henrik Grubbström (Grubba)  privs = 0; errors++;
40cae52004-05-09Henrik Grubbström (Grubba)  report_error(LOCALE(46,"Creation of %O failed. Permission denied.\n"),
c7ec632001-08-15Henrik Grubbström (Grubba)  oldf);
a4d1ad2003-06-11Henrik Grubbström (Grubba)  TRACE_LEAVE(sprintf("%s: Contains symlinks. Permission denied", id->method));
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(403, "Permission denied.");
8764c01998-05-01Henrik Grubbström (Grubba)  }
7aa8982004-04-20Martin Stjernholm  code = mkdir(f);
0c8b5b2003-12-23Henrik Grubbström (Grubba)  int err_code = errno();
5e5fd62000-03-07Martin Stjernholm 
a4d1ad2003-06-11Henrik Grubbström (Grubba)  TRACE_ENTER(sprintf("%s: Accepted", id->method), 0);
5e5fd62000-03-07Martin Stjernholm 
8764c01998-05-01Henrik Grubbström (Grubba)  if (code) {
9aea232005-10-19Henrik Grubbström (Grubba)  string msg = safe_chmod(f, 0777 & ~(id->misc->umask || 022)); privs = 0; if (msg) { TRACE_LEAVE(sprintf("%s: chmod %O failed: %s", id->method, f, msg)); } else { TRACE_LEAVE(sprintf("%s: Success", id->method)); }
8764c01998-05-01Henrik Grubbström (Grubba)  TRACE_LEAVE("Success");
a4d1ad2003-06-11Henrik Grubbström (Grubba)  if (id->method == "MKCOL") {
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(201, "Created");
a4d1ad2003-06-11Henrik Grubbström (Grubba)  }
f4b8bc2000-05-01Martin Nilsson  return Roxen.http_string_answer("Ok");
8764c01998-05-01Henrik Grubbström (Grubba)  } else {
9aea232005-10-19Henrik Grubbström (Grubba)  privs = 0;
f819222004-05-08Henrik Grubbström (Grubba)  SIMPLE_TRACE_LEAVE("%s: Failed (errcode:%d)", id->method, errcode);
8764c01998-05-01Henrik Grubbström (Grubba)  TRACE_LEAVE("Failure");
a4d1ad2003-06-11Henrik Grubbström (Grubba)  if (id->method == "MKCOL") {
0c8b5b2003-12-23Henrik Grubbström (Grubba)  if (err_code == #if constant(system.ENOENT) system.ENOENT #elif constant(System.ENOENT) System.ENOENT #else 2 #endif ) {
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(409, "Missing intermediate.");
0c8b5b2003-12-23Henrik Grubbström (Grubba)  } else {
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(507, "Failed.");
0c8b5b2003-12-23Henrik Grubbström (Grubba)  }
a4d1ad2003-06-11Henrik Grubbström (Grubba)  }
8764c01998-05-01Henrik Grubbström (Grubba)  return 0; }
0681342004-04-20Henrik Grubbström (Grubba) #endif /* 1 */
8764c01998-05-01Henrik Grubbström (Grubba)  break;
b1fca01996-11-12Per Hedbor  case "PUT":
ed540b2001-01-13Martin Nilsson  if(!query("put"))
f6d62d1997-03-26Per Hedbor  { id->misc->error_code = 405;
8afb501998-04-15Henrik Grubbström (Grubba)  TRACE_LEAVE("PUT disallowed");
b1fca01996-11-12Per Hedbor  return 0;
b04ec82000-01-31Per Hedbor  }
f6d62d1997-03-26Per Hedbor 
54b4802000-03-07Martin Stjernholm  if (FILTER_INTERNAL_FILE (f, id)) { id->misc->error_code = 405; TRACE_LEAVE("PUT of internal file is disallowed"); return 0; }
f819222004-05-08Henrik Grubbström (Grubba)  if (mapping(string:mixed) ret = write_access(oldf, 0, id)) {
699fa72004-05-06Henrik Grubbström (Grubba)  TRACE_LEAVE("PUT: Locked"); return ret; }
b1fca01996-11-12Per Hedbor  puts++;
2f83081999-05-05Henrik Grubbström (Grubba) 
ac69b51999-12-28Martin Nilsson  QUOTA_WERR("Checking quota.\n");
1358d51999-05-06Henrik Grubbström (Grubba)  if (id->misc->quota_obj && (id->misc->len > 0) &&
3f286a2000-08-23Per Hedbor  !id->misc->quota_obj->check_quota(URI, id->misc->len)) {
2f83081999-05-05Henrik Grubbström (Grubba)  errors++;
40cae52004-05-09Henrik Grubbström (Grubba)  report_warning(LOCALE(47,"Creation of %O failed. Out of quota.\n"),f);
2f83081999-05-05Henrik Grubbström (Grubba)  TRACE_LEAVE("PUT: Out of quota.");
f53aae2004-05-13Martin Stjernholm  return Roxen.http_status(507, "Out of disk quota.");
2f83081999-05-05Henrik Grubbström (Grubba)  }
b04ec82000-01-31Per Hedbor 
ed540b2001-01-13Martin Nilsson  if (query("no_symlinks") && (contains_symlinks(path, oldf))) {
afb1581997-07-06Henrik Grubbström (Grubba)  errors++;
40cae52004-05-09Henrik Grubbström (Grubba)  report_error(LOCALE(46,"Creation of %O failed. Permission denied.\n"),f);
8afb501998-04-15Henrik Grubbström (Grubba)  TRACE_LEAVE("PUT: Contains symlinks. Permission denied");
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(403, "Permission denied.");
afb1581997-07-06Henrik Grubbström (Grubba)  }
40cae52004-05-09Henrik Grubbström (Grubba)  SETUID_TRACE("Saving file", 0);
c7ec632001-08-15Henrik Grubbström (Grubba)  rm(f); mkdirhier(f);
8afb501998-04-15Henrik Grubbström (Grubba) 
2f83081999-05-05Henrik Grubbström (Grubba)  if (id->misc->quota_obj) {
c7ec632001-08-15Henrik Grubbström (Grubba)  QUOTA_WERR("Checking if the file already existed.");
2f83081999-05-05Henrik Grubbström (Grubba)  if (size > 0) {
ac69b51999-12-28Martin Nilsson  QUOTA_WERR("Deallocating " + size + "bytes.");
3f286a2000-08-23Per Hedbor  id->misc->quota_obj->deallocate(URI, size);
2f83081999-05-05Henrik Grubbström (Grubba)  } }
c7ec632001-08-15Henrik Grubbström (Grubba)  object to = open(f, "wct");
f53aae2004-05-13Martin Stjernholm  int err = errno();
5e5fd62000-03-07Martin Stjernholm  TRACE_ENTER("PUT: Accepted", 0);
8afb501998-04-15Henrik Grubbström (Grubba)  /* Clear the stat-cache for this file */ if (stat_cache) { cache_set("stat_cache", f, 0); }
b1fca01996-11-12Per Hedbor  if(!to)
f6d62d1997-03-26Per Hedbor  {
8593572005-08-25Henrik Grubbström (Grubba)  privs = 0;
8afb501998-04-15Henrik Grubbström (Grubba)  TRACE_LEAVE("PUT: Open failed");
f53aae2004-05-13Martin Stjernholm  return errno_to_status (err, 1, id);
f6d62d1997-03-26Per Hedbor  }
0163161999-04-21Henrik Grubbström (Grubba)  // FIXME: Race-condition.
9aea232005-10-19Henrik Grubbström (Grubba)  string msg = safe_chmod(f, 0666 & ~(id->misc->umask || 022));
8593572005-08-25Henrik Grubbström (Grubba)  privs = 0;
0163161999-04-21Henrik Grubbström (Grubba) 
2f83081999-05-05Henrik Grubbström (Grubba)  putting[id->my_fd] = id->misc->len;
b1fca01996-11-12Per Hedbor  if(id->data && strlen(id->data)) {
2f83081999-05-05Henrik Grubbström (Grubba)  // FIXME: What if sizeof(id->data) > id->misc->len ? if (id->misc->len > 0) { putting[id->my_fd] -= strlen(id->data); } int bytes = to->write( id->data ); if (id->misc->quota_obj) {
ac69b51999-12-28Martin Nilsson  QUOTA_WERR("Allocating " + bytes + "bytes.");
2f83081999-05-05Henrik Grubbström (Grubba)  if (!id->misc->quota_obj->allocate(f, bytes)) { TRACE_LEAVE("PUT: A string"); TRACE_LEAVE("PUT: Out of quota");
f53aae2004-05-13Martin Stjernholm  return Roxen.http_status(507, "Out of disk quota.");
2f83081999-05-05Henrik Grubbström (Grubba)  } }
b1fca01996-11-12Per Hedbor  }
8afb501998-04-15Henrik Grubbström (Grubba)  if(!putting[id->my_fd]) { TRACE_LEAVE("PUT: Just a string"); TRACE_LEAVE("Put: Success");
0c8b5b2003-12-23Henrik Grubbström (Grubba)  if (size < 0) {
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(201, "Created.");
0c8b5b2003-12-23Henrik Grubbström (Grubba)  } else {
5c27782004-05-12Martin Stjernholm  // FIXME: Isn't 204 better? /mast
0c8b5b2003-12-23Henrik Grubbström (Grubba)  return Roxen.http_string_answer("Ok"); }
8afb501998-04-15Henrik Grubbström (Grubba)  }
b1fca01996-11-12Per Hedbor 
a2f3461997-06-12Henrik Grubbström (Grubba)  if(id->clientprot == "HTTP/1.1") {
b1fca01996-11-12Per Hedbor  id->my_fd->write("HTTP/1.1 100 Continue\r\n");
a2f3461997-06-12Henrik Grubbström (Grubba)  }
0c8b5b2003-12-23Henrik Grubbström (Grubba)  id->my_fd->set_id( ({ to, id->my_fd, id, URI, size }) );
b1fca01996-11-12Per Hedbor  id->my_fd->set_nonblocking(got_put_data, 0, done_with_put);
8afb501998-04-15Henrik Grubbström (Grubba)  TRACE_LEAVE("PUT: Pipe in progress"); TRACE_LEAVE("PUT: Success so far");
f4b8bc2000-05-01Martin Nilsson  return Roxen.http_pipe_in_progress();
b1fca01996-11-12Per Hedbor  break;
f53aae2004-05-13Martin Stjernholm  case "CHMOD": {
b04ec82000-01-31Per Hedbor  // Change permission of a file.
1358d51999-05-06Henrik Grubbström (Grubba)  // FIXME: !!
b04ec82000-01-31Per Hedbor 
ed540b2001-01-13Martin Nilsson  if(!query("put"))
6cec001998-05-14David Hedbor  { id->misc->error_code = 405; TRACE_LEAVE("CHMOD disallowed (since PUT is disallowed)"); return 0;
b04ec82000-01-31Per Hedbor  }
6cec001998-05-14David Hedbor 
54b4802000-03-07Martin Stjernholm  if (FILTER_INTERNAL_FILE (f, id)) { id->misc->error_code = 405; TRACE_LEAVE("CHMOD of internal file is disallowed"); return 0; }
f819222004-05-08Henrik Grubbström (Grubba)  if (mapping(string:mixed) ret = write_access(oldf, 0, id)) {
534e292004-05-10Martin Stjernholm  TRACE_LEAVE("CHMOD: Locked");
699fa72004-05-06Henrik Grubbström (Grubba)  return ret; }
b04ec82000-01-31Per Hedbor 
4dea322001-12-21Henrik Grubbström (Grubba)  SETUID_TRACE("CHMODing file", 0);
b04ec82000-01-31Per Hedbor 
ed540b2001-01-13Martin Nilsson  if (query("no_symlinks") && (contains_symlinks(path, oldf))) {
6cec001998-05-14David Hedbor  privs = 0; errors++; TRACE_LEAVE("CHMOD: Contains symlinks. Permission denied");
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(403, "Permission denied.");
6cec001998-05-14David Hedbor  }
9aea232005-10-19Henrik Grubbström (Grubba)  string msg = safe_chmod(f, id->misc->mode & 0777);
f53aae2004-05-13Martin Stjernholm  int err_code = errno();
5e5fd62000-03-07Martin Stjernholm  privs = 0;
6cec001998-05-14David Hedbor  chmods++; TRACE_ENTER("CHMOD: Accepted", 0); if (stat_cache) { cache_set("stat_cache", f, 0); }
b04ec82000-01-31Per Hedbor 
9aea232005-10-19Henrik Grubbström (Grubba)  if(msg)
6cec001998-05-14David Hedbor  {
9aea232005-10-19Henrik Grubbström (Grubba)  TRACE_LEAVE(sprintf("CHMOD: Failure: %s", msg));
f53aae2004-05-13Martin Stjernholm  return errno_to_status (err_code, 0, id);
6cec001998-05-14David Hedbor  } TRACE_LEAVE("CHMOD: Success"); TRACE_LEAVE("Success");
f4b8bc2000-05-01Martin Nilsson  return Roxen.http_string_answer("Ok");
f53aae2004-05-13Martin Stjernholm  }
b04ec82000-01-31Per Hedbor 
f53aae2004-05-13Martin Stjernholm  case "MV": {
b04ec82000-01-31Per Hedbor  // This little kluge is used by ftp2 to move files.
2f83081999-05-05Henrik Grubbström (Grubba)  // FIXME: Support for quota.
ed540b2001-01-13Martin Nilsson  if(!query("put"))
dba2b51998-05-14David Hedbor  { id->misc->error_code = 405; TRACE_LEAVE("MV disallowed (since PUT is disallowed)"); return 0;
b04ec82000-01-31Per Hedbor  }
ed540b2001-01-13Martin Nilsson  if(!query("delete") && size != -1)
dba2b51998-05-14David Hedbor  { id->misc->error_code = 405; TRACE_LEAVE("MV disallowed (DELE disabled, can't overwrite file)"); return 0; }
f37e471998-05-14Henrik Grubbström (Grubba)  if(size < -1)
dba2b51998-05-14David Hedbor  { id->misc->error_code = 405; TRACE_LEAVE("MV: Cannot overwrite directory"); return 0; } string movefrom; if(!id->misc->move_from ||
f819222004-05-08Henrik Grubbström (Grubba)  !has_prefix(id->misc->move_from, mountpoint) ||
dba2b51998-05-14David Hedbor  !(movefrom = id->conf->real_file(id->misc->move_from, id))) { id->misc->error_code = 405; errors++; TRACE_LEAVE("MV: No source file"); return 0; }
54b4802000-03-07Martin Stjernholm 
f819222004-05-08Henrik Grubbström (Grubba)  string relative_from = id->misc->move_from[sizeof(mountpoint)..];
54b4802000-03-07Martin Stjernholm  if (FILTER_INTERNAL_FILE (movefrom, id) || FILTER_INTERNAL_FILE (f, id)) { id->misc->error_code = 405; TRACE_LEAVE("MV to or from internal file is disallowed"); return 0; }
ed540b2001-01-13Martin Nilsson  if (query("no_symlinks") &&
f37e471998-05-14Henrik Grubbström (Grubba)  ((contains_symlinks(path, oldf)) || (contains_symlinks(path, id->misc->move_from)))) {
dba2b51998-05-14David Hedbor  errors++; TRACE_LEAVE("MV: Contains symlinks. Permission denied");
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(403, "Permission denied.");
dba2b51998-05-14David Hedbor  }
f819222004-05-08Henrik Grubbström (Grubba)  // FIXME: What about moving of directories containing locked files? if (mapping(string:mixed) ret = write_access(oldf, 0, id) || write_access(relative_from, 0, id)) { TRACE_LEAVE("MV: Locked"); return ret; } SETUID_TRACE("Moving file", 0);
c7ec632001-08-15Henrik Grubbström (Grubba)  code = mv(movefrom, f);
f53aae2004-05-13Martin Stjernholm  int err_code = errno();
5e5fd62000-03-07Martin Stjernholm  privs = 0; moves++;
dba2b51998-05-14David Hedbor  TRACE_ENTER("MV: Accepted", 0); /* Clear the stat-cache for this file */ if (stat_cache) { cache_set("stat_cache", movefrom, 0); cache_set("stat_cache", f, 0); } if(!code) { TRACE_LEAVE("MV: Move failed");
f53aae2004-05-13Martin Stjernholm  return errno_to_status (err_code, 1, id);
dba2b51998-05-14David Hedbor  } TRACE_LEAVE("MV: Success");
f37e471998-05-14Henrik Grubbström (Grubba)  TRACE_LEAVE("Success");
f4b8bc2000-05-01Martin Nilsson  return Roxen.http_string_answer("Ok");
f53aae2004-05-13Martin Stjernholm  }
de14f21998-10-19 Wim Bonis 
f53aae2004-05-13Martin Stjernholm  case "MOVE": {
699fa72004-05-06Henrik Grubbström (Grubba)  // This little kluge is used by NETSCAPE 4.5 and RFC 2518.
2f83081999-05-05Henrik Grubbström (Grubba)  // FIXME: Support for quota.
b04ec82000-01-31Per Hedbor 
ed540b2001-01-13Martin Nilsson  if(!query("put"))
de14f21998-10-19 Wim Bonis  { id->misc->error_code = 405; TRACE_LEAVE("MOVE disallowed (since PUT is disallowed)"); return 0;
b04ec82000-01-31Per Hedbor  }
38c7f92003-02-25Henrik Grubbström (Grubba)  if(size == -1)
de14f21998-10-19 Wim Bonis  { id->misc->error_code = 404; TRACE_LEAVE("MOVE failed (no such file)"); return 0; }
78b6032004-05-10Henrik Grubbström (Grubba)  string new_uri = id->misc["new-uri"] || "";
0681342004-04-20Henrik Grubbström (Grubba)  if (new_uri == "") {
de14f21998-10-19 Wim Bonis  id->misc->error_code = 405; errors++; TRACE_LEAVE("MOVE: No dest file"); return 0; }
3a26d12000-12-29Henrik Grubbström (Grubba)  // FIXME: The code below doesn't allow for this module being overloaded.
f819222004-05-08Henrik Grubbström (Grubba)  if (!has_prefix(new_uri, mountpoint)) {
de14f21998-10-19 Wim Bonis  id->misc->error_code = 405; TRACE_LEAVE("MOVE: Dest file on other filesystem."); return(0); }
f819222004-05-08Henrik Grubbström (Grubba)  new_uri = new_uri[sizeof(mountpoint)..]; string moveto = path + "/" + new_uri;
de14f21998-10-19 Wim Bonis 
a23fa52004-05-13Henrik Grubbström (Grubba)  // Workaround for Linux, Tru64 and FreeBSD. if (has_suffix(moveto, "/")) { moveto = moveto[..sizeof(moveto)-2]; }
54b4802000-03-07Martin Stjernholm  if (FILTER_INTERNAL_FILE (f, id) || FILTER_INTERNAL_FILE (moveto, id)) { id->misc->error_code = 405; TRACE_LEAVE("MOVE to or from internal file is disallowed"); return 0; }
ed540b2001-01-13Martin Nilsson  if (query("no_symlinks") &&
de14f21998-10-19 Wim Bonis  ((contains_symlinks(path, f)) || (contains_symlinks(path, moveto)))) { privs = 0; errors++; TRACE_LEAVE("MOVE: Contains symlinks. Permission denied");
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(403, "Permission denied.");
de14f21998-10-19 Wim Bonis  }
f819222004-05-08Henrik Grubbström (Grubba)  if (mapping(string:mixed) ret = write_access(new_uri, 0, id) || write_access(oldf, 0, id)) { TRACE_LEAVE("MOVE: Locked"); return ret; }
78b6032004-05-10Henrik Grubbström (Grubba)  size = _file_size(moveto,id);
f819222004-05-08Henrik Grubbström (Grubba)  SETUID_TRACE("Moving file", 0);
78b6032004-05-10Henrik Grubbström (Grubba)  if (size != -1) { // Destination exists.
aaed922004-05-13Martin Stjernholm  int(0..1) overwrite = !id->request_headers->overwrite || id->request_headers->overwrite == "T"; if (!overwrite) {
78b6032004-05-10Henrik Grubbström (Grubba)  privs = 0; TRACE_LEAVE("MOVE disallowed (overwrite header:F)."); return Roxen.http_status(412); } if(!query("delete")) { privs = 0; id->misc->error_code = 405; TRACE_LEAVE("MOVE disallowed (DELE disabled)"); return 0; }
aaed922004-05-13Martin Stjernholm  if (overwrite || (size > -1)) {
8dbd1b2004-05-10Henrik Grubbström (Grubba)  mapping(string:mixed) res =
534e292004-05-10Martin Stjernholm  recurse_delete_files(new_uri, id);
5c27782004-05-12Martin Stjernholm  if (res && (!sizeof (res) || res->error >= 300)) {
78b6032004-05-10Henrik Grubbström (Grubba)  privs = 0; TRACE_LEAVE("MOVE: Recursive delete failed.");
5c27782004-05-12Martin Stjernholm  if (sizeof (res)) set_status_for_path (new_uri, res->error, res->rettext); return ([]);
78b6032004-05-10Henrik Grubbström (Grubba)  } } else { privs = 0; TRACE_LEAVE("MOVE: Cannot overwrite directory"); return Roxen.http_status(412); } }
c7ec632001-08-15Henrik Grubbström (Grubba)  code = mv(f, decode_path(moveto));
f53aae2004-05-13Martin Stjernholm  int err_code = errno();
5e5fd62000-03-07Martin Stjernholm  privs = 0;
de14f21998-10-19 Wim Bonis  TRACE_ENTER("MOVE: Accepted", 0); moves++; /* Clear the stat-cache for this file */ if (stat_cache) { cache_set("stat_cache", moveto, 0); cache_set("stat_cache", f, 0); } if(!code) {
2045292004-05-14Martin Stjernholm  SIMPLE_TRACE_LEAVE("MOVE: Move failed (%s)", strerror (err_code));
f53aae2004-05-13Martin Stjernholm  return errno_to_status (err_code, 1, id);
de14f21998-10-19 Wim Bonis  } TRACE_LEAVE("MOVE: Success"); TRACE_LEAVE("Success");
78b6032004-05-10Henrik Grubbström (Grubba)  if (size != -1) return Roxen.http_status(204); return Roxen.http_status(201);
f53aae2004-05-13Martin Stjernholm  }
b04ec82000-01-31Per Hedbor 
b1fca01996-11-12Per Hedbor  case "DELETE":
1f11d52003-12-29Henrik Grubbström (Grubba)  if (size==-1) { id->misc->error_code = 404; TRACE_LEAVE("DELETE: Not found"); return 0; }
0c8b5b2003-12-23Henrik Grubbström (Grubba)  if(!query("delete"))
f6d62d1997-03-26Per Hedbor  { id->misc->error_code = 405;
8afb501998-04-15Henrik Grubbström (Grubba)  TRACE_LEAVE("DELETE: Disabled");
b1fca01996-11-12Per Hedbor  return 0;
f6d62d1997-03-26Per Hedbor  }
54b4802000-03-07Martin Stjernholm  if (FILTER_INTERNAL_FILE (f, id)) { id->misc->error_code = 405; TRACE_LEAVE("DELETE of internal file is disallowed"); return 0; }
ed540b2001-01-13Martin Nilsson  if (query("no_symlinks") && (contains_symlinks(path, oldf))) {
afb1581997-07-06Henrik Grubbström (Grubba)  errors++;
de9ca82000-11-27Per Hedbor  report_error(LOCALE(48,"Deletion of %s failed. Permission denied.\n"),f);
8afb501998-04-15Henrik Grubbström (Grubba)  TRACE_LEAVE("DELETE: Contains symlinks");
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(403, "Permission denied.");
afb1581997-07-06Henrik Grubbström (Grubba)  }
a4d1ad2003-06-11Henrik Grubbström (Grubba)  if ((size < 0) &&
0c8b5b2003-12-23Henrik Grubbström (Grubba)  (String.trim_whites(id->request_headers->depth||"infinity") != "infinity")) {
a4d1ad2003-06-11Henrik Grubbström (Grubba)  // RFC 2518 8.6.2: // The DELETE method on a collection MUST act as if a "Depth: infinity" // header was used on it. TRACE_LEAVE(sprintf("DELETE: Bad depth header: %O.", id->request_headers->depth));
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(400, "Unsupported depth.");
a4d1ad2003-06-11Henrik Grubbström (Grubba)  }
f6d62d1997-03-26Per Hedbor 
a4d1ad2003-06-11Henrik Grubbström (Grubba)  if (size < 0) {
40cae52004-05-09Henrik Grubbström (Grubba)  mapping|int(0..1) res; if (mappingp(res = write_access(combine_path(oldf, "../"), 1, id)) || (res && mappingp(res = write_access(oldf, 1, id)))) { SIMPLE_TRACE_LEAVE("DELETE: Recursive write access denied.");
8dbd1b2004-05-10Henrik Grubbström (Grubba)  return res;
40cae52004-05-09Henrik Grubbström (Grubba)  }
5c27782004-05-12Martin Stjernholm #if 0
eb73ee2003-06-26Anders Johansson  report_notice(LOCALE(64,"DELETING the directory %s.\n"), f);
5c27782004-05-12Martin Stjernholm #endif
f6d62d1997-03-26Per Hedbor 
a4d1ad2003-06-11Henrik Grubbström (Grubba)  accesses++;
8afb501998-04-15Henrik Grubbström (Grubba) 
a4d1ad2003-06-11Henrik Grubbström (Grubba)  SETUID_TRACE("Deleting directory", 0);
39edd92004-05-05Martin Stjernholm  int start_ms_size = id->multi_status_size();
40cae52004-05-09Henrik Grubbström (Grubba)  recursive_rm(f, query_location() + oldf, res, id);
a4d1ad2003-06-11Henrik Grubbström (Grubba) 
f53aae2004-05-13Martin Stjernholm  if (!rm(f) && errno() != System.ENOENT) {
39edd92004-05-05Martin Stjernholm  if (id->multi_status_size() > start_ms_size) {
f53aae2004-05-13Martin Stjernholm  if (errno() != System.EEXIST #if constant (System.ENOTEMPTY) && errno() != System.ENOTEMPTY
a4d1ad2003-06-11Henrik Grubbström (Grubba) #endif
f53aae2004-05-13Martin Stjernholm  )
a4d1ad2003-06-11Henrik Grubbström (Grubba)  {
f53aae2004-05-13Martin Stjernholm  return errno_to_status (errno(), 0, id);
a4d1ad2003-06-11Henrik Grubbström (Grubba)  } } else {
f53aae2004-05-13Martin Stjernholm  return errno_to_status (errno(), 0, id);
a4d1ad2003-06-11Henrik Grubbström (Grubba)  }
f53aae2004-05-13Martin Stjernholm  if (id->multi_status_size() > start_ms_size) { TRACE_LEAVE("DELETE: Partial failure."); return ([]); }
a4d1ad2003-06-11Henrik Grubbström (Grubba)  } } else {
40cae52004-05-09Henrik Grubbström (Grubba)  mapping|int(0..1) res; if ((res = write_access(combine_path(oldf, "../"), 0, id)) || (res = write_access(oldf, 0, id))) { SIMPLE_TRACE_LEAVE("DELETE: Write access denied.");
8dbd1b2004-05-10Henrik Grubbström (Grubba)  return res;
40cae52004-05-09Henrik Grubbström (Grubba)  }
5c27782004-05-12Martin Stjernholm #if 0
a4d1ad2003-06-11Henrik Grubbström (Grubba)  report_notice(LOCALE(49,"DELETING the file %s.\n"),f);
5c27782004-05-12Martin Stjernholm #endif
a4d1ad2003-06-11Henrik Grubbström (Grubba)  accesses++; /* Clear the stat-cache for this file */ if (stat_cache) { cache_set("stat_cache", f, 0); }
40cae52004-05-09Henrik Grubbström (Grubba)  SETUID_TRACE("Deleting file", 0);
a4d1ad2003-06-11Henrik Grubbström (Grubba)  if(!rm(f)) { privs = 0; id->misc->error_code = 405; TRACE_LEAVE("DELETE: Failed"); return 0; }
64c51c1997-10-11Henrik Grubbström (Grubba)  privs = 0;
a4d1ad2003-06-11Henrik Grubbström (Grubba)  deletes++;
2f83081999-05-05Henrik Grubbström (Grubba) 
a4d1ad2003-06-11Henrik Grubbström (Grubba)  if (id->misc->quota_obj && (size > 0)) { id->misc->quota_obj->deallocate(oldf, size); }
2f83081999-05-05Henrik Grubbström (Grubba)  }
8afb501998-04-15Henrik Grubbström (Grubba)  TRACE_LEAVE("DELETE: Success");
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(204,(f+" DELETED from the server"));
b1fca01996-11-12Per Hedbor  default:
1f11d52003-12-29Henrik Grubbström (Grubba)  id->misc->error_code = 501;
0e14762004-05-10Henrik Grubbström (Grubba)  SIMPLE_TRACE_LEAVE("%s: Not supported", id->method);
b1fca01996-11-12Per Hedbor  return 0; }
8afb501998-04-15Henrik Grubbström (Grubba)  TRACE_LEAVE("Not reached");
b1fca01996-11-12Per Hedbor  return 0; }
f35f472004-05-10Henrik Grubbström (Grubba) mapping copy_file(string source, string dest, PropertyBehavior behavior, Overwrite overwrite, RequestID id)
0681342004-04-20Henrik Grubbström (Grubba) {
40cae52004-05-09Henrik Grubbström (Grubba)  SIMPLE_TRACE_ENTER(this, "COPY: Copy %O to %O.", source, dest); Stat source_st = stat_file(source, id);
0681342004-04-20Henrik Grubbström (Grubba)  if (!source_st) { TRACE_LEAVE("COPY: Source doesn't exist.");
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(404, "File not found.");
0681342004-04-20Henrik Grubbström (Grubba)  } if (!query("put")) { TRACE_LEAVE("COPY: Put not allowed.");
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(405, "Not allowed.");
0681342004-04-20Henrik Grubbström (Grubba)  }
3284412004-05-13Henrik Grubbström (Grubba)  mapping|int(0..1) res = write_access(dest, 0, id) || write_access(combine_path(dest, "../"), 0, id);
40cae52004-05-09Henrik Grubbström (Grubba)  if (mappingp(res)) return res;
0681342004-04-20Henrik Grubbström (Grubba)  string dest_path = path + dest; catch { dest_path = decode_path(dest_path); };
f53aae2004-05-13Martin Stjernholm  dest_path = NORMALIZE_PATH (dest_path);
40cae52004-05-09Henrik Grubbström (Grubba)  if (query("no_symlinks") && (contains_symlinks(path, dest_path))) { errors++;
496d162004-05-14Anders Johansson  report_error(LOCALE(57,"Copy to %O failed. Permission denied.\n"),
40cae52004-05-09Henrik Grubbström (Grubba)  dest); TRACE_LEAVE("COPY: Contains symlinks. Permission denied");
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(403, "Permission denied.");
40cae52004-05-09Henrik Grubbström (Grubba)  } Stat dest_st = stat_file(dest, id); if (dest_st) {
f53aae2004-05-13Martin Stjernholm  SIMPLE_TRACE_ENTER (this, "COPY: Destination exists");
f35f472004-05-10Henrik Grubbström (Grubba)  switch(overwrite) { case NEVER_OVERWRITE:
f53aae2004-05-13Martin Stjernholm  TRACE_LEAVE(""); TRACE_LEAVE("");
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(412, "Destination already exists.");
f35f472004-05-10Henrik Grubbström (Grubba)  case DO_OVERWRITE:
40cae52004-05-09Henrik Grubbström (Grubba)  if (!query("delete")) { TRACE_LEAVE("COPY: Deletion not allowed.");
f53aae2004-05-13Martin Stjernholm  TRACE_LEAVE("");
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(405, "Not allowed.");
40cae52004-05-09Henrik Grubbström (Grubba)  } object privs; SETUID_TRACE("Deleting destination", 0); if (dest_st->isdir) {
f53aae2004-05-13Martin Stjernholm  int start_ms_size = id->multi_status_size();
40cae52004-05-09Henrik Grubbström (Grubba)  recursive_rm(dest_path, mountpoint + dest, 1, id);
f53aae2004-05-13Martin Stjernholm  werror ("dest_path %O\n", dest_path); if (!rm(dest_path) && errno() != System.ENOENT) {
40cae52004-05-09Henrik Grubbström (Grubba)  privs = 0;
f53aae2004-05-13Martin Stjernholm  if (id->multi_status_size() > start_ms_size) { if (errno() != System.EEXIST #if constant (System.ENOTEMPTY) && errno() != System.ENOTEMPTY
40cae52004-05-09Henrik Grubbström (Grubba) #endif
f53aae2004-05-13Martin Stjernholm  ) { TRACE_LEAVE(""); return errno_to_status (errno(), 0, id); } } else { TRACE_LEAVE(""); return errno_to_status (errno(), 0, id); } if (id->multi_status_size() > start_ms_size) { privs = 0; TRACE_LEAVE("COPY: Partial failure in destination directory delete."); TRACE_LEAVE(""); return ([]);
40cae52004-05-09Henrik Grubbström (Grubba)  } }
f53aae2004-05-13Martin Stjernholm  SIMPLE_TRACE_LEAVE("COPY: Delete ok.");
40cae52004-05-09Henrik Grubbström (Grubba)  } else if (source_st->isdir) { if (!rm(dest_path)) { privs = 0;
f53aae2004-05-13Martin Stjernholm  if (errno() != System.ENOENT)
40cae52004-05-09Henrik Grubbström (Grubba)  {
f53aae2004-05-13Martin Stjernholm  mapping(string:mixed) status = errno_to_status (errno(), 0, id); if (!status) status = (["error": id->misc->error_code]); id->set_status_for_path(mountpoint + dest, status->error, status->rettext); TRACE_LEAVE(""); return ([]);
40cae52004-05-09Henrik Grubbström (Grubba)  }
f53aae2004-05-13Martin Stjernholm  SIMPLE_TRACE_LEAVE("COPY: File deletion failed (destination disappeared).");
40cae52004-05-09Henrik Grubbström (Grubba)  } else { SIMPLE_TRACE_LEAVE("COPY: File deletion ok."); } } else { SIMPLE_TRACE_LEAVE("COPY: No need to perform deletion."); } privs = 0;
f35f472004-05-10Henrik Grubbström (Grubba)  break; case MAYBE_OVERWRITE: if ((source_st->isreg != dest_st->isreg) || (source_st->isdir != dest_st->isdir)) { TRACE_LEAVE("COPY: Resource types for source and destination differ.");
f53aae2004-05-13Martin Stjernholm  TRACE_LEAVE("");
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(412, "Destination and source are different resource types.");
f35f472004-05-10Henrik Grubbström (Grubba)  } else if (source_st->isdir) { TRACE_LEAVE("Already done (both are directories).");
f53aae2004-05-13Martin Stjernholm  TRACE_LEAVE("");
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(204, "Destination already existed.");
f35f472004-05-10Henrik Grubbström (Grubba)  } break;
40cae52004-05-09Henrik Grubbström (Grubba)  } }
f53aae2004-05-13Martin Stjernholm 
0681342004-04-20Henrik Grubbström (Grubba)  if (source_st->isdir) { mkdirs++; object privs; SETUID_TRACE("Creating directory/collection", 0); int code = mkdir(dest_path); int err_code = errno(); if (code) {
9aea232005-10-19Henrik Grubbström (Grubba)  string msg = safe_chmod(dest_path, 0777 & ~(id->misc->umask || 022)); privs = 0; if (msg) { TRACE_LEAVE(sprintf("Chmod %O failed: %s", dest_path, msg)); } else { TRACE_LEAVE("Success"); }
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(dest_st?204:201, "Created");
0681342004-04-20Henrik Grubbström (Grubba)  } else {
f53aae2004-05-13Martin Stjernholm  return errno_to_status (err_code, 1, id);
0681342004-04-20Henrik Grubbström (Grubba)  }
f819222004-05-08Henrik Grubbström (Grubba)  } else {
40cae52004-05-09Henrik Grubbström (Grubba)  string source_path = path + source; catch { source_path = decode_path(source_path); };
f53aae2004-05-13Martin Stjernholm  source_path = NORMALIZE_PATH (source_path);
40cae52004-05-09Henrik Grubbström (Grubba)  if (query("no_symlinks") && (contains_symlinks(path, source_path))) { errors++;
496d162004-05-14Anders Johansson  report_error(LOCALE(57,"Copy to %O failed. Permission denied.\n"),
40cae52004-05-09Henrik Grubbström (Grubba)  dest); TRACE_LEAVE("COPY: Contains symlinks. Permission denied");
5c27782004-05-12Martin Stjernholm  return Roxen.http_status(403, "Permission denied.");
40cae52004-05-09Henrik Grubbström (Grubba)  } puts++; QUOTA_WERR("Checking quota.\n"); if (id->misc->quota_obj && (id->misc->len > 0) && !id->misc->quota_obj->check_quota(mountpoint + dest, source_st->size)) { errors++; report_warning(LOCALE(47,"Creation of %O failed. Out of quota.\n"), dest_path); TRACE_LEAVE("PUT: Out of quota.");
f53aae2004-05-13Martin Stjernholm  return Roxen.http_status(507, "Out of disk quota.");
40cae52004-05-09Henrik Grubbström (Grubba)  } object source_file = open(source_path, "r"); if (!source_file) { TRACE_LEAVE("Failed to open source file."); return Roxen.http_status(404); }
a23fa52004-05-13Henrik Grubbström (Grubba)  // Workaround for Linux, Tru64 and FreeBSD.
1e41c92004-05-13Henrik Grubbström (Grubba)  if (has_suffix(dest_path, "/")) { dest_path = dest_path[..sizeof(dest_path)-2]; }
40cae52004-05-09Henrik Grubbström (Grubba)  object privs;
7866962004-05-10Henrik Grubbström (Grubba)  SETUID_TRACE("COPY: Copying file.", 0);
40cae52004-05-09Henrik Grubbström (Grubba)  object dest_file = open(dest_path, "cwt"); privs = 0; if (!dest_file) {
f53aae2004-05-13Martin Stjernholm  return errno_to_status (errno(), 1, id);
40cae52004-05-09Henrik Grubbström (Grubba)  } int len = source_st->size; while (len > 0) { string buf = source_file->read((len > 4096)?4096:len); if (buf && sizeof(buf)) { int sub_len; len -= (sub_len = sizeof(buf)); while (sub_len > 0) { int written = dest_file->write(buf); if ((sub_len -= written) > 0) { if (!written) { SIMPLE_TRACE_LEAVE("Write failed with errno %d", dest_file->errno()); dest_file->close(); source_file->close(); return Roxen.http_status(Protocols.HTTP.DAV_STORAGE_FULL); } buf = buf[written..]; } } } else { break; } } if (len > 0) { SIMPLE_TRACE_LEAVE("Read failed with %d bytes left.", len); } else { SIMPLE_TRACE_LEAVE("Copy complete."); } dest_file->close(); source_file->close(); return Roxen.http_status(dest_st?Protocols.HTTP.HTTP_NO_CONTENT: Protocols.HTTP.HTTP_CREATED);
0681342004-04-20Henrik Grubbström (Grubba)  } }
b1fca01996-11-12Per Hedbor string query_name() {
daf00c2001-11-26Chris Jantzen  if (sizeof(path) > 20) {
6fbcab2002-07-01Anders Johansson  return sprintf((string)LOCALE(63,"%s from %s...%s"),
daf00c2001-11-26Chris Jantzen  mountpoint, path[..7], path[sizeof(path)-8..]); }
06225a2001-05-16Martin Nilsson  return sprintf((string)LOCALE(50,"%s from %s"), mountpoint, path);
b1fca01996-11-12Per Hedbor }