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