]> git.sur5r.net Git - openldap/blob - clients/ud/edit.c
Use a cursor to step through ID_BLOCKS.
[openldap] / clients / ud / edit.c
1 /*
2  * Copyright (c) 1994  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 #include "portable.h"
14
15 #include <stdio.h>
16
17 #include <ac/stdlib.h>
18
19 #include <ac/signal.h>
20 #include <ac/string.h>
21 #include <ac/ctype.h>
22 #include <ac/time.h>
23 #include <ac/wait.h>
24 #include <ac/unistd.h>
25
26 #ifdef HAVE_SYS_RESOURCE_H
27 #include <sys/resource.h>
28 #endif
29 #ifdef HAVE_PROCESS_H
30 #include <process.h>
31 #endif
32
33 #include <lber.h>
34 #include <ldap.h>
35
36 #include "ldap_defaults.h"
37 #include "ud.h"
38
39 static int  load_editor( void );
40 static int  modifiable( char *s, short flag );
41 static int  print_attrs_and_values( FILE *fp, struct attribute *attrs, short flag );
42 static int  ovalues( char *attr );
43 static void write_entry( void );
44
45 static char *entry_temp_file;
46
47
48 void
49 edit( char *who )
50 {
51         LDAPMessage *mp;                        /* returned from find() */
52         char *dn, **rdns;                       /* distinguished name */
53         char name[MED_BUF_SIZE];                /* entry to modify */
54
55 #ifdef DEBUG
56         if (debug & D_TRACE)
57                 printf("->edit(%s)\n", who);
58 #endif
59         /*
60          *  One must be bound in order to edit an entry.
61          */
62         if (bind_status == UD_NOT_BOUND) {
63                 if (auth((char *) NULL, 1) < 0)
64                         return;
65         }
66
67         /*
68          *  First, decide what entry we are going to modify.  If the
69          *  user has not included a name on the modify command line,
70          *  we will use the person who was last looked up with a find
71          *  command.  If there is no value there either, we don't know
72          *  who to modify.
73          *
74          *  Once we know who to modify, be sure that they exist, and
75          *  parse out their DN.
76          */
77         if (who == NULL) {
78                 if (verbose) {
79                         printf("  Enter the name of the person or\n");
80                         printf("  group whose entry you want to edit: ");
81                 }
82                 else
83                         printf("  Edit whose entry? ");
84                 fflush(stdout);
85                 fetch_buffer(name, sizeof(name), stdin);
86                 if (name[0] != '\0')
87                         who = name;
88                 else
89                         return;
90         }
91         if ((mp = find(who, TRUE)) == NULL) {
92                 (void) ldap_msgfree(mp);
93                 printf("  Could not locate \"%s\" in the Directory.\n", who);
94                 return;
95         }
96         dn = ldap_get_dn(ld, ldap_first_entry(ld, mp));
97         rdns = ldap_explode_dn(dn, TRUE);
98         ldap_memfree(dn);
99         if (verbose) {
100                 printf("\n  Editing directory entry \"%s\"...\n", *rdns);
101         }
102         parse_answer(mp);
103         (void) ldap_msgfree(mp);
104         (void) ldap_value_free(rdns);
105         if (load_editor() < 0)
106                 return;
107         write_entry();
108         (void) unlink(entry_temp_file);
109         ldap_uncache_entry(ld, Entry.DN);
110         return;
111 }
112
113 static int
114 load_editor( void )
115 {
116         FILE *fp;
117         char *cp, *editor = UD_DEFAULT_EDITOR;
118         static char template[MED_BUF_SIZE];
119 #ifndef HAVE_SPAWNLP
120         int pid;
121         int status;
122 #endif
123         int rc;
124         
125 #ifdef DEBUG
126         if (debug & D_TRACE)
127                 printf("->load_editor()\n");
128 #endif
129
130         /* write the entry into a temp file */
131         (void) strcpy(template, "/tmp/udEdit.XXXXXX");
132         if ((entry_temp_file = mktemp(template)) == NULL) {
133                 perror("mktemp");
134                 return(-1);
135         }
136         if ((fp = fopen(entry_temp_file, "w")) == NULL) {
137                 perror("fopen");
138                 return(-1);
139         }
140         fprintf(fp, "## Directory entry of %s\n", Entry.name);
141         fprintf(fp, "##\n");
142         fprintf(fp, "## Syntax is:\n");
143         fprintf(fp, "## <attribute-name>\n");
144         fprintf(fp, "##         <TAB> <value 1>\n");
145         fprintf(fp, "##         <TAB>   :  :\n");
146         fprintf(fp, "##         <TAB> <value N>\n");
147         fprintf(fp, "## Lines beginning with a hash mark are comments.\n");
148         fprintf(fp, "##\n");
149         fflush(fp);
150         if (isgroup())
151                 rc = print_attrs_and_values(fp, Entry.attrs, ATTR_FLAG_GROUP_MOD);
152         else
153                 rc = print_attrs_and_values(fp, Entry.attrs, ATTR_FLAG_PERSON_MOD);
154         fclose(fp);
155
156         if ( rc != 0 ) {
157             (void) unlink(entry_temp_file);
158             return( rc );
159         }
160
161         /* edit the temp file with the editor of choice */
162         if ((cp = getenv("EDITOR")) != NULL)
163                 editor = cp;
164         if (verbose) {
165                 char    *p;
166
167                 if (( p = strrchr( editor, '/' )) == NULL ) {
168                         p = editor;
169                 } else {
170                         ++p;
171                 }
172                 printf("  Using %s as the editor...\n", p );
173 #ifndef HAVE_SPAWNLP
174                 sleep(2);
175 #endif
176         }
177 #ifdef HAVE_SPAWNLP
178         rc = _spawnlp( _P_WAIT, editor, editor, entry_temp_file, NULL );
179         if(rc != 0) {
180                 fatal("spawnlp");
181         }
182 #else
183         if ((pid = fork()) == 0) {      
184                 /* child - edit the Directory entry */
185                 (void) SIGNAL(SIGINT, SIG_IGN);
186                 (void) execlp(editor, editor, entry_temp_file, NULL);
187                 /*NOTREACHED*/
188                 (void) fatal(editor);   
189         }
190         else if (pid > 0) {
191                 /* parent - wait until the child proc is done editing */
192                 RETSIGTYPE (*handler)();
193                 handler = SIGNAL(SIGINT, SIG_IGN);
194                 (void) wait(&status);
195                 (void) SIGNAL(SIGINT, handler);
196         }
197         else {
198                 fatal("fork");
199                 /*NOTREACHED*/
200         }
201 #endif
202         return(0);
203 }
204
205 static int
206 print_attrs_and_values( FILE *fp, struct attribute *attrs, short int flag )
207 {
208         register int i, j;
209
210         for (i = 0; attrs[i].quipu_name != NULL; i++) {
211                 if (!modifiable(attrs[i].quipu_name,
212                         (short) (flag|ATTR_FLAG_MAY_EDIT)))
213                 {
214                         continue;
215                 }
216
217                 fprintf(fp, "%s\n", attrs[i].quipu_name);
218                 if ( attrs[i].number_of_values > MAX_VALUES ) {
219                         printf("  The %s attribute has more than %d values.\n",
220                                 attrs[i].quipu_name, MAX_VALUES );
221                         printf("  You cannot use the vedit command on this entry.  Sorry!\n" );
222                         return( -1 );
223                 }
224                 for (j = 0; j < attrs[i].number_of_values; j++)
225                         fprintf(fp, "\t%s\n", attrs[i].values[j]);
226         }
227         return( 0 );
228 }
229
230 static int
231 modifiable( char *s, short int flag )
232 {
233         register int i;
234
235         for (i = 0; attrlist[i].quipu_name != NULL; i++) {
236                 if (strcasecmp(s, attrlist[i].quipu_name))
237                         continue;
238                 if ((attrlist[i].flags & flag) == ATTR_FLAG_NONE)
239                         return(FALSE);
240                 return(TRUE);
241         }
242         /* should never be here */
243         return(FALSE);
244 }
245
246 static void
247 write_entry( void )
248 {
249         int i = 0, j, number_of_values = -1;
250
251         FILE *fp;
252         char *cp, line[LARGE_BUF_SIZE], *values[MAX_VALUES], **vp;
253
254         LDAPMod *mods[MAX_ATTRS + 1];
255         LDAPMod *modp = NULL;
256
257         /* parse the file and write the values to the Directory */
258         if ((fp = fopen(entry_temp_file, "r")) == NULL) {
259                 perror("fopen");
260                 return;
261         }
262         for (;;) {
263                 (void) fgets(line, sizeof(line), fp);
264                 if (feof(fp))
265                         break;
266                 line[strlen(line) - 1] = '\0';          /* kill newline */
267                 cp = line;
268                 if (*cp == '#')
269                         continue;
270                 if (isspace((unsigned char)*cp)) {      /* value */
271                         while (isspace((unsigned char)*cp))
272                                 cp++;
273                         values[number_of_values++] = strdup(cp);
274                         if ( number_of_values >= MAX_VALUES ) {
275                                 printf("  A maximum of %d values can be handled at one time.  Sorry!\n", MAX_VALUES );
276                                 return;
277                         }
278                         continue;
279                 }
280                 /* attribute */
281                 while (isspace((unsigned char)*cp))
282                         cp++;
283                 /*
284                  *  If the number of values is greater than zero, then we
285                  *  know that this is not the first time through this
286                  *  loop, and we also know that we have a little bit
287                  *  of work to do:
288                  *
289                  *      o The modify operation needs to be changed from
290                  *        a DELETE to a REPLACE
291                  *
292                  *      o The list of values pointer needs to be changed
293                  *        from NULL, to a NULL-terminated list of char
294                  *        pointers.
295                  */
296                 if (number_of_values > 0) {
297                         modp->mod_op = LDAP_MOD_REPLACE;
298                         if ((vp = (char **) Malloc(sizeof(char *) * (number_of_values + 2))) == (char **) NULL) {
299                                 fatal("Malloc");
300                                 /*NOTREACHED*/
301                         }
302                         modp->mod_values = vp;
303                         for (j = 0; j < number_of_values; j++) {
304                                 *vp++ = strdup(values[j]);
305                                 (void) Free(values[j]);
306                         }
307                         *vp = NULL;
308                 }
309                 /*
310                  *  If there are no values, and there were no values to begin
311                  *  with, then there is nothing to do.
312                  */
313                 if ((number_of_values == 0) && (ovalues(modp->mod_type) == 0)) {
314 #ifdef DEBUG
315                         if (debug & D_MODIFY)
316                                 printf(" %s has zero values - skipping\n",
317                                                 modp->mod_type);
318 #endif
319                         (void) Free(modp->mod_type);
320                         modp->mod_type = strdup(cp);
321                         modp->mod_op = LDAP_MOD_DELETE;
322                         modp->mod_values = NULL;
323                         continue;
324                 }
325                 /*
326                  *  Fetch a new modify structure.
327                  *
328                  *  Assume a DELETE operation with no values.
329                  */
330                 if ((modp = (LDAPMod *) Malloc(sizeof(LDAPMod))) == NULL) {
331                         fatal("Malloc");
332                         /*NOTREACHED*/
333                 }
334                 modp->mod_values = NULL;
335                 modp->mod_type = strdup(cp);
336                 modp->mod_op = LDAP_MOD_DELETE;
337                 mods[i++] = modp;
338                 number_of_values = 0;
339         }
340         fclose(fp);
341
342         /* check the last one too */
343         if (number_of_values > 0) {
344                 modp->mod_op = LDAP_MOD_REPLACE;
345                 /*
346                  *  Fetch some value pointers.
347                  *
348                  *      number_of_values        To store the values
349                  *              1               For the NULL terminator
350                  *              1               In case we need it to store
351                  *                              the RDN as on of the values
352                  *                              of 'cn' in case it isn't there
353                  */
354                 if ((vp = (char **) Malloc(sizeof(char *) * (number_of_values +
355                                                  2))) == (char **) NULL) {
356                         fatal("Malloc");
357                         /*NOTREACHED*/
358                 }
359                 modp->mod_values = vp;
360                 for (j = 0; j < number_of_values; j++) {
361                         *vp++ = strdup(values[j]);
362                         (void) Free(values[j]);
363                 }
364                 *vp = NULL;
365         }
366         else if ((number_of_values == 0) && 
367                                 (ovalues(mods[i - 1]->mod_type) == 0)) {
368 #ifdef DEBUG
369                 if (debug & D_MODIFY)
370                         printf(" %s has zero values - skipping\n",
371                                         mods[i - 1]->mod_type);
372 #endif
373                 Free(mods[i - 1]->mod_type);
374                 Free(mods[i - 1]);
375                 i--;
376         }
377         mods[i] = (LDAPMod *) NULL;
378
379         /* 
380          * If one of the mods pointers is 'cn', be sure that the RDN is one
381          * of the values.
382          */
383         for (j = 0; j < i; j++) {
384                 if (strcasecmp("cn", mods[j]->mod_type))
385                         continue;
386                 /*
387                  *  True only if there WERE values, but the person deleted
388                  *  them all.
389                  */
390                 if (mods[j]->mod_values == NULL) {
391                         mods[j]->mod_op = LDAP_MOD_REPLACE;
392                         if ((vp = (char **) Malloc(sizeof(char *) * 2)) == (char **) NULL) {
393                                 fatal("Malloc");
394                                 /*NOTREACHED*/
395                         }
396                         mods[j]->mod_values = vp;
397                         *vp++ = strdup(Entry.name);
398                         *vp = NULL;
399                         break;
400                 }
401                 /*
402                  *  Be sure that one of the values of 'cn' is the RDN.
403                  */
404                 for (vp = mods[j]->mod_values; *vp != NULL; vp++) {
405                         if (strcasecmp(*vp, Entry.DN))
406                                 continue;
407                         break;
408                 }
409                 if (*vp == NULL) {
410                         *vp++ = strdup(Entry.name);
411                         *vp = NULL;
412                         break;
413                 }
414         }
415 #ifdef DEBUG
416         if (debug & D_MODIFY) {
417                 register int x, y;
418
419                 printf("  ld = 0x%x\n", ld);
420                 printf("  dn = [%s]\n", Entry.DN);
421                 for (x = 0; mods[x] != (LDAPMod *) NULL; x++) {
422                         printf("  mods[%d]->mod_op = %s\n", 
423                                         x, code_to_str(mods[x]->mod_op));
424                         printf("  mods[%d]->mod_type = %s\n", 
425                                                         x, mods[x]->mod_type);
426                         if (mods[x]->mod_values == NULL)
427                                 printf("  mods[%d]->mod_values = NULL\n", x);
428                         else {
429                                 for (y = 0; mods[x]->mod_values[y] != NULL; y++)
430                                    printf("  mods[%d]->mod_values[%1d] = %s\n", 
431                                                 x, y, mods[x]->mod_values[y]);
432                                 printf("  mods[%d]->mod_values[%1d] = NULL\n",
433                                                                         x, y);
434                         }
435                         printf("\n");
436                 }
437         }
438 #endif
439         if (ldap_modify_s(ld, Entry.DN, mods))
440                 mod_perror(ld);
441         else
442                 printf("  Modification complete.\n" );
443
444         /* clean up */
445         for (i = 0; mods[i] != NULL; i++)
446                 free_mod_struct(mods[i]);
447         return;
448 }
449
450 static int
451 ovalues( char *attr )
452 {
453         struct attribute *ap;
454
455         /*
456          *  Lookup the attribute with quipu_name 'attr' in the Entry
457          *  structure and return the number of values.
458          */
459         for (ap = Entry.attrs; ap->quipu_name != NULL; ap++) {
460                 if (!strcasecmp(ap->quipu_name, attr))
461                         return(ap->number_of_values);
462         }
463         /* should never get to this point unless 'attr' was something odd */
464         return(0);
465 }