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