"Fossies" - the Fresh Open Source Software Archive

Member "mod_roaming-2.0.0/mod_roaming.c" (30 Apr 2002, 15999 Bytes) of package /linux/www/apache_httpd_modules/old/mod_roaming-2.0.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /* ====================================================================
    2  * Copyright (c) 1999-2002 Vincent Partington.  All rights reserved.
    3  *
    4  * Redistribution and use in source and binary forms, with or without
    5  * modification, are permitted provided that the following conditions
    6  * are met:
    7  *
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer. 
   10  *
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in
   13  *    the documentation and/or other materials provided with the
   14  *    distribution.
   15  *
   16  * 3. The name of the author may not be used to endorse or promote products
   17  *    derived from this software without specific prior written permission.
   18  *
   19  * THIS SOFTWARE IS PROVIDED BY VINCENT PARTINGTON ``AS IS'' AND ANY
   20  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL VINCENT PARTINGTON OR
   23  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
   28  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
   30  * OF THE POSSIBILITY OF SUCH DAMAGE.
   31  * ====================================================================
   32  */
   33 
   34 #include    "apr_strings.h"
   35 #include    "httpd.h"
   36 #include    "http_config.h"
   37 #include    "http_log.h"
   38 #include    "http_main.h"
   39 #include    "http_protocol.h"
   40 #include    "http_request.h"
   41 
   42 #if MODULE_MAGIC_NUMBER_MAJOR < 20020128
   43 #error "You need at least Apache 2.0.32 to compile this module"
   44 #endif
   45 
   46 #define     COMMUNICATOR_HACK_ENABLED       1
   47 
   48 #define     ROAMING_FILE        "roaming-file"
   49 #define     ROAMING_HANDLER     "roaming-handler"
   50 #define     ROAMING_FILE_PERMS  APR_UREAD|APR_UWRITE
   51 #define     ROAMING_DIR_PERMS   APR_UREAD|APR_UWRITE|APR_UEXECUTE
   52 #define     LOG_URL_FORM_WARNING()  \
   53     ap_log_rerror(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, r, \
   54     "Is the URL of the form http://<host>/<roamingalias>/<userid>/<file>?");
   55 
   56 /*
   57  * Configuration data types for mod_roaming.
   58  */
   59 
   60 module AP_MODULE_DECLARE_DATA roaming_module;
   61 
   62 typedef struct {
   63     apr_array_header_t  *aliases;
   64 } roaming_config_t;
   65 
   66 typedef struct {
   67     const char  *uri;
   68     const char  *dir;
   69 } roaming_alias_t;
   70 
   71 /*
   72  * Creates mod_roaming configuration struct.
   73  */
   74 static void *roaming_create_server_config(apr_pool_t *p, server_rec * s) {
   75     roaming_config_t    *rc;
   76 
   77     rc = (roaming_config_t *) apr_pcalloc(p, sizeof(roaming_config_t));
   78     rc->aliases = apr_array_make(p, 3, sizeof(roaming_alias_t));
   79 
   80     return rc;
   81 }
   82 
   83 /*
   84  * Implements RoamingAlias directive by adding the uri->dir mapping
   85  * to the list of roaming aliases.
   86  */
   87 static const char *roaming_alias(cmd_parms *cmd, void *dummy,
   88                                 const char *uri, const char *dir)
   89 {
   90     apr_status_t        rv;
   91     apr_finfo_t         dir_info;
   92     roaming_config_t    *rc;
   93     roaming_alias_t     *ra;
   94 
   95     rv = apr_stat(&dir_info, dir, APR_FINFO_TYPE, cmd->pool);
   96     if(rv != APR_SUCCESS) {
   97         return apr_pstrcat(cmd->pool, "\"", dir, "\" does not exist", NULL);
   98     } else if(dir_info.filetype != APR_DIR) {
   99         return apr_pstrcat(cmd->pool, "\"", dir, "\" is not a directory", NULL);
  100     }
  101 
  102     rc = ap_get_module_config(cmd->server->module_config, &roaming_module);
  103     ra = (roaming_alias_t *) apr_array_push(rc->aliases);
  104     ra->uri = uri;
  105     if(dir[strlen(dir)-1] == '/') {
  106         ra->dir = dir;
  107     } else {
  108         ra->dir = apr_pstrcat(cmd->pool, dir, "/", NULL);
  109     }
  110 
  111     return NULL;
  112 }
  113 
  114 /*
  115  * Tests whether a particular roaming access uri
  116  * is being referenced.
  117  */
  118 static int roaming_test_uri(const char *request_uri, const char *alias_uri) {
  119     const char  *request_uri_p, *alias_uri_end;
  120 
  121     request_uri_p = request_uri;
  122     alias_uri_end = alias_uri + strlen(alias_uri);
  123 
  124     while(alias_uri < alias_uri_end) {
  125         if(*alias_uri == '/') {
  126             if(*request_uri_p != '/') {
  127                 return 0;
  128             }
  129             while(*alias_uri == '/') {
  130                 alias_uri++;
  131             }
  132             while(*request_uri_p == '/') {
  133                 request_uri_p++;
  134             }
  135         } else {
  136             if(*alias_uri++ != *request_uri_p++) {
  137                 return 0;
  138             }
  139         }
  140     }
  141 
  142     if(alias_uri[-1] != '/' && *request_uri_p != '\0' &&
  143             *request_uri_p != '/')
  144     {
  145         return 0;
  146     }
  147 
  148     return request_uri_p - request_uri;
  149 }
  150 
  151 /*
  152  * Catches request for roaming files.
  153  */
  154 static int roaming_translate_name(request_rec *r)
  155 {
  156     roaming_config_t    *rc;
  157     roaming_alias_t     *aliases;
  158     int                 i, l, ret;
  159     char                *file, *user, *next_slash;
  160 
  161     rc = ap_get_module_config(r->server->module_config, &roaming_module);
  162     aliases = (roaming_alias_t *) rc->aliases->elts;
  163 
  164     for(i = 0; i < rc->aliases->nelts; i++) {
  165         l = roaming_test_uri(r->uri, aliases[i].uri);
  166         if(l > 0) {
  167             /*  Roaming uri's should be of the form:
  168                     /<roamingalias>/<userid>/<file>
  169                 and only the user <userid> may access that uri.
  170                 The following uri's are forbidden:
  171                     /<roamingalias>/<file>
  172                     /<roamingalias>/<userid>/
  173                     /<roamingalias>/<userid>/<dir>/<file>
  174             */
  175 
  176             /* determine user part of uri */
  177             file = r->uri + l;
  178             ret = ap_unescape_url(file);
  179             if(ret != OK) {
  180                 return ret;
  181             }
  182 
  183             while(*file == '/') {
  184                 file++;
  185             }
  186             next_slash = strchr(file, '/');
  187             if(next_slash == NULL) {
  188                 ap_log_rerror(APLOG_MARK,
  189                     APLOG_ERR|APLOG_NOERRNO, 0, r,
  190                     "Roaming uri must contain a userid");
  191                 LOG_URL_FORM_WARNING();
  192                 return HTTP_FORBIDDEN;
  193             }
  194             user = apr_pstrndup(r->pool, file, next_slash - file);
  195             apr_table_setn(r->notes, "roaming-user", user);
  196             apr_table_setn(r->notes, "roaming-user-dir",
  197                     apr_pstrcat(r->pool, aliases[i].dir, user, NULL));
  198 
  199             /* determine user's file part of uri */
  200             file = next_slash;
  201             while(*file == '/') {
  202                 file++;
  203             }
  204             if(*file == '\0') {
  205                 /* no directory indexes */
  206                 ap_log_rerror(APLOG_MARK,
  207                     APLOG_ERR|APLOG_NOERRNO, 0, r,
  208                     "Directory listings of roaming uri's are not allowed");
  209                 LOG_URL_FORM_WARNING();
  210                 return HTTP_FORBIDDEN;
  211             } else if(strchr(file, '/') != NULL) {
  212                 /* no subdirectories */
  213                 ap_log_rerror(APLOG_MARK,
  214                     APLOG_ERR|APLOG_NOERRNO, 0, r,
  215                     "Subdirectories in roaming uri's are not allowed");
  216                 LOG_URL_FORM_WARNING();
  217                 return HTTP_FORBIDDEN;
  218             }
  219 
  220             /* Ugly hack to work around Communicator's invalid
  221              * HTTP request problem
  222              */
  223 #if COMMUNICATOR_HACK_ENABLED
  224             if(strcmp(file, "IMAP") == 0) {
  225                 char    *real_filename_start, *real_filename_end, *s;
  226 
  227                 real_filename_start = strstr(r->the_request, "/IMAP ");
  228                 if(real_filename_start != NULL) {
  229                     real_filename_end = strchr(real_filename_start + 6, ' ');
  230                     if(real_filename_end != NULL &&
  231                             strcmp(real_filename_end, " HTTP/1.0") == 0)
  232                     {
  233                         s = strchr(real_filename_start + 1, '/');
  234                         if(s == NULL || s > real_filename_end) {
  235                             file = apr_pstrndup(r->pool,
  236                                 real_filename_start + 1,
  237                                 (real_filename_end - real_filename_start) - 1);
  238                             ap_log_rerror(APLOG_MARK,
  239                                     APLOG_WARNING|APLOG_NOERRNO, 0, r,
  240                                     "Fixed filename on invalid HTTP request:"
  241                                     " %s", file);
  242                         }
  243                     }
  244                 }
  245             }
  246 #endif
  247 
  248             apr_table_setn(r->notes, ROAMING_FILE, file);
  249 
  250             /* construct filename */
  251             r->filename = apr_pstrcat(r->pool,
  252                 aliases[i].dir, user, "/", file, NULL);
  253 
  254             /* install our own handler */
  255             r->handler = ROAMING_HANDLER;
  256             return OK;
  257         }
  258     }
  259     
  260     return DECLINED;
  261 }
  262 
  263 /*
  264  * Handles the GET, HEAD, PUT, DELETE and MOVE methods for roaming files.
  265  */
  266 static int roaming_handler(request_rec *r)
  267 {
  268     const char          *user, *file, *userdir, *new_uri;
  269     char                *last_uri_slash, *last_new_uri_slash,
  270                         *last_filename_slash, *new_filename;
  271     char                buffer[HUGE_STRING_LEN];
  272     apr_file_t          *f;
  273     apr_status_t        rv;
  274     apr_finfo_t         dir_info;   
  275     int                 ret;
  276     apr_size_t          chars_read;
  277 
  278     /* check whether this one is for us */
  279     if(strcmp(r->handler, ROAMING_HANDLER) != 0) {
  280         return DECLINED;
  281     }
  282 
  283     /* check whether the correct user has logged on */
  284     /* to access these roaming files */
  285     user = apr_table_get(r->notes, "roaming-user");
  286     if(user == NULL) {
  287         ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r,
  288                 "No roaming-user request note set");
  289         return HTTP_INTERNAL_SERVER_ERROR;
  290     } else if(r->user == NULL) {
  291         ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r,
  292                 "Unauthenticated user has no access to roaming files for %s",
  293                 user);
  294         ap_log_rerror(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, r,
  295                 "Have you put a .htaccess file in the roaming directory?");
  296         return HTTP_FORBIDDEN;
  297     } else if(strcmp(r->user, user) != 0) {
  298         ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r,
  299                 "User %s has no access to roaming files for %s",
  300                 r->user, user);
  301         return HTTP_FORBIDDEN;
  302     }
  303 
  304     /* get the name of the file being requested */
  305     file = apr_table_get(r->notes, ROAMING_FILE);
  306     if(file == NULL) {
  307         ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r,
  308                 "No roaming-file request note set");
  309         return HTTP_INTERNAL_SERVER_ERROR;
  310     }
  311 
  312     /* create directory to hold user's roaming files (if necessary) */
  313     userdir = apr_table_get(r->notes, "roaming-user-dir");
  314     if(userdir != NULL && apr_stat(&dir_info, userdir,
  315                             APR_FINFO_TYPE, r->pool) != APR_SUCCESS)
  316     {
  317         rv = apr_dir_make(userdir, ROAMING_DIR_PERMS, r->pool);
  318         if(rv != APR_SUCCESS) {
  319             ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r,
  320                     "Cannot create directory: %s", userdir);
  321             return HTTP_FORBIDDEN;
  322         }
  323 
  324         if(r->path_info != NULL && *r->path_info != '\0') {
  325             r->filename = apr_pstrcat(r->pool,
  326                     r->filename, r->path_info, NULL);
  327             r->path_info = NULL;
  328         }
  329         rv = apr_stat(&r->finfo, r->filename, APR_FINFO_NORM, r->pool);
  330         if(rv != APR_SUCCESS) {
  331             r->finfo.filetype = APR_NOFILE;
  332         }
  333     }
  334 
  335     /* check that we have no path_info lying about */
  336     if(r->path_info != NULL && *r->path_info != '\0') {
  337         ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r,
  338                 "File not found: %s%s", r->filename, r->path_info);
  339         return HTTP_NOT_FOUND;
  340     }
  341 
  342     /* check that we are about to handle a normal file */
  343     if(r->finfo.filetype != APR_NOFILE && r->finfo.filetype != APR_REG) {
  344         ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r,
  345                 "Not a regular file: %s", r->filename);
  346         return HTTP_FORBIDDEN;
  347     }
  348 
  349     /* prepare to read the request body */
  350     if(r->method_number == M_PUT) {
  351         ret = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR);
  352     } else {
  353         ret = ap_discard_request_body(r);
  354     }
  355     if(ret != OK) {
  356         return ret;
  357     }
  358 
  359     if(r->method_number == M_GET) {
  360     /* GET */
  361         if(r->finfo.filetype == APR_NOFILE) {
  362             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r,
  363                     "File not found: %s", r->filename);
  364             return HTTP_NOT_FOUND;
  365         }
  366 
  367         ap_update_mtime(r, r->finfo.mtime);
  368         ap_set_last_modified(r);
  369         ap_set_content_length(r, r->finfo.size);
  370         r->content_type = "text/html";
  371 
  372         rv = apr_file_open(&f, r->filename, APR_READ|APR_BINARY,
  373                 ROAMING_FILE_PERMS, r->pool);
  374         if(rv != APR_SUCCESS) {
  375             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
  376                     "Cannot open file %s", r->filename);
  377             return HTTP_FORBIDDEN;
  378         }
  379 
  380         if(!r->header_only) {
  381             for(;;) {
  382                 apr_size_t chars_read = sizeof(buffer);
  383                 rv = apr_file_read(f, buffer, &chars_read);
  384                 if(rv == APR_EOF) {
  385                     break;
  386                 }
  387                 if(rv != APR_SUCCESS) {
  388                     ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
  389                             "Cannot read file %s", r->filename);
  390                     return HTTP_INTERNAL_SERVER_ERROR;
  391                 }
  392                 ap_rwrite(buffer, chars_read, r);
  393             }
  394         }
  395 
  396         rv = apr_file_close(f);
  397         if(rv != APR_SUCCESS) {
  398             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
  399                     "Cannot close file %s", r->filename);
  400             return HTTP_INTERNAL_SERVER_ERROR;
  401         }
  402 
  403         return OK;
  404     } else if(r->method_number == M_PUT) {
  405     /* PUT */
  406         rv = apr_file_open(&f, r->filename, APR_WRITE|APR_CREATE|APR_BINARY,
  407                 ROAMING_FILE_PERMS, r->pool);
  408         if(rv != APR_SUCCESS) {
  409             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
  410                     "Cannot open file %s", r->filename);
  411             return HTTP_FORBIDDEN;
  412         }
  413 
  414         if(ap_should_client_block(r)) {
  415             while((chars_read =
  416                     ap_get_client_block(r, buffer, sizeof(buffer))) > 0 )
  417             {
  418                 rv = apr_file_write(f, buffer, &chars_read);
  419                 if(rv != APR_SUCCESS) {
  420                     while(ap_get_client_block(r, buffer, sizeof(buffer)) > 0)
  421                         ;
  422                     ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
  423                             "Cannot write file %s", r->filename);
  424                     rv = apr_file_close(f);
  425                     /* return value ignored, already in error state. */
  426                     return HTTP_INTERNAL_SERVER_ERROR;
  427                 }
  428             }
  429 
  430             rv = apr_file_flush(f);
  431             if(rv != APR_SUCCESS) {
  432                 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
  433                         "Cannot flush output to file %s", r->filename);
  434                 return HTTP_INTERNAL_SERVER_ERROR;
  435             }
  436 
  437             rv = apr_file_close(f);
  438             if(rv != APR_SUCCESS) {
  439                 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
  440                         "Cannot close file %s", r->filename);
  441                 return HTTP_INTERNAL_SERVER_ERROR;
  442             }
  443         }
  444     } else if(r->method_number == M_DELETE) {
  445     /* DELETE */
  446         rv = apr_file_remove(r->filename, r->pool);
  447         if(rv != APR_SUCCESS) {
  448             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
  449                     "Cannot delete file %s", r->filename);
  450             return HTTP_INTERNAL_SERVER_ERROR;
  451         }
  452     } else if(r->method_number == M_MOVE) {
  453     /* MOVE */
  454         new_uri = apr_table_get(r->headers_in, "New-uri");
  455         if(new_uri == NULL) {
  456             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r,
  457                 "No New-uri specified");
  458             return HTTP_BAD_REQUEST;
  459         }
  460         ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r,
  461                 "New-uri: %s", new_uri);
  462 
  463         last_uri_slash = strrchr(r->uri, '/');
  464         last_filename_slash = strrchr(r->filename, '/');
  465         if(last_uri_slash == NULL || last_filename_slash == NULL) {
  466             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r,
  467                 "r->uri \"%s\" or r->filename \"%s\" do not contain slashes",
  468                 r->uri, r->filename);
  469             return HTTP_INTERNAL_SERVER_ERROR;
  470         }
  471 
  472         last_new_uri_slash = strrchr(new_uri, '/');
  473         if(last_new_uri_slash == NULL || last_new_uri_slash[1] == '\0') {
  474             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r,
  475                 "New-uri %s does not contain slash or ends in slash", new_uri);
  476             return HTTP_BAD_REQUEST;
  477         }
  478 
  479         if((last_uri_slash - r->uri) != (last_new_uri_slash - new_uri) ||
  480             strncmp(r->uri, new_uri, (last_uri_slash - r->uri)) != 0)
  481         {
  482             ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r,
  483                 "New-uri %s does not refer to the same directory as uri %s",
  484                 new_uri, r->uri);
  485             return HTTP_BAD_REQUEST;
  486         }
  487 
  488         new_filename = apr_pstrcat(r->pool,
  489             apr_pstrndup(r->pool,
  490                 r->filename, (last_filename_slash - r->filename + 1)),
  491             last_new_uri_slash+1,
  492             NULL);
  493 
  494         rv = apr_file_rename(r->filename, new_filename, r->pool);
  495         if(rv != APR_SUCCESS) {
  496             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
  497                 "Cannot rename file %s to %s", r->filename, new_filename);
  498             return HTTP_INTERNAL_SERVER_ERROR;
  499         }
  500     } else {
  501     /* Unknown request method. */
  502         return HTTP_METHOD_NOT_ALLOWED;
  503     }
  504 
  505     r->content_type = "text/html";
  506     ap_rprintf(r, "<HTML>\n"
  507         "<HEAD><TITLE>Success</TITLE></HEAD>\n"
  508         "<BODY><H1>%s succesfull</H1>\n"
  509         "The %s operation on %s was succesfull.<BR>\n"
  510         "</BODY>\n"
  511         "</HTML>\n",
  512         r->method, r->method, r->uri);
  513 
  514     return OK;
  515 }
  516 
  517 /*
  518  * Registers hooks at interesting points in the request handling process.
  519  */
  520 static void roaming_register_hooks(apr_pool_t *p) {
  521     ap_hook_translate_name(roaming_translate_name, NULL, NULL, APR_HOOK_MIDDLE);
  522     ap_hook_handler(roaming_handler, NULL, NULL, APR_HOOK_MIDDLE);
  523 }
  524 
  525 /*
  526  * Table of commands for mod_roaming.
  527  */
  528 static const command_rec roaming_commands[] =
  529 {
  530     AP_INIT_TAKE2(
  531         "RoamingAlias",
  532         roaming_alias,
  533         NULL,
  534         RSRC_CONF,
  535         "the roaming URI and the directory containing the roaming files"
  536     ),
  537     {NULL}
  538 };
  539 
  540 /*
  541  * Module info for mod_roaming.
  542  */
  543 module AP_MODULE_DECLARE_DATA roaming_module =
  544 {
  545     STANDARD20_MODULE_STUFF,
  546     NULL,
  547     NULL,
  548     roaming_create_server_config,
  549     NULL,
  550     roaming_commands,
  551     roaming_register_hooks
  552 };
  553