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