]> git.sur5r.net Git - openldap/blob - servers/slapd/tools/chlog2replog.c
cd982936c85b2c55564ebb21e68882f862d7cb1a
[openldap] / servers / slapd / tools / chlog2replog.c
1 /*
2  * Copyright (c) 1990, 1995 Regents of the University of Michigan.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that this notice is preserved and that due credit is given
7  * to the University of Michigan at Ann Arbor. The name of the University
8  * may not be used to endorse or promote products derived from this
9  * software without specific prior written permission. This software
10  * is provided ``as is'' without express or implied warranty.
11  */
12
13 /*
14  * chlog2replog - read a quipu-style changelog on stdin and write a
15  * slapd-style replog on stdout, or write to a file, respecting
16  * slapd/slurpd locking conventions.
17  */
18
19 #include "portable.h"
20
21 #include <stdio.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <ctype.h>
25
26 #include <quipu/commonarg.h>
27 #include <quipu/attrvalue.h>
28
29 #include "ldif.h"
30
31 static int dn2ldif();
32 static void de_t61();
33
34 extern FILE *lock_fopen( char *, char *, FILE ** );
35 extern int lock_fclose( FILE *, FILE * );
36 extern char *ch_realloc( char *, unsigned long ); 
37
38 short   ldap_dn_syntax;
39 PS      rps;
40 char    *progname;
41 int     ldap_syslog = 0;
42 int     ldap_syslog_level = 0;
43
44
45 #define ST_START        0
46 #define ST_DN           2
47 #define ST_TYPE         3
48 #define ST_ARGS         4
49 #define ST_NL1          5
50 #define ST_PUNT         6
51 #define ST_BAD          7
52 #define ST_CONCAT       8
53
54 #define TY_MODIFYTYPE   1
55 #define TY_ADD          2
56 #define TY_REMOVE       3
57 #define TY_NEWRDN       4
58 #define TY_PUNT         5
59 #define TY_MODIFYARGS   6
60
61 #define MOD_ADDVALUES           1
62 #define MOD_ADDATTRIBUTE        2
63 #define MOD_REMOVEATTRIBUTE     3
64 #define MOD_REMOVEVALUES        4
65
66
67 char *
68 dn2ldap( char *edbdn )
69 {
70     DN          dn;
71     PS          str_ps;
72     char        *ldapdn;
73     int         len;
74     static int  inited = 0;
75
76     if ( !inited ) {
77         /* load & initialize quipu syntax handlers */
78         quipu_syntaxes();
79
80 #ifdef LDAP_USE_PP
81         pp_quipu_init( progname );
82 #endif
83
84         dsap_init( NULL, NULL );
85
86         if (( ldap_dn_syntax = str2syntax( "DN" )) == 0 ) {
87             return( NULL );
88         }
89         inited = 1;
90     }
91
92     if (( dn = str2dn( edbdn )) == NULLDN ) {
93         return( NULL );
94     }
95
96     if (( str_ps = ps_alloc( str_open )) == NULLPS ||
97             str_setup( str_ps, NULLCP, 0, 0 ) == NOTOK ) {
98         dn_free( dn );
99         return( NULL );
100     }
101
102     if ( dn2ldif( str_ps, dn ) != 0 ) {
103         ps_free( str_ps );
104         dn_free( dn );
105         return( NULL );
106     }
107
108     dn_free( dn );
109     len = ( str_ps->ps_ptr - str_ps->ps_base );
110
111     if (( ldapdn = malloc( len + 1 )) == NULL ) {
112         ps_free( str_ps );
113         return( NULL );
114     }
115
116     memcpy( ldapdn, str_ps->ps_base, len );
117     ldapdn[ len ] = '\0';
118     ps_free( str_ps );
119     return( ldapdn );
120 }
121
122
123 #define SEPARATOR(c)    (c == ',' || c == ';')
124 #define SPACE(c)        (c == ' ' || c == '\n')
125
126 static int
127 dn2ldif( PS ps, DN dn )
128 {
129     RDN rdn;
130     int firstrdn, rc;
131     char        *value;
132     PS  rps;
133
134     if ( dn == NULLDN ) {
135         return( 0 );
136     }
137
138     if ( dn->dn_parent != NULLDN ) {
139         if (( rc = dn2ldif( ps, dn->dn_parent )) != 0 ) {
140             return( rc );
141         }
142         ps_print( ps, ", " );
143     }
144
145     if ( (rps = ps_alloc( str_open )) == NULLPS ||
146             str_setup( rps, NULLCP, 0, 0 ) == NOTOK ) {
147         return( -1 );
148     }
149
150     firstrdn = 1;
151     for ( rdn = dn->dn_rdn; rdn != NULLRDN; rdn = rdn->rdn_next ) {
152         if ( firstrdn ) {
153             firstrdn = 0;
154         } else {
155             ps_print( ps, " + " );
156         }
157
158         AttrT_print( ps, rdn->rdn_at, EDBOUT );
159         ps_print( ps, "=" );
160
161         if ( rdn->rdn_at->oa_syntax == ldap_dn_syntax ) {
162             if (( rc = dn2ldif( rps, (DN) rdn->rdn_av.av_struct )) != 0 ) {
163                 return( rc );
164             }
165             *rps->ps_ptr = '\0';
166             value = rps->ps_base;
167         } else {
168             AttrV_print( rps, &rdn->rdn_av, EDBOUT );
169             *rps->ps_ptr = '\0';
170             value = rps->ps_base;
171             de_t61( value, 0 );
172         }
173
174         /*
175          * ,+="\\\n all go in quotes.  " and \\ need to
176          * be preceeded by \\.
177          */
178
179         if ( strpbrk( value, ",+=\"\\\n" ) != NULL || SPACE( value[0] )
180                 || SPACE( value[max( strlen(value) - 1, 0 )] ) ) {
181             char        *p, *t, *tmp;
182             int specialcount;
183
184             ps_print( ps, "\"" );
185
186             specialcount = 0;
187             for ( p = value; *p != '\0'; p++ ) {
188                 if ( *p == '"' || *p == '\\' ) {
189                     specialcount++;
190                 }
191             }
192             if ( specialcount > 0 ) {
193                 tmp = smalloc( strlen( value ) + specialcount + 1 );
194                 for ( p = value, t = tmp; *p != '\0'; p++ ) {
195                     switch ( *p ) {
196                     case '"':
197                     case '\\':
198                             *t++ = '\\';
199                             /* FALL THROUGH */
200                     default:
201                             *t++ = *p;
202                     }
203                 }
204                 *t = '\0';
205                 ps_print( ps, tmp );
206                 free( tmp );
207             } else {
208                 ps_print( ps, value );
209             }
210
211             ps_print( ps, "\"" );
212         } else {
213             ps_print( ps, value );
214         }
215
216         rps->ps_ptr = rps->ps_base;
217     }
218
219     ps_free( rps );
220
221     return( 0 );
222 }
223
224 #define T61     "{T.61}"
225 #define T61LEN  6
226
227
228
229 static void
230 de_t61( s, t61mark )
231 char    *s;
232 int     t61mark;
233 {
234         char    *next = s;
235         int     c, hex;
236
237         while ( *s ) {
238                 switch ( *s ) {
239                 case '{' :
240                         if ( strncasecmp( s, T61, T61LEN) == 0 ) {
241                                 s += T61LEN;
242                                 if ( t61mark )
243                                         *next++ = '@';
244                         } else {
245                                 *next++ = *s++;
246                         }
247                         break;
248
249                 case '\\':
250                         c = *(s + 1);
251                         if ( c == '\n' ) {
252                                 s += 2;
253                                 if ( *s == '\t' )
254                                         s++;
255                                 break;
256                         }
257                         if ( isdigit( c ) )
258                                 hex = c - '0';
259                         else if ( c >= 'A' && c <= 'F' )
260                                 hex = c - 'A' + 10;
261                         else if ( c >= 'a' && c <= 'f' )
262                                 hex = c - 'a' + 10;
263                         else {
264                                 *next++ = *s++;
265                                 break;
266                         }
267                         hex <<= 4;
268                         c = *(s + 2);
269                         if ( isdigit( c ) )
270                                 hex += c - '0';
271                         else if ( c >= 'A' && c <= 'F' )
272                                 hex += c - 'A' + 10;
273                         else if ( c >= 'a' && c <= 'f' )
274                                 hex += c - 'a' + 10;
275                         else {
276                                 *next++ = *s++;
277                                 *next++ = *s++;
278                                 break;
279                         }
280
281                         *next++ = hex;
282                         s += 3;
283                         break;
284
285                 default:
286                         *next++ = *s++;
287                         break;
288                 }
289         }
290         *next = '\0';
291 }
292
293
294
295
296 char *
297 getattr( buf, sep )
298 char *buf;
299 char sep;
300 {
301     char *val;
302 #define RBSIZE 255
303     static char retbuf[ RBSIZE ];
304
305     if (( val = strchr( buf, sep )) != NULL ) {
306         strncpy( retbuf, buf, val - buf );
307         retbuf[ val - buf ] = '\0';
308     } else {
309         retbuf[ 0 ] = '\0';
310     }
311     return( retbuf );
312 }
313
314
315 char *
316 getattr_ldif( buf )
317 char *buf;
318 {
319     return( getattr( buf, ':' ));
320 }
321
322
323 char *
324 getattr_edb( buf )
325 char *buf;
326 {
327     return( getattr( buf, '=' ));
328 }
329
330 char *
331 getval( buf, sep )
332 char *buf;
333 char sep;
334 {
335     char *val;
336
337     if (( val = strchr( buf, sep )) != NULL ) {
338         return( strdup( ++val ));
339     } else {
340         return( NULL );
341     }
342 }
343
344 char *
345 getval_ldif( buf )
346 char *buf;
347 {
348     return( getval( buf, ':' ));
349 }
350
351
352 char *
353 getval_edb( buf )
354 char *buf;
355 {
356     return( getval( buf, '=' ));
357 }
358
359
360
361
362 int
363 isDNsyntax( attr )
364 char *attr;
365 {
366     oid_table_attr *p, *name2attr();
367
368     p = name2attr( attr );
369     if ( p == ( oid_table_attr * ) 0 ) {
370         return( -1 );
371     }
372     if ( p->oa_syntax == ldap_dn_syntax ) {
373         return( 1 );
374     } else {
375         return( 0 );
376     }
377 }
378
379
380
381 void
382 print_as( as, modtype, ofp )
383 Attr_Sequence as;
384 int modtype;
385 FILE *ofp;
386 {
387     Attr_Sequence p;
388     AV_Sequence av;
389     char *attrname, *tmpdn, *obuf;
390
391     p = as;
392     for ( p = as; p != NULLATTR; p = p->attr_link) {
393         rps->ps_ptr = rps->ps_base;
394         AttrT_print( rps,  p->attr_type, EDBOUT );
395         *rps->ps_ptr = '\0';
396         attrname = strdup( rps->ps_base  );
397         if ( modtype != 0 ) {
398             switch ( modtype ) {
399             case MOD_ADDVALUES:
400             case MOD_ADDATTRIBUTE:
401                 fprintf( ofp, "add: %s\n", attrname );
402                 break;
403             case MOD_REMOVEATTRIBUTE:
404             case MOD_REMOVEVALUES:
405                 fprintf( ofp, "delete: %s\n", attrname );
406                 break;
407             default:
408                 break;
409             }
410         }
411         for ( av = p->attr_value; av != NULLAV; av = av->avseq_next ) {
412             rps->ps_ptr = rps->ps_base;
413             AttrV_print( rps, &av->avseq_av, EDBOUT );
414             *rps->ps_ptr = '\0';
415             de_t61( rps->ps_base, 0 );
416             if ( isDNsyntax( attrname )) {
417                 tmpdn = dn2ldap( rps->ps_base );
418                 obuf = ldif_type_and_value( attrname, tmpdn,
419                         strlen( tmpdn ));
420                 free( tmpdn );
421             } else {
422                 obuf = ldif_type_and_value( attrname, rps->ps_base,
423                         strlen( rps->ps_base ));
424             }
425             if ( obuf != NULL ) {
426                 fputs( obuf, ofp );
427                 free( obuf );
428             }
429         }
430         if ( modtype != 0 ) {
431             fprintf( ofp, "-\n" );
432         }
433         free( attrname );
434     }
435 }
436
437
438
439 void
440 usage( char *name )
441 {
442     fprintf( stderr, "usage: %s -d dn-suffix -r replica:port ", name );
443     fprintf( stderr, "[-r replica:port...] [-o outputfile]\n" );
444 }
445
446
447
448 main( int argc, char **argv )
449 {
450     char                *ldapdn, nbuf[ 4096 ], *buf, *p;
451     int                 state, prevstate, modstate, modtype, i;
452     int                 buflen, nbuflen;
453     Attr_Sequence       as;
454     PS                  std_ps;
455     int                 arg;
456     char                *ofile = NULL;
457     FILE                *ofp, *lfp;
458
459     extern char         *optarg;
460     char                **replicas = NULL;
461     int                 nreplicas = 0;
462     char                *dn_suffix = NULL;
463
464     if (( progname = strrchr( argv[ 0 ], '/' )) == NULL ) {
465         progname = argv[ 0 ];
466     } else {
467         ++progname;
468     }
469
470     while (( arg = getopt( argc, argv, "o:r:d:" )) != EOF ) {
471         switch( arg ) {
472         case 'o':
473             ofile = optarg;
474             break;
475         case 'r':
476             replicas = (char **) ch_realloc( (char *) replicas, (unsigned long)
477                     ( nreplicas + 2 ) * sizeof( char * ));
478             replicas[ nreplicas ] = optarg;
479             replicas[ nreplicas + 1 ] = NULL;
480             nreplicas++;
481             break;
482         case 'd':
483             dn_suffix = optarg;
484             break;
485         default:
486             usage( progname );
487             exit( 1 );
488         }
489     }
490
491     if (( dn_suffix == NULL ) || ( nreplicas == 0 )) {
492         usage( progname );
493         exit( 1 );
494     }
495
496     if ( ofile == NULL ) {
497         /* Just write to stdout */
498         ofp = stdout;
499     }
500
501
502     state = prevstate = ST_START;
503     buf = NULL;
504     as = NULL;
505     if (( std_ps = ps_alloc( std_open )) == NULLPS ||
506             std_setup( std_ps, ofp ) != OK ) {
507         fprintf( stderr, "std_ps setup failed - help!\n" );
508         exit( 1 );
509     }
510     if (( rps = ps_alloc( str_open )) == NULLPS ||
511             str_setup( rps, NULLCP, 0, 0 ) != OK ) {
512         fprintf( stderr, "rps setup failed - help!\n" );
513         exit( 1 );
514     }
515
516
517     while ( gets( nbuf ) != NULL ) {
518         if ( nbuf[ 0 ] == '\0' ) {
519             if ( state == ST_NL1 ) {
520                 if ( prevstate == ST_ARGS ) {
521                     /* We've got an attribute sequence to print */
522                     if ( modtype == TY_ADD ) {
523                         print_as( as, 0, ofp ); 
524                     } else {
525                         print_as( as, modstate, ofp ); 
526                     }
527                     /* as_print( std_ps, as, EDBOUT ); */
528                     as_free( as );
529                     as = NULL;
530                 }
531                 state = ST_START;
532                 fprintf( ofp, "\n" );
533                 fflush( ofp );
534                 /* If writing to a file, release the lock */
535                 if ( ofile != NULL ) {
536                     lock_fclose( ofp, lfp );
537                 }
538             } else {
539                 prevstate = state;
540                 state = ST_NL1;
541             }
542             continue;
543         }
544
545         /* See if we've got a line continuation to deal with */
546         nbuflen = strlen( nbuf );
547         if ( state == ST_CONCAT ) {
548             for ( p = nbuf; isspace( *p ); p++, nbuflen-- ); /* skip space */
549             buf = realloc( buf, buflen + nbuflen + 1 );
550             strcat( buf, p );
551             buflen += ( nbuflen );
552         } else {
553             if ( buf != NULL ) {
554                 free( buf );
555             }
556             buf = strdup( nbuf );
557             buflen = nbuflen;
558         }
559         if ( buf[ buflen - 1 ] == '\\' ) {
560             if ( state != ST_CONCAT ) {
561                 prevstate = state;
562             }
563             state = ST_CONCAT;
564             buf[ buflen - 1 ] = '\0';
565             buflen--;
566             continue;
567         } else if ( state == ST_CONCAT ) {
568             state = prevstate;
569         }
570
571         if ( state == ST_PUNT ) {
572             continue;
573         }
574
575         if ( state == ST_START ) {
576             /*
577              * Acquire the file lock if writing to a file.
578              */
579             if ( ofile != NULL ) {
580                 if (( ofp = lock_fopen( ofile, "a", &lfp )) == NULL ) {
581                     perror( "open" );
582                     exit( 1 );
583                 }
584             }
585             /*
586              * If we have a changelog entry, then go ahead
587              * and write the replica: lines for the replog entry.
588              */
589             for ( i = 0; replicas[ i ] != NULL; i++ ) {
590                 fprintf( ofp, "replica: %s\n", replicas[ i ] );
591             }
592             fprintf( ofp, "time: %ld\n", time( NULL ));
593             state = ST_DN;
594             continue;
595         }
596
597         if ( state == ST_DN ) {
598             /* Second line - dn (quipu-style) of entry to be modified */
599             if (( ldapdn = dn2ldap( buf )) == NULL ) {
600                 fprintf( ofp, "dn: (conversion failed)\n" );
601             } else {
602                 fprintf( ofp, "dn: %s%s\n", ldapdn, dn_suffix );
603                 free( ldapdn );
604             }
605             state = ST_TYPE;
606             continue;
607         }
608
609         if ( state == ST_TYPE ) {
610             state = ST_ARGS;
611             modstate = 0;
612             if ( !strcmp( buf, "modify" )) {
613                 modtype = TY_MODIFYTYPE;
614                 fprintf( ofp, "changetype: modify\n" );
615             } else if ( !strcmp( buf, "add" )) {
616                 modtype = TY_ADD;
617                 fprintf( ofp, "changetype: add\n" );
618                 as = NULL;
619             } else if ( !strcmp( buf, "remove" )) {
620                 modtype = TY_REMOVE;
621                 fprintf( ofp, "changetype: delete\n" );
622             } else if ( !strcmp( buf, "newrdn" )) {
623                 modtype = TY_NEWRDN;
624                 fprintf( ofp, "changetype: modrdn\n" );
625             } else {
626                 modtype = TY_PUNT;
627                 state = ST_BAD;
628             }
629             continue;
630         }
631
632         if ( state == ST_ARGS ) {
633             switch ( modtype ) {
634             case TY_NEWRDN:
635                 fprintf( ofp, "newrdn: %s\n", buf );
636                 break;
637             case TY_REMOVE:     /* No additional args */
638                 break;
639             case TY_ADD:
640                 as = as_combine( as, buf, 0 );
641                 break;
642             case TY_MODIFYTYPE:
643             case TY_MODIFYARGS:
644                 if ( buf[ 0 ] == '\0' ) {
645                     state == ST_NL1;
646                     if ( as != NULL ) {
647                         print_as( as, modstate, ofp);
648                         as_free( as );
649                         as = NULL;
650                     }
651                     continue;
652                 }
653                 if (!strcmp( buf, "addvalues" )) {
654                     if ( as != NULL ) {
655                         print_as( as, modstate, ofp );
656                         as_free( as );
657                         as = NULL;
658                     }
659                     modstate = MOD_ADDVALUES;
660                     continue;
661                 } else if (!strcmp( buf, "removevalues" )) {
662                     if ( as != NULL ) {
663                         print_as( as, modstate, ofp );
664                         as_free( as );
665                         as = NULL;
666                     }
667                     modstate = MOD_REMOVEVALUES;
668                     continue;
669                 } else if (!strcmp( buf, "addattribute" )) {
670                     if ( as != NULL ) {
671                         print_as( as, modstate, ofp );
672                         as_free( as );
673                         as = NULL;
674                     }
675                     modstate = MOD_ADDATTRIBUTE;
676                     continue;
677                 } else if (!strcmp( buf, "removeattribute" )) {
678                     if ( as != NULL ) {
679                         print_as( as, modstate, ofp );
680                         as_free( as );
681                         as = NULL;
682                     }
683                     modstate = MOD_REMOVEATTRIBUTE;
684                     continue;
685                 } 
686                 switch ( modstate ) {
687                 case MOD_ADDVALUES:
688                     as = as_combine( as, buf, 0 );
689                     break;
690                 case MOD_REMOVEVALUES:
691                     as = as_combine( as, buf, 0 );
692                     break;
693                 case MOD_ADDATTRIBUTE:
694                     as = as_combine( as, buf, 0 );
695                     break;
696                 case MOD_REMOVEATTRIBUTE:
697                     fprintf( ofp, "delete: %s\n-\n", buf);
698                     break;
699                 }
700             }
701             continue;
702         }
703     }
704
705     if ( ofile != NULL ) {
706         lock_fclose( ofp, lfp );
707         sprintf( nbuf, "%s.lock", ofile );
708         (void) unlink( nbuf );
709     }
710     exit( 0 );
711 }