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