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