Back to index

lightning-sunbird  0.9+nobinonly
line64.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is Mozilla Communicator client code, released
00015  * March 31, 1998.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 /* line64.c - routines for dealing with the slapd line format */
00039 
00040 #include <stdio.h>
00041 #include <string.h>
00042 #include <stdlib.h>
00043 #include <ctype.h>
00044 #ifndef macintosh
00045 #include <sys/types.h>
00046 #endif
00047 #ifdef _WIN32
00048 #include <windows.h>
00049 #elif !defined( macintosh )
00050 #include <sys/socket.h>
00051 #endif
00052 #include "ldaplog.h"
00053 #include "ldif.h"
00054 
00055 #ifndef isascii
00056 #define isascii( c ) (!((c) & ~0177))
00057 #endif
00058 
00059 #define RIGHT2                     0x03
00060 #define RIGHT4                     0x0f
00061 #define CONTINUED_LINE_MARKER      '\001'
00062 
00063 #define ISBLANK(c) (c == ' ' || c == '\t' || c == '\n') /* not "\r\v\f" */
00064 
00065 #define LDIF_OPT_ISSET( value, opt )      (((value) & (opt)) != 0 )
00066 
00067 static char nib2b64[0x40f] =
00068         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
00069 
00070 static unsigned char b642nib[0x80] = {
00071        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
00072        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
00073        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
00074        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
00075        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
00076        0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
00077        0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
00078        0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
00079        0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
00080        0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
00081        0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
00082        0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
00083        0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
00084        0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
00085        0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
00086        0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
00087 };
00088 
00089 static int ldif_base64_encode_internal( unsigned char *src, char *dst, int srclen,
00090        int lenused, int wraplen );
00091 
00092 /*
00093  * ldif_parse_line - takes a line of the form "type:[:] value" and splits it
00094  * into components "type" and "value".  if a double colon separates type from
00095  * value, then value is encoded in base 64, and parse_line un-decodes it
00096  * (in place) before returning.
00097  */
00098 
00099 int
00100 ldif_parse_line(
00101     char      *line,
00102     char      **type,
00103     char      **value,
00104     int              *vlen
00105 )
00106 {
00107        char   *p, *s, *d;
00108        int    b64;
00109 
00110        /* skip any leading space */
00111        while ( ISBLANK( *line ) ) {
00112               line++;
00113        }
00114        *type = line;
00115 
00116        for ( s = line; *s && *s != ':'; s++ )
00117               ;      /* NULL */
00118        if ( *s == '\0' ) {
00119 
00120               /* Comment-out while we address calling libldif from ns-back-ldbm
00121                      on NT. 1 of 3 */
00122 #if defined( _WIN32 )
00123               /*
00124 #endif
00125                LDAPDebug( LDAP_DEBUG_PARSE, "ldif_parse_line: missing ':' "
00126                      "on line \"%s\"\n", line, 0, 0 ); 
00127 #if defined( _WIN32 )
00128               */
00129 #endif
00130               return( -1 );
00131        }
00132 
00133        /* trim any space between type and : */
00134        for ( p = s - 1; p > line && ISBLANK( *p ); p-- ) {
00135               *p = '\0';
00136        }
00137        *s++ = '\0';
00138 
00139        /* check for double : - indicates base 64 encoded value */
00140        if ( *s == ':' ) {
00141               s++;
00142               b64 = 1;
00143 
00144        /* single : - normally encoded value */
00145        } else {
00146               b64 = 0;
00147        }
00148 
00149        /* skip space between : and value */
00150        while ( ISBLANK( *s ) ) {
00151               s++;
00152        }
00153 
00154        /* 
00155         * If no value is present, return a zero-length string for
00156         * *value, with *vlen set to zero.
00157         */
00158        if ( *s == '\0' ) {
00159               *value = s;
00160               *vlen = 0;
00161               return( 0 );
00162        }
00163 
00164        /* check for continued line markers that should be deleted */
00165        for ( p = s, d = s; *p; p++ ) {
00166               if ( *p != CONTINUED_LINE_MARKER )
00167                      *d++ = *p;
00168        }
00169        *d = '\0';
00170 
00171        *value = s;
00172        if ( b64 ) {
00173               if (( *vlen = ldif_base64_decode( s, (unsigned char *)s ))
00174                   < 0 ) {
00175                      /* Comment-out while we address calling libldif from ns-back-ldbm
00176                             on NT. 3 of 3 */
00177 #if defined( _WIN32 )
00178               /*
00179 #endif
00180                       LDAPDebug( LDAP_DEBUG_ANY,
00181                          "ldif_parse_line: invalid base 64 char on line \"%s\"\n",
00182                          line, 0, 0 ); 
00183 #if defined( _WIN32 )
00184               */
00185 #endif
00186                      return( -1 );
00187               }
00188               s[ *vlen ] = '\0';
00189        } else {
00190               *vlen = (int) (d - s);
00191        }
00192 
00193        return( 0 );
00194 }
00195 
00196 
00197 /*
00198  * ldif_base64_decode - take the BASE64-encoded characters in "src"
00199  * (a zero-terminated string) and decode them into the the buffer "dst".
00200  * "src" and "dst" can be the same if in-place decoding is desired.
00201  * "dst" must be large enough to hold the decoded octets.  No more than
00202  *     3 * strlen( src ) / 4 bytes will be produced.
00203  * "dst" may contain zero octets anywhere within it, but it is not
00204  *     zero-terminated by this function.
00205  *
00206  * The number of bytes copied to "dst" is returned if all goes well.
00207  * -1 is returned if the BASE64 encoding in "src" is invalid.
00208  */
00209 
00210 int
00211 ldif_base64_decode( char *src, unsigned char *dst )
00212 {
00213        char          *p, *stop;
00214        unsigned char nib, *byte;
00215        int           i, len;
00216 
00217        stop = strchr( src, '\0' );
00218        byte = dst;
00219        for ( p = src, len = 0; p < stop; p += 4, len += 3 ) {
00220               for ( i = 0; i < 4; i++ ) {
00221                      if ( p[i] != '=' && (p[i] & 0x80 ||
00222                          b642nib[ p[i] & 0x7f ] > 0x3f) ) {
00223                             return( -1 );
00224                      }
00225               }
00226 
00227               /* first digit */
00228               nib = b642nib[ p[0] & 0x7f ];
00229               byte[0] = nib << 2;
00230 
00231               /* second digit */
00232               nib = b642nib[ p[1] & 0x7f ];
00233               byte[0] |= nib >> 4;
00234 
00235               /* third digit */
00236               if ( p[2] == '=' ) {
00237                      len += 1;
00238                      break;
00239               }
00240               byte[1] = (nib & RIGHT4) << 4;
00241               nib = b642nib[ p[2] & 0x7f ];
00242               byte[1] |= nib >> 2;
00243 
00244               /* fourth digit */
00245               if ( p[3] == '=' ) {
00246                      len += 2;
00247                      break;
00248               }
00249               byte[2] = (nib & RIGHT2) << 6;
00250               nib = b642nib[ p[3] & 0x7f ];
00251               byte[2] |= nib;
00252 
00253               byte += 3;
00254        }
00255 
00256        return( len );
00257 }
00258 
00259 /*
00260  * ldif_getline - return the next "line" (minus newline) of input from a
00261  * string buffer of lines separated by newlines, terminated by \n\n
00262  * or \0.  this routine handles continued lines, bundling them into
00263  * a single big line before returning.  if a line begins with a white
00264  * space character, it is a continuation of the previous line. the white
00265  * space character (nb: only one char), and preceeding newline are changed
00266  * into CONTINUED_LINE_MARKER chars, to be deleted later by the
00267  * ldif_parse_line() routine above.
00268  *
00269  * it takes a pointer to a pointer to the buffer on the first call,
00270  * which it updates and must be supplied on subsequent calls.
00271  *
00272  * XXX need to update this function to also support <CR><LF> as EOL.
00273  * XXX supports <CR><LF> as of 07/29/1998 (richm)
00274  */
00275 
00276 char *
00277 ldif_getline( char **next )
00278 {
00279        char   *l;
00280        char   c;
00281        char   *p;
00282 
00283        if ( *next == NULL || **next == '\n' || **next == '\0' ) {
00284               return( NULL );
00285        }
00286 
00287        while ( **next == '#' ) {   /* skip comment lines */
00288               if (( *next = strchr( *next, '\n' )) == NULL ) {
00289                      return( NULL );
00290               }
00291               (*next)++;
00292        }
00293 
00294        l = *next;
00295        while ( (*next = strchr( *next, '\n' )) != NULL ) {
00296               p = *next - 1; /* pointer to character previous to the newline */
00297               c = *(*next + 1); /* character after the newline */
00298               if ( ISBLANK( c ) && c != '\n' ) {
00299                      /* DOS EOL is \r\n, so if the character before */
00300                      /* the \n is \r, continue it too */
00301                      if (*p == '\r')
00302                             *p = CONTINUED_LINE_MARKER;
00303                      **next = CONTINUED_LINE_MARKER;
00304                      *(*next+1) = CONTINUED_LINE_MARKER;
00305               } else {
00306                      /* DOS EOL is \r\n, so if the character before */
00307                      /* the \n is \r, null it too */
00308                      if (*p == '\r')
00309                             *p = '\0';
00310                      *(*next)++ = '\0';
00311                      break;
00312               }
00313               (*next)++;
00314        }
00315 
00316        return( l );
00317 }
00318 
00319 
00320 #define LDIF_SAFE_CHAR( c )        ( (c) != '\r' && (c) != '\n' )
00321 #define LDIF_CONSERVATIVE_CHAR( c )       ( LDIF_SAFE_CHAR(c) && isascii((c)) \
00322                                     && ( isprint((c)) || (c) == '\t' ))
00323 #define LDIF_SAFE_INITCHAR( c )           ( LDIF_SAFE_CHAR(c) && (c) != ':' \
00324                                     && (c) != ' ' && (c) != '<' )
00325 #define LDIF_CONSERVATIVE_INITCHAR( c ) ( LDIF_SAFE_INITCHAR( c ) && \
00326                                     ! ( isascii((c)) && isspace((c))))
00327 #define LDIF_CONSERVATIVE_FINALCHAR( c ) ( (c) != ' ' )
00328 
00329 
00330 void
00331 ldif_put_type_and_value_with_options( char **out, char *t, char *val,
00332        int vlen, unsigned long options )
00333 {
00334        unsigned char *p, *byte, *stop;
00335        char          *save;
00336        int           b64, len, savelen, wraplen;
00337        len = 0;
00338 
00339        if ( LDIF_OPT_ISSET( options, LDIF_OPT_NOWRAP )) {
00340               wraplen = -1;
00341        } else {
00342               wraplen = LDIF_MAX_LINE_WIDTH;
00343        }
00344 
00345        /* put the type + ": " */
00346        for ( p = (unsigned char *) t; *p; p++, len++ ) {
00347               *(*out)++ = *p;
00348        }
00349        *(*out)++ = ':';
00350        len++;
00351        if ( LDIF_OPT_ISSET( options, LDIF_OPT_VALUE_IS_URL )) {
00352               *(*out)++ = '<';     /* add '<' for URLs */
00353               len++;
00354        }
00355        save = *out;
00356        savelen = len;
00357        b64 = 0;
00358 
00359        stop = (unsigned char *)val;
00360        if ( val && vlen > 0 ) {
00361               *(*out)++ = ' ';
00362               stop = (unsigned char *) (val + vlen);
00363               if ( LDIF_OPT_ISSET( options, LDIF_OPT_MINIMAL_ENCODING )) {
00364                      if ( !LDIF_SAFE_INITCHAR( val[0] )) {
00365                             b64 = 1;
00366                      }
00367               } else {
00368                      if ( !LDIF_CONSERVATIVE_INITCHAR( val[0] ) ||
00369                              !LDIF_CONSERVATIVE_FINALCHAR( val[vlen-1] )) {
00370                             b64 = 1;
00371                      }
00372               }
00373        }
00374 
00375        if ( !b64 ) {
00376               for ( byte = (unsigned char *) val; byte < stop;
00377                   byte++, len++ ) {
00378                      if ( LDIF_OPT_ISSET( options,
00379                          LDIF_OPT_MINIMAL_ENCODING )) {
00380                             if ( !LDIF_SAFE_CHAR( *byte )) {
00381                                    b64 = 1;
00382                                    break;
00383                             }
00384                      } else if ( !LDIF_CONSERVATIVE_CHAR( *byte )) {
00385                             b64 = 1;
00386                             break;
00387                      }
00388                      
00389                      if ( wraplen != -1 && len > wraplen ) {
00390                             *(*out)++ = '\n';
00391                             *(*out)++ = ' ';
00392                             len = 1;
00393                      }
00394                      *(*out)++ = *byte;
00395               }
00396        }
00397 
00398        if ( b64 ) {
00399               *out = save;
00400               *(*out)++ = ':';
00401               *(*out)++ = ' ';
00402               len = ldif_base64_encode_internal( (unsigned char *)val, *out, vlen,
00403                   savelen + 2, wraplen );
00404               *out += len;
00405        }
00406 
00407        *(*out)++ = '\n';
00408 }
00409 
00410 void 
00411 ldif_put_type_and_value( char **out, char *t, char *val, int vlen )
00412 {
00413     ldif_put_type_and_value_with_options( out, t, val, vlen, 0 );
00414 }
00415 
00416 void 
00417 ldif_put_type_and_value_nowrap( char **out, char *t, char *val, int vlen )
00418 {
00419     ldif_put_type_and_value_with_options( out, t, val, vlen, LDIF_OPT_NOWRAP );
00420 }
00421 
00422 /*
00423  * ldif_base64_encode_internal - encode "srclen" bytes in "src", place BASE64
00424  * encoded bytes in "dst" and return the length of the BASE64
00425  * encoded string.  "dst" is also zero-terminated by this function.
00426  *
00427  * If "lenused" >= 0, newlines will be included in "dst" and "lenused" if
00428  * appropriate.  "lenused" should be a count of characters already used
00429  * on the current line.  The LDIF lines we create will contain at most
00430  * "wraplen" characters on each line, unless "wraplen" is -1, in which
00431  * case output line length is unlimited.
00432  *
00433  * If "lenused" < 0, no newlines will be included, and the LDIF_BASE64_LEN()
00434  * macro can be used to determine how many bytes will be placed in "dst."
00435  */
00436 
00437 static int
00438 ldif_base64_encode_internal( unsigned char *src, char *dst, int srclen, int lenused, int wraplen )
00439 {
00440        unsigned char *byte, *stop;
00441        unsigned char buf[3];
00442        char          *out;
00443        unsigned long bits;
00444        int           i, pad, len;
00445 
00446        len = 0;
00447        out = dst;
00448        stop = src + srclen;
00449 
00450        /* convert to base 64 (3 bytes => 4 base 64 digits) */
00451        for ( byte = src; byte < stop - 2; byte += 3 ) {
00452               bits = (byte[0] & 0xff) << 16;
00453               bits |= (byte[1] & 0xff) << 8;
00454               bits |= (byte[2] & 0xff);
00455 
00456               for ( i = 0; i < 4; i++, bits <<= 6 ) {
00457                      if ( wraplen != -1 &&  lenused >= 0 && lenused++ > wraplen ) {
00458                             *out++ = '\n';
00459                             *out++ = ' ';
00460                             lenused = 2;
00461                      }
00462 
00463                      /* get b64 digit from high order 6 bits */
00464                      *out++ = nib2b64[ (bits & 0xfc0000L) >> 18 ];
00465               }
00466        }
00467 
00468        /* add padding if necessary */
00469        if ( byte < stop ) {
00470               for ( i = 0; byte + i < stop; i++ ) {
00471                      buf[i] = byte[i];
00472               }
00473               for ( pad = 0; i < 3; i++, pad++ ) {
00474                      buf[i] = '\0';
00475               }
00476               byte = buf;
00477               bits = (byte[0] & 0xff) << 16;
00478               bits |= (byte[1] & 0xff) << 8;
00479               bits |= (byte[2] & 0xff);
00480 
00481               for ( i = 0; i < 4; i++, bits <<= 6 ) {
00482                      if ( wraplen != -1 && lenused >= 0 && lenused++ > wraplen ) {
00483                             *out++ = '\n';
00484                             *out++ = ' ';
00485                             lenused = 2;
00486                      }
00487 
00488                      if (( i == 3 && pad > 0 ) || ( i == 2 && pad == 2 )) {
00489                             /* Pad as appropriate */
00490                             *out++ = '=';
00491                      } else {
00492                             /* get b64 digit from low order 6 bits */
00493                             *out++ = nib2b64[ (bits & 0xfc0000L) >> 18 ];
00494                      }
00495               }
00496        }
00497 
00498        *out = '\0';
00499 
00500        return( out - dst );
00501 }
00502 
00503 int
00504 ldif_base64_encode( unsigned char *src, char *dst, int srclen, int lenused )
00505 {
00506     return ldif_base64_encode_internal( src, dst, srclen, lenused, LDIF_MAX_LINE_WIDTH );
00507 }
00508 
00509 int
00510 ldif_base64_encode_nowrap( unsigned char *src, char *dst, int srclen, int lenused )
00511 {
00512     return ldif_base64_encode_internal( src, dst, srclen, lenused, -1 );
00513 }
00514 
00515 
00516 /*
00517  * return malloc'd, zero-terminated LDIF line
00518  */
00519 char *
00520 ldif_type_and_value_with_options( char *type, char *val, int vlen,
00521        unsigned long options )
00522 {
00523     char      *buf, *p;
00524     int              tlen;
00525 
00526     tlen = strlen( type );
00527     if (( buf = (char *)malloc( LDIF_SIZE_NEEDED( tlen, vlen ) + 1 )) !=
00528            NULL ) {
00529        p = buf;
00530        ldif_put_type_and_value_with_options( &p, type, val, vlen, options );
00531        *p = '\0';
00532     }
00533 
00534     return( buf );
00535 }
00536 
00537 char *
00538 ldif_type_and_value( char *type, char *val, int vlen )
00539 {
00540     return ldif_type_and_value_with_options( type, val, vlen, 0 );
00541 }
00542 
00543 char *
00544 ldif_type_and_value_nowrap( char *type, char *val, int vlen )
00545 {
00546     return ldif_type_and_value_with_options( type, val, vlen, LDIF_OPT_NOWRAP );
00547 }
00548 
00549 /*
00550  * ldif_get_entry - read the next ldif entry from the FILE referenced
00551  * by fp. return a pointer to a malloc'd, null-terminated buffer. also
00552  * returned is the last line number read, in *lineno.
00553  */
00554 char *
00555 ldif_get_entry( FILE *fp, int *lineno )
00556 {
00557        char   line[BUFSIZ];
00558        char   *buf;
00559        int    max, cur, len, gotsome;
00560 
00561        buf = NULL;
00562        max = cur = gotsome = 0;
00563        while ( fgets( line, sizeof(line), fp ) != NULL ) {
00564               if ( lineno != NULL ) {
00565                      (*lineno)++;
00566               }
00567               /* ldif entries are terminated by a \n on a line by itself */
00568               if ( line[0] == '\0' || line[0] == '\n'
00569 #if !defined( XP_WIN32 )
00570                    || ( line[0] == '\r' && line[1] == '\n' ) /* DOS format */
00571 #endif
00572                  ) {
00573                      if ( gotsome ) {
00574                             break;
00575                      } else {
00576                             continue;
00577                      }
00578               } else if ( line[0] == '#' ) {
00579                      continue;
00580               }
00581               gotsome = 1;
00582               len = strlen( line );
00583 #if !defined( XP_WIN32 )
00584               /* DOS format */
00585               if ( len > 0 && line[len-1] == '\r' ) {
00586                      --len;
00587                      line[len] = '\0';
00588               } else if ( len > 1 && line[len-2] == '\r' && line[len-1] == '\n' ) {
00589                      --len;
00590                      line[len-1] = line[len];
00591                      line[len] = '\0';
00592               }
00593 #endif
00594               while ( cur + (len + 1) > max ) {
00595                      if ( buf == NULL ) {
00596                             max += BUFSIZ;
00597                             buf = (char *) malloc( max );
00598                      } else {
00599                             max *= 2;
00600                             buf = (char *) realloc( buf, max );
00601                      }
00602                      if ( buf == NULL ) {
00603                             return( NULL );
00604                      }
00605               }
00606 
00607               memcpy( buf + cur, line, len + 1 );
00608               cur += len;
00609        }
00610 
00611        return( buf );
00612 }