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

ControlBase.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  *  ControlBase.cc - Base class to process generic modifiers to
00027  *                         ControlMatcher Directives
00028  *
00029  *
00030  ****************************************************************************/
00031 #include "ink_platform.h"
00032 #include "ink_defs.h"
00033 #include "ink_time.h"
00034 
00035 #include "Main.h"
00036 #include "URL.h"
00037 #include "Tokenizer.h"
00038 #include "ControlBase.h"
00039 #include "MatcherUtils.h"
00040 #include "HTTP.h"
00041 #include "ControlMatcher.h"
00042 #include "HdrUtils.h"
00043 #include "Vec.h"
00044 
00045 #include <ts/TsBuffer.h>
00046 
00047 /** Used for printing IP address.
00048     @code
00049     uint32_t addr; // IP address.
00050     printf("IP address = " TS_IP_PRINTF_CODE,TS_IP_OCTETS(addr));
00051     @endcode
00052     @internal Need to move these to a common header.
00053  */
00054 # define TS_IP_OCTETS(x) \
00055   reinterpret_cast<unsigned char const*>(&(x))[0],   \
00056     reinterpret_cast<unsigned char const*>(&(x))[1], \
00057     reinterpret_cast<unsigned char const*>(&(x))[2], \
00058     reinterpret_cast<unsigned char const*>(&(x))[3]
00059 
00060 // ----------
00061 ControlBase::Modifier::~Modifier() {}
00062 ControlBase::Modifier::Type ControlBase::Modifier::type() const { return MOD_INVALID; }
00063 // --------------------------
00064 namespace {
00065 // ----------
00066 struct TimeMod : public ControlBase::Modifier {
00067   time_t start_time;
00068   time_t end_time;
00069 
00070   static char const * const NAME;
00071 
00072   virtual Type type() const;
00073   virtual char const * name() const;
00074   virtual bool check(HttpRequestData* req) const;
00075   virtual void print(FILE* f) const;
00076   static TimeMod* make(char * value, char const ** error);
00077   static const char* timeOfDayToSeconds(const char *time_str, time_t * seconds);
00078 };
00079 
00080 char const * const TimeMod::NAME = "Time";
00081 ControlBase::Modifier::Type TimeMod::type() const { return MOD_TIME; }
00082 char const * TimeMod::name() const { return NAME; }
00083 
00084 void TimeMod::print(FILE* f) const {
00085   fprintf(f, "%s=%ld-%ld  ",
00086     // Have to cast because time_t can be 32 or 64 bits and the compiler
00087     // will barf if format code doesn't match.
00088     this->name(), static_cast<long>(start_time), static_cast<long>(end_time)
00089   );
00090 }
00091 bool TimeMod::check(HttpRequestData* req) const {
00092   struct tm cur_time;
00093   time_t timeOfDay = req->xact_start;
00094   // Use this to account for daylight savings time.
00095   ink_localtime_r(&timeOfDay, &cur_time);
00096   timeOfDay = cur_time.tm_hour*(60 * 60) + cur_time.tm_min*60 + cur_time.tm_sec;
00097   return start_time <= timeOfDay && timeOfDay <= end_time;
00098 }
00099 
00100 TimeMod*
00101 TimeMod::make(char * value, char const ** error) {
00102   Tokenizer rangeTok("-");
00103   TimeMod* mod = 0;
00104   TimeMod tmp;
00105   int num_tok;
00106 
00107   num_tok = rangeTok.Initialize(value, SHARE_TOKS);
00108   if (num_tok == 1) {
00109     *error = "End time not specified";
00110   } else if (num_tok > 2) {
00111     *error = "Malformed time range";
00112   } else if (
00113     0 == (*error = timeOfDayToSeconds(rangeTok[0], &tmp.start_time))
00114     && 0 == (*error = timeOfDayToSeconds(rangeTok[1], &tmp.end_time))
00115   ) {
00116     mod = new TimeMod(tmp);
00117   }
00118   return mod;
00119 }
00120 /**   Converts TimeOfDay (TOD) to second value.
00121       @a *seconds is set to number of seconds since midnight
00122       represented by @a time_str.
00123 
00124       @return 0 on success, static error string on failure.
00125 */
00126 const char *
00127 TimeMod::timeOfDayToSeconds(const char *time_str, time_t * seconds) {
00128   int hour = 0;
00129   int min = 0;
00130   int sec = 0;
00131   time_t tmp = 0;
00132 
00133   // coverity[secure_coding]
00134   if (sscanf(time_str, "%d:%d:%d", &hour, &min, &sec) != 3) {
00135     // coverity[secure_coding]
00136     if (sscanf(time_str, "%d:%d", &hour, &min) != 2) {
00137       return "Malformed time specified";
00138     }
00139   }
00140 
00141   if (!(hour >= 0 && hour <= 23)) return "Illegal hour specification";
00142 
00143   tmp = hour * 60;
00144 
00145   if (!(min >= 0 && min <= 59)) return "Illegal minute specification";
00146 
00147   tmp = (tmp + min) * 60;
00148 
00149   if (!(sec >= 0 && sec <= 59)) return "Illegal second specification";
00150 
00151   tmp += sec;
00152 
00153   *seconds = tmp;
00154   return 0;
00155 }
00156 
00157 // ----------
00158 struct PortMod : public ControlBase::Modifier {
00159   int start_port;
00160   int end_port;
00161 
00162   static char const * const NAME;
00163 
00164   virtual char const * name() const;
00165   virtual bool check(HttpRequestData* req) const;
00166   virtual void print(FILE* f) const;
00167 
00168   static PortMod* make(char* value, char const **error);
00169 };
00170 
00171 char const * const PortMod::NAME = "Port";
00172 char const * PortMod::name() const { return NAME; }
00173 
00174 void PortMod::print(FILE* f) const {
00175   fprintf(f, "%s=%d-%d  ", this->name(), start_port, end_port);
00176 }
00177 
00178 bool PortMod::check(HttpRequestData* req) const {
00179   int port = req->hdr->port_get();
00180   return start_port <= port && port <= end_port;
00181 }
00182 
00183 PortMod*
00184 PortMod::make(char* value, char const ** error) {
00185   Tokenizer rangeTok("-");
00186   PortMod tmp;
00187   int num_tok = rangeTok.Initialize(value, SHARE_TOKS);
00188 
00189   *error = 0;
00190   if (num_tok > 2) {
00191     *error = "Malformed Range";
00192     // coverity[secure_coding]
00193   } else if (sscanf(rangeTok[0], "%d", &tmp.start_port) != 1) {
00194     *error = "Invalid start port";
00195   } else if (num_tok == 2) {
00196     // coverity[secure_coding]
00197     if (sscanf(rangeTok[1], "%d", &tmp.end_port) != 1)
00198       *error = "Invalid end port";
00199     else if (tmp.end_port < tmp.start_port)
00200       *error = "Malformed Range: end port < start port";
00201   } else {
00202     tmp.end_port = tmp.start_port;
00203   }
00204 
00205   // If there's an error message, return null.
00206   // Otherwise create a new item and return it.
00207   return *error ? 0 : new PortMod(tmp);
00208 }
00209 
00210 // ----------
00211 struct IPortMod : public ControlBase::Modifier {
00212   int _port;
00213 
00214   static char const * const NAME;
00215 
00216   IPortMod(int port);
00217 
00218   virtual char const * name() const;
00219   virtual bool check(HttpRequestData* req) const;
00220   virtual void print(FILE* f) const;
00221   static IPortMod* make(char* value, char const ** error);
00222 };
00223 
00224 char const * const IPortMod::NAME = "IPort";
00225 IPortMod::IPortMod(int port) : _port(port) {}
00226 char const * IPortMod::name() const { return NAME; }
00227 
00228 void IPortMod::print(FILE* f) const {
00229   fprintf(f, "%s=%d  ", this->name(), _port);
00230 }
00231 bool IPortMod::check(HttpRequestData* req) const {
00232   return req->incoming_port == _port;
00233 }
00234 
00235 IPortMod*
00236 IPortMod::make(char* value, char const ** error) {
00237   IPortMod* zret = 0;
00238   int port;
00239   // coverity[secure_coding]
00240   if (sscanf(value, "%u", &port) == 1) {
00241     zret = new IPortMod(port);
00242   } else {
00243     *error = "Invalid incoming port";
00244   }
00245   return zret;
00246 }
00247 // ----------
00248 struct SrcIPMod : public ControlBase::Modifier {
00249   // Stored in host order because that's how they are compared.
00250   IpEndpoint start_addr; ///< Start address in HOST order.
00251   IpEndpoint end_addr; ///< End address in HOST order.
00252 
00253   static char const * const NAME;
00254 
00255   virtual Type type() const;
00256   virtual char const * name() const;
00257   virtual bool check(HttpRequestData* req) const;
00258   virtual void print(FILE* f) const;
00259   static SrcIPMod* make(char * value, char const ** error);
00260 };
00261 
00262 char const * const SrcIPMod::NAME = "SrcIP";
00263 ControlBase::Modifier::Type SrcIPMod::type() const { return MOD_SRC_IP; }
00264 char const * SrcIPMod::name() const { return NAME; }
00265 
00266 void SrcIPMod::print(FILE* f) const {
00267   ip_text_buffer b1, b2;
00268   fprintf(f, "%s=%s-%s  "
00269     ,this->name()
00270     , ats_ip_ntop(&start_addr.sa, b1, sizeof(b1))
00271     , ats_ip_ntop(&end_addr.sa, b2, sizeof(b2))
00272   );
00273 }
00274 bool SrcIPMod::check(HttpRequestData* req) const {
00275   // Compare in host order
00276   return ats_ip_addr_cmp(&start_addr, &req->src_ip) <= 0
00277     && ats_ip_addr_cmp(&req->src_ip, &end_addr) <= 0
00278     ;
00279 }
00280 SrcIPMod*
00281 SrcIPMod::make(char * value, char const ** error ) {
00282   SrcIPMod tmp;
00283   SrcIPMod* zret = 0;
00284   *error = ExtractIpRange(value, &tmp.start_addr.sa, &tmp.end_addr.sa);
00285 
00286   if (!*error) zret = new SrcIPMod(tmp);
00287   return zret;
00288 }
00289 // ----------
00290 struct SchemeMod : public ControlBase::Modifier {
00291   int _scheme; ///< Tokenized scheme.
00292 
00293   static char const * const NAME;
00294 
00295   SchemeMod(int scheme);
00296 
00297   virtual Type type() const;
00298   virtual char const * name() const;
00299   virtual bool check(HttpRequestData* req) const;
00300   virtual void print(FILE* f) const;
00301 
00302   char const* getWksText() const;
00303 
00304   static SchemeMod* make(char * value, char const ** error);
00305 };
00306 
00307 char const * const SchemeMod::NAME = "Scheme";
00308 
00309 SchemeMod::SchemeMod(int scheme) : _scheme(scheme) {}
00310 
00311 ControlBase::Modifier::Type SchemeMod::type() const { return MOD_SCHEME; }
00312 char const * SchemeMod::name() const { return NAME; }
00313 char const *
00314 SchemeMod::getWksText() const {
00315   return hdrtoken_index_to_wks(_scheme);
00316 }
00317 
00318 bool SchemeMod::check(HttpRequestData* req) const {
00319   return req->hdr->url_get()->scheme_get_wksidx() == _scheme;
00320 }
00321 void SchemeMod::print(FILE* f) const {
00322   fprintf(f, "%s=%s  ", this->name(), hdrtoken_index_to_wks(_scheme));
00323 }
00324 SchemeMod*
00325 SchemeMod::make(char * value, char const ** error) {
00326   SchemeMod* zret = 0;
00327   int scheme = hdrtoken_tokenize(value, strlen(value));
00328   if (scheme < 0) {
00329     *error = "Unknown scheme";
00330   } else {
00331     zret = new SchemeMod(scheme);
00332   }
00333   return zret;
00334 }
00335 
00336 // ----------
00337 // This is a base class for all of the mods that have a
00338 // text string.
00339 struct TextMod : public ControlBase::Modifier {
00340   ts::Buffer text;
00341 
00342   TextMod();
00343   ~TextMod();
00344 
00345   // Calls name() which the subclass must provide.
00346   virtual void print(FILE* f) const;
00347 
00348   // Copy the given NUL-terminated string to the text buffer.
00349   void set(const char * value);
00350 
00351 };
00352 
00353 TextMod::TextMod() : text() {}
00354 TextMod::~TextMod() {
00355   free(text.data());
00356 }
00357 
00358 void TextMod::print(FILE* f) const {
00359   fprintf(f, "%s=%*s  ", this->name(), static_cast<int>(text.size()), text.data());
00360 }
00361 
00362 void TextMod::set(const char * value) {
00363   free(this->text.data());
00364   this->text.set(ats_strdup(value), strlen(value));
00365 }
00366 
00367 struct MultiTextMod : public ControlBase::Modifier {
00368   Vec<ts::Buffer> text_vec;
00369   MultiTextMod();
00370   ~MultiTextMod();
00371 
00372   // Copy the value to the MultiTextMod buffer.
00373   void set(char * value);
00374 
00375   // Calls name() which the subclass must provide.
00376   virtual void print(FILE* f) const;
00377 };
00378 
00379 MultiTextMod::MultiTextMod() {}
00380 MultiTextMod::~MultiTextMod() {
00381   text_vec.clear();
00382 }
00383 
00384 void MultiTextMod::print(FILE* f) const {
00385   for_Vec(ts::Buffer, text_iter, this->text_vec)
00386     fprintf(f, "%s=%*s ", this->name(),static_cast<int>(text_iter.size()),text_iter.data());
00387 }
00388 
00389 void MultiTextMod::set(char * value) {
00390   Tokenizer rangeTok(",");
00391   int num_tok = rangeTok.Initialize(value, SHARE_TOKS);
00392   for(int i = 0; i < num_tok; i++){
00393     ts::Buffer text;
00394     text.set(ats_strdup(rangeTok[i]), strlen(rangeTok[i]));
00395     this->text_vec.push_back(text);
00396   }
00397 }
00398 
00399 // ----------
00400 struct MethodMod : public TextMod {
00401   static char const * const NAME;
00402 
00403   virtual Type type() const;
00404   virtual char const * name() const;
00405   virtual bool check(HttpRequestData* req) const;
00406 
00407   static MethodMod* make(char * value, char const ** error);
00408 };
00409 char const * const MethodMod::NAME = "Method";
00410 ControlBase::Modifier::Type MethodMod::type() const { return MOD_METHOD; }
00411 char const * MethodMod::name() const { return NAME; }
00412 bool MethodMod::check(HttpRequestData* req) const {
00413   int method_len;
00414   char const* method = req->hdr->method_get(&method_len);
00415   return method_len >= static_cast<int>(text.size())
00416     && 0 == strncasecmp(method, text.data(), text.size())
00417     ;
00418 }
00419 MethodMod*
00420 MethodMod::make(char * value, char const **) {
00421   MethodMod* mod = new MethodMod();
00422   mod->set(value);
00423   return mod;
00424 }
00425 
00426 // ----------
00427 struct PrefixMod : public TextMod {
00428   static char const * const NAME;
00429 
00430   virtual Type type() const;
00431   virtual char const * name() const;
00432   virtual bool check(HttpRequestData* req) const;
00433   static PrefixMod* make(char * value, char const ** error);
00434 };
00435 
00436 char const * const PrefixMod::NAME = "Prefix";
00437 ControlBase::Modifier::Type PrefixMod::type() const { return MOD_PREFIX; }
00438 char const * PrefixMod::name() const { return NAME; }
00439 bool PrefixMod::check(HttpRequestData* req) const {
00440   int path_len;
00441   char const* path = req->hdr->url_get()->path_get(&path_len);
00442   bool zret = path_len >= static_cast<int>(text.size())
00443     && 0 == memcmp(path, text.data(), text.size())
00444     ;
00445 /*
00446   Debug("cache_control", "Prefix check: URL=%0.*s Mod=%0.*s Z=%s",
00447     path_len, path, text.size(), text.data(),
00448     zret ? "Match" : "Fail"
00449   );
00450 */
00451   return zret;
00452 }
00453 PrefixMod*
00454 PrefixMod::make(char * value, char const ** /* error ATS_UNUSED */) {
00455   PrefixMod* mod = new PrefixMod();
00456   // strip leading slashes because get_path which is used later
00457   // doesn't include them from the URL.
00458   while ('/' == *value) ++value;
00459   mod->set(value);
00460   return mod;
00461 }
00462 
00463 // ----------
00464 struct SuffixMod : public MultiTextMod {
00465   static char const * const NAME;
00466 
00467   virtual Type type() const;
00468   virtual char const * name() const;
00469   virtual bool check(HttpRequestData* req) const;
00470   static SuffixMod* make(char * value, char const ** error);
00471 };
00472 char const * const SuffixMod::NAME = "Suffix";
00473 ControlBase::Modifier::Type SuffixMod::type() const { return MOD_SUFFIX; }
00474 char const * SuffixMod::name() const { return NAME; }
00475 bool SuffixMod::check(HttpRequestData* req) const {
00476   int path_len;
00477   char const* path = req->hdr->url_get()->path_get(&path_len);
00478   if(1 == static_cast<int>(this->text_vec.count()) && 1 == static_cast<int>(this->text_vec[0].size()) && 0 == strcmp(this->text_vec[0].data(),"*"))
00479     return true;
00480   for_Vec(ts::Buffer, text_iter, this->text_vec){
00481     if (path_len >= static_cast<int>(text_iter.size()) && 0 == strncasecmp(path + path_len - text_iter.size(), text_iter.data(), text_iter.size()))
00482       return true;
00483   }
00484   return false;
00485 }
00486 SuffixMod*
00487 SuffixMod::make(char * value, char const ** /* error ATS_UNUSED */) {
00488   SuffixMod* mod = new SuffixMod();
00489   mod->set(value);
00490   return mod;
00491 }
00492 
00493 // ----------
00494 struct TagMod : public TextMod {
00495   static char const * const NAME;
00496 
00497   virtual Type type() const;
00498   virtual char const * name() const;
00499   virtual bool check(HttpRequestData* req) const;
00500   static TagMod* make(char * value, char const ** error);
00501 };
00502 char const * const TagMod::NAME = "Tag";
00503 ControlBase::Modifier::Type TagMod::type() const { return MOD_TAG; }
00504 char const * TagMod::name() const { return NAME; }
00505 bool TagMod::check(HttpRequestData* req) const {
00506   return 0 == strcmp(req->tag, text.data());
00507 }
00508 TagMod*
00509 TagMod::make(char * value, char const ** /* error ATS_UNUSED */) {
00510   TagMod* mod = new TagMod();
00511   mod->set(value);
00512   return mod;
00513 }
00514 
00515 // ----------
00516 } // anon name space
00517 // ------------------------------------------------
00518 ControlBase::~ControlBase() {
00519   this->clear();
00520 }
00521 
00522 void
00523 ControlBase::clear() {
00524   _mods.delete_and_clear();
00525 }
00526 
00527 // static const modifier_el default_el = { MOD_INVALID, NULL };
00528 
00529 void
00530 ControlBase::Print() {
00531   int n = _mods.length();
00532 
00533   if (0 >= n) return;
00534 
00535   printf("\t\t\t");
00536   for (intptr_t i = 0; i < n; ++i) {
00537     Modifier* cur_mod = _mods[i];
00538     if (!cur_mod) printf("INVALID  ");
00539     else cur_mod->print(stdout);
00540   }
00541   printf("\n");
00542 }
00543 
00544 char const *
00545 ControlBase::getSchemeModText() const {
00546   char const* zret = 0;
00547   Modifier* mod = this->findModOfType(Modifier::MOD_SCHEME);
00548   if (mod) zret = static_cast<SchemeMod*>(mod)->getWksText();
00549   return zret;
00550 }
00551 
00552 bool
00553 ControlBase::CheckModifiers(HttpRequestData * request_data) {
00554   if (!request_data->hdr) {
00555     //we use the same request_data for Socks as well (only IpMatcher)
00556     //we just return false here
00557     return true;
00558   }
00559 
00560   // If the incoming request has no tag but the entry does, or both
00561   // have tags that do not match, then we do NOT have a match.
00562   if (!request_data->tag && findModOfType(Modifier::MOD_TAG))
00563     return false;
00564 
00565   forv_Vec(Modifier, cur_mod, _mods)
00566     if (cur_mod && ! cur_mod->check(request_data)) return false;
00567 
00568   return true;
00569 }
00570 
00571 enum mod_errors {
00572   ME_UNKNOWN,
00573   ME_PARSE_FAILED,
00574   ME_BAD_MOD,
00575   ME_CALLEE_GENERATED
00576 };
00577 
00578 static const char *errorFormats[] = {
00579   "Unknown error parsing modifier",
00580   "Unable to parse modifier",
00581   "Unknown modifier",
00582   "Callee Generated",
00583 };
00584 
00585 ControlBase::Modifier*
00586 ControlBase::findModOfType(Modifier::Type t) const {
00587   forv_Vec(Modifier, m, _mods)
00588     if (m && t == m->type()) return m;
00589   return 0;
00590 }
00591 
00592 const char *
00593 ControlBase::ProcessModifiers(matcher_line * line_info) {
00594   // Variables for error processing
00595   const char *errBuf = NULL;
00596   mod_errors err = ME_UNKNOWN;
00597 
00598   int n_elts = line_info->num_el; // Element count for line.
00599 
00600   // No elements -> no modifiers.
00601   if (0 >= n_elts) return 0;
00602   // Can't have more modifiers than elements, so reasonable upper bound.
00603   _mods.clear();
00604   _mods.reserve(n_elts);
00605 
00606   // As elements are consumed, the labels are nulled out and the element
00607   // count decremented. So we have to scan the entire array to be sure of
00608   // finding all the elements. We'll track the element count so we can
00609   // escape if we've found all of the elements.
00610   for (int i = 0; n_elts && ME_UNKNOWN == err && i < MATCHER_MAX_TOKENS; ++i) {
00611     Modifier* mod = 0;
00612 
00613     char * label = line_info->line[0][i];
00614     char * value = line_info->line[1][i];
00615 
00616     if (!label) continue; // Already use.
00617     if (!value) {
00618       err = ME_PARSE_FAILED;
00619       break;
00620     }
00621 
00622     if (strcasecmp(label, "port") == 0) {
00623       mod = PortMod::make(value, &errBuf);
00624     } else if (strcasecmp(label, "iport") == 0) {
00625       mod = IPortMod::make(value, &errBuf);
00626     } else if (strcasecmp(label, "scheme") == 0) {
00627       mod = SchemeMod::make(value, &errBuf);
00628     } else if (strcasecmp(label, "method") == 0) {
00629       mod = MethodMod::make(value, &errBuf);
00630     } else if (strcasecmp(label, "prefix") == 0) {
00631       mod = PrefixMod::make(value, &errBuf);
00632     } else if (strcasecmp(label, "suffix") == 0) {
00633       mod = SuffixMod::make(value, &errBuf);
00634     } else if (strcasecmp(label, "src_ip") == 0) {
00635       mod = SrcIPMod::make(value, &errBuf);
00636     } else if (strcasecmp(label, "time") == 0) {
00637       mod = TimeMod::make(value, &errBuf);
00638     } else if (strcasecmp(label, "tag") == 0) {
00639       mod = TagMod::make(value, &errBuf);
00640     } else {
00641       err = ME_BAD_MOD;
00642     }
00643 
00644     if (errBuf) err = ME_CALLEE_GENERATED; // Mod make failed.
00645 
00646     // If nothing went wrong, add the mod and bump the element count.
00647     if (ME_UNKNOWN == err) {
00648       _mods.push_back(mod);
00649       --n_elts;
00650     } else {
00651       delete mod; // we still need to clean up because we didn't transfer ownership.
00652     }
00653   }
00654 
00655   if (err != ME_UNKNOWN) {
00656     this->clear();
00657     if (err != ME_CALLEE_GENERATED) {
00658       errBuf = errorFormats[err];
00659     }
00660   }
00661 
00662   return errBuf;
00663 }

Generated by  doxygen 1.7.1