Roxen.git / server / modules / filesystems / filesystem.pike

version» Context lines:

Roxen.git/server/modules/filesystems/filesystem.pike:1:   // This is a roxen module. Copyright © 1996 - 2001, Roxen IS.      // This is a virtual "file-system".   // It will be located somewhere in the name-space of the server.   // Also inherited by some of the other filesystems.      inherit "module";   inherit "socket";    - constant cvs_version= "$Id: filesystem.pike,v 1.136 2004/05/10 21:38:45 mast Exp $"; + constant cvs_version= "$Id: filesystem.pike,v 1.137 2004/05/12 21:21:56 mast Exp $";   constant thread_safe=1;      #include <module.h>   #include <roxen.h>   #include <stat.h>   #include <request_trace.h>         //<locale-token project="mod_filesystem">LOCALE</locale-token>   #define LOCALE(X,Y) _DEF_LOCALE("mod_filesystem",X,Y)
Roxen.git/server/modules/filesystems/filesystem.pike:346:    }   }      // We support locking if put is enabled.   mapping(string:mixed) lock_file(string path, DAVLock lock, RequestID id)   {    if (!query("put")) return 0;    if(query("check_auth") && (!id->conf->authenticate( id ) ) ) {    TRACE_LEAVE("PUT: Permission denied");    return +  // FIXME: Sane realm.    Roxen.http_auth_required("foo",    "<h1>Permission to 'PUT' files denied</h1>");    }    register_lock(path, lock, id);    return 0;   }      int dir_filter_function(string f, RequestID id)   {    if(f[0]=='.' && !dotfiles) return 0;
Roxen.git/server/modules/filesystems/filesystem.pike:494:    FILESYSTEM_WERR(sprintf("done_with_put(%O)\n"    "from: %O\n",    id_arr, mkmapping(indices(from), values(from))));       to->close();    from->set_blocking();    m_delete(putting, from);       if (putting[from] && (putting[from] != 0x7fffffff)) {    // Truncated! -  id->send_result(http_low_answer(400, -  "<h2>Bad Request - " -  "Expected more data.</h2>")); +  id->send_result(Roxen.http_status(400, +  "Bad Request - " +  "Expected more data."));    } else { -  id->send_result(http_low_answer((size < 0)?201:200, -  "<h2>Transfer Complete.</h2>")); +  id->send_result(Roxen.http_status((size < 0)?201:200, +  "Transfer Complete."));    }   }      void got_put_data( array(object|string|int) id_arr, string data )   {   // werror(strlen(data)+" .. ");       object to;    object from;    object id;
Roxen.git/server/modules/filesystems/filesystem.pike:523:    [to, from, id, oldf, size] = id_arr;       // Truncate at end.    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); -  id->send_result(http_low_answer(413, "<h2>Out of disk quota.</h2>", -  "413 Out of disk quota")); +  // FIXME: Ought to be 507 when it's a WebDAV request? +  id->send_result(Roxen.http_status(413, "Out of disk quota."));    return;    }       int bytes = to->write( data );    if (bytes < sizeof(data)) {    // Out of disk!    to->close();    from->set_blocking();    m_delete(putting, from); -  id->send_result(http_low_answer(413, "<h2>Disk full.</h2>")); +  // FIXME: Ought to be 507 when it's a WebDAV request? +  id->send_result(Roxen.http_status(413, "Disk full."));    return;    } else {    if (id->misc->quota_obj &&    !id->misc->quota_obj->allocate(oldf, bytes)) {    to->close();    from->set_blocking();    m_delete(putting, from); -  id->send_result(http_low_answer(413, "<h2>Out of disk quota.</h2>", -  "413 Out of disk quota")); +  // FIXME: Ought to be 507 when it's a WebDAV request? +  id->send_result(Roxen.http_status(413, "Out of disk quota."));    return;    }    if (putting[from] != 0x7fffffff) {    putting[from] -= bytes;    }    if(putting[from] <= 0) {    putting[from] = 0; // Paranoia    done_with_put( id_arr );    }    }
Roxen.git/server/modules/filesystems/filesystem.pike:603:   }      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)); -  return Roxen.http_low_answer(405, "Bad path."); +  return Roxen.http_status(405, "Bad path.");    }       if(!query("put"))    {    TRACE_LEAVE(sprintf("%s disallowed (since PUT is disallowed)",    id->method)); -  return http_low_answer(405, "Disallowed."); +  return Roxen.http_status(405, "Disallowed.");    }       // 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") { -  return http_low_answer(405, -  "<h2>Collection already exists.</h2>"); +  return Roxen.http_status(405, +  "Collection already exists.");    }    return 0;    }       if(query("check_auth") && (!id->conf->authenticate( id ) ) ) {    TRACE_LEAVE(sprintf("%s: Permission denied", id->method)); -  +  // FIXME: Sane realm.    return Roxen.http_auth_required("foo",    sprintf("<h1>Permission to '%s' denied</h1>",    id->method));    }       // 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;   
Roxen.git/server/modules/filesystems/filesystem.pike:649:    object privs;    SETUID_TRACE("Creating directory/collection", 0);       if (query("no_symlinks") && (contains_symlinks(path, coll))) {    privs = 0;    errors++;    report_error(LOCALE(46,"Creation of %O failed. Permission denied.\n"),    coll);    TRACE_LEAVE(sprintf("%s: Contains symlinks. Permission denied",    id->method)); -  return http_low_answer(403, "<h2>Permission denied.</h2>"); +  return Roxen.http_status(403, "Permission denied.");    }    -  werror("mkdir(%O)\n", norm_f); -  +     int code = mkdir(norm_f);    int err_code = errno();    privs = 0;       TRACE_ENTER(sprintf("%s: Accepted", id->method), 0);       if (code) {    chmod(norm_f, 0777 & ~(id->misc->umask || 022));    TRACE_LEAVE(sprintf("%s: Success", id->method));    TRACE_LEAVE(sprintf("%s: Success", id->method)); -  return Roxen.http_low_answer(201, "Created"); +  return Roxen.http_status(201, "Created");    }       TRACE_LEAVE(sprintf("%s: Failed", id->method));    id->misc->error_code = 507;    if (err_code ==   #if constant(system.ENOENT)    system.ENOENT   #elif constant(System.ENOENT)    System.ENOENT   #else    2   #endif    ) {    TRACE_LEAVE(sprintf("%s: Missing intermediate.", id->method)); -  return Roxen.http_low_answer(409, "Missing intermediate."); +  return Roxen.http_status(409, "Missing intermediate.");    } else {    TRACE_LEAVE(sprintf("%s: Failed.", id->method));    }    return 0;   }      mixed find_file( string f, RequestID id )   {    TRACE_ENTER("find_file(\""+f+"\")", 0);    object o;
Roxen.git/server/modules/filesystems/filesystem.pike:723: Inside #if constant(system.normalize_path)
  #else /* !__NT__ */    (norm_f+"/" != normalized_path)   #endif /* __NT__ */    ) {    errors++;    report_error(LOCALE(52, "Path verification of %O failed:\n"    "%O is not a prefix of %O\n"    ), oldf, normalized_path, norm_f);    TRACE_LEAVE("");    TRACE_LEAVE("Permission denied."); -  return http_low_answer(403, "<h2>File exists, but access forbidden " -  "by user</h2>"); +  return Roxen.http_status(403, "File exists, but access forbidden " +  "by user");    }       /* Adjust not_query */    id->not_query = mountpoint + replace(norm_f[sizeof(normalized_path)..],    "\\", "/");    if (sizeof(oldf) && (oldf[-1] == '/')) {    id->not_query += "/";    }   #endif /* constant(system.normalize_path) */    };
Roxen.git/server/modules/filesystems/filesystem.pike:801:    if(!o->open(norm_f, "r" )) o = 0;    privs = 0;       if(!o || (no_symlinks && (contains_symlinks(path, oldf))))    {    errors++;    report_error(LOCALE(45,"Open of %s failed. Permission denied.\n"),f);       TRACE_LEAVE("");    TRACE_LEAVE("Permission denied."); -  return http_low_answer(403, "<h2>File exists, but access forbidden " -  "by user</h2>"); +  return Roxen.http_status(403, "File exists, but access forbidden " +  "by user");    }       id->realfile = norm_f;    TRACE_LEAVE("");    accesses++;    TRACE_LEAVE("Normal return");    if( charset != "iso-8859-1" )    {    if( id->set_output_charset )    id->set_output_charset( charset, 2 );
Roxen.git/server/modules/filesystems/filesystem.pike:852:    id->misc->error_code = 405;    TRACE_LEAVE(sprintf("%s disallowed (since the dir name matches internal file glob)",    id->method));    return 0;    }       if (size != -1) {    TRACE_LEAVE(sprintf("%s failed. Directory name already exists. ",    id->method));    if (id->method == "MKCOL") { -  return http_low_answer(405, -  "<h2>Collection already exists.</h2>"); +  return Roxen.http_status(405, +  "Collection already exists.");    }    return 0;    }       if(query("check_auth") && (!id->conf->authenticate( id ) ) ) {    TRACE_LEAVE(sprintf("%s: Permission denied", id->method)); -  +  // FIXME: Sane realm.    return Roxen.http_auth_required("foo",    sprintf("<h1>Permission to '%s' denied</h1>",    id->method));    }    mkdirs++;    SETUID_TRACE("Creating directory/collection", 0);       if (query("no_symlinks") && (contains_symlinks(path, oldf))) {    privs = 0;    errors++;    report_error(LOCALE(46,"Creation of %O failed. Permission denied.\n"),    oldf);    TRACE_LEAVE(sprintf("%s: Contains symlinks. Permission denied",    id->method)); -  return http_low_answer(403, "<h2>Permission denied.</h2>"); +  return Roxen.http_status(403, "Permission denied.");    }       code = mkdir(f);    int err_code = errno();    privs = 0;       TRACE_ENTER(sprintf("%s: Accepted", id->method), 0);       if (code) {    chmod(f, 0777 & ~(id->misc->umask || 022));    TRACE_LEAVE(sprintf("%s: Success", id->method));    TRACE_LEAVE("Success");    if (id->method == "MKCOL") { -  return http_low_answer(201, "Created"); +  return Roxen.http_status(201, "Created");    }    return Roxen.http_string_answer("Ok");    } else {    SIMPLE_TRACE_LEAVE("%s: Failed (errcode:%d)", id->method, errcode);    TRACE_LEAVE("Failure");    if (id->method == "MKCOL") {    if (err_code ==   #if constant(system.ENOENT)    system.ENOENT   #elif constant(System.ENOENT)    System.ENOENT   #else    2   #endif    ) { -  return http_low_answer(409, "Missing intermediate."); +  return Roxen.http_status(409, "Missing intermediate.");    } else { -  return http_low_answer(507, "Failed."); +  return Roxen.http_status(507, "Failed.");    }    }    return 0;    }   #endif /* 1 */    break;       case "PUT":    if(!query("put"))    {
Roxen.git/server/modules/filesystems/filesystem.pike:930:    }       if (FILTER_INTERNAL_FILE (f, id)) {    id->misc->error_code = 405;    TRACE_LEAVE("PUT of internal file is disallowed");    return 0;    }       if(query("check_auth") && (!id->conf->authenticate( id ) ) ) {    TRACE_LEAVE("PUT: Permission denied"); +  // FIXME: Sane realm.    return Roxen.http_auth_required("foo",    "<h1>Permission to 'PUT' files denied</h1>");    }       if (mapping(string:mixed) ret = write_access(oldf, 0, id)) {    TRACE_LEAVE("PUT: Locked");    return ret;    }       puts++;       QUOTA_WERR("Checking quota.\n");    if (id->misc->quota_obj && (id->misc->len > 0) &&    !id->misc->quota_obj->check_quota(URI, id->misc->len)) {    errors++;    report_warning(LOCALE(47,"Creation of %O failed. Out of quota.\n"),f);    TRACE_LEAVE("PUT: Out of quota."); -  return http_low_answer(413, "<h2>Out of disk quota.</h2>", -  "413 Out of disk quota"); +  // FIXME: Ought to be 507 when it's a WebDAV request? +  return Roxen.http_status(413, "Out of disk quota.");    }       if (query("no_symlinks") && (contains_symlinks(path, oldf))) {    errors++;    report_error(LOCALE(46,"Creation of %O failed. Permission denied.\n"),f);    TRACE_LEAVE("PUT: Contains symlinks. Permission denied"); -  return http_low_answer(403, "<h2>Permission denied.</h2>"); +  return Roxen.http_status(403, "Permission denied.");    }       SETUID_TRACE("Saving file", 0);       rm(f);    mkdirhier(f);       if (id->misc->quota_obj) {    QUOTA_WERR("Checking if the file already existed.");    if (size > 0) {
Roxen.git/server/modules/filesystems/filesystem.pike:1005:    // 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) {    QUOTA_WERR("Allocating " + bytes + "bytes.");    if (!id->misc->quota_obj->allocate(f, bytes)) {    TRACE_LEAVE("PUT: A string");    TRACE_LEAVE("PUT: Out of quota"); -  return http_low_answer(413, "<h2>Out of disk quota.</h2>", -  "413 Out of disk quota"); +  // FIXME: Ought to be 507 when it's a WebDAV request? +  return Roxen.http_status(413, "Out of disk quota.");    }    }    }    if(!putting[id->my_fd]) {    TRACE_LEAVE("PUT: Just a string");    TRACE_LEAVE("Put: Success");    if (size < 0) { -  return Roxen.http_low_answer(201, "Created."); +  return Roxen.http_status(201, "Created.");    } else { -  +  // FIXME: Isn't 204 better? /mast    return Roxen.http_string_answer("Ok");    }    }       if(id->clientprot == "HTTP/1.1") {    id->my_fd->write("HTTP/1.1 100 Continue\r\n");    }    id->my_fd->set_id( ({ to, id->my_fd, id, URI, size }) );    id->my_fd->set_nonblocking(got_put_data, 0, done_with_put);    TRACE_LEAVE("PUT: Pipe in progress");
Roxen.git/server/modules/filesystems/filesystem.pike:1049:    }       if (FILTER_INTERNAL_FILE (f, id)) {    id->misc->error_code = 405;    TRACE_LEAVE("CHMOD of internal file is disallowed");    return 0;    }       if(query("check_auth") && (!id->conf->authenticate( id ) ) ) {    TRACE_LEAVE("CHMOD: Permission denied"); +  // FIXME: Sane realm.    return Roxen.http_auth_required("foo",    "<h1>Permission to 'CHMOD' files denied</h1>");    }       if (mapping(string:mixed) ret = write_access(oldf, 0, id)) {    TRACE_LEAVE("CHMOD: Locked");    return ret;    }       SETUID_TRACE("CHMODing file", 0);       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>"); +  return Roxen.http_status(403, "Permission denied.");    }       array err = catch(chmod(f, id->misc->mode & 0777));    privs = 0;       chmods++;       TRACE_ENTER("CHMOD: Accepted", 0);       if (stat_cache) {
Roxen.git/server/modules/filesystems/filesystem.pike:1116:       if(size < -1)    {    id->misc->error_code = 405;    TRACE_LEAVE("MV: Cannot overwrite directory");    return 0;    }       if(query("check_auth") && (!id->conf->authenticate( id ) ) ) {    TRACE_LEAVE("MV: Permission denied"); +  // FIXME: Sane realm.    return Roxen.http_auth_required("foo",    "<h1>Permission to 'MV' files denied</h1>");    }    string movefrom;    if(!id->misc->move_from ||    !has_prefix(id->misc->move_from, mountpoint) ||    !(movefrom = id->conf->real_file(id->misc->move_from, id))) {    id->misc->error_code = 405;    errors++;    TRACE_LEAVE("MV: No source file");
Roxen.git/server/modules/filesystems/filesystem.pike:1143:    id->misc->error_code = 405;    TRACE_LEAVE("MV to or from internal file is disallowed");    return 0;    }       if (query("no_symlinks") &&    ((contains_symlinks(path, oldf)) ||    (contains_symlinks(path, id->misc->move_from)))) {    errors++;    TRACE_LEAVE("MV: Contains symlinks. Permission denied"); -  return http_low_answer(403, "<h2>Permission denied.</h2>"); +  return Roxen.http_status(403, "Permission denied.");    }       // 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);
Roxen.git/server/modules/filesystems/filesystem.pike:1199:    }    if(size == -1)    {    id->misc->error_code = 404;    TRACE_LEAVE("MOVE failed (no such file)");    return 0;    }       if(query("check_auth") && (!id->conf->authenticate( id ) ) ) {    TRACE_LEAVE("MOVE: Permission denied"); +  // FIXME: Sane realm.    return Roxen.http_auth_required("foo",    "<h1>Permission to 'MOVE' files denied</h1>");    }       string new_uri = id->misc["new-uri"] || "";    if (new_uri == "") {    id->misc->error_code = 405;    errors++;    TRACE_LEAVE("MOVE: No dest file");    return 0;
Roxen.git/server/modules/filesystems/filesystem.pike:1233:    TRACE_LEAVE("MOVE to or from internal file is disallowed");    return 0;    }       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>"); +  return Roxen.http_status(403, "Permission denied.");    }       if (mapping(string:mixed) ret =    write_access(new_uri, 0, id) ||    write_access(oldf, 0, id)) {    TRACE_LEAVE("MOVE: Locked");    return ret;    }       size = _file_size(moveto,id);
Roxen.git/server/modules/filesystems/filesystem.pike:1270:    {    privs = 0;    id->misc->error_code = 405;    TRACE_LEAVE("MOVE disallowed (DELE disabled)");    return 0;    }       if ((overwrite == DO_OVERWRITE) || (size > -1)) {    mapping(string:mixed) res =    recurse_delete_files(new_uri, id); -  if (res && res->error >= 300) { +  if (res && (!sizeof (res) || res->error >= 300)) {    privs = 0;    TRACE_LEAVE("MOVE: Recursive delete failed."); -  return Roxen.http_status(412); +  if (sizeof (res)) +  set_status_for_path (new_uri, res->error, res->rettext); +  return ([]);    }    } else {    privs = 0;    TRACE_LEAVE("MOVE: Cannot overwrite directory");    return Roxen.http_status(412);    }    }       code = mv(f, decode_path(moveto));    privs = 0;
Roxen.git/server/modules/filesystems/filesystem.pike:1328:    }       if (FILTER_INTERNAL_FILE (f, id)) {    id->misc->error_code = 405;    TRACE_LEAVE("DELETE of internal file is disallowed");    return 0;    }       if(query("check_auth") && (!id->conf->authenticate( id ) ) ) {    TRACE_LEAVE("DELETE: Permission denied"); -  return http_low_answer(403, "<h1>Permission to DELETE file denied</h1>"); +  return Roxen.http_status(403, "Permission to DELETE file denied");    }       if (query("no_symlinks") && (contains_symlinks(path, oldf))) {    errors++;    report_error(LOCALE(48,"Deletion of %s failed. Permission denied.\n"),f);    TRACE_LEAVE("DELETE: Contains symlinks"); -  return http_low_answer(403, "<h2>Permission denied.</h2>"); +  return Roxen.http_status(403, "Permission denied.");    }       if ((size < 0) &&    (String.trim_whites(id->request_headers->depth||"infinity") !=    "infinity")) {    // 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)); -  return http_low_answer(400, "<h2>Unsupported depth.</h2>"); +  return Roxen.http_status(400, "Unsupported depth.");    }       if (size < 0) {    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.");    return res;    } -  + #if 0    report_notice(LOCALE(64,"DELETING the directory %s.\n"), f); -  + #endif       accesses++;       SETUID_TRACE("Deleting directory", 0);       int start_ms_size = id->multi_status_size();    recursive_rm(f, query_location() + oldf, res, id);       if (!rm(f)) {    if (id->multi_status_size() > start_ms_size) {   #if constant(system.EEXIST)    if (errno() != system.EEXIST)   #endif    {    return Roxen.http_status(403);    //id->set_status_for_path(query_location() + oldf, 403);    }    } else {    TRACE_LEAVE("DELETE: Failed to delete directory."); -  return http_low_answer(403, "<h2>Failed to delete directory.</h2>"); +  return Roxen.http_status(403, "Failed to delete directory.");    }    }       if (id->multi_status_size() > start_ms_size) {    TRACE_LEAVE("DELETE: Partial failure.");    return ([]);    }    } else {    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.");    return res;    }    -  + #if 0    report_notice(LOCALE(49,"DELETING the file %s.\n"),f); -  + #endif       accesses++;       /* Clear the stat-cache for this file */    if (stat_cache) {    cache_set("stat_cache", f, 0);    }       SETUID_TRACE("Deleting file", 0);   
Roxen.git/server/modules/filesystems/filesystem.pike:1418:    return 0;    }    privs = 0;    deletes++;       if (id->misc->quota_obj && (size > 0)) {    id->misc->quota_obj->deallocate(oldf, size);    }    }    TRACE_LEAVE("DELETE: Success"); -  return http_low_answer(204,(f+" DELETED from the server")); +  return Roxen.http_status(204,(f+" DELETED from the server"));       default:    id->misc->error_code = 501;    SIMPLE_TRACE_LEAVE("%s: Not supported", id->method);    return 0;    }    TRACE_LEAVE("Not reached");    return 0;   }      mapping copy_file(string source, string dest, PropertyBehavior behavior,    Overwrite overwrite, RequestID id)   {    SIMPLE_TRACE_ENTER(this, "COPY: Copy %O to %O.", source, dest);    Stat source_st = stat_file(source, id);    if (!source_st) {    TRACE_LEAVE("COPY: Source doesn't exist."); -  return Roxen.http_low_answer(404, "File not found."); +  return Roxen.http_status(404, "File not found.");    }    if (!query("put")) {    TRACE_LEAVE("COPY: Put not allowed."); -  return Roxen.http_low_answer(405, "Not allowed."); +  return Roxen.http_status(405, "Not allowed.");    }    if(query("check_auth") && (!id->conf->authenticate( id ) ) ) {    TRACE_LEAVE("COPY: Authentication required.");    return -  +  // FIXME: Sane realm.    Roxen.http_auth_required("foo",    sprintf("<h1>Permission to 'COPY' denied</h1>",    id->method));    }    mapping|int(0..1) res = write_access(combine_path(dest, "../"), 0, id);    if (mappingp(res)) return res;    string dest_path = path + dest;    catch { dest_path = decode_path(dest_path); };    if (query("no_symlinks") && (contains_symlinks(path, dest_path))) {    errors++;    report_error(LOCALE(46,"Copy to %O failed. Permission denied.\n"),    dest);    TRACE_LEAVE("COPY: Contains symlinks. Permission denied"); -  return http_low_answer(403, "<h2>Permission denied.</h2>"); +  return Roxen.http_status(403, "Permission denied.");    }    Stat dest_st = stat_file(dest, id);    if (dest_st) {    switch(overwrite) {    case NEVER_OVERWRITE:    TRACE_LEAVE("COPY: Destination already exists."); -  return Roxen.http_low_answer(412, "Destination already exists."); +  return Roxen.http_status(412, "Destination already exists.");    case DO_OVERWRITE:    if (!query("delete")) {    TRACE_LEAVE("COPY: Deletion not allowed."); -  return Roxen.http_low_answer(405, "Not allowed."); +  return Roxen.http_status(405, "Not allowed.");    }    object privs;    SETUID_TRACE("Deleting destination", 0);    if (dest_st->isdir) {    recursive_rm(dest_path, mountpoint + dest, 1, id);    if (source_st->isdir) {    privs = 0;    SIMPLE_TRACE_LEAVE("COPY: Cleared directory %O", dest_path);    SIMPLE_TRACE_LEAVE("Copy done.");    return Roxen.http_status(204);    }    if (!rm(dest_path)) {    privs = 0;    SIMPLE_TRACE_LEAVE("COPY: Delete failed."); -  +  // Shouldn't this bail out? /mast   #if constant(system.EEXIST)    if (errno() != system.EEXIST)   #endif    {    id->set_status_for_path(mountpoint + dest, 403);    }    } else {    SIMPLE_TRACE_LEAVE("COPY: Delete ok.");    }    } else if (source_st->isdir) {    if (!rm(dest_path)) {    SIMPLE_TRACE_LEAVE("COPY: File deletion failed.");    privs = 0; -  +  // Shouldn't this bail out? /mast   #if constant(system.EEXIST)    if (errno() != system.EEXIST)   #endif    {    id->set_status_for_path(mountpoint + dest, 403);    }    } else {    SIMPLE_TRACE_LEAVE("COPY: File deletion ok.");    }    } else {    SIMPLE_TRACE_LEAVE("COPY: No need to perform deletion.");    }    privs = 0;    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."); -  return Roxen.http_low_answer(412, "Destination and source are different resource types."); +  return Roxen.http_status(412, "Destination and source are different resource types.");    } else if (source_st->isdir) {    TRACE_LEAVE("Already done (both are directories)."); -  return Roxen.http_low_answer(204, "Destination already existed."); +  return Roxen.http_status(204, "Destination already existed.");    }    break;    }    }    if (source_st->isdir) {    mkdirs++;    object privs;    SETUID_TRACE("Creating directory/collection", 0);       int code = mkdir(dest_path);    int err_code = errno();    privs = 0;    TRACE_ENTER("COPY: Accepted", this_object());       if (code) {    chmod(dest_path, 0777 & ~(id->misc->umask || 022));    TRACE_LEAVE("COPY: Success");    TRACE_LEAVE("Success"); -  return http_low_answer(dest_st?204:201, "Created"); +  return Roxen.http_status(dest_st?204:201, "Created");    } else {    TRACE_LEAVE("COPY: Failed");    TRACE_LEAVE("Failure");    if (err_code ==   #if constant(system.ENOENT)    system.ENOENT   #elif constant(System.ENOENT)    System.ENOENT   #else    2   #endif    ) { -  return http_low_answer(409, "Missing intermediate."); +  return Roxen.http_status(409, "Missing intermediate.");    } else { -  return http_low_answer(507, "Failed."); +  return Roxen.http_status(507, "Failed.");    }    }    } else {    if (res = write_access(dest, 0, id)) {    SIMPLE_TRACE_LEAVE("COPY: Write access to file %O denied.", dest);    return res;    }    string source_path = path + source;    catch { source_path = decode_path(source_path); };    if (query("no_symlinks") && (contains_symlinks(path, source_path))) {    errors++;    report_error(LOCALE(46,"Copy to %O failed. Permission denied.\n"),    dest);    TRACE_LEAVE("COPY: Contains symlinks. Permission denied"); -  return http_low_answer(403, "<h2>Permission denied.</h2>"); +  return Roxen.http_status(403, "Permission denied.");    }    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."); -  return http_low_answer(413, "<h2>Out of disk quota.</h2>", -  "413 Out of disk quota"); +  // FIXME: Shouldn't this be 507? +  return Roxen.http_status(413, "Out of disk quota.");    }    object source_file = open(source_path, "r");    if (!source_file) {    TRACE_LEAVE("Failed to open source file.");    return Roxen.http_status(404);    }    object privs;    SETUID_TRACE("COPY: Copying file.", 0);    object dest_file = open(dest_path, "cwt");    privs = 0;