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