ac69b51999-12-28Martin Nilsson // This is a roxen module. Copyright © 1996 - 1999, Idonex AB.
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 "roxenlib"; inherit "socket";
0aa9d62000-02-14Per Hedbor constant cvs_version= "$Id: filesystem.pike,v 1.64 2000/02/14 09:25:26 per 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  #if DEBUG_LEVEL > 20 # ifndef FILESYSTEM_DEBUG # define FILESYSTEM_DEBUG # endif #endif
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
c5e0961999-10-04Per Hedbor constant module_type = MODULE_LOCATION; constant module_name = "Filesystem";
b04ec82000-01-31Per Hedbor constant module_doc =
c5e0961999-10-04Per Hedbor ("This is a virtual filesystem, use it to make files available to "+ "the users of your WWW-server. If you want to serve any 'normal' " "files from your server, you will have to have atleast one filesystem.") ; 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) { mapping res = ::http_low_answer(errno, data); if (desc) { res->rettext = desc; } return res; }
b1fca01996-11-12Per Hedbor static int do_stat = 1; string status() {
ac69b51999-12-28Martin Nilsson  return "<h2>Accesses to this filesystem</h2>"+ (redirects?"<b>Redirects</b>: "+redirects+"<br>":"")+ (accesses?"<b>Normal files</b>: "+accesses+"<br>" :"No file accesses<br>")+ (QUERY(put)&&puts?"<b>Puts</b>: "+puts+"<br>":"")+ (QUERY(put)&&mkdirs?"<b>Mkdirs</b>: "+mkdirs+"<br>":"")+ (QUERY(put)&&QUERY(delete)&&moves? "<b>Moved files</b>: "+moves+"<br>":"")+ (QUERY(put)&&chmods?"<b>CHMODs</b>: "+chmods+"<br>":"")+ (QUERY(delete)&&deletes?"<b>Deletes</b>: "+deletes+"<br>":"")+ (errors?"<b>Permission denied</b>: "+errors +" (not counting .htaccess)<br>":"")+ (dirlists?"<b>Directories</b>:"+dirlists+"<br>":"");
b1fca01996-11-12Per Hedbor } void create() {
b04ec82000-01-31Per Hedbor  defvar("mountpoint", "/", "Mount point", TYPE_LOCATION|VAR_INITIAL,
b1fca01996-11-12Per Hedbor  "This is where the module will be inserted in the "+ "namespace of your server.");
b04ec82000-01-31Per Hedbor  defvar("searchpath", "NONE", "Search path", TYPE_DIR|VAR_INITIAL,
b1fca01996-11-12Per Hedbor  "This is where the module will find the files in the real "+ "file system");
9b9f701997-08-12Per Hedbor  defvar(".files", 0, "Show hidden files", TYPE_FLAG|VAR_MORE,
b1fca01996-11-12Per Hedbor  "If set, hidden files will be shown in dirlistings and you " "will be able to retrieve them.");
9b9f701997-08-12Per Hedbor  defvar("dir", 1, "Enable directory listings per default", TYPE_FLAG|VAR_MORE,
b1fca01996-11-12Per Hedbor  "If set, you have to create a file named .www_not_browsable (" "or .nodiraccess) in a directory to disable directory listings." " If unset, a file named .www_browsable in a directory will " "_enable_ directory listings.\n");
0026281999-06-07Martin Stjernholm  defvar("tilde", 0, "Show backup files", TYPE_FLAG|VAR_MORE,
b1fca01996-11-12Per Hedbor  "If set, files ending with '~' or '#' or '.bak' will "+ "be shown in directory listings");
b04ec82000-01-31Per Hedbor  defvar("put", 0, "Handle the PUT method", TYPE_FLAG|VAR_INITIAL,
b1fca01996-11-12Per Hedbor  "If set, PUT can be used to upload files to the server.");
b04ec82000-01-31Per Hedbor  defvar("delete", 0, "Handle the DELETE method", TYPE_FLAG|VAR_INITIAL,
b1fca01996-11-12Per Hedbor  "If set, DELETE can be used to delete files from the " "server.");
26d1321997-01-29David KÃ¥gedal  defvar("check_auth", 1, "Require authentication for modification",
b1fca01996-11-12Per Hedbor  TYPE_FLAG, "Only allow authenticated users to use methods other than " "GET and POST. If unset, this filesystem will be a _very_ " "public one (anyone can edit files located on it)");
14179b1997-01-29Per Hedbor 
6e82081999-12-07Martin Stjernholm  defvar("stat_cache", 0, "Cache the results of stat(2)",
9b9f701997-08-12Per Hedbor  TYPE_FLAG|VAR_MORE,
8d10df2000-01-06Martin Stjernholm  "This can speed up the retrieval of files up to 60/70% if you " "use NFS, but it does use some memory.\n" "<p>Also note that the cached results are only rechecked when the " "file is fetched with the no-cache pragma (produced e.g. by " "Alt-Ctrl-Reload in Netscape) and when the module is reloaded. " "Thus, it's not recommended to use this on filesystems that " "change a lot, e.g. where users are allowed to upload.");
2e7c231997-06-10Henrik Grubbström (Grubba)  defvar("access_as_user", 0, "Access file as the logged in user",
9b9f701997-08-12Per Hedbor  TYPE_FLAG|VAR_MORE,
2e7c231997-06-10Henrik Grubbström (Grubba)  "EXPERIMENTAL. Access file as the logged in user.<br>\n" "This is useful for eg named-ftp.");
afb1581997-07-06Henrik Grubbström (Grubba) 
9b9f701997-08-12Per Hedbor  defvar("no_symlinks", 0, "Forbid access to symlinks", TYPE_FLAG|VAR_MORE,
afb1581997-07-06Henrik Grubbström (Grubba)  "EXPERIMENTAL.\n" "Forbid access to paths containing symbolic links.<br>\n" "NOTE: This can cause *alot* of lstat system-calls to be performed " "and can make the server much slower.");
0aa9d62000-02-14Per Hedbor  defvar("charset", "iso-8859-1", "File charset", TYPE_STRING, "The charset the files on disk have.");
b1fca01996-11-12Per Hedbor }
32ae661997-01-27Per Hedbor 
b1fca01996-11-12Per Hedbor string path;
14179b1997-01-29Per Hedbor int stat_cache;
b1fca01996-11-12Per Hedbor  void start() {
8afc811998-02-04Per Hedbor #ifdef THREADS if(QUERY(access_as_user)) report_warning("It is not possible to use 'Access as user' when " "running with threads. Remove -DENABLE_THREADS from " "the start script if you really need this function\n"); #endif
b04ec82000-01-31Per Hedbor 
b1fca01996-11-12Per Hedbor  path = QUERY(searchpath);
14179b1997-01-29Per Hedbor  stat_cache = QUERY(stat_cache);
ac69b51999-12-28Martin Nilsson  FILESYSTEM_WERR("Online at "+QUERY(mountpoint)+" (path="+path+")");
8d10df2000-01-06Martin Stjernholm  cache_expire("stat_cache");
b1fca01996-11-12Per Hedbor } string query_location() { return QUERY(mountpoint); } mixed stat_file( mixed f, mixed id ) {
64c51c1997-10-11Henrik Grubbström (Grubba)  array fs; if(stat_cache && !id->pragma["no-cache"] && (fs=cache_lookup("stat_cache",path+f)))
1b8f6b1998-02-24Per Hedbor  return fs[0];
64c51c1997-10-11Henrik Grubbström (Grubba) 
8afc811998-02-04Per Hedbor #ifndef THREADS
1b8f6b1998-02-24Per Hedbor  object privs;
0216e21997-08-12Henrik Grubbström (Grubba)  if (((int)id->misc->uid) && ((int)id->misc->gid) && (QUERY(access_as_user))) { // NB: Root-access is prevented.
f7d9811997-09-12Per Hedbor  privs=Privs("Statting file", (int)id->misc->uid, (int)id->misc->gid );
0216e21997-08-12Henrik Grubbström (Grubba)  }
8afc811998-02-04Per Hedbor #endif
64c51c1997-10-11Henrik Grubbström (Grubba)  fs = file_stat(path + f); /* No security currently in this function */
1b8f6b1998-02-24Per Hedbor #ifndef THREADS
64c51c1997-10-11Henrik Grubbström (Grubba)  privs = 0;
1b8f6b1998-02-24Per Hedbor #endif
14179b1997-01-29Per Hedbor  if(!stat_cache) return fs;
1b8f6b1998-02-24Per Hedbor  cache_set("stat_cache", path+f, ({fs}));
14179b1997-01-29Per Hedbor  return fs;
b1fca01996-11-12Per Hedbor } string real_file( mixed f, mixed id ) {
b04ec82000-01-31Per Hedbor  if(this->stat_file( f, id ))
b1fca01996-11-12Per Hedbor /* This filesystem might be inherited by other filesystem, therefore
27b0e11996-11-26Per Hedbor  'this' */
b1fca01996-11-12Per Hedbor  return path + f; } int dir_filter_function(string f) { if(f[0]=='.' && !QUERY(.files)) return 0; if(!QUERY(tilde) && backup_extension(f)) return 0; return 1; } array find_dir( string f, object id ) { mixed ret; array dir;
0216e21997-08-12Henrik Grubbström (Grubba)  object privs;
8afc811998-02-04Per Hedbor #ifndef THREADS
0216e21997-08-12Henrik Grubbström (Grubba)  if (((int)id->misc->uid) && ((int)id->misc->gid) && (QUERY(access_as_user))) { // NB: Root-access is prevented.
f7d9811997-09-12Per Hedbor  privs=Privs("Getting dir", (int)id->misc->uid, (int)id->misc->gid );
0216e21997-08-12Henrik Grubbström (Grubba)  }
8afc811998-02-04Per Hedbor #endif
0216e21997-08-12Henrik Grubbström (Grubba) 
64c51c1997-10-11Henrik Grubbström (Grubba)  if(!(dir = get_dir( path + f ))) { privs = 0;
b1fca01996-11-12Per Hedbor  return 0;
64c51c1997-10-11Henrik Grubbström (Grubba)  }
0216e21997-08-12Henrik Grubbström (Grubba)  privs = 0;
b1fca01996-11-12Per Hedbor  if(!QUERY(dir)) // Access to this dir is allowed. if(search(dir, ".www_browsable") == -1) { errors++; return 0; } // Access to this dir is not allowed. if(sizeof(dir & ({".nodiraccess",".www_not_browsable",".nodir_access"}))) { errors++; return 0; } dirlists++; // Pass _all_ files, hide none. if(QUERY(tilde) && QUERY(.files)) /* This is quite a lot faster */ return dir;
e5bad21998-02-10Per Hedbor  return Array.filter(dir, dir_filter_function);
b1fca01996-11-12Per Hedbor } mapping putting = ([]);
2f83081999-05-05Henrik Grubbström (Grubba) void done_with_put( array(object|string) 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; [to, from, id, oldf] = id_arr;
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!
2f83081999-05-05Henrik Grubbström (Grubba)  id->send_result(http_low_answer(400, "<h2>Bad Request - " "Expected more data.</h2>"));
0163161999-04-21Henrik Grubbström (Grubba)  } else {
2f83081999-05-05Henrik Grubbström (Grubba)  id->send_result(http_low_answer(200, "<h2>Transfer Complete.</h2>"));
0163161999-04-21Henrik Grubbström (Grubba)  }
b1fca01996-11-12Per Hedbor }
2f83081999-05-05Henrik Grubbström (Grubba) void got_put_data( array (object|string) 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; [to, from, id, oldf] = id_arr;
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);
1358d51999-05-06Henrik Grubbström (Grubba)  id->send_result(http_low_answer(413, "<h2>Out of disk quota.</h2>", "413 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); id->send_result(http_low_answer(413, "<h2>Disk full.</h2>")); 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);
1358d51999-05-06Henrik Grubbström (Grubba)  id->send_result(http_low_answer(413, "<h2>Out of disk quota.</h2>", "413 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 }
14179b1997-01-29Per Hedbor int _file_size(string X,object id) { array fs; if(!id->pragma["no-cache"]&&(fs=cache_lookup("stat_cache",(X)))) {
1b8f6b1998-02-24Per Hedbor  id->misc->stat = fs[0]; return fs[0]?fs[0][ST_SIZE]:-1;
14179b1997-01-29Per Hedbor  } if(fs = file_stat(X)) { id->misc->stat = fs;
1b8f6b1998-02-24Per Hedbor  cache_set("stat_cache",(X),({fs}));
14179b1997-01-29Per Hedbor  return fs[ST_SIZE];
1b8f6b1998-02-24Per Hedbor  } else cache_set("stat_cache",(X),({0}));
14179b1997-01-29Per Hedbor  return -1; }
6396111997-02-14Per Hedbor #define FILE_SIZE(X) (stat_cache?_file_size((X),id):Stdio.file_size(X))
14179b1997-01-29Per Hedbor 
afb1581997-07-06Henrik Grubbström (Grubba) int contains_symlinks(string root, string path) { array arr = path/"/"; foreach(arr - ({ "" }), path) { root += "/" + path; if (arr = file_stat(root, 1)) { if (arr[1] == -3) { return(1); } } else { return(0); } } return(0); }
b1fca01996-11-12Per Hedbor mixed find_file( string f, object id ) {
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;
01128d1998-04-27Henrik Grubbström (Grubba) 
ac69b51999-12-28Martin Nilsson  FILESYSTEM_WERR("Request for \""+f+"\"");
01128d1998-04-27Henrik Grubbström (Grubba) 
1358d51999-05-06Henrik Grubbström (Grubba)  string mountpoint = QUERY(mountpoint); string uri = combine_path(mountpoint + "/" + f, ".");
f2e1da1998-04-24Per Hedbor  f = path + f;
da9e9f1998-04-24Per Hedbor #ifdef __NT__ if(f[-1]=='/') f = f[..strlen(f)-2];
b1fca01996-11-12Per Hedbor #endif
f2e1da1998-04-24Per Hedbor  size = FILE_SIZE( f );
b1fca01996-11-12Per Hedbor 
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: if(f[ -1 ] == '/') /* Trying to access file with '/' appended */ {
465eb61998-08-26Henrik Grubbström (Grubba)  /* Neotron was here. I changed this to always return 0 since * CGI-scripts with path info = / won't work otherwise. If * someone accesses a file with "/" appended, a 404 no such * file isn't that weird. Both Apache and Netscape return the * accessed page, resulting in incorrect links from that page. * * FIXME: The proper way to do this would probably be to set path info * here, and have the redirect be done by the extension modules, * or by the protocol module if there isn't any extension module. * /grubba 1998-08-26 */
b04ec82000-01-31Per Hedbor  return 0;
5e429c1997-05-25Wilhelm Köhler  /* Do not try redirect on top level directory */ if(sizeof(id->not_query) < 2) return 0;
b1fca01996-11-12Per Hedbor  redirects++;
a20b321999-01-14Henrik Grubbström (Grubba)  // Note: Keep the query part. /* FIXME: Should probably keep prestates etc too. * /grubba 1999-01-14 */
575f781999-01-14Henrik Grubbström (Grubba)  string new_query = http_encode_string(id->not_query[..sizeof(id->not_query)-2]) +
a20b321999-01-14Henrik Grubbström (Grubba)  (id->query?("?" + id->query):""); TRACE_LEAVE("Redirecting to \"" + new_query + "\""); return http_redirect(new_query, id);
b1fca01996-11-12Per Hedbor  }
0730621999-12-22Johan Sundström  if(!id->misc->internal_get && !QUERY(.files)
b1fca01996-11-12Per Hedbor  && (tmp = (id->not_query/"/")[-1])
8afb501998-04-15Henrik Grubbström (Grubba)  && tmp[0] == '.') { TRACE_LEAVE("Is .-file");
b1fca01996-11-12Per Hedbor  return 0;
8afb501998-04-15Henrik Grubbström (Grubba)  }
8afc811998-02-04Per Hedbor #ifndef THREADS
1b8f6b1998-02-24Per Hedbor  object privs;
2e7c231997-06-10Henrik Grubbström (Grubba)  if (((int)id->misc->uid) && ((int)id->misc->gid) && (QUERY(access_as_user))) {
0216e21997-08-12Henrik Grubbström (Grubba)  // NB: Root-access is prevented.
f7d9811997-09-12Per Hedbor  privs=Privs("Getting file", (int)id->misc->uid, (int)id->misc->gid );
2e7c231997-06-10Henrik Grubbström (Grubba)  }
8afc811998-02-04Per Hedbor #endif
2e7c231997-06-10Henrik Grubbström (Grubba) 
8afb501998-04-15Henrik Grubbström (Grubba)  TRACE_ENTER("Opening file \"" + f + "\"", 0);
b1fca01996-11-12Per Hedbor  o = open( f, "r" );
1b8f6b1998-02-24Per Hedbor #ifndef THREADS
2e7c231997-06-10Henrik Grubbström (Grubba)  privs = 0;
1b8f6b1998-02-24Per Hedbor #endif
2e7c231997-06-10Henrik Grubbström (Grubba) 
afb1581997-07-06Henrik Grubbström (Grubba)  if(!o || (QUERY(no_symlinks) && (contains_symlinks(path, oldf))))
b1fca01996-11-12Per Hedbor  { errors++; report_error("Open of " + f + " failed. Permission denied.\n");
b04ec82000-01-31Per Hedbor 
8afb501998-04-15Henrik Grubbström (Grubba)  TRACE_LEAVE(""); TRACE_LEAVE("Permission denied.");
b1fca01996-11-12Per Hedbor  return http_low_answer(403, "<h2>File exists, but access forbidden " "by user</h2>"); } id->realfile = f;
8afb501998-04-15Henrik Grubbström (Grubba)  TRACE_LEAVE("");
b1fca01996-11-12Per Hedbor  accesses++;
8afb501998-04-15Henrik Grubbström (Grubba)  TRACE_LEAVE("Normal return");
0aa9d62000-02-14Per Hedbor  id->misc->input_charset = query("charset");
b1fca01996-11-12Per Hedbor  return o; } break;
b04ec82000-01-31Per Hedbor 
8764c01998-05-01Henrik Grubbström (Grubba)  case "MKDIR": if(!QUERY(put)) { id->misc->error_code = 405; TRACE_LEAVE("MKDIR disallowed (since PUT is disallowed)"); return 0;
b04ec82000-01-31Per Hedbor  }
8764c01998-05-01Henrik Grubbström (Grubba)  if(QUERY(check_auth) && (!id->auth || !id->auth[0])) { TRACE_LEAVE("MKDIR: Permission denied"); return http_auth_required("foo", "<h1>Permission to 'MKDIR' denied</h1>"); } mkdirs++; object privs; // #ifndef THREADS // Ouch. This is is _needed_. Well well... if (((int)id->misc->uid) && ((int)id->misc->gid)) { // NB: Root-access is prevented. privs=Privs("Creating directory", (int)id->misc->uid, (int)id->misc->gid ); } // #endif if (QUERY(no_symlinks) && (contains_symlinks(path, oldf))) { privs = 0; errors++; report_error("Creation of " + f + " failed. Permission denied.\n"); TRACE_LEAVE("MKDIR: Contains symlinks. Permission denied"); return http_low_answer(403, "<h2>Permission denied.</h2>"); } TRACE_ENTER("MKDIR: Accepted", 0); int code = mkdir( f ); privs = 0; if (code) {
2422401998-06-04David Hedbor  chmod(f, 0777 & ~(id->misc->umask || 022));
8764c01998-05-01Henrik Grubbström (Grubba)  TRACE_LEAVE("MKDIR: Success"); TRACE_LEAVE("Success"); return http_string_answer("Ok"); } else { TRACE_LEAVE("MKDIR: Failed"); TRACE_LEAVE("Failure"); return 0; } break;
b1fca01996-11-12Per Hedbor  case "PUT": 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 
8afb501998-04-15Henrik Grubbström (Grubba)  if(QUERY(check_auth) && (!id->auth || !id->auth[0])) { TRACE_LEAVE("PUT: Permission denied");
f6d62d1997-03-26Per Hedbor  return http_auth_required("foo", "<h1>Permission to 'PUT' files denied</h1>");
8afb501998-04-15Henrik Grubbström (Grubba)  }
2f83081999-05-05Henrik Grubbström (Grubba) 
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) && !id->misc->quota_obj->check_quota(uri, id->misc->len)) {
2f83081999-05-05Henrik Grubbström (Grubba)  errors++; report_warning("Creation of " + f + " failed. Out of quota.\n"); TRACE_LEAVE("PUT: Out of quota.");
1358d51999-05-06Henrik Grubbström (Grubba)  return http_low_answer(413, "<h2>Out of disk quota.</h2>", "413 Out of disk quota");
2f83081999-05-05Henrik Grubbström (Grubba)  }
b04ec82000-01-31Per Hedbor 
f6d62d1997-03-26Per Hedbor  object privs;
8afc811998-02-04Per Hedbor // #ifndef THREADS // Ouch. This is is _needed_. Well well...
2e7c231997-06-10Henrik Grubbström (Grubba)  if (((int)id->misc->uid) && ((int)id->misc->gid)) {
0216e21997-08-12Henrik Grubbström (Grubba)  // NB: Root-access is prevented.
f7d9811997-09-12Per Hedbor  privs=Privs("Saving file", (int)id->misc->uid, (int)id->misc->gid );
2e7c231997-06-10Henrik Grubbström (Grubba)  }
8afc811998-02-04Per Hedbor // #endif
f6d62d1997-03-26Per Hedbor 
afb1581997-07-06Henrik Grubbström (Grubba)  if (QUERY(no_symlinks) && (contains_symlinks(path, oldf))) {
64c51c1997-10-11Henrik Grubbström (Grubba)  privs = 0;
afb1581997-07-06Henrik Grubbström (Grubba)  errors++; report_error("Creation of " + f + " failed. Permission denied.\n");
8afb501998-04-15Henrik Grubbström (Grubba)  TRACE_LEAVE("PUT: Contains symlinks. Permission denied");
afb1581997-07-06Henrik Grubbström (Grubba)  return http_low_answer(403, "<h2>Permission denied.</h2>"); }
8afb501998-04-15Henrik Grubbström (Grubba)  TRACE_ENTER("PUT: Accepted", 0);
b1fca01996-11-12Per Hedbor  rm( f ); mkdirhier( f );
8afb501998-04-15Henrik Grubbström (Grubba) 
2f83081999-05-05Henrik Grubbström (Grubba)  if (id->misc->quota_obj) {
ac69b51999-12-28Martin Nilsson  QUOTA_WERR("Checking if the file already exists.");
2f83081999-05-05Henrik Grubbström (Grubba)  if (size > 0) {
ac69b51999-12-28Martin Nilsson  QUOTA_WERR("Deallocating " + size + "bytes.");
1358d51999-05-06Henrik Grubbström (Grubba)  id->misc->quota_obj->deallocate(uri, size);
2f83081999-05-05Henrik Grubbström (Grubba)  } if (size) {
ac69b51999-12-28Martin Nilsson  QUOTA_WERR("Deleting old file.");
2f83081999-05-05Henrik Grubbström (Grubba)  rm(f); } }
8afb501998-04-15Henrik Grubbström (Grubba)  /* Clear the stat-cache for this file */ if (stat_cache) { cache_set("stat_cache", f, 0); }
c6f8041998-06-04David Hedbor  object to = open(f, "wct");
b04ec82000-01-31Per Hedbor 
2e7c231997-06-10Henrik Grubbström (Grubba)  privs = 0;
b1fca01996-11-12Per Hedbor  if(!to)
f6d62d1997-03-26Per Hedbor  { id->misc->error_code = 403;
8afb501998-04-15Henrik Grubbström (Grubba)  TRACE_LEAVE("PUT: Open failed"); TRACE_LEAVE("Failure");
f6d62d1997-03-26Per Hedbor  return 0; }
0163161999-04-21Henrik Grubbström (Grubba)  // FIXME: Race-condition.
2422401998-06-04David Hedbor  chmod(f, 0666 & ~(id->misc->umask || 022));
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");
1358d51999-05-06Henrik Grubbström (Grubba)  return http_low_answer(413, "<h2>Out of disk quota.</h2>", "413 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");
b1fca01996-11-12Per Hedbor  return 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)  }
1358d51999-05-06Henrik Grubbström (Grubba)  id->my_fd->set_id( ({ to, id->my_fd, id, uri }) );
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");
b1fca01996-11-12Per Hedbor  return http_pipe_in_progress(); break;
6cec001998-05-14David Hedbor  case "CHMOD":
b04ec82000-01-31Per Hedbor  // Change permission of a file.
1358d51999-05-06Henrik Grubbström (Grubba)  // FIXME: !!
b04ec82000-01-31Per Hedbor 
6cec001998-05-14David Hedbor  if(!QUERY(put)) { id->misc->error_code = 405; TRACE_LEAVE("CHMOD disallowed (since PUT is disallowed)"); return 0;
b04ec82000-01-31Per Hedbor  }
6cec001998-05-14David Hedbor  if(QUERY(check_auth) && (!id->auth || !id->auth[0])) { TRACE_LEAVE("CHMOD: Permission denied"); return http_auth_required("foo", "<h1>Permission to 'CHMOD' files denied</h1>"); }
b04ec82000-01-31Per Hedbor 
6cec001998-05-14David Hedbor  object privs;
b04ec82000-01-31Per Hedbor 
6cec001998-05-14David Hedbor // #ifndef THREADS // Ouch. This is is _needed_. Well well... if (((int)id->misc->uid) && ((int)id->misc->gid)) { // NB: Root-access is prevented. privs=Privs("CHMODing file", (int)id->misc->uid, (int)id->misc->gid ); } // #endif
b04ec82000-01-31Per Hedbor 
6cec001998-05-14David Hedbor  if (QUERY(no_symlinks) && (contains_symlinks(path, oldf))) { privs = 0; errors++; TRACE_LEAVE("CHMOD: Contains symlinks. Permission denied"); return http_low_answer(403, "<h2>Permission denied.</h2>"); } chmods++; TRACE_ENTER("CHMOD: Accepted", 0); if (stat_cache) { cache_set("stat_cache", f, 0); } #ifdef DEBUG
ac69b51999-12-28Martin Nilsson  werror(sprintf("CHMODing file "+f+" to 0%o\n", id->misc->mode));
6cec001998-05-14David Hedbor #endif
def7171998-05-15Henrik Grubbström (Grubba)  array err = catch(chmod(f, id->misc->mode & 0777));
6cec001998-05-14David Hedbor  privs = 0;
b04ec82000-01-31Per Hedbor 
6cec001998-05-14David Hedbor  if(err) { id->misc->error_code = 403; TRACE_LEAVE("CHMOD: Failure"); TRACE_LEAVE("Failure"); return 0; } TRACE_LEAVE("CHMOD: Success"); TRACE_LEAVE("Success"); return http_string_answer("Ok");
b04ec82000-01-31Per Hedbor 
dba2b51998-05-14David Hedbor  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.
dba2b51998-05-14David Hedbor  if(!QUERY(put)) { id->misc->error_code = 405; TRACE_LEAVE("MV disallowed (since PUT is disallowed)"); return 0;
b04ec82000-01-31Per Hedbor  }
dba2b51998-05-14David Hedbor  if(!QUERY(delete) && size != -1) { 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; } if(QUERY(check_auth) && (!id->auth || !id->auth[0])) { TRACE_LEAVE("MV: Permission denied"); return http_auth_required("foo", "<h1>Permission to 'MV' files denied</h1>"); } string movefrom; if(!id->misc->move_from || !(movefrom = id->conf->real_file(id->misc->move_from, id))) { id->misc->error_code = 405; errors++; TRACE_LEAVE("MV: No source file"); return 0; } moves++;
b04ec82000-01-31Per Hedbor 
dba2b51998-05-14David Hedbor  object privs;
b04ec82000-01-31Per Hedbor 
dba2b51998-05-14David Hedbor // #ifndef THREADS // Ouch. This is is _needed_. Well well... if (((int)id->misc->uid) && ((int)id->misc->gid)) { // NB: Root-access is prevented. privs=Privs("Moving file", (int)id->misc->uid, (int)id->misc->gid ); }
f37e471998-05-14Henrik Grubbström (Grubba) // #endif
b04ec82000-01-31Per Hedbor 
f37e471998-05-14Henrik Grubbström (Grubba)  if (QUERY(no_symlinks) && ((contains_symlinks(path, oldf)) || (contains_symlinks(path, id->misc->move_from)))) {
dba2b51998-05-14David Hedbor  privs = 0; errors++; TRACE_LEAVE("MV: Contains symlinks. Permission denied"); return http_low_answer(403, "<h2>Permission denied.</h2>"); } TRACE_ENTER("MV: Accepted", 0); /* Clear the stat-cache for this file */ #ifdef __NT__
f2a51c1998-05-26Per Hedbor  // if(movefrom[-1] == '/') // movefrom = move_from[..strlen(movefrom)-2];
dba2b51998-05-14David Hedbor #endif if (stat_cache) { cache_set("stat_cache", movefrom, 0); cache_set("stat_cache", f, 0); }
f37e471998-05-14Henrik Grubbström (Grubba) #ifdef DEBUG
ac69b51999-12-28Martin Nilsson  werror("Moving file "+movefrom+" to "+ f+"\n"); #endif
f37e471998-05-14Henrik Grubbström (Grubba) 
dba2b51998-05-14David Hedbor  int code = mv(movefrom, f); privs = 0; if(!code) { id->misc->error_code = 403; TRACE_LEAVE("MV: Move failed"); TRACE_LEAVE("Failure"); return 0; } TRACE_LEAVE("MV: Success");
f37e471998-05-14Henrik Grubbström (Grubba)  TRACE_LEAVE("Success");
dba2b51998-05-14David Hedbor  return http_string_answer("Ok");
de14f21998-10-19 Wim Bonis  case "MOVE": // This little kluge is used by NETSCAPE 4.5
2f83081999-05-05Henrik Grubbström (Grubba)  // FIXME: Support for quota.
b04ec82000-01-31Per Hedbor 
de14f21998-10-19 Wim Bonis  if(!QUERY(put)) { id->misc->error_code = 405; TRACE_LEAVE("MOVE disallowed (since PUT is disallowed)"); return 0;
b04ec82000-01-31Per Hedbor  }
de14f21998-10-19 Wim Bonis  if(size != -1) { id->misc->error_code = 404; TRACE_LEAVE("MOVE failed (no such file)"); return 0; } if(QUERY(check_auth) && (!id->auth || !id->auth[0])) { TRACE_LEAVE("MOVE: Permission denied"); return http_auth_required("foo", "<h1>Permission to 'MOVE' files denied</h1>"); }
b04ec82000-01-31Per Hedbor  if(!sizeof(id->misc["new-uri"] || "")) {
de14f21998-10-19 Wim Bonis  id->misc->error_code = 405; errors++; TRACE_LEAVE("MOVE: No dest file"); return 0; }
1358d51999-05-06Henrik Grubbström (Grubba)  string new_uri = combine_path(uri + "/../", id->misc["new-uri"]);
de14f21998-10-19 Wim Bonis 
1358d51999-05-06Henrik Grubbström (Grubba)  if (new_uri[..sizeof(mountpoint)-1] != mountpoint) {
de14f21998-10-19 Wim Bonis  id->misc->error_code = 405; TRACE_LEAVE("MOVE: Dest file on other filesystem."); return(0); }
1358d51999-05-06Henrik Grubbström (Grubba)  string moveto = path + "/" + new_uri[sizeof(mountpoint)..];
de14f21998-10-19 Wim Bonis  size = FILE_SIZE(moveto); if(!QUERY(delete) && size != -1) { id->misc->error_code = 405; TRACE_LEAVE("MOVE disallowed (DELE disabled, can't overwrite file)"); return 0; }
b04ec82000-01-31Per Hedbor 
de14f21998-10-19 Wim Bonis  if(size < -1) { id->misc->error_code = 405; TRACE_LEAVE("MOVE: Cannot overwrite directory"); return 0; } object privs; // #ifndef THREADS // Ouch. This is is _needed_. Well well... if (((int)id->misc->uid) && ((int)id->misc->gid)) { // NB: Root-access is prevented. privs=Privs("Moving file", (int)id->misc->uid, (int)id->misc->gid ); } // #endif if (QUERY(no_symlinks) && ((contains_symlinks(path, f)) || (contains_symlinks(path, moveto)))) { privs = 0; errors++; TRACE_LEAVE("MOVE: Contains symlinks. Permission denied"); return http_low_answer(403, "<h2>Permission denied.</h2>"); } TRACE_ENTER("MOVE: Accepted", 0); moves++; /* Clear the stat-cache for this file */ #ifdef __NT__ // if(movefrom[-1] == '/') // movefrom = move_from[..strlen(movefrom)-2]; #endif if (stat_cache) { cache_set("stat_cache", moveto, 0); cache_set("stat_cache", f, 0); } #ifdef DEBUG
ac69b51999-12-28Martin Nilsson  werror("Moving file " + f + " to " + moveto + "\n"); #endif
de14f21998-10-19 Wim Bonis  int code = mv(f, moveto); privs = 0; if(!code) { id->misc->error_code = 403; TRACE_LEAVE("MOVE: Move failed"); TRACE_LEAVE("Failure"); return 0; } TRACE_LEAVE("MOVE: Success"); TRACE_LEAVE("Success"); return http_string_answer("Ok");
b04ec82000-01-31Per Hedbor 
b1fca01996-11-12Per Hedbor  case "DELETE": if(!QUERY(delete) || size==-1)
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  }
8afb501998-04-15Henrik Grubbström (Grubba)  if(QUERY(check_auth) && (!id->auth || !id->auth[0])) { TRACE_LEAVE("DELETE: Permission denied");
b1fca01996-11-12Per Hedbor  return http_low_answer(403, "<h1>Permission to DELETE file denied</h1>");
8afb501998-04-15Henrik Grubbström (Grubba)  }
b1fca01996-11-12Per Hedbor 
afb1581997-07-06Henrik Grubbström (Grubba)  if (QUERY(no_symlinks) && (contains_symlinks(path, oldf))) { errors++; report_error("Deletion of " + f + " failed. Permission denied.\n");
8afb501998-04-15Henrik Grubbström (Grubba)  TRACE_LEAVE("DELETE: Contains symlinks");
afb1581997-07-06Henrik Grubbström (Grubba)  return http_low_answer(403, "<h2>Permission denied.</h2>"); }
dbf74f1997-06-12Marcus Comstedt  report_notice("DELETING the file "+f+"\n");
b1fca01996-11-12Per Hedbor  accesses++;
f6d62d1997-03-26Per Hedbor 
2e7c231997-06-10Henrik Grubbström (Grubba)  if (((int)id->misc->uid) && ((int)id->misc->gid)) {
0216e21997-08-12Henrik Grubbström (Grubba)  // NB: Root-access is prevented.
f7d9811997-09-12Per Hedbor  privs=Privs("Deleting file", id->misc->uid, id->misc->gid );
2e7c231997-06-10Henrik Grubbström (Grubba)  }
f6d62d1997-03-26Per Hedbor 
8afb501998-04-15Henrik Grubbström (Grubba)  /* Clear the stat-cache for this file */ if (stat_cache) { cache_set("stat_cache", f, 0); }
f6d62d1997-03-26Per Hedbor  if(!rm(f)) {
64c51c1997-10-11Henrik Grubbström (Grubba)  privs = 0;
f6d62d1997-03-26Per Hedbor  id->misc->error_code = 405;
8afb501998-04-15Henrik Grubbström (Grubba)  TRACE_LEAVE("DELETE: Failed");
f6d62d1997-03-26Per Hedbor  return 0; }
64c51c1997-10-11Henrik Grubbström (Grubba)  privs = 0;
f6d62d1997-03-26Per Hedbor  deletes++;
2f83081999-05-05Henrik Grubbström (Grubba) 
2ac4001999-05-05Henrik Grubbström (Grubba)  if (id->misc->quota_obj && (size > 0)) {
2f83081999-05-05Henrik Grubbström (Grubba)  id->misc->quota_obj->deallocate(oldf, size); }
8afb501998-04-15Henrik Grubbström (Grubba)  TRACE_LEAVE("DELETE: Success");
b1fca01996-11-12Per Hedbor  return http_low_answer(200,(f+" DELETED from the server")); default:
8afb501998-04-15Henrik Grubbström (Grubba)  TRACE_LEAVE("Not supported");
b1fca01996-11-12Per Hedbor  return 0; } report_error("Not reached..\n");
8afb501998-04-15Henrik Grubbström (Grubba)  TRACE_LEAVE("Not reached");
b1fca01996-11-12Per Hedbor  return 0; } string query_name() { return sprintf("<i>%s</i> mounted on <i>%s</i>", query("searchpath"), query("mountpoint")); }