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