From: Andrew McNab Date: Wed, 7 Sep 2005 09:38:51 +0000 (+0000) Subject: Commit PUT/MOVE stuff X-Git-Tag: gridsite-core_R_1_1_11~25 X-Git-Url: http://scientific.zcu.cz/git/?a=commitdiff_plain;h=94f07eea7575ac3e92247cf16a907a6aa69fb75c;p=jra1mw.git Commit PUT/MOVE stuff --- diff --git a/org.gridsite.core/CHANGES b/org.gridsite.core/CHANGES index 5a7716a..fc041b2 100644 --- a/org.gridsite.core/CHANGES +++ b/org.gridsite.core/CHANGES @@ -1,3 +1,9 @@ +* Fri Aug 26 2005 Andrew McNab +- Fix for HTTP PUT lack-of-truncation bug found by + Mike Jones, and support for HTTP/WebDAV MOVE. +- Add MOVE support to htcp and update htcp manpage. +- Unset CURLOPT_SSL_VERIFYPEER in htcp etc when using + --noverify option. * Fri Jun 10 2005 Andrew McNab - ==== GridSite version 1.1.11 ==== * Fri Jun 10 2005 Andrew McNab diff --git a/org.gridsite.core/doc/htcp.1 b/org.gridsite.core/doc/htcp.1 index 984aaaf..df4a07d 100644 --- a/org.gridsite.core/doc/htcp.1 +++ b/org.gridsite.core/doc/htcp.1 @@ -1,6 +1,6 @@ -.TH htcp 1 "July 2004" htcp "HTCP Manual" +.TH htcp 1 "September 2005" htcp "HTCP Manual" .SH NAME -.B htcp, htrm, htls, htll, htmkdir +.B htcp, htrm, htls, htll, htmkdir, htmv \- get, put, delete or list HTTP/HTTPS files or directories .SH SYNOPSIS .B htcp [options] @@ -53,6 +53,11 @@ with HTTP PUT. The server must support the convention that PUT to a URL with a trailing slash means create a directory. No file body is sent. Calling the program as htmkdir has the same effect. +.IP "--move" +Move/rename files on a single remote server, given the two, absolute URLs +of the remote file names. Server must support HTTP/WebDAV MOVE. Calling the +program as htmv has the same effect. + .IP "--anon" .br Do not attempt to use X.509 user certificates or GSI proxies to authenticate @@ -136,7 +141,7 @@ Recursive copying. Server-side wildcards. Parallel streams. Error recovery. Not enough beta testing (hint hint...) .SH AUTHOR -Andrew McNab +Andrew McNab htcp is part of GridSite: http://www.gridsite.org/ .SH "SEE ALSO" diff --git a/org.gridsite.core/doc/htmv.1 b/org.gridsite.core/doc/htmv.1 new file mode 100644 index 0000000..11a60d1 --- /dev/null +++ b/org.gridsite.core/doc/htmv.1 @@ -0,0 +1 @@ +.so man1/htcp.1 diff --git a/org.gridsite.core/doc/module.html b/org.gridsite.core/doc/module.html index 9cc97d4..f4a3acf 100644 --- a/org.gridsite.core/doc/module.html +++ b/org.gridsite.core/doc/module.html @@ -290,6 +290,12 @@ in effect. file being requested.

+

GRST_DESTINATION_TRANSLATED +
Present if a WebDAV Destination header was given in the request with a + local URL. Contains the translation of the URL given into an + absolute path in the local filesystem. +

+

GRST_HELP_URI
URI of website help pages set by GridSiteHelpURI directive.

diff --git a/org.gridsite.core/src/gridsite.spec b/org.gridsite.core/src/gridsite.spec index f55c349..badfdce 100644 --- a/org.gridsite.core/src/gridsite.spec +++ b/org.gridsite.core/src/gridsite.spec @@ -90,11 +90,13 @@ rm -f %(echo ${MYPREFIX:-/usr})/share/doc/gridsite %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/bin/htll %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/bin/htrm %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/bin/htmkdir +%attr(-, root, root) %(echo ${MYPREFIX:-/usr})/bin/htmv %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/man/man1/htcp.1.gz %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/man/man1/htrm.1.gz %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/man/man1/htls.1.gz %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/man/man1/htll.1.gz %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/man/man1/htmkdir.1.gz +%attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/man/man1/htmv.1.gz %files gsexec %attr(4510, root, apache) %(echo ${MYPREFIX:-/usr})/sbin/gsexec diff --git a/org.gridsite.core/src/htcp b/org.gridsite.core/src/htcp index 8a64842..f3d67c5 100644 Binary files a/org.gridsite.core/src/htcp and b/org.gridsite.core/src/htcp differ diff --git a/org.gridsite.core/src/htcp.c b/org.gridsite.core/src/htcp.c index 3275806..21b6c5b 100644 --- a/org.gridsite.core/src/htcp.c +++ b/org.gridsite.core/src/htcp.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2002-4, Andrew McNab, University of Manchester + Copyright (c) 2002-5, Andrew McNab, University of Manchester All rights reserved. Redistribution and use in source and binary forms, with or @@ -72,6 +72,7 @@ #define HTCP_LIST 4 #define HTCP_LONGLIST 5 #define HTCP_MKDIR 6 +#define HTCP_MOVE 7 struct grst_stream_data { char *source; char *destination; @@ -200,8 +201,15 @@ int set_std_opts(CURL *easyhandle, struct grst_stream_data *common_data) } if (common_data->noverify) - curl_easy_setopt(easyhandle, CURLOPT_SSL_VERIFYHOST, 0); - else curl_easy_setopt(easyhandle, CURLOPT_SSL_VERIFYHOST, 2); + { + curl_easy_setopt(easyhandle, CURLOPT_SSL_VERIFYPEER, 0); + curl_easy_setopt(easyhandle, CURLOPT_SSL_VERIFYHOST, 0); + } + else + { + curl_easy_setopt(easyhandle, CURLOPT_SSL_VERIFYPEER, 2); + curl_easy_setopt(easyhandle, CURLOPT_SSL_VERIFYHOST, 2); + } return 1; } @@ -448,6 +456,64 @@ int do_deletes(char *sources[], struct grst_stream_data *common_data) return anyerror; } +int do_move(char *source, char *destination, + struct grst_stream_data *common_data) +{ + int anyerror = 0, thiserror; + char *destination_header; + CURL *easyhandle; + struct grst_header_data header_data; + struct curl_slist *header_slist = NULL; + + easyhandle = curl_easy_init(); + + header_data.common_data = common_data; + + easyhandle = curl_easy_init(); + + asprintf(&destination_header, "Destination: %s", destination); + header_slist = curl_slist_append(header_slist, destination_header); + curl_easy_setopt(easyhandle, CURLOPT_HTTPHEADER, header_slist); + + curl_easy_setopt(easyhandle, CURLOPT_USERAGENT, common_data->useragent); + if (common_data->verbose > 1) + curl_easy_setopt(easyhandle, CURLOPT_VERBOSE, 1); + + curl_easy_setopt(easyhandle, CURLOPT_HEADERFUNCTION, headers_callback); + curl_easy_setopt(easyhandle, CURLOPT_WRITEHEADER, &header_data); + + curl_easy_setopt(easyhandle, CURLOPT_ERRORBUFFER, common_data->errorbuf); + curl_easy_setopt(easyhandle, CURLOPT_CUSTOMREQUEST, "MOVE"); + curl_easy_setopt(easyhandle, CURLOPT_NOBODY, 1); + + set_std_opts(easyhandle, common_data); + + if (common_data->verbose > 0) + fprintf(stderr, "Moving %s to %s\n", source, destination); + + curl_easy_setopt(easyhandle, CURLOPT_URL, source); + + header_data.retcode = 0; + thiserror = curl_easy_perform(easyhandle); + + if ((thiserror != 0) || + (header_data.retcode < 200) || + (header_data.retcode >= 300)) + { + fprintf(stderr, "... curl error: %s (%d), HTTP error: %d\n", + common_data->errorbuf, thiserror, header_data.retcode); + + if (thiserror != 0) anyerror = thiserror; + else anyerror = header_data.retcode; + } + else if (common_data->verbose > 0) + fprintf(stderr, "... OK (%d)\n", header_data.retcode); + + curl_easy_cleanup(easyhandle); + + return anyerror; +} + int do_mkdirs(char *sources[], struct grst_stream_data *common_data) { int isrc, anyerror = 0, thiserror; @@ -988,6 +1054,7 @@ int main(int argc, char *argv[]) {"no-verify", 0, 0, 0}, {"anon", 0, 0, 0}, {"downgrade-size", 1, 0, 0}, + {"move", 0, 0, 0}, // {"streams", 1, 0, 0}, // {"blocksize", 1, 0, 0}, // {"recursive", 0, 0, 0}, @@ -1034,6 +1101,7 @@ int main(int argc, char *argv[]) else if (option_index == 8) common_data.noverify = 1; else if (option_index == 9) common_data.anonymous = 1; else if (option_index ==10) common_data.downgrade = atoll(optarg); + else if (option_index ==11) common_data.method = HTCP_MOVE; } else if (c == 'v') ++(common_data.verbose); } @@ -1115,6 +1183,7 @@ int main(int argc, char *argv[]) else if (strcmp(executable,"htll")==0) common_data.method=HTCP_LONGLIST; else if (strcmp(executable,"htrm")==0) common_data.method=HTCP_DELETE; else if (strcmp(executable,"htmkdir")==0) common_data.method=HTCP_MKDIR; + else if (strcmp(executable,"htmv")==0) common_data.method=HTCP_MOVE; } if ((common_data.method == HTCP_DELETE) || @@ -1159,6 +1228,22 @@ int main(int argc, char *argv[]) return anyerror; } + if (common_data.method == HTCP_MOVE) + { + if (optind >= argc - 1) + { + fputs("Must give exactly 2 non-option arguments\n\n", stderr); + printsyntax(argv[0]); + return CURLE_URL_MALFORMAT; + } + + anyerror = do_move(argv[optind], argv[optind + 1], &common_data); + + if (anyerror > 99) anyerror = CURLE_HTTP_RETURNED_ERROR; + + return anyerror; + } + if (optind >= argc - 1) { fputs("Must give at least 2 non-option arguments\n\n", stderr); diff --git a/org.gridsite.core/src/mod_gridsite.c b/org.gridsite.core/src/mod_gridsite.c index 450640f..7757c77 100644 --- a/org.gridsite.core/src/mod_gridsite.c +++ b/org.gridsite.core/src/mod_gridsite.c @@ -971,7 +971,8 @@ int http_put_method(request_rec *r, mod_gridsite_cfg *conf) /* *** otherwise assume trying to create a regular file *** */ - if (apr_file_open(&fp, r->filename, APR_WRITE | APR_CREATE | APR_BUFFERED, + if (apr_file_open(&fp, r->filename, + APR_WRITE | APR_CREATE | APR_BUFFERED | APR_TRUNCATE, conf->diskmode, r->pool) != 0) return HTTP_INTERNAL_SERVER_ERROR; /* we force the permissions, rather than accept any existing ones */ @@ -979,6 +980,7 @@ int http_put_method(request_rec *r, mod_gridsite_cfg *conf) apr_file_perms_set(r->filename, conf->diskmode); // TODO: need to add Range: support at some point too +// Also return 201 Created rather than 200 OK if not already existing retcode = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK); if (retcode == OK) @@ -1002,7 +1004,7 @@ int http_put_method(request_rec *r, mod_gridsite_cfg *conf) int http_delete_method(request_rec *r, mod_gridsite_cfg *conf) { - if (remove(r->filename) != 0) return HTTP_FORBIDDEN; + if (apr_file_remove(r->filename, r->pool) != 0) return HTTP_FORBIDDEN; ap_set_content_length(r, 0); ap_set_content_type(r, "text/html"); @@ -1010,6 +1012,24 @@ int http_delete_method(request_rec *r, mod_gridsite_cfg *conf) return OK; } +int http_move_method(request_rec *r, mod_gridsite_cfg *conf) +{ + char *destination_translated = NULL; + + if (r->notes != NULL) destination_translated = + (char *) apr_table_get(r->notes, "GRST_DESTINATION_TRANSLATED"); + + + if ((destination_translated == NULL) || + (apr_file_rename(r->filename, destination_translated, r->pool) != 0)) + return HTTP_FORBIDDEN; + + ap_set_content_length(r, 0); + ap_set_content_type(r, "text/html"); + + return OK; +} + static int mod_gridsite_dir_handler(request_rec *r, mod_gridsite_cfg *conf) /* handler switch for directories @@ -1074,6 +1094,11 @@ static int mod_gridsite_nondir_handler(request_rec *r, mod_gridsite_cfg *conf) (conf->methods != NULL) && (strstr(conf->methods, " DELETE ") != NULL)) return http_delete_method(r, conf); + + if ((r->method_number == M_MOVE) && + (conf->methods != NULL) && + (strstr(conf->methods, " MOVE ") != NULL)) + return http_move_method(r, conf); } /* *** check if a special ghost admin CGI *** */ @@ -1935,18 +1960,22 @@ static int mod_gridsite_perm_handler(request_rec *r) We also publish environment variables here if requested by GridSiteEnv. */ { - int retcode = DECLINED, i, n; + int retcode = DECLINED, i, n, file_is_acl = 0, + destination_is_acl = 0; char *dn, *p, envname[14], *grst_cred_0 = NULL, *dir_path, - *remotehost, s[99], *grst_cred_i, *file, *cookies, - *gridauthonetime, *cookiefile, oneline[1025], *key_i; + *remotehost, s[99], *grst_cred_i, *cookies, *file, + *gridauthonetime, *cookiefile, oneline[1025], *key_i, + *destination = NULL, *destination_uri = NULL, + *destination_prefix = NULL, *destination_translated = NULL; const char *content_type; time_t now, notbefore, notafter; apr_table_t *env; apr_finfo_t cookiefile_info; apr_file_t *fp; + request_rec *destreq; GRSTgaclCred *cred = NULL, *cred_0 = NULL; GRSTgaclUser *user = NULL; - GRSTgaclPerm perm = GRST_PERM_NONE; + GRSTgaclPerm perm = GRST_PERM_NONE, destination_perm = GRST_PERM_NONE; GRSTgaclAcl *acl = NULL; mod_gridsite_cfg *cfg; @@ -2094,26 +2123,93 @@ static int mod_gridsite_perm_handler(request_rec *r) if ((user != NULL) && ((mod_gridsite_cfg *) cfg)->dnlists) GRSTgaclUserSetDNlists(user, ((mod_gridsite_cfg *) cfg)->dnlists); - /* this checks for NULL arguments itself */ - if (GRSTgaclDNlistHasUser(((mod_gridsite_cfg *) cfg)->adminlist, user)) - perm = GRST_PERM_ALL; - else - { - remotehost = (char *) ap_get_remote_host(r->connection, + /* add DNS credential */ + + remotehost = (char *) ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_DOUBLE_REV, NULL); - if ((remotehost != NULL) && (*remotehost != '\0')) - { - cred = GRSTgaclCredNew("dns"); - GRSTgaclCredAddValue(cred, "hostname", remotehost); + if ((remotehost != NULL) && (*remotehost != '\0')) + { + cred = GRSTgaclCredNew("dns"); + GRSTgaclCredAddValue(cred, "hostname", remotehost); + + if (user == NULL) user = GRSTgaclUserNew(cred); + else GRSTgaclUserAddCred(user, cred); + } - if (user == NULL) user = GRSTgaclUserNew(cred); - else GRSTgaclUserAddCred(user, cred); + /* check for Destination: header and evaluate if present */ + + if ((destination = (char *) apr_table_get(r->headers_in, + "Destination")) != NULL) + { + destination_prefix = apr_psprintf(r->pool, "https://%s:%d/", + r->server->server_hostname, (int) r->server->port); + + if (strncmp(destination_prefix, destination, + strlen(destination_prefix)) == 0) + destination_uri = &destination[strlen(destination_prefix)-1]; + else if ((int) r->server->port == 443) + { + destination_prefix = apr_psprintf(r->pool, "https://%s/", + r->server->server_hostname); + + if (strncmp(destination_prefix, destination, + strlen(destination_prefix)) == 0) + destination_uri = &destination[strlen(destination_prefix)-1]; } + + if (destination_uri != NULL) + { + destreq = ap_sub_req_method_uri("GET", destination_uri, r, NULL); + + if ((destreq != NULL) && (destreq->filename != NULL) + && (destreq->path_info != NULL)) + { + destination_translated = apr_pstrcat(r->pool, + destreq->filename, destreq->path_info, NULL); + apr_table_setn(r->notes, "GRST_DESTINATION_TRANSLATED", + destination_translated); + + if (((mod_gridsite_cfg *) cfg)->envs) + apr_table_setn(env, "GRST_DESTINATION_TRANSLATED", + destination_translated); + + p = rindex(destination_translated, '/'); + if ((p != NULL) && (strcmp(&p[1], GRST_ACL_FILE) == 0)) + destination_is_acl = 1; + } + } + } + + /* this checks for NULL arguments itself */ + if (GRSTgaclDNlistHasUser(((mod_gridsite_cfg *) cfg)->adminlist, user)) + { + perm = GRST_PERM_ALL; + if (destination_translated != NULL) destination_perm = GRST_PERM_ALL; + } + else + { acl = GRSTgaclAclLoadforFile(r->filename); if (acl != NULL) perm = GRSTgaclAclTestUser(acl, user); - } + GRSTgaclAclFree(acl); + + if (destination_translated != NULL) + { + acl = GRSTgaclAclLoadforFile(destination_translated); + if (acl != NULL) destination_perm = GRSTgaclAclTestUser(acl, user); + GRSTgaclAclFree(acl); + apr_table_setn(r->notes, "GRST_DESTINATION_PERM", + apr_psprintf(r->pool, "%d", destination_perm)); + + if (((mod_gridsite_cfg *) cfg)->envs) + apr_table_setn(env, "GRST_DESTINATION_PERM", + apr_psprintf(r->pool, "%d", destination_perm)); + } + } + + /* set permission and GACL environment variables */ + apr_table_setn(r->notes, "GRST_PERM", apr_psprintf(r->pool, "%d", perm)); if (((mod_gridsite_cfg *) cfg)->envs) @@ -2207,13 +2303,9 @@ static int mod_gridsite_perm_handler(request_rec *r) { /* *** Check HTTP method to decide which perm bits to check *** */ - if (r->filename != NULL) - { - file = rindex(r->filename, '/'); - if (file != NULL) ++file; - else file = r->filename; - } - else file = NULL; + if ((r->filename != NULL) && + ((p = rindex(r->filename, '/')) != NULL) && + (strcmp(&p[1], GRST_ACL_FILE) == 0)) file_is_acl = 1; content_type = r->content_type; if ((content_type != NULL) && @@ -2248,14 +2340,23 @@ static int mod_gridsite_perm_handler(request_rec *r) ((r->method_number == M_POST) && !GRSTgaclPermHasRead(perm) ) || - (((r->method_number == M_PUT) || (r->method_number == M_DELETE)) && - !GRSTgaclPermHasWrite(perm) && - ((file == NULL) || (strcmp(file, GRST_ACL_FILE) != 0)) ) || - - (((r->method_number == M_PUT) || (r->method_number == M_DELETE)) && - !GRSTgaclPermHasAdmin(perm) && - (file != NULL) && - (strcmp(file, GRST_ACL_FILE) == 0) ) ) retcode = HTTP_FORBIDDEN; + (((r->method_number == M_PUT) || + (r->method_number == M_DELETE)) && + !GRSTgaclPermHasWrite(perm) && !file_is_acl) || + + ((r->method_number == M_MOVE) && + ((!GRSTgaclPermHasWrite(perm) && !file_is_acl) || + (!GRSTgaclPermHasAdmin(perm) && file_is_acl) || + (!GRSTgaclPermHasWrite(destination_perm) + && !destination_is_acl) || + (!GRSTgaclPermHasAdmin(destination_perm) + && destination_is_acl)) ) || + + (((r->method_number == M_PUT) || + (r->method_number == M_DELETE)) && + !GRSTgaclPermHasAdmin(perm) && file_is_acl) + + ) retcode = HTTP_FORBIDDEN; } return retcode;