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