nis-util  1.0.D108
lib/space/protocols.cc
Go to the documentation of this file.
00001 //
00002 // nis-util - NIS Administration Utilities
00003 // Copyright (C) 2002, 2008, 2009, 2011, 2012 Peter Miller
00004 //
00005 // This program is free software; you can redistribute it and/or modify
00006 // it under the terms of the GNU General Public License as published by
00007 // the Free Software Foundation; either version 2 of the License, or (at
00008 // your option) any later version.
00009 //
00010 // This program is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 // General Public License for more details.
00014 //
00015 // You should have received a copy of the GNU General Public License
00016 // along with this program. If not, see <http://www.gnu.org/licenses/>.
00017 //
00018 
00019 #include <lib/rcstring/accumulator.h>
00020 #include <lib/space/protocols.h>
00021 
00022 
00023 space_protocols::~space_protocols()
00024 {
00025     discard_comments();
00026     discard_blank_lines();
00027 }
00028 
00029 
00030 space_protocols::space_protocols(const rcstring &a_filename) :
00031     space(a_filename),
00032     last_num(-1)
00033 {
00034     discard_comments();
00035     discard_blank_lines();
00036 }
00037 
00038 
00039 space_protocols::pointer
00040 space_protocols::create(const rcstring &a_filename)
00041 {
00042     return pointer(new space_protocols(a_filename));
00043 }
00044 
00045 
00046 space_protocols::record::pointer
00047 space_protocols::get(void)
00048 {
00049     for (;;)
00050     {
00051         source_location line_locn;
00052         line_t line;
00053         if (!read_one_line(line_locn, line))
00054             return record::pointer();
00055         if (line.size() < 2)
00056         {
00057             error
00058             (
00059                 line_locn,
00060                 "too few fields (expected 2, given %d)",
00061                 (int)line.size()
00062             );
00063             continue;
00064         }
00065 
00066         //
00067         // The first field is the name.
00068         //
00069         rcstring rec_name = line[0];
00070         rcstring name_suggestion;
00071         // NB: primary key is not allowed to contain upper case
00072         if (!is_ok_name(rec_name, false, name_suggestion))
00073         {
00074             error
00075             (
00076                 line_locn,
00077                 "protocol name %s not valid, suggest %s instead",
00078                 rec_name.quote_c().c_str(),
00079                 name_suggestion.quote_c().c_str()
00080             );
00081             rec_name = name_suggestion;
00082         }
00083 
00084         //
00085         // The second field is the number.
00086         //
00087         rcstring rec_number = line[1];
00088         long rec_number_binary = 0;
00089         if (!rec_number.to_long(rec_number_binary))
00090         {
00091             error
00092             (
00093                 line_locn,
00094                 "protocol number %s not valid",
00095                 rec_number.quote_c().c_str()
00096             );
00097             continue;
00098         }
00099         if (rec_number_binary < 0 || rec_number_binary >= 256)
00100         {
00101             error
00102             (
00103                 line_locn,
00104                 "protocol number %ld out of range (0..255)",
00105                 rec_number_binary
00106             );
00107             continue;
00108         }
00109         // normalise the number
00110         rec_number = rcstring::format("%ld", rec_number_binary);
00111 
00112         //
00113         // Check record sequencing.
00114         //
00115         if (rec_number_binary < last_num)
00116         {
00117             error
00118             (
00119                 line_locn,
00120                 "protocol number %ld out of order",
00121                 rec_number_binary
00122             );
00123             explain_sequence_errors();
00124         }
00125         last_num = rec_number_binary;
00126 
00127         //
00128         // The third and subsequent arguments are aliases.
00129         //
00130         record::aliases_t rec_aliases;
00131         for
00132         (
00133             line_t::const_iterator it = line.begin() + 2;
00134             it != line.end();
00135             ++it
00136         )
00137         {
00138             rcstring alias_name = *it;
00139             // NB: aliases are permitted to contain upper case
00140             if (!is_ok_name(alias_name, true, name_suggestion))
00141             {
00142                 error
00143                 (
00144                     line_locn,
00145                     "protocol alias %s not valid, suggest %s instead",
00146                     alias_name.quote_c().c_str(),
00147                     name_suggestion.quote_c().c_str()
00148                 );
00149                 alias_name = name_suggestion;
00150             }
00151             rec_aliases.push_back(alias_name);
00152         }
00153 
00154         //
00155         // Create a new record.
00156         //
00157         record::pointer rp =
00158             record::create(line_locn, rec_name, rec_number, rec_aliases);
00159 
00160         //
00161         // Check line length.
00162         //
00163         if (rp->representation().size() >= 512)
00164         {
00165             error
00166             (
00167                 rp->get_source_location(),
00168                 "line too long (NIS limits records to 511 bytes)"
00169             );
00170         }
00171 
00172         //
00173         // Return the new record to the caller.
00174         //
00175         return rp;
00176     }
00177 }
00178 
00179 
00180 space_protocols::record::~record()
00181 {
00182 }
00183 
00184 
00185 space_protocols::record::record(
00186     const source_location &a_locn,
00187     const rcstring &a_name,
00188     const rcstring &a_number,
00189     const aliases_t &a_aliases
00190 ) :
00191     locn(a_locn),
00192     name(a_name),
00193     number(a_number),
00194     aliases(a_aliases)
00195 {
00196 }
00197 
00198 
00199 space_protocols::record::pointer
00200 space_protocols::record::create(const source_location &a_locn,
00201     const rcstring &a_name, const rcstring &a_number,
00202     const aliases_t &a_aliases)
00203 {
00204     return pointer(new record(a_locn, a_name, a_number, a_aliases));
00205 }
00206 
00207 
00208 int
00209 space_protocols::record::get_number_binary(void)
00210     const
00211 {
00212     long value = 0;
00213     if (!number.to_long(value))
00214         return -1;
00215     return value;
00216 }
00217 
00218 
00219 rcstring
00220 space_protocols::record::representation(void)
00221     const
00222 {
00223     rcstring_accumulator acc;
00224     acc.push_back(name);
00225     acc.push_back(' ');
00226     acc.push_back(number);
00227     for
00228     (
00229         aliases_t::const_iterator it = aliases.begin();
00230         it != aliases.end();
00231         ++it
00232     )
00233     {
00234         acc.push_back(' ');
00235         acc.push_back(*it);
00236     }
00237     return acc.mkstr();
00238 }
00239 
00240 
00241 bool
00242 space_protocols::is_ok_name(const rcstring &text, bool upper_case_ok,
00243     rcstring &suggest)
00244 {
00245     if (text.empty())
00246     {
00247         suggest = "a";
00248         return false;
00249     }
00250 
00251     //
00252     // See http://www.iana.org/assignments/protocol-numbers for assigned
00253     // numbers.
00254     //
00255     // According to http://www.iana.org/assignments/service-names
00256     // "A protocol name may be up to 40 characters taken from the set of
00257     // upper case letters, digits, and the punctuation character hyphen.
00258     // It must start with a letter, and end with a letter or digit."
00259     //
00260     enum { maxlen = 40 };
00261 
00262     //
00263     // However, IANA themselves don't follow the rules.
00264     // Examples are "3PC", "TP++", "AX.25" and "A/N"
00265     //
00266     // Traditionally unix protocol names have been lowercase, so we will
00267     // continue to allow lower case names.  The upper_case_ok argument
00268     // is to indicate that upper case is acceptable in aliases.
00269     //
00270     const char *cp = text.c_str();
00271     const char *end =
00272         cp + (text.size() < maxlen ? text.size() : (size_t)maxlen);
00273     rcstring_accumulator acc;
00274     bool all_numeric = true;
00275     while (cp < end)
00276     {
00277         unsigned char c = *cp++;
00278         switch (c)
00279         {
00280         case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
00281         case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
00282         case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
00283         case 'v': case 'w': case 'x': case 'y': case 'z':
00284             all_numeric = false;
00285             break;
00286 
00287         case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
00288         case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
00289         case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
00290         case 'V': case 'W': case 'X': case 'Y': case 'Z':
00291             if (!upper_case_ok)
00292                 c = tolower(c);
00293             all_numeric = false;
00294             break;
00295 
00296         case '0': case '1': case '2': case '3': case '4':
00297         case '5': case '6': case '7': case '8': case '9':
00298             break;
00299 
00300 #ifndef STRICT_IANA_RULES
00301         //
00302         // To cope with "TP++", "A/N" and "AX.25" we allow some
00303         // additional characters.
00304         //
00305         case '/':
00306             all_numeric = false;
00307             break;
00308 
00309         case '.':
00310         case '+':
00311 #endif
00312         case '-':
00313             break;
00314 
00315         default:
00316             c = '-';
00317             break;
00318         }
00319         acc.push_back(c);
00320     }
00321 
00322     //
00323     // To cope with the "3PC" case, we will allow leading digits.
00324     //
00325 #ifdef STRICT_IANA_RULES
00326     if (!isalpha(acc.front()))
00327 #else
00328     if (!isalnum(acc.front()))
00329 #endif
00330     {
00331         if (acc.size() >= maxlen)
00332             acc.pop_back();
00333         acc.push_front('a');
00334         all_numeric = false;
00335     }
00336 
00337     //
00338     // To cope with the "TP++' case, we don't enforce the "ends in a
00339     // letter or digit" rule.
00340     //
00341 #ifdef STRICT_IANA_RULES
00342     if (!isalnum(acc.back()))
00343     {
00344         if (acc.size() >= maxlen)
00345             acc.pop_back();
00346         acc.push_back('a');
00347         all_numeric = false;
00348     }
00349 #endif
00350 
00351     // one last check
00352     if (all_numeric)
00353     {
00354         if (acc.size() >= maxlen)
00355             acc.pop_back();
00356         acc.push_front('a');
00357     }
00358 
00359     // build the suggestion
00360     suggest = acc.mkstr();
00361 
00362     // the name is ok if the suggestion is identical to the original text
00363     return (text == suggest);
00364 }
00365 
00366 
00367 // vim: set ts=8 sw=4 et :