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