Main Page | Modules | File List | Globals

sup_emb.c

00001 /*
00002  * Copyright (c) 2005, 2006 by KoanLogic s.r.l. <http://www.koanlogic.com>
00003  * All rights reserved.
00004  *
00005  * This file is part of KLone, and as such it is subject to the license stated
00006  * in the LICENSE file which you have received as part of this distribution.
00007  *
00008  * $Id: sup_emb.c,v 1.33 2008/04/25 18:59:08 tat Exp $
00009  */
00010 
00011 #include "klone_conf.h"
00012 #include <sys/stat.h>
00013 #include <sys/types.h>
00014 #include <unistd.h>
00015 #include <u/libu.h>
00016 #include <klone/supplier.h>
00017 #include <klone/io.h>
00018 #include <klone/ioprv.h>
00019 #include <klone/page.h>
00020 #include <klone/http.h>
00021 #include <klone/emb.h>
00022 #include <klone/codecs.h>
00023 #include <klone/ses_prv.h>
00024 #include <klone/rsfilter.h>
00025 #include "http_s.h"
00026 
00027 static int supemb_is_valid_uri(http_t *h, request_t *rq, const char *uri, 
00028         size_t len, time_t *mtime)
00029 {
00030     embres_t *e;
00031     char filename[U_FILENAME_MAX] = { 0 };
00032 
00033     dbg_err_if (uri == NULL);
00034     dbg_err_if (mtime == NULL);
00035     dbg_err_if (len >= U_FILENAME_MAX);
00036 
00037     u_unused_args(h, rq);
00038 
00039     strncpy(filename, uri, len);
00040     filename[len] = 0;
00041 
00042     if(emb_lookup(filename, &e) == 0)
00043     {   /* resource found */
00044 
00045         if(e->type == ET_FILE)
00046             *mtime = ((embfile_t*)e)->mtime;
00047         else
00048             *mtime = 0; /* dynamic pages cannot be cached */
00049 
00050         return 1;
00051     }
00052 
00053 err:
00054     return 0; /* not found */
00055 }
00056 
00057 static int supemb_get_cipher_key(request_t *rq, response_t *rs, char *key, 
00058     size_t keysz)
00059 {
00060     session_t *ss = NULL;
00061     http_t *http = NULL;
00062     session_opt_t *so;
00063     vars_t *vars;
00064     var_t *v;
00065 
00066     dbg_err_if (rq == NULL);
00067     dbg_err_if (rs == NULL);
00068     dbg_err_if (key == NULL);
00069 
00070     /* get session options */
00071     dbg_err_if((http = request_get_http(rq)) == NULL);
00072     dbg_err_if((so = http_get_session_opt(http)) == NULL);
00073 
00074     /* create/get the session */
00075     dbg_err_if(session_create(so, rq, rs, &ss));
00076 
00077     /* get variables list */
00078     vars = session_get_vars(ss);
00079     dbg_err_if(vars == NULL);
00080 
00081     v = vars_geti(vars,"KLONE_CIPHER_KEY", 0); 
00082     dbg_err_if(v == NULL); /* no such variable */
00083 
00084     dbg_err_if(var_get_value_size(v) > keysz);
00085 
00086     /* zero-out key array */
00087     memset(key, 0, keysz);
00088 
00089     /* set the key */
00090     memcpy(key, var_get_value(v), var_get_value_size(v));
00091 
00092     session_free(ss);
00093 
00094     return 0;
00095 err:
00096     if(ss)
00097         session_free(ss);
00098     return ~0;
00099 }
00100 
00101 static int supemb_static_set_header_fields(request_t *rq, response_t *rs, 
00102     embfile_t *e, int *sai)
00103 {
00104     vhost_t *vhost;
00105 
00106     dbg_err_if (rq == NULL);
00107     dbg_err_if (rs == NULL);
00108     dbg_err_if (e == NULL);
00109     dbg_err_if (sai == NULL);
00110 
00111     dbg_err_if((vhost = request_get_vhost(rq)) == NULL);
00112 
00113     /* set header fields based on embfile_t struct */
00114 
00115     /* set content-type, last-modified and content-length*/
00116     dbg_err_if(response_set_content_type(rs, e->mime_type));
00117     dbg_err_if(response_set_last_modified(rs, e->mtime));
00118     dbg_err_if(response_set_content_length(rs, e->file_size));
00119 
00120     /* if the client can accept deflated content don't uncompress the 
00121        resource but send as it is (if enabled by config) */
00122     if(vhost->send_enc_deflate)
00123     {
00124         if(e->comp && (*sai = request_is_encoding_accepted(rq, "deflate")) != 0)
00125         {   /* we can send compressed responses */
00126             dbg_err_if(response_set_content_encoding(rs, "deflate"));
00127             dbg_err_if(response_set_content_length(rs, e->size));
00128             /*  dbg("sending deflated content"); */
00129         } 
00130     }
00131 
00132     return 0;
00133 err:
00134     return ~0;
00135 }
00136 
00137 static int supemb_serve_static(request_t *rq, response_t *rs, embfile_t *e)
00138 {
00139     codec_t *gzip = NULL, *decrypt = NULL;
00140     int sai = 0; /* send as is */
00141     int decrypting = 0;
00142     char key[CODEC_CIPHER_KEY_SIZE];
00143     codec_t *rsf = NULL;
00144 
00145     dbg_return_if (rq == NULL, ~0);
00146     dbg_return_if (rs == NULL, ~0);
00147     dbg_return_if (e == NULL, 0);
00148     
00149     /* create a response filter and attach it to the response io */
00150     dbg_err_if(response_filter_create(rq, rs, NULL, &rsf));
00151     dbg_err_if(io_codec_add_tail(response_io(rs), rsf));
00152     rsf = NULL;
00153 
00154     /* set HTTP header based on 'e' (we have the cipher key here) */
00155     dbg_err_if(supemb_static_set_header_fields(rq, rs, e, &sai));
00156 
00157     /* if this is a HEAD request print the header and exit */
00158     if(request_get_method(rq) == HM_HEAD)
00159         return 0; /* just the header is requested */
00160 
00161 #ifdef HAVE_LIBZ
00162     /* if needed apply a gzip codec to uncompress content data */
00163     if(e->comp && !sai)
00164         dbg_err_if(codec_gzip_create(GZIP_UNCOMPRESS, &gzip));
00165 #endif
00166 
00167 #ifdef HAVE_LIBOPENSSL
00168     /* if the resource is encrypted unencrypt using the key stored in 
00169        KLONE_CIPHER_KEY session variable */
00170     if(e->encrypted)
00171     {
00172         if(supemb_get_cipher_key(rq, rs, key, CODEC_CIPHER_KEY_SIZE))
00173         {   /* if the content is encrypted and there's no key then exit */
00174             dbg_err_if(response_set_status(rs, 401));
00175             dbg_err("cipher key not found, aborting");
00176         }
00177         dbg_err_if(codec_cipher_create(CIPHER_DECRYPT, EVP_aes_256_cbc(),
00178                     key, NULL, &decrypt));
00179         /* delete the key from the stack */
00180         memset(key, 0, CODEC_CIPHER_KEY_SIZE);
00181     } 
00182 #endif
00183 
00184     if(gzip)
00185     {   /* set gzip filter */
00186         dbg_err_if(io_codec_add_head(response_io(rs), gzip));
00187         gzip = NULL; /* io_t owns it after io_codec_add_tail */
00188     }
00189 
00190     if(decrypt)
00191     {   /* set decrypt filter */
00192         dbg_err_if(io_codec_add_head(response_io(rs), decrypt));
00193         decrypt = NULL; /* io_t owns it after io_codec_add_tail */
00194         decrypting = 1;
00195     }
00196 
00197     /* print out page content (the header will be autoprinted by the 
00198        response io filter) */
00199     dbg_err_if(!io_write(response_io(rs), (const char*)e->data, e->size));
00200 
00201     /* remove and free the gzip codec (if it has been set) */
00202     dbg_err_if(io_codecs_remove(response_io(rs))); 
00203 
00204     return 0;
00205 err:
00206     if(decrypting)
00207         dbg_if(response_set_status(rs, 401)); /* usually wrong key given */
00208     /* remove codecs and rs filter */
00209     dbg_if(io_codecs_remove(response_io(rs))); 
00210     if(decrypt)
00211         codec_free(decrypt);
00212     if(gzip)
00213         codec_free(gzip);
00214     return ~0;
00215 }
00216 
00217 static int supemb_serve_dynamic(request_t *rq, response_t *rs, embpage_t *e)
00218 {
00219     session_t *ss = NULL;
00220     http_t *http = NULL;
00221     codec_t *filter = NULL;
00222     session_opt_t *so;
00223     io_t *oio;
00224 
00225     dbg_return_if (rq == NULL, ~0);
00226     dbg_return_if (rs == NULL, ~0);
00227     dbg_return_if (e == NULL, ~0);
00228 
00229     /* output io object */
00230     oio = response_io(rs);
00231 
00232     /* get session options */
00233     dbg_err_if((http = request_get_http(rq)) == NULL);
00234     dbg_err_if((so = http_get_session_opt(http)) == NULL);
00235 
00236     /* parse URL encoded or POSTed data (POST must be read before) */
00237     dbg_err_if(request_parse_data(rq));
00238 
00239     /* create/get the session */
00240     dbg_err_if(session_create(so, rq, rs, &ss));
00241 
00242     /* set some default values */
00243     dbg_err_if(response_set_content_type(rs, "text/html"));
00244 
00245     /* by default disable caching */
00246     response_disable_caching(rs);
00247 
00248     /* create a response filter (used to automatically print all header fields 
00249      * when the header buffer fills up) and attach it to the response io */
00250     dbg_err_if(response_filter_create(rq, rs, ss, &filter));
00251     io_codec_add_tail(oio, filter);
00252 
00253     /* run the page code */
00254     e->run(rq, rs, ss);
00255 
00256     /* flush the output buffer */
00257     io_flush(oio);
00258 
00259     /* if nothing has been printed by the sciprt then write a dummy byte so 
00260      * the io_t calls the filter function that, in turn, will print out the 
00261      * HTTP header (rsfilter will handle it) */
00262     if(!response_filter_feeded(filter))
00263         io_write(oio, "\n", 1);
00264 
00265     /* save and destroy the session */
00266     session_free(ss);
00267 
00268     return 0;
00269 err:
00270     io_flush(response_io(rs));
00271     if(ss)
00272         session_free(ss);
00273     return ~0;
00274 }
00275 
00276 static int supemb_serve(request_t *rq, response_t *rs)
00277 {
00278     const char *file_name;
00279     embres_t *e;
00280 
00281     dbg_err_if (rq == NULL);
00282     dbg_err_if (rs == NULL);
00283     
00284     file_name = request_get_resolved_filename(rq);
00285     dbg_ifb(file_name == NULL || emb_lookup(file_name, &e))
00286     {
00287         response_set_status(rs, HTTP_STATUS_NOT_FOUND); 
00288         return 0;
00289     }
00290 
00291     /* dbg("serving %s", e->filename); */
00292 
00293     switch(e->type)
00294     {
00295     case ET_FILE:
00296         dbg_err_if(supemb_serve_static(rq, rs, (embfile_t*)e));
00297         break;
00298     case ET_PAGE:
00299         dbg_err_if(supemb_serve_dynamic(rq, rs, (embpage_t*)e));
00300         break;
00301     default:
00302         dbg_err_if("unknown res type");
00303     }
00304 
00305     return 0;
00306 err:
00307     return ~0;
00308 }
00309 
00310 static int supemb_init(void)
00311 {
00312     return 0;
00313 }
00314 
00315 static void supemb_term(void)
00316 {
00317     return;
00318 }
00319 
00320 supplier_t sup_emb = {
00321     "embedded content supplier",
00322     supemb_init,
00323     supemb_term,
00324     supemb_is_valid_uri,
00325     supemb_serve
00326 };
00327