telepathygabble/src/base64.c
changeset 10 59927b2d3b75
parent 0 d0f3a028347a
equal deleted inserted replaced
0:d0f3a028347a 10:59927b2d3b75
     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 "base64.h"
       
    21 
       
    22 #define DEBUG_FLAG GABBLE_DEBUG_VCARD
       
    23 #include "debug.h"
       
    24 
       
    25 #include <ctype.h>
       
    26 #include <string.h>
       
    27 
       
    28 #include <glib.h>
       
    29 
       
    30 /*
       
    31 |AAAA AABB|BBBB CCCC|CCDD DDDD|
       
    32 
       
    33 0xFC = 1111 1100
       
    34 0x03 = 0000 0011
       
    35 0xF0 = 1111 0000
       
    36 0x0F = 0000 1111
       
    37 0xC0 = 1100 0000
       
    38 0x3F = 0011 1111
       
    39 
       
    40 3 input bytes = 4 output bytes;
       
    41 2 input bytes = 2 output bytes;
       
    42 1 input byte  = 1 output byte.
       
    43 */
       
    44 
       
    45 static const gchar *encoding =
       
    46   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
       
    47 
       
    48 static const guint decoding[256] =
       
    49 {
       
    50   /* ... */
       
    51    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       
    52    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       
    53    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       
    54    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       
    55    0, 0, 0,
       
    56   /* + */
       
    57   62,
       
    58   /* ... */
       
    59    0, 0, 0,
       
    60   /* / , 0-9 */
       
    61   63,
       
    62   52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
       
    63   /* ... */
       
    64    0, 0, 0, 0, 0, 0, 0,
       
    65   /* A */
       
    66    0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12,
       
    67   13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
       
    68   /* ... */
       
    69    0, 0, 0, 0, 0, 0,
       
    70   /* a */
       
    71   26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
       
    72   39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
       
    73 };
       
    74 
       
    75 #define GET_6_BITS_0(s) (((s)[0] & 0xFC) >> 2)
       
    76 #define GET_6_BITS_1(s) (((s)[0] & 0x03) << 4) | \
       
    77                         (((s)[1] & 0xF0) >> 4)
       
    78 #define GET_6_BITS_2(s) (((s)[1] & 0x0F) << 2) | \
       
    79                         (((s)[2] & 0xC0) >> 6)
       
    80 #define GET_6_BITS_3(s) (((s)[2] & 0x3F) << 0)
       
    81 
       
    82 #define GET_BYTE_0(s) (((decoding[(guchar)(s)[0]] & 0x3F) << 2) | \
       
    83                        ((decoding[(guchar)(s)[1]] & 0x30) >> 4))
       
    84 #define GET_BYTE_1(s) (((decoding[(guchar)(s)[1]] & 0x0F) << 4) | \
       
    85                        ((decoding[(guchar)(s)[2]] & 0x3C) >> 2))
       
    86 #define GET_BYTE_2(s) (((decoding[(guchar)(s)[2]] & 0x03) << 6) | \
       
    87                        ((decoding[(guchar)(s)[3]] & 0xFF) << 0))
       
    88 
       
    89 gchar *base64_encode (guint len, const gchar *str)
       
    90 {
       
    91   guint i;
       
    92   GString *tmp;
       
    93 
       
    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       if (i > 0 && (i * 4) % 76 == 0)
       
   103           g_string_append_c (tmp, '\n');
       
   104 
       
   105       switch (i + 3 - len)
       
   106         {
       
   107         case 1:
       
   108           c1 = encoding[GET_6_BITS_0 (str + i)];
       
   109           c2 = encoding[GET_6_BITS_1 (str + i)];
       
   110           /* can't use GET_6_BITS_2 because str[i+2] is out of range */
       
   111           c3 = encoding[(str[i + 1] & 0x0f) << 2];
       
   112           c4 = '=';
       
   113           break;
       
   114         case 2:
       
   115           c1 = encoding[GET_6_BITS_0 (str + i)];
       
   116           /* can't use GET_6_BITS_1 because str[i+1] is out of range */
       
   117           c2 = encoding[(str[i] & 0x03) << 4];
       
   118           c3 = '=';
       
   119           c4 = '=';
       
   120           break;
       
   121         default:
       
   122           c1 = encoding[GET_6_BITS_0 (str + i)];
       
   123           c2 = encoding[GET_6_BITS_1 (str + i)];
       
   124           c3 = encoding[GET_6_BITS_2 (str + i)];
       
   125           c4 = encoding[GET_6_BITS_3 (str + i)];
       
   126         }
       
   127 
       
   128       g_string_append_printf (tmp, "%c%c%c%c", c1, c2, c3, c4);
       
   129     }
       
   130 
       
   131   return g_string_free (tmp, FALSE);
       
   132 }
       
   133 
       
   134 GString *base64_decode (const gchar *str)
       
   135 {
       
   136   guint i;
       
   137   GString *tmp;
       
   138   char group[4];
       
   139   guint filled = 0;
       
   140 
       
   141   for (i = 0; str[i]; i++)
       
   142     {
       
   143       if (str[i] != 'A' &&
       
   144           str[i] != '=' &&
       
   145           !isspace (str[i]) &&
       
   146           decoding[(guchar) str[i]] == 0)
       
   147         {
       
   148           gabble_debug (DEBUG_FLAG, "bad character %x at byte %u", (guchar)str[i], i);
       
   149           return NULL;
       
   150         }
       
   151     }
       
   152 
       
   153   tmp = g_string_new ("");
       
   154 
       
   155   for (i = 0; str[i]; i++)
       
   156     {
       
   157       if (isspace (str[i]))
       
   158         continue;
       
   159 
       
   160       group[filled++] = str[i];
       
   161 
       
   162       if (filled == 4)
       
   163         {
       
   164           if (group[3] == '=')
       
   165             {
       
   166               if (group[2] == '=')
       
   167                 {
       
   168                   g_string_append_c (tmp, GET_BYTE_0(group));
       
   169                 }
       
   170               else
       
   171                 {
       
   172                   g_string_append_c (tmp, GET_BYTE_0(group));
       
   173                   g_string_append_c (tmp, GET_BYTE_1(group));
       
   174                 }
       
   175              }
       
   176            else
       
   177             {
       
   178               g_string_append_c (tmp, GET_BYTE_0(group));
       
   179               g_string_append_c (tmp, GET_BYTE_1(group));
       
   180               g_string_append_c (tmp, GET_BYTE_2(group));
       
   181             }
       
   182           filled = 0;
       
   183         }
       
   184     }
       
   185 
       
   186   if (filled)
       
   187     {
       
   188       gabble_debug (DEBUG_FLAG, "insufficient padding at end of base64 string:\n%s", str);
       
   189       g_string_free (tmp, TRUE);
       
   190       return NULL;
       
   191     }
       
   192 
       
   193   return tmp;
       
   194 }
       
   195 
       
   196