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