LCOV - code coverage report
Current view: top level - edhttp - quoted_printable.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1 143 0.7 %
Date: 2022-07-09 10:44:38 Functions: 2 21 9.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2011-2022  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/edhttp
       4             : // contact@m2osw.com
       5             : //
       6             : // This program is free software: you can redistribute it and/or modify
       7             : // it under the terms of the GNU General Public License as published by
       8             : // the Free Software Foundation, either version 3 of the License, or
       9             : // (at your option) any later version.
      10             : //
      11             : // This program is distributed in the hope that it will be useful,
      12             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             : // GNU General Public License for more details.
      15             : //
      16             : // You should have received a copy of the GNU General Public License
      17             : // along with this program.  If not, see <https://www.gnu.org/licenses/>.
      18             : 
      19             : // self
      20             : //
      21             : #include    "edhttp/quoted_printable.h"
      22             : 
      23             : 
      24             : // snapdev lib
      25             : //
      26             : #include    <snapdev/not_reached.h>
      27             : 
      28             : 
      29             : // last include
      30             : //
      31             : #include    <snapdev/poison.h>
      32             : 
      33             : 
      34             : 
      35             : namespace edhttp
      36             : {
      37             : 
      38             : 
      39             : 
      40           0 : std::string quoted_printable_encode(std::string const & input, int flags)
      41             : {
      42           0 :     class result
      43             :     {
      44             :     public:
      45           0 :         result(size_t input_length, int flags)
      46           0 :             : f_flags(flags)
      47             :         {
      48           0 :             f_result.reserve(input_length * 2);
      49           0 :         }
      50             : 
      51           0 :         char to_hex(int c)
      52             :         {
      53           0 :             c &= 15;
      54           0 :             if(c >= 0 && c <= 9)
      55             :             {
      56           0 :                 return static_cast<char>(c + '0');
      57             :             }
      58           0 :             return static_cast<char>(c + 'A' - 10);
      59             :         }
      60             : 
      61           0 :         void add_byte(char c)
      62             :         {
      63           0 :             if(c == '\n' || c == '\r')
      64             :             {
      65           0 :                 if(c != '\r' || (f_flags & QUOTED_PRINTABLE_FLAG_LFONLY) == 0)
      66             :                 {
      67           0 :                     f_result += c;
      68             :                 }
      69           0 :                 f_line = 0;
      70           0 :                 return;
      71             :             }
      72             :             // the maximum line length is 76
      73             :             // it is not clear whether that includes the CR+LF or not
      74             :             // "=\r\n" is 3 characters
      75           0 :             if(f_line >= 75)
      76             :             {
      77           0 :                 if((f_flags & QUOTED_PRINTABLE_FLAG_LFONLY) == 0)
      78             :                 {
      79           0 :                     f_result += "=\r\n";
      80             :                 }
      81             :                 else
      82             :                 {
      83           0 :                     f_result += "=\n";
      84             :                 }
      85           0 :                 f_line = 0;
      86             :             }
      87           0 :             f_result += c;
      88           0 :             ++f_line;
      89             :         }
      90             : 
      91           0 :         void add_hex(int c)
      92             :         {
      93             :             // make sure there is enough space on the current line before
      94             :             // adding the 3 encoded bytes
      95           0 :             if(f_line >= 73)
      96             :             {
      97             :                 // IMPORTANT: we cannot call add_byte while adding
      98             :                 // an '=' as character 75 otherwise it will add "=\r\n"!
      99           0 :                 if((f_flags & QUOTED_PRINTABLE_FLAG_LFONLY) == 0)
     100             :                 {
     101           0 :                     f_result += "=\r\n";
     102             :                 }
     103             :                 else
     104             :                 {
     105           0 :                     f_result += "=\n";
     106             :                 }
     107           0 :                 f_line = 0;
     108             :             }
     109           0 :             add_byte('=');
     110           0 :             add_byte(to_hex(c >> 4));
     111           0 :             add_byte(to_hex(c));
     112           0 :         }
     113             : 
     114           0 :         void add_data(char c)
     115             :         {
     116           0 :             if(c == ' ' || c == '\t')
     117             :             {
     118             :                 // we have to buffer the last space or tab because they
     119             :                 // cannot appear as the last character on a line (i.e.
     120             :                 // when followed by \r or \n)
     121           0 :                 if(f_buffer != '\0')
     122             :                 {
     123           0 :                     add_byte(f_buffer);
     124             :                 }
     125           0 :                 f_buffer = c;
     126           0 :                 return;
     127             :             }
     128           0 :             if(c == '\r')
     129             :             {
     130           0 :                 f_cr = true;
     131             :             }
     132           0 :             else if(c == '\n' && f_cr)
     133             :             {
     134             :                 // CR+LF already taken cared of
     135           0 :                 f_cr = false;
     136           0 :                 return;
     137             :             }
     138             :             else
     139             :             {
     140           0 :                 f_cr = false;
     141             :             }
     142           0 :             if(c == '\n' || c == '\r')
     143             :             {
     144             :                 // spaces and tabs must be encoded in this case
     145           0 :                 if(f_buffer != '\0')
     146             :                 {
     147           0 :                     add_hex(f_buffer);
     148           0 :                     f_buffer = '\0';
     149             :                 }
     150             :                 // force the CR+LF sequence
     151           0 :                 add_byte('\r');
     152           0 :                 add_byte('\n');
     153           0 :                 return;
     154             :             }
     155           0 :             if(f_buffer != '\0')
     156             :             {
     157           0 :                 add_byte(f_buffer);
     158           0 :                 f_buffer = '\0';
     159             :             }
     160           0 :             add_byte(c);
     161             :         }
     162             : 
     163           0 :         bool encode_char(char c)
     164             :         {
     165           0 :             switch(c)
     166             :             {
     167           0 :             case '\n':
     168             :             case '\r':
     169             :             case '\t':
     170             :             case ' ':
     171           0 :                 return (f_flags & QUOTED_PRINTABLE_FLAG_BINARY) != 0;
     172             : 
     173           0 :             case '=':
     174           0 :                 return true;
     175             : 
     176           0 :             case '!': // !"#$@[\]^`{|}~
     177             :             case '"':
     178             :             case '#':
     179             :             case '$':
     180             :             case '@':
     181             :             case '[':
     182             :             case '\\':
     183             :             case ']':
     184             :             case '^':
     185             :             case '`':
     186             :             case '{':
     187             :             case '|':
     188             :             case '}':
     189             :             case '~':
     190           0 :                 return (f_flags & QUOTED_PRINTABLE_FLAG_EDBIC) != 0;
     191             : 
     192           0 :             default:
     193             :                 // note: the following won't match ' ' and '~' which are
     194             :                 //       already captured by other cases
     195           0 :                 return !(c >= ' ' && c <= '~');
     196             : 
     197             :             }
     198             :             snapdev::NOT_REACHED();
     199             :         }
     200             : 
     201           0 :         void add_char(char c)
     202             :         {
     203             :             // a few controls are allowed as is
     204           0 :             if(encode_char(c))
     205             :             {
     206             :                 // needs to be encoded
     207           0 :                 add_hex(c);
     208             :             }
     209             :             else
     210             :             {
     211           0 :                 add_data(c);
     212             :             }
     213           0 :         }
     214             : 
     215           0 :         void add_string(char const * s)
     216             :         {
     217           0 :             bool const lone_periods((f_flags & QUOTED_PRINTABLE_FLAG_NO_LONE_PERIOD) != 0);
     218             :             // reset the buffer, just in case
     219           0 :             f_buffer = '\0';
     220           0 :             for(; *s != '\0'; ++s)
     221             :             {
     222           0 :                 if(lone_periods
     223           0 :                 && *s == '.'
     224           0 :                 && (s[1] == '\r' || s[1] == '\n' || s[1] == '\0')
     225           0 :                 && (f_line == 0 || f_line >= 75))
     226             :                 {
     227             :                     // special case of a lone period at the start of a line
     228           0 :                     add_hex('.');
     229             :                 }
     230             :                 else
     231             :                 {
     232           0 :                     add_char(*s);
     233             :                 }
     234             :             }
     235             : 
     236             :             // at the end we may still have a space or tab to add
     237           0 :             if(f_buffer != '\0')
     238             :             {
     239           0 :                 add_hex(f_buffer);
     240           0 :                 f_buffer = '\0';
     241             :             }
     242           0 :         }
     243             : 
     244           0 :         std::string get_result() const
     245             :         {
     246           0 :             return f_result;
     247             :         }
     248             : 
     249             :     private:
     250             :         int32_t         f_flags = 0;
     251             :         char            f_buffer = 0;
     252             :         std::string     f_result = std::string();
     253             :         int32_t         f_line = 0;
     254             :         bool            f_cr = false;
     255             :     };
     256             : 
     257           0 :     result r(input.length(), flags);
     258           0 :     r.add_string(input.c_str());
     259           0 :     return r.get_result();
     260             : }
     261             : 
     262             : 
     263           0 : std::string quoted_printable_decode(std::string const & input)
     264             : {
     265           0 :     class result
     266             :     {
     267             :     public:
     268           0 :         result(std::string const & input)
     269             :             //: f_result("") -- auto-initiazlied
     270           0 :             : f_input(input)
     271           0 :             , f_str(f_input.c_str())
     272             :         {
     273           0 :         }
     274             : 
     275             :         result(result const & rhs) = delete;
     276             :         result & operator = (result const & rhs) = delete;
     277             : 
     278           0 :         int from_hex(int c)
     279             :         {
     280           0 :             if(c >= '0' && c <= '9')
     281             :             {
     282           0 :                 return c - '0';
     283             :             }
     284             :             // note that the documentation clearly says that only capitalized
     285             :             // (A-F) characters are acceptable...
     286           0 :             if(c >= 'a' && c <= 'f')
     287             :             {
     288           0 :                 return c - 'a' + 10;
     289             :             }
     290           0 :             if(c >= 'A' && c <= 'F')
     291             :             {
     292           0 :                 return c - 'A' + 10;
     293             :             }
     294           0 :             return -1;
     295             :         }
     296             : 
     297           0 :         int get_byte()
     298             :         {
     299             :             for(;;)
     300             :             {
     301           0 :                 if(*f_str == '\0')
     302             :                 {
     303           0 :                     return '\0';
     304             :                 }
     305           0 :                 if(*f_str == '=')
     306             :                 {
     307           0 :                     if(f_str[1] == '\r')
     308             :                     {
     309           0 :                         if(f_str[2] == '\n')
     310             :                         {
     311           0 :                             f_str += 3;
     312             :                         }
     313             :                         else
     314             :                         {
     315           0 :                             f_str += 2;
     316             :                         }
     317           0 :                         continue;
     318             :                     }
     319           0 :                     if(f_str[1] == '\n')
     320             :                     {
     321           0 :                         f_str += 2;
     322           0 :                         continue;
     323             :                     }
     324             :                 }
     325           0 :                 return *f_str++;
     326             :             }
     327             :         }
     328             : 
     329           0 :         int getc()
     330             :         {
     331           0 :             int c(get_byte());
     332           0 :             if(c == '\0')
     333             :             {
     334           0 :                 return '\0';
     335             :             }
     336           0 :             if(c == '=')
     337             :             {
     338             :                 // all equal must be followed by a newline (taken care
     339             :                 // off already) or a 2 hex digits
     340           0 :                 c = get_byte();
     341           0 :                 int const p(from_hex(c));
     342           0 :                 if(p == -1)
     343             :                 {
     344           0 :                     return '?';
     345             :                 }
     346           0 :                 c = get_byte();
     347           0 :                 int const q(from_hex(c));
     348           0 :                 if(q == -1)
     349             :                 {
     350           0 :                     return '?';
     351             :                 }
     352           0 :                 return p * 16 + q;
     353             :             }
     354           0 :             return c;
     355             :         }
     356             : 
     357           0 :         void process()
     358             :         {
     359           0 :             for(int c(getc()); c != '\0'; c = getc())
     360             :             {
     361           0 :                 f_result += static_cast<char>(c);
     362             :             }
     363           0 :         }
     364             : 
     365           0 :         std::string get_result() const
     366             :         {
     367           0 :             return f_result;
     368             :         }
     369             : 
     370             :     private:
     371             :         std::string     f_input = std::string();
     372             :         std::string     f_result = std::string();
     373             :         char const *    f_str = nullptr;
     374             :     };
     375             : 
     376           0 :     result r(input);
     377           0 :     r.process();
     378           0 :     return r.get_result();
     379             : }
     380             : 
     381             : 
     382             : 
     383           6 : } // namespace edhttp
     384             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13