• Main Page
  • Related Pages
  • Namespaces
  • Data Structures
  • Files
  • File List
  • Globals

HttpBodyFactory.cc

Go to the documentation of this file.
00001 /** @file
00002 
00003   A brief file description
00004 
00005   @section license License
00006 
00007   Licensed to the Apache Software Foundation (ASF) under one
00008   or more contributor license agreements.  See the NOTICE file
00009   distributed with this work for additional information
00010   regarding copyright ownership.  The ASF licenses this file
00011   to you under the Apache License, Version 2.0 (the
00012   "License"); you may not use this file except in compliance
00013   with the License.  You may obtain a copy of the License at
00014 
00015       http://www.apache.org/licenses/LICENSE-2.0
00016 
00017   Unless required by applicable law or agreed to in writing, software
00018   distributed under the License is distributed on an "AS IS" BASIS,
00019   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00020   See the License for the specific language governing permissions and
00021   limitations under the License.
00022  */
00023 
00024 /****************************************************************************
00025 
00026   HttpBodyFactory.cc
00027 
00028 
00029  ****************************************************************************/
00030 
00031 #include "libts.h"
00032 #include "HttpBodyFactory.h"
00033 #include <unistd.h>
00034 #include <dirent.h>
00035 #include <sys/types.h>
00036 #include <sys/stat.h>
00037 #include "URL.h"
00038 #include <logging/Log.h>
00039 #include <logging/LogAccess.h>
00040 #include <logging/LogAccessHttp.h>
00041 #include "HttpCompat.h"
00042 #include "I_Layout.h"
00043 
00044 //////////////////////////////////////////////////////////////////////
00045 // The HttpBodyFactory creates HTTP response page bodies, supported //
00046 // configurable customization and language-targeting.               //
00047 //                                                                  //
00048 // The body factory can be reconfigured dynamically by a manager    //
00049 // callback, so locking is required.  The callback takes a lock,    //
00050 // and the user entry points take a lock.  These locks may limit    //
00051 // the speed of error page generation.                              //
00052 //////////////////////////////////////////////////////////////////////
00053 
00054 ////////////////////////////////////////////////////////////////////////
00055 //
00056 // User-Callable APIs --- locks will be taken internally
00057 //
00058 ////////////////////////////////////////////////////////////////////////
00059 
00060 char *
00061 HttpBodyFactory::fabricate_with_old_api(const char *type, HttpTransact::State * context,
00062                                         int64_t max_buffer_length, int64_t *resulting_buffer_length,
00063                                         char* content_language_out_buf, size_t content_language_buf_size,
00064                                         char* content_type_out_buf, size_t content_type_buf_size,
00065                                         const char *format, va_list ap)
00066 {
00067   char *buffer = NULL;
00068   const char *lang_ptr = NULL;
00069   const char *charset_ptr = NULL;
00070   char url[1024];
00071   const char *set = NULL;
00072   bool found_requested_template = false;
00073   bool plain_flag = false;
00074 
00075   lock();
00076 
00077   *resulting_buffer_length = 0;
00078 
00079   ink_strlcpy(content_language_out_buf, "en", content_language_buf_size);
00080   ink_strlcpy(content_type_out_buf, "text/html", content_type_buf_size);
00081 
00082   ///////////////////////////////////////////////////////////////////
00083   // if logging turned on, buffer up the URL string for simplicity //
00084   ///////////////////////////////////////////////////////////////////
00085   if (enable_logging) {
00086     url[0] = '\0';
00087     URL *u = context->hdr_info.client_request.url_get();
00088 
00089     if (u->valid()) {           /* if url exists, copy the string into buffer */
00090       size_t i;
00091       char *s = u->string_get(&context->arena);
00092       for (i = 0; (i < sizeof(url) - 1) && s[i]; i++)
00093         url[i] = s[i];
00094       url[i] = '\0';
00095       if (s)
00096         context->arena.str_free(s);
00097     }
00098   }
00099   ///////////////////////////////////////////////
00100   // if suppressing this response, return NULL //
00101   ///////////////////////////////////////////////
00102   if (is_response_suppressed(context)) {
00103     if (enable_logging) {
00104       Log::error("BODY_FACTORY: suppressing '%s' response for url '%s'", type, url);
00105     }
00106     unlock();
00107     return (NULL);
00108   }
00109   //////////////////////////////////////////////////////////////////////////////////
00110   // if language-targeting activated, get client Accept-Language & Accept-Charset //
00111   //////////////////////////////////////////////////////////////////////////////////
00112 
00113   StrList acpt_language_list(false);
00114   StrList acpt_charset_list(false);
00115 
00116   if (enable_customizations == 2) {
00117     context->hdr_info.client_request.value_get_comma_list(MIME_FIELD_ACCEPT_LANGUAGE, MIME_LEN_ACCEPT_LANGUAGE,
00118                                                           &acpt_language_list);
00119     context->hdr_info.client_request.value_get_comma_list(MIME_FIELD_ACCEPT_CHARSET, MIME_LEN_ACCEPT_CHARSET,
00120                                                           &acpt_charset_list);
00121   }
00122   ///////////////////////////////////////////
00123   // check if we don't need to format body //
00124   ///////////////////////////////////////////
00125   if (format) {
00126     // The length from ink_bvsprintf includes the trailing NUL, so adjust the final
00127     // length accordingly.
00128     int l = ink_bvsprintf(NULL, format, ap);
00129     if (l <= max_buffer_length) {
00130       buffer = (char *)ats_malloc(l);
00131       *resulting_buffer_length = ink_bvsprintf(buffer, format, ap) - 1;
00132       plain_flag = true;
00133     }
00134   }
00135   /////////////////////////////////////////////////////////
00136   // try to fabricate the desired type of error response //
00137   /////////////////////////////////////////////////////////
00138   if (buffer == NULL) {
00139     buffer = fabricate(&acpt_language_list, &acpt_charset_list,
00140                        type, context, resulting_buffer_length, &lang_ptr, &charset_ptr, &set);
00141     found_requested_template = (buffer != NULL);
00142   }
00143   /////////////////////////////////////////////////////////////
00144   // if failed, try to fabricate the default custom response //
00145   /////////////////////////////////////////////////////////////
00146   if (buffer == NULL) {
00147     buffer = fabricate(&acpt_language_list, &acpt_charset_list,
00148                        "default", context, resulting_buffer_length, &lang_ptr, &charset_ptr, &set);
00149   }
00150   ///////////////////////////////////
00151   // enforce the max buffer length //
00152   ///////////////////////////////////
00153   if (buffer && (*resulting_buffer_length > max_buffer_length)) {
00154     if (enable_logging) {
00155       Log::error(("BODY_FACTORY: template '%s/%s' consumed %" PRId64 " bytes, "
00156                   "exceeding %" PRId64 " byte limit, using internal default"),
00157                  set, type, *resulting_buffer_length, max_buffer_length);
00158     }
00159     *resulting_buffer_length = 0;
00160     buffer = (char *)ats_free_null(buffer);
00161   }
00162   /////////////////////////////////////////////////////////////////////
00163   // handle return of instantiated template and generate the content //
00164   // language and content type return values                         //
00165   /////////////////////////////////////////////////////////////////////
00166 
00167   if (buffer) {                  // got an instantiated template
00168     if (!plain_flag) {
00169       snprintf(content_language_out_buf, content_language_buf_size, "%s", lang_ptr);
00170       snprintf(content_type_out_buf, content_type_buf_size, "text/html; charset=%s", charset_ptr);
00171     }
00172 
00173     if (enable_logging) {
00174       if (found_requested_template) {    // got exact template
00175         Log::error(("BODY_FACTORY: using custom template "
00176                     "'%s/%s' for url '%s' (language '%s', charset '%s')"), set, type, url, lang_ptr, charset_ptr);
00177       } else {                   // got default template
00178         Log::error(("BODY_FACTORY: can't find custom template "
00179                     "'%s/%s', using '%s/%s' for url '%s' (language '%s', charset '%s')"),
00180                    set, type, set, "default", url, lang_ptr, charset_ptr);
00181       }
00182     }
00183   } else {                       // no template
00184     if (enable_logging) {
00185         Log::error(("BODY_FACTORY: can't find templates '%s' or '%s' for url `%s'"),
00186                    type, "default", url);
00187     }
00188   }
00189   unlock();
00190 
00191   return (buffer);
00192 }
00193 
00194 void
00195 HttpBodyFactory::dump_template_tables(FILE * fp)
00196 {
00197   RawHashTable *h1, *h2;
00198   RawHashTable_Key k1, k2;
00199   RawHashTable_Value v1, v2;
00200   RawHashTable_Binding *b1, *b2;
00201   RawHashTable_IteratorState i1, i2;
00202   HttpBodySet *body_set;
00203 
00204   lock();
00205 
00206   h1 = table_of_sets;
00207 
00208   if (h1 != NULL) {
00209     ///////////////////////////////////////////
00210     // loop over set->body-types hash table //
00211     ///////////////////////////////////////////
00212 
00213     for (b1 = h1->firstBinding(&i1); b1 != NULL; b1 = h1->nextBinding(&i1)) {
00214       k1 = table_of_sets->getKeyFromBinding(b1);
00215       v1 = table_of_sets->getValueFromBinding(b1);
00216       body_set = (HttpBodySet *) v1;
00217 
00218       if (body_set != NULL) {
00219         fprintf(fp, "set %s: name '%s', lang '%s', charset '%s'\n",
00220                 k1, body_set->set_name, body_set->content_language, body_set->content_charset);
00221 
00222         ///////////////////////////////////////////
00223         // loop over body-types->body hash table //
00224         ///////////////////////////////////////////
00225 
00226         ink_assert(body_set->is_sane());
00227         h2 = body_set->table_of_pages;
00228 
00229         for (b2 = h2->firstBinding(&i2); b2 != NULL; b2 = h2->nextBinding(&i2)) {
00230           k2 = table_of_sets->getKeyFromBinding(b2);
00231           v2 = table_of_sets->getValueFromBinding(b2);
00232           HttpBodyTemplate *t = (HttpBodyTemplate *) v2;
00233 
00234           fprintf(fp, "  %-30s: %" PRId64 " bytes\n", k2, t->byte_count);
00235         }
00236       }
00237     }
00238   }
00239 
00240   unlock();
00241 }
00242 
00243 
00244 ////////////////////////////////////////////////////////////////////////
00245 //
00246 // Configuration Change Callback
00247 //
00248 ////////////////////////////////////////////////////////////////////////
00249 
00250 static int
00251 config_callback(const char * /* name ATS_UNUSED */, RecDataT /* data_type ATS_UNUSED */,
00252                 RecData /* data ATS_UNUSED */, void *cookie)
00253 {
00254   HttpBodyFactory *body_factory = (HttpBodyFactory *) cookie;
00255   body_factory->reconfigure();
00256   return (0);
00257 }
00258 
00259 void
00260 HttpBodyFactory::reconfigure()
00261 //#endif
00262 {
00263   RecInt e;
00264   RecString s = NULL;
00265   bool all_found;
00266   int rec_err;
00267 
00268   lock();
00269   sanity_check();
00270 
00271   if (!callbacks_established) {
00272     unlock();
00273     return;
00274   }                             // callbacks not setup right
00275 
00276   ////////////////////////////////////////////
00277   // extract relevant records.config values //
00278   ////////////////////////////////////////////
00279 
00280   Debug("body_factory", "config variables changed, reconfiguring...");
00281 
00282   all_found = true;
00283 
00284   // enable_customizations if records.config set
00285   rec_err = RecGetRecordInt("proxy.config.body_factory.enable_customizations", &e);
00286   enable_customizations = ((rec_err == REC_ERR_OKAY) ? e : 0);
00287   all_found = all_found && (rec_err == REC_ERR_OKAY);
00288   Debug("body_factory", "enable_customizations = %d (found = %" PRId64")", enable_customizations, e);
00289 
00290   rec_err = RecGetRecordInt("proxy.config.body_factory.enable_logging", &e);
00291   enable_logging = ((rec_err == REC_ERR_OKAY) ? (e ? true : false) : false);
00292   all_found = all_found && (rec_err == REC_ERR_OKAY);
00293   Debug("body_factory", "enable_logging = %d (found = %" PRId64")", enable_logging, e);
00294 
00295   rec_err = RecGetRecordInt("proxy.config.body_factory.response_suppression_mode", &e);
00296   response_suppression_mode = ((rec_err == REC_ERR_OKAY) ? e : 0);
00297   all_found = all_found && (rec_err == REC_ERR_OKAY);
00298   Debug("body_factory", "response_suppression_mode = %d (found = %" PRId64")", response_suppression_mode, e);
00299 
00300   ats_scoped_str directory_of_template_sets;
00301 
00302   rec_err = RecGetRecordString_Xmalloc("proxy.config.body_factory.template_sets_dir", &s);
00303   all_found = all_found && (rec_err == REC_ERR_OKAY);
00304   if (rec_err == REC_ERR_OKAY) {
00305     directory_of_template_sets = Layout::get()->relative(s);
00306     if (access(directory_of_template_sets, R_OK) < 0) {
00307       Warning("Unable to access() directory '%s': %d, %s", (const char *)directory_of_template_sets, errno, strerror(errno));
00308       Warning(" Please set 'proxy.config.body_factory.template_sets_dir' ");
00309     }
00310 
00311   }
00312 
00313   Debug("body_factory", "directory_of_template_sets = '%s' (found = %s)", (const char *)directory_of_template_sets, s);
00314   ats_free(s);
00315 
00316   if (!all_found) {
00317     Warning("config changed, but can't fetch all proxy.config.body_factory values");
00318   }
00319 
00320   /////////////////////////////////////////////
00321   // clear out previous template hash tables //
00322   /////////////////////////////////////////////
00323 
00324   nuke_template_tables();
00325 
00326   /////////////////////////////////////////////////////////////
00327   // at this point, the body hash table is gone, so we start //
00328   // building a new one, by scanning the template directory. //
00329   /////////////////////////////////////////////////////////////
00330 
00331   if (directory_of_template_sets)
00332     table_of_sets = load_sets_from_directory(directory_of_template_sets);
00333 
00334   unlock();
00335 }
00336 
00337 ////////////////////////////////////////////////////////////////////////
00338 //
00339 // class HttpBodyFactory
00340 //
00341 ////////////////////////////////////////////////////////////////////////
00342 
00343 HttpBodyFactory::HttpBodyFactory()
00344 {
00345   int i;
00346   bool status, no_registrations_failed;
00347 
00348   ////////////////////////////////////
00349   // initialize first-time defaults //
00350   ////////////////////////////////////
00351 
00352   magic = HTTP_BODY_FACTORY_MAGIC;
00353   ink_mutex_init(&mutex, "HttpBodyFactory::lock");
00354 
00355   table_of_sets = NULL;
00356   enable_customizations = 0;
00357   enable_logging = true;
00358   callbacks_established = false;
00359 
00360   //////////////////////////////////////////////////////
00361   // set up management configuration-change callbacks //
00362   //////////////////////////////////////////////////////
00363 
00364   static const char *config_record_names[] = {
00365     "proxy.config.body_factory.enable_customizations",
00366     "proxy.config.body_factory.enable_logging",
00367     "proxy.config.body_factory.template_sets_dir",
00368     "proxy.config.body_factory.response_suppression_mode",
00369     NULL
00370   };
00371 
00372   no_registrations_failed = true;
00373   for (i = 0; config_record_names[i] != NULL; i++) {
00374     status = HTTP_RegisterConfigUpdateFunc(config_record_names[i], config_callback, (void *) this);
00375     if (status != REC_ERR_OKAY) {
00376       Warning("couldn't register variable '%s', is records.config up to date?", config_record_names[i]);
00377     }
00378     no_registrations_failed = no_registrations_failed && (status == REC_ERR_OKAY);
00379   }
00380 
00381   if (no_registrations_failed == false) {
00382     Warning("couldn't setup all body_factory callbacks, disabling body_factory");
00383   } else {
00384     Debug("body_factory", "all callbacks established successfully");
00385     callbacks_established = true;
00386     reconfigure();
00387   }
00388 }
00389 
00390 
00391 HttpBodyFactory::~HttpBodyFactory()
00392 {
00393   // FIX: need to implement destructor
00394   delete table_of_sets;
00395 }
00396 
00397 
00398 // LOCKING: must be called with lock taken
00399 char *
00400 HttpBodyFactory::fabricate(StrList * acpt_language_list,
00401                            StrList * acpt_charset_list,
00402                            const char *type, HttpTransact::State * context,
00403                            int64_t *buffer_length_return,
00404                            const char **content_language_return,
00405                            const char **content_charset_return, const char **set_return)
00406 {
00407   char *buffer;
00408   const char *set;
00409   HttpBodyTemplate *t;
00410   HttpBodySet *body_set;
00411 
00412   if (set_return)
00413     *set_return = "???";
00414   *content_language_return = NULL;
00415   *content_charset_return = NULL;
00416 
00417   Debug("body_factory", "calling fabricate(type '%s')", type);
00418   *buffer_length_return = 0;
00419 
00420   // if error body suppressed, return NULL
00421   if (is_response_suppressed(context)) {
00422     Debug("body_factory", "  error suppression enabled, returning NULL template");
00423     return (NULL);
00424   }
00425   // if custom error pages are disabled, return NULL
00426   if (!enable_customizations) {
00427     Debug("body_factory", "  customization disabled, returning NULL template");
00428     return (NULL);
00429   }
00430   // what set should we use (language target if enable_customizations == 2)
00431   if (enable_customizations == 2)
00432     set = determine_set_by_language(acpt_language_list, acpt_charset_list);
00433   else
00434     set = "default";
00435 
00436   if (set_return)
00437     *set_return = set;
00438 
00439   // see if we have a custom error page template
00440   t = find_template(set, type, &body_set);
00441   if (t == NULL) {
00442     Debug("body_factory", "  can't find template, returning NULL template");
00443     return (NULL);
00444   }
00445 
00446   *content_language_return = body_set->content_language;
00447   *content_charset_return = body_set->content_charset;
00448 
00449   // build the custom error page
00450   buffer = t->build_instantiated_buffer(context, buffer_length_return);
00451   return (buffer);
00452 }
00453 
00454 
00455 // LOCKING: must be called with lock taken
00456 const char *
00457 HttpBodyFactory::determine_set_by_language(StrList * acpt_language_list, StrList * acpt_charset_list)
00458 {
00459   float Q_best;
00460   const char *set_best;
00461   int La_best, Lc_best, I_best;
00462 
00463   set_best = HttpCompat::determine_set_by_language(table_of_sets, acpt_language_list, acpt_charset_list,
00464                                                    &Q_best, &La_best, &Lc_best, &I_best);
00465 
00466   return (set_best);
00467 }
00468 
00469 // LOCKING: must be called with lock taken
00470 HttpBodyTemplate *
00471 HttpBodyFactory::find_template(const char *set, const char *type, HttpBodySet ** body_set_return)
00472 {
00473   RawHashTable_Value v;
00474 
00475   Debug("body_factory", "calling find_template(%s,%s)", set, type);
00476 
00477   *body_set_return = NULL;
00478 
00479   if (table_of_sets == NULL)
00480     return (NULL);
00481   if (table_of_sets->getValue((RawHashTable_Key) set, &v)) {
00482     HttpBodySet *body_set = (HttpBodySet *) v;
00483     RawHashTable *table_of_types = body_set->table_of_pages;
00484 
00485     if (table_of_types == NULL)
00486       return (NULL);
00487 
00488     if (table_of_types->getValue((RawHashTable_Key) type, &v)) {
00489       HttpBodyTemplate *t = (HttpBodyTemplate *) v;
00490       if ((t == NULL) || (!t->is_sane()))
00491         return (NULL);
00492       *body_set_return = body_set;
00493 
00494       Debug("body_factory", "find_template(%s,%s) -> (file %s, length %" PRId64", lang '%s', charset '%s')",
00495             set, type, t->template_pathname, t->byte_count, body_set->content_language, body_set->content_charset);
00496 
00497       return (t);
00498     }
00499   }
00500   Debug("body_factory", "find_template(%s,%s) -> NULL", set, type);
00501 
00502   return (NULL);
00503 }
00504 
00505 
00506 // LOCKING: must be called with lock taken
00507 bool
00508 HttpBodyFactory::is_response_suppressed(HttpTransact::State * context)
00509 {
00510   // Since a tunnel may not always be an SSL connection,
00511   // we may want to return an error message.
00512   // Even if it's an SSL connection, it won't cause any harm
00513   // as the connection is going to be closed anyway.
00514   /*
00515      if (context->client_info.port_attribute == SERVER_PORT_BLIND_TUNNEL) {
00516      // Blind SSL tunnels always supress error messages
00517      return true;
00518      } else
00519    */
00520   if (response_suppression_mode == 0) {
00521     return (false);
00522   } else if (response_suppression_mode == 1) {
00523     return (true);
00524   } else if (response_suppression_mode == 2) {
00525     if (context->req_flavor == HttpTransact::REQ_FLAVOR_INTERCEPTED)
00526       return (true);
00527     else
00528       return (false);
00529   } else {
00530     return (false);
00531   }
00532 }
00533 
00534 
00535 // LOCKING: must be called with lock taken
00536 void
00537 HttpBodyFactory::nuke_template_tables()
00538 {
00539   RawHashTable *h1, *h2;
00540   RawHashTable_Value v1, v2;
00541   RawHashTable_Binding *b1, *b2;
00542   RawHashTable_IteratorState i1, i2;
00543   HttpBodySet *body_set;
00544   HttpBodyTemplate *hbt;
00545 
00546   h1 = table_of_sets;
00547 
00548   if (h1) {
00549     Debug("body_factory", "deleting pre-existing template tables");
00550   } else {
00551     Debug("body_factory", "no pre-existing template tables");
00552   }
00553   if (h1 != NULL) {
00554     ///////////////////////////////////////////
00555     // loop over set->body-types hash table //
00556     ///////////////////////////////////////////
00557 
00558     for (b1 = h1->firstBinding(&i1); b1 != NULL; b1 = h1->nextBinding(&i1)) {
00559       v1 = h1->getValueFromBinding(b1);
00560 
00561       body_set = (HttpBodySet *) v1;
00562       ink_assert(body_set->is_sane());
00563       h2 = body_set->table_of_pages;
00564 
00565       if (h2 != NULL) {
00566         body_set->table_of_pages = NULL;
00567 
00568         ///////////////////////////////////////////
00569         // loop over body-types->body hash table //
00570         ///////////////////////////////////////////
00571 
00572         for (b2 = h2->firstBinding(&i2); b2 != NULL; b2 = h2->nextBinding(&i2)) {
00573           v2 = h2->getValueFromBinding(b2);
00574           if (v2) {
00575             // need a cast here
00576             hbt = (HttpBodyTemplate *) v2;
00577             delete hbt;
00578           }
00579         }
00580 
00581         delete h2;
00582       }
00583 
00584       delete body_set;
00585     }
00586     delete h1;
00587   }
00588 
00589   table_of_sets = NULL;
00590 }
00591 
00592 
00593 // LOCKING: must be called with lock taken
00594 RawHashTable *
00595 HttpBodyFactory::load_sets_from_directory(char *set_dir)
00596 {
00597   DIR *dir;
00598   struct dirent *entry_buffer, *result;
00599   RawHashTable *new_table_of_sets;
00600 
00601   if (set_dir == NULL)
00602     return (NULL);
00603 
00604   Debug("body_factory", "load_sets_from_directory(%s)", set_dir);
00605 
00606   //////////////////////////////////////////////////
00607   // try to open the requested template directory //
00608   //////////////////////////////////////////////////
00609 
00610   dir = opendir(set_dir);
00611   if (dir == NULL) {
00612     Warning("can't open response template directory '%s' (%s)",
00613             set_dir, (strerror(errno) ? strerror(errno) : "unknown reason"));
00614     Warning("no response templates --- using default error pages");
00615     return (NULL);
00616   }
00617 
00618   new_table_of_sets = new RawHashTable(RawHashTable_KeyType_String);
00619   entry_buffer = (struct dirent *)ats_malloc(sizeof(struct dirent) + MAXPATHLEN + 1);
00620 
00621   //////////////////////////////////////////
00622   // loop over each language subdirectory //
00623   //////////////////////////////////////////
00624 
00625   while ((readdir_r(dir, entry_buffer, &result) == 0) && (result != NULL)) {
00626     int status;
00627     struct stat stat_buf;
00628     char subdir[MAXPATHLEN + 1];
00629 
00630     //////////////////////////////////////////////////////
00631     // ensure a subdirectory, and not starting with '.' //
00632     //////////////////////////////////////////////////////
00633 
00634     if ((entry_buffer->d_name)[0] == '.')
00635       continue;
00636     ink_filepath_make(subdir, sizeof(subdir), set_dir, entry_buffer->d_name);
00637     status = stat(subdir, &stat_buf);
00638     if (status != 0)
00639       continue;                 // can't stat
00640     if (!S_ISDIR(stat_buf.st_mode))
00641       continue;                 // not a dir
00642 
00643     ///////////////////////////////////////////////////////////
00644     // at this point, 'subdir' might be a valid template dir //
00645     ///////////////////////////////////////////////////////////
00646 
00647     HttpBodySet *body_set = load_body_set_from_directory(entry_buffer->d_name, subdir);
00648     if (body_set != NULL) {
00649       Debug("body_factory", "  %s -> %p", entry_buffer->d_name, body_set);
00650       new_table_of_sets->setValue((RawHashTable_Key) (entry_buffer->d_name), (RawHashTable_Value) body_set);
00651     }
00652   }
00653 
00654   ats_free(entry_buffer);
00655   closedir(dir);
00656 
00657   return (new_table_of_sets);
00658 }
00659 
00660 
00661 // LOCKING: must be called with lock taken
00662 HttpBodySet *
00663 HttpBodyFactory::load_body_set_from_directory(char *set_name, char *tmpl_dir)
00664 {
00665   DIR *dir;
00666   int status;
00667   struct stat stat_buf;
00668   char path[MAXPATHLEN + 1];
00669   struct dirent *entry_buffer, *result;
00670 
00671   ////////////////////////////////////////////////
00672   // ensure we can open tmpl_dir as a directory //
00673   ////////////////////////////////////////////////
00674 
00675   Debug("body_factory", "  load_body_set_from_directory(%s)", tmpl_dir);
00676   dir = opendir(tmpl_dir);
00677   if (dir == NULL)
00678     return (NULL);
00679 
00680   /////////////////////////////////////////////
00681   // ensure a .body_factory_info file exists //
00682   /////////////////////////////////////////////
00683 
00684   ink_filepath_make(path, sizeof(path), tmpl_dir, ".body_factory_info");
00685   status = stat(path, &stat_buf);
00686   if ((status < 0) || !S_ISREG(stat_buf.st_mode)) {
00687     closedir(dir);
00688     return (NULL);
00689   }
00690   Debug("body_factory", "    found '%s'", path);
00691 
00692   /////////////////////////////////////////////////////////////////
00693   // create body set, and loop over template files, loading them //
00694   /////////////////////////////////////////////////////////////////
00695 
00696   HttpBodySet *body_set = new HttpBodySet;
00697   body_set->init(set_name, tmpl_dir);
00698 
00699   Debug("body_factory", "  body_set = %p (set_name '%s', lang '%s', charset '%s')",
00700         body_set, body_set->set_name, body_set->content_language, body_set->content_charset);
00701   entry_buffer = (struct dirent *)ats_malloc(sizeof(struct dirent) + MAXPATHLEN + 1);
00702 
00703   while ((readdir_r(dir, entry_buffer, &result) == 0) && (result != NULL)) {
00704     HttpBodyTemplate *tmpl;
00705 
00706     ///////////////////////////////////////////////////////////////
00707     // all template files have name of the form <type>#<subtype> //
00708     ///////////////////////////////////////////////////////////////
00709 
00710     if ((strchr(entry_buffer->d_name, '#') == NULL) && (strcmp(entry_buffer->d_name, "default") != 0))
00711       continue;
00712 
00713     snprintf(path, sizeof(path), "%s/%s", tmpl_dir, entry_buffer->d_name);
00714     status = stat(path, &stat_buf);
00715     if (status != 0)
00716       continue;                 // can't stat
00717     if (!S_ISREG(stat_buf.st_mode))
00718       continue;                 // not a file
00719 
00720     ////////////////////////////////
00721     // read in this template file //
00722     ////////////////////////////////
00723 
00724     tmpl = new HttpBodyTemplate();
00725     if (!tmpl->load_from_file(tmpl_dir, entry_buffer->d_name)) {
00726       delete tmpl;
00727     } else {
00728       Debug("body_factory", "      %s -> %p", entry_buffer->d_name, tmpl);
00729       body_set->set_template_by_name(entry_buffer->d_name, tmpl);
00730     }
00731   }
00732   ats_free(entry_buffer);
00733   closedir(dir);
00734   return (body_set);
00735 }
00736 
00737 
00738 ////////////////////////////////////////////////////////////////////////
00739 //
00740 // class HttpBodySet
00741 //
00742 ////////////////////////////////////////////////////////////////////////
00743 
00744 HttpBodySet::HttpBodySet()
00745 {
00746   magic = HTTP_BODY_SET_MAGIC;
00747 
00748   set_name = NULL;
00749   content_language = NULL;
00750   content_charset = NULL;
00751 
00752   table_of_pages = NULL;
00753 }
00754 
00755 
00756 HttpBodySet::~HttpBodySet()
00757 {
00758   ats_free(set_name);
00759   ats_free(content_language);
00760   ats_free(content_charset);
00761   if (table_of_pages)
00762     delete table_of_pages;
00763 }
00764 
00765 
00766 int
00767 HttpBodySet::init(char *set, char *dir)
00768 {
00769   int fd, lineno, lines_added = 0;
00770   char info_path[MAXPATHLEN];
00771 
00772   char buffer[1024], name[1025], value[1024];
00773 
00774   ink_filepath_make(info_path, sizeof(info_path), dir, ".body_factory_info");
00775   fd = open(info_path, O_RDONLY | _O_ATTRIB_NORMAL);
00776   if (fd < 0)
00777     return (-1);
00778 
00779   this->set_name = ats_strdup(set);
00780 
00781   if (this->table_of_pages)
00782     delete(this->table_of_pages);
00783   this->table_of_pages = new RawHashTable(RawHashTable_KeyType_String);
00784 
00785   lineno = 0;
00786 
00787   while (1) {
00788     char *name_s, *name_e, *value_s, *value_e, *hash;
00789 
00790     ++lineno;
00791     int bytes = ink_file_fd_readline(fd, sizeof(buffer), buffer);
00792     if (bytes <= 0)
00793       break;
00794 
00795     ///////////////////////////////////////////////
00796     // chop anything on and after first '#' sign //
00797     ///////////////////////////////////////////////
00798 
00799     hash = index(buffer, '#');
00800     if (hash)
00801       *hash = '\0';
00802 
00803     ////////////////////////////////
00804     // find start and end of name //
00805     ////////////////////////////////
00806 
00807     name_s = buffer;
00808     while (*name_s && ParseRules::is_wslfcr(*name_s))
00809       ++name_s;
00810 
00811     name_e = name_s;
00812     while (*name_e && ParseRules::is_http_field_name(*name_e))
00813       ++name_e;
00814 
00815     if (name_s == name_e)
00816       continue;                 // blank line
00817 
00818     /////////////////////////////////
00819     // find start and end of value //
00820     /////////////////////////////////
00821 
00822     value_s = name_e;
00823     while (*value_s && (ParseRules::is_wslfcr(*value_s)))
00824       ++value_s;
00825     if (*value_s != ':') {
00826       Warning("ignoring invalid body factory info line #%d in %s", lineno, info_path);
00827       continue;
00828     }
00829     ++value_s;                  // skip the colon
00830     while (*value_s && (ParseRules::is_wslfcr(*value_s)))
00831       ++value_s;
00832     value_e = buffer + strlen(buffer) - 1;
00833     while ((value_e > value_s) && ParseRules::is_wslfcr(*(value_e - 1)))
00834       --value_e;
00835 
00836     /////////////////////////////////
00837     // insert line into hash table //
00838     /////////////////////////////////
00839 
00840     memcpy(name, name_s, name_e - name_s);
00841     name[name_e - name_s] = '\0';
00842 
00843     memcpy(value, value_s, value_e - value_s);
00844     value[value_e - value_s] = '\0';
00845 
00846     //////////////////////////////////////////////////
00847     // so far, we only support 2 pieces of metadata //
00848     //////////////////////////////////////////////////
00849 
00850     if (strcasecmp(name, "Content-Language") == 0) {
00851       ats_free(this->content_language);
00852       this->content_language = ats_strdup(value);
00853     } else if (strcasecmp(name, "Content-Charset") == 0) {
00854       ats_free(this->content_charset);
00855       this->content_charset = ats_strdup(value);
00856     }
00857   }
00858 
00859   ////////////////////////////////////////////////////
00860   // fill in default language & charset, if not set //
00861   ////////////////////////////////////////////////////
00862 
00863   if (!this->content_language) {
00864     if (strcmp(set, "default") == 0)
00865       this->content_language = ats_strdup("en");
00866     else
00867       this->content_language = ats_strdup(set);
00868   }
00869   if (!this->content_charset)
00870     this->content_charset = ats_strdup("utf-8");
00871 
00872   close(fd);
00873   return (lines_added);
00874 }
00875 
00876 
00877 HttpBodyTemplate *
00878 HttpBodySet::get_template_by_name(const char *name)
00879 {
00880   RawHashTable_Value v;
00881 
00882   Debug("body_factory", "    calling get_template_by_name(%s)", name);
00883 
00884   if (table_of_pages == NULL)
00885     return (NULL);
00886 
00887   if (table_of_pages->getValue((RawHashTable_Key) name, &v)) {
00888     HttpBodyTemplate *t = (HttpBodyTemplate *) v;
00889     if ((t == NULL) || (!t->is_sane()))
00890       return (NULL);
00891     Debug("body_factory", "    get_template_by_name(%s) -> (file %s, length %" PRId64")",
00892           name, t->template_pathname, t->byte_count);
00893     return (t);
00894   }
00895   Debug("body_factory", "    get_template_by_name(%s) -> NULL", name);
00896   return (NULL);
00897 }
00898 
00899 
00900 void
00901 HttpBodySet::set_template_by_name(const char *name, HttpBodyTemplate * t)
00902 {
00903   table_of_pages->setValue((RawHashTable_Key) name, (RawHashTable_Value) t);
00904 }
00905 
00906 ////////////////////////////////////////////////////////////////////////
00907 //
00908 // class HttpBodyTemplate
00909 //
00910 ////////////////////////////////////////////////////////////////////////
00911 
00912 HttpBodyTemplate::HttpBodyTemplate()
00913 {
00914   magic = HTTP_BODY_TEMPLATE_MAGIC;
00915   byte_count = 0;
00916   template_buffer = NULL;
00917   template_pathname = NULL;
00918 }
00919 
00920 
00921 HttpBodyTemplate::~HttpBodyTemplate()
00922 {
00923   reset();
00924 }
00925 
00926 
00927 void
00928 HttpBodyTemplate::reset()
00929 {
00930   ats_free(template_buffer);
00931   template_buffer = NULL;
00932   byte_count = 0;
00933   ats_free(template_pathname);
00934 }
00935 
00936 
00937 int
00938 HttpBodyTemplate::load_from_file(char *dir, char *file)
00939 {
00940   int fd, status;
00941   int64_t bytes_read;
00942   struct stat stat_buf;
00943   char path[MAXPATHLEN + 1];
00944   char *new_template_buffer;
00945   int64_t new_byte_count;
00946 
00947   ////////////////////////////////////
00948   // ensure this is actually a file //
00949   ////////////////////////////////////
00950 
00951   snprintf(path, sizeof(path), "%s/%s", dir, file);
00952   // coverity[fs_check_call]
00953   status = stat(path, &stat_buf);
00954   if (status != 0)
00955     return (0);
00956   if (!S_ISREG(stat_buf.st_mode))
00957     return (0);
00958 
00959   ///////////////////
00960   // open the file //
00961   ///////////////////
00962 
00963   // coverity[toctou]
00964   fd = open(path, O_RDONLY | _O_ATTRIB_NORMAL);
00965   if (fd < 0)
00966     return (0);
00967 
00968   ////////////////////////////////////////
00969   // read in the template file contents //
00970   ////////////////////////////////////////
00971 
00972   new_byte_count = stat_buf.st_size;
00973   new_template_buffer = (char *)ats_malloc(new_byte_count + 1);
00974   bytes_read = read(fd, new_template_buffer, new_byte_count);
00975   new_template_buffer[new_byte_count] = '\0';
00976   close(fd);
00977 
00978   ///////////////////////////
00979   // check for read errors //
00980   ///////////////////////////
00981 
00982   if (bytes_read != new_byte_count) {
00983     Warning("reading template file '%s', got %" PRId64" bytes instead of %" PRId64" (%s)",
00984             path, bytes_read, new_byte_count, (strerror(errno) ? strerror(errno) : "unknown error"));
00985     ats_free(new_template_buffer);
00986     return (0);
00987   }
00988 
00989   Debug("body_factory", "    read %" PRId64" bytes from '%s'", new_byte_count, path);
00990 
00991   /////////////////////////////////
00992   // actually commit the changes //
00993   /////////////////////////////////
00994 
00995   reset();
00996   template_buffer = new_template_buffer;
00997   byte_count = new_byte_count;
00998   template_pathname = ats_strdup(path);
00999 
01000   return (1);
01001 }
01002 
01003 char *
01004 HttpBodyTemplate::build_instantiated_buffer(HttpTransact::State * context, int64_t *buflen_return)
01005 {
01006   char *buffer = NULL;
01007 
01008   Debug("body_factory_instantiation", "    before instantiation: [%s]", template_buffer);
01009 
01010   LogAccessHttp la(context->state_machine);
01011 
01012   // TODO: Should we check the return code from Log::access() ?
01013   Log::access(&la);
01014   buffer = resolve_logfield_string((LogAccess *) & la, template_buffer);
01015 
01016   *buflen_return = ((buffer == NULL) ? 0 : strlen(buffer));
01017   Debug("body_factory_instantiation", "    after instantiation: [%s]", buffer);
01018   Debug("body_factory", "  returning %" PRId64" byte instantiated buffer", *buflen_return);
01019 
01020   return (buffer);
01021 }

Generated by  doxygen 1.7.1