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