loudmouth/base64.c
changeset 407 4401f2aa0692
parent 406 3c6b13d37d41
child 408 b07ef2183e6b
equal deleted inserted replaced
406:3c6b13d37d41 407:4401f2aa0692
     1 /*
       
     2  * base64.c - Base 64 encoding/decoding implementation
       
     3  * Copyright (C) 2006 Collabora Ltd.
       
     4  *
       
     5  * This library is free software; you can redistribute it and/or
       
     6  * modify it under the terms of the GNU Lesser General Public
       
     7  * License as published by the Free Software Foundation; either
       
     8  * version 2.1 of the License, or (at your option) any later version.
       
     9  *
       
    10  * This library is distributed in the hope that it will be useful,
       
    11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    13  * Lesser General Public License for more details.
       
    14  *
       
    15  * You should have received a copy of the GNU Lesser General Public
       
    16  * License along with this library; if not, write to the Free Software
       
    17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
       
    18  */
       
    19 
       
    20 #include <ctype.h>
       
    21 #include <string.h>
       
    22 
       
    23 #include <glib.h>
       
    24 
       
    25 #include "base64.h"
       
    26 
       
    27 /*
       
    28 |AAAA AABB|BBBB CCCC|CCDD DDDD|
       
    29 
       
    30 0xFC = 1111 1100
       
    31 0x03 = 0000 0011
       
    32 0xF0 = 1111 0000
       
    33 0x0F = 0000 1111
       
    34 0xC0 = 1100 0000
       
    35 0x3F = 0011 1111
       
    36 
       
    37 3 input bytes = 4 output bytes;
       
    38 2 input bytes = 2 output bytes;
       
    39 1 input byte  = 1 output byte.
       
    40 */
       
    41 
       
    42 static const gchar *encoding =
       
    43   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
       
    44 
       
    45 static const guint decoding[256] =
       
    46 {
       
    47   /* ... */
       
    48    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       
    49    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       
    50    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       
    51    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       
    52    0, 0, 0,
       
    53   /* + */
       
    54   62,
       
    55   /* ... */
       
    56    0, 0, 0,
       
    57   /* / , 0-9 */
       
    58   63,
       
    59   52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
       
    60   /* ... */
       
    61    0, 0, 0, 0, 0, 0, 0,
       
    62   /* A */
       
    63    0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12,
       
    64   13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
       
    65   /* ... */
       
    66    0, 0, 0, 0, 0, 0,
       
    67   /* a */
       
    68   26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
       
    69   39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
       
    70 };
       
    71 
       
    72 #define GET_6_BITS_0(s) (((s)[0] & 0xFC) >> 2)
       
    73 #define GET_6_BITS_1(s) (((s)[0] & 0x03) << 4) | \
       
    74                         (((s)[1] & 0xF0) >> 4)
       
    75 #define GET_6_BITS_2(s) (((s)[1] & 0x0F) << 2) | \
       
    76                         (((s)[2] & 0xC0) >> 6)
       
    77 #define GET_6_BITS_3(s) (((s)[2] & 0x3F) << 0)
       
    78 
       
    79 #define GET_BYTE_0(s) (((decoding[(guchar)(s)[0]] & 0x3F) << 2) | \
       
    80                        ((decoding[(guchar)(s)[1]] & 0x30) >> 4))
       
    81 #define GET_BYTE_1(s) (((decoding[(guchar)(s)[1]] & 0x0F) << 4) | \
       
    82                        ((decoding[(guchar)(s)[2]] & 0x3C) >> 2))
       
    83 #define GET_BYTE_2(s) (((decoding[(guchar)(s)[2]] & 0x03) << 6) | \
       
    84                        ((decoding[(guchar)(s)[3]] & 0xFF) << 0))
       
    85 
       
    86 gchar *_lm_base64_encode (const gchar *txt, gsize n)
       
    87 {
       
    88   guint i;
       
    89   guint len;
       
    90   GString *tmp;
       
    91   GString *str = g_string_new_len (txt, n);
       
    92 
       
    93   len = str->len;
       
    94   /* TODO: calculate requisite output string length and allocate that big a
       
    95    * GString */
       
    96   tmp = g_string_new ("");
       
    97 
       
    98   for (i = 0; i < len; i += 3)
       
    99     {
       
   100       guint c1, c2, c3, c4;
       
   101 
       
   102       switch (i + 3 - len)
       
   103         {
       
   104         case 1:
       
   105           c1 = encoding[GET_6_BITS_0 (str->str + i)];
       
   106           c2 = encoding[GET_6_BITS_1 (str->str + i)];
       
   107           c3 = encoding[GET_6_BITS_2 (str->str + i)];
       
   108           c4 = '=';
       
   109           break;
       
   110         case 2:
       
   111           c1 = encoding[GET_6_BITS_0 (str->str + i)];
       
   112           c2 = encoding[GET_6_BITS_1 (str->str + i)];
       
   113           c3 = '=';
       
   114           c4 = '=';
       
   115           break;
       
   116         default:
       
   117           c1 = encoding[GET_6_BITS_0 (str->str + i)];
       
   118           c2 = encoding[GET_6_BITS_1 (str->str + i)];
       
   119           c3 = encoding[GET_6_BITS_2 (str->str + i)];
       
   120           c4 = encoding[GET_6_BITS_3 (str->str + i)];
       
   121         }
       
   122 
       
   123       g_string_append_printf (tmp, "%c%c%c%c", c1, c2, c3, c4);
       
   124     }
       
   125 
       
   126   return g_string_free (tmp, FALSE);
       
   127 }
       
   128 
       
   129 gchar *_lm_base64_decode (const gchar *str, gsize *len)
       
   130 {
       
   131   guint i;
       
   132   GString *tmp;
       
   133   char group[4];
       
   134   guint filled = 0;
       
   135 
       
   136   *len = 0;
       
   137 
       
   138   for (i = 0; str[i]; i++)
       
   139     {
       
   140       if (str[i] != 'A' &&
       
   141           str[i] != '=' &&
       
   142           !isspace(str[i]) &&
       
   143           decoding[(guchar) str[i]] == 0)
       
   144         {
       
   145           g_debug ("bad character %x at byte %u", (guchar)str[i], i);
       
   146           return NULL;
       
   147         }
       
   148     }
       
   149 
       
   150   tmp = g_string_new ("");
       
   151 
       
   152   for (i = 0; str[i]; i++)
       
   153     {
       
   154       if (isspace(str[i]))
       
   155         continue;
       
   156 
       
   157       group[filled++] = str[i];
       
   158 
       
   159       if (filled == 4)
       
   160         {
       
   161           if (group[3] == '=')
       
   162             {
       
   163               if (group[2] == '=')
       
   164                 {
       
   165                   g_string_append_c (tmp, GET_BYTE_0(group));
       
   166                 }
       
   167               else
       
   168                 {
       
   169                   g_string_append_c (tmp, GET_BYTE_0(group));
       
   170                   g_string_append_c (tmp, GET_BYTE_1(group));
       
   171                 }
       
   172              }
       
   173            else
       
   174             {
       
   175               g_string_append_c (tmp, GET_BYTE_0(group));
       
   176               g_string_append_c (tmp, GET_BYTE_1(group));
       
   177               g_string_append_c (tmp, GET_BYTE_2(group));
       
   178             }
       
   179           filled = 0;
       
   180         }
       
   181     }
       
   182 
       
   183   if (filled)
       
   184     {
       
   185       g_debug ("insufficient padding at end of base64 string:\n%s", str);
       
   186       g_string_free (tmp, TRUE);
       
   187       return NULL;
       
   188     }
       
   189 
       
   190   *len = tmp->len;
       
   191   return g_string_free (tmp, FALSE);
       
   192 }
       
   193 
       
   194