LCOV - code coverage report
Current view: top level - snapwebsites - quoted_printable.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1 143 0.7 %
Date: 2019-12-15 17:13:15 Functions: 2 21 9.5 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13