]> git.sur5r.net Git - openldap/blob - clients/tools/common.c
import tool_perror
[openldap] / clients / tools / common.c
1 /* common.c - common routines for the ldap client tools */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 1998-2005 The OpenLDAP Foundation.
6  * Portions Copyright 2003 Kurt D. Zeilenga.
7  * Portions Copyright 2003 IBM Corporation.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 /* ACKNOWLEDGEMENTS:
19  * This file was initially created by Hallvard B. Furuseth based (in
20  * part) upon argument parsing code for individual tools located in
21  * this directory.   Additional contributors include:
22  *   Kurt D. Zeilenga (additional common argument and control support)
23  */
24
25 #include "portable.h"
26
27 #include <stdio.h>
28
29 #include <ac/stdlib.h>
30 #include <ac/signal.h>
31 #include <ac/string.h>
32 #include <ac/unistd.h>
33 #include <ac/errno.h>
34
35 #ifdef HAVE_CYRUS_SASL
36 #ifdef HAVE_SASL_SASL_H
37 #include <sasl/sasl.h>
38 #else
39 #include <sasl.h>
40 #endif
41 #endif
42
43 #include <ldap.h>
44
45 #include "lutil_ldap.h"
46 #include "ldap_defaults.h"
47 #include "ldap_pvt.h"
48 #include "lber_pvt.h"
49
50 #include "common.h"
51
52
53 int   authmethod = -1;
54 char *binddn = NULL;
55 int   contoper = 0;
56 int   debug = 0;
57 char *infile = NULL;
58 char *ldapuri = NULL;
59 char *ldaphost = NULL;
60 int   ldapport = 0;
61 #ifdef HAVE_CYRUS_SASL
62 unsigned sasl_flags = LDAP_SASL_AUTOMATIC;
63 char    *sasl_realm = NULL;
64 char    *sasl_authc_id = NULL;
65 char    *sasl_authz_id = NULL;
66 char    *sasl_mech = NULL;
67 char    *sasl_secprops = NULL;
68 #endif
69 int   use_tls = 0;
70
71 int       assertctl;
72 char *assertion = NULL;
73 char *authzid = NULL;
74 int   manageDIT = 0;
75 int   manageDSAit = 0;
76 int   noop = 0;
77 int   ppolicy = 0;
78 int   preread = 0;
79 char *preread_attrs = NULL;
80 int   postread = 0;
81 char *postread_attrs = NULL;
82
83 int   not = 0;
84 int   want_bindpw = 0;
85 struct berval passwd = { 0, NULL };
86 char *pw_file = NULL;
87 int   referrals = 0;
88 int   protocol = -1;
89 int   verbose = 0;
90 int   version = 0;
91
92 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
93 int chaining = 0;
94 static int chainingResolve = -1;
95 static int chainingContinuation = -1;
96 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
97
98 static int gotintr;
99 static int abcan;
100
101 RETSIGTYPE
102 do_sig( int sig )
103 {
104         gotintr = abcan;
105 }
106
107 /* Set in main() */
108 char *prog = NULL;
109
110 void
111 tool_init( void )
112 {
113         ldap_pvt_setlocale(LC_MESSAGES, "");
114         ldap_pvt_bindtextdomain(OPENLDAP_PACKAGE, LDAP_LOCALEDIR);
115         ldap_pvt_textdomain(OPENLDAP_PACKAGE);
116 }
117
118 void
119 tool_destroy( void )
120 {
121 #ifdef HAVE_CYRUS_SASL
122         sasl_done();
123 #endif
124 #ifdef HAVE_TLS
125         ldap_pvt_tls_destroy();
126 #endif
127 }
128
129 void
130 tool_common_usage( void )
131 {
132         static const char *const descriptions[] = {
133 N_("  -c         continuous operation mode (do not stop on errors)\n"),
134 N_("  -C         chase referrals (anonymously)\n"),
135 N_("  -d level   set LDAP debugging level to `level'\n"),
136 N_("  -D binddn  bind DN\n"),
137 N_("  -e [!]<ext>[=<extparam>] general extensions (! indicates criticality)\n")
138 N_("             [!]assert=<filter>     (an RFC 2254 Filter)\n")
139 N_("             [!]authzid=<authzid>   (\"dn:<dn>\" or \"u:<user>\")\n")
140 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
141 N_("             [!]chaining[=<resolveBehavior>[/<continuationBehavior>]]\n")
142 N_("                     one of \"chainingPreferred\", \"chainingRequired\",\n")
143 N_("                     \"referralsPreferred\", \"referralsRequired\"\n")
144 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
145 #ifdef LDAP_DEVEL
146 N_("             [!]manageDIT\n")
147 #endif
148 N_("             [!]manageDSAit\n")
149 N_("             [!]noop\n")
150 #ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
151 N_("             ppolicy\n")
152 #endif
153 N_("             [!]postread[=<attrs>]  (a comma-separated attribute list)\n")
154 N_("             [!]preread[=<attrs>]   (a comma-separated attribute list)\n"),
155 N_("             abandon, cancel (SIGINT sends abandon/cancel; not really controls)\n")
156 N_("  -f file    read operations from `file'\n"),
157 N_("  -h host    LDAP server\n"),
158 N_("  -H URI     LDAP Uniform Resource Indentifier(s)\n"),
159 N_("  -I         use SASL Interactive mode\n"),
160 N_("  -k         use Kerberos authentication\n"),
161 N_("  -K         like -k, but do only step 1 of the Kerberos bind\n"),
162 N_("  -M         enable Manage DSA IT control (-MM to make critical)\n"),
163 N_("  -n         show what would be done but don't actually do it\n"),
164 N_("  -O props   SASL security properties\n"),
165 N_("  -p port    port on LDAP server\n"),
166 N_("  -P version procotol version (default: 3)\n"),
167 N_("  -Q         use SASL Quiet mode\n"),
168 N_("  -R realm   SASL realm\n"),
169 N_("  -U authcid SASL authentication identity\n"),
170 N_("  -v         run in verbose mode (diagnostics to standard output)\n"),
171 N_("  -V         print version info (-VV only)\n"),
172 N_("  -w passwd  bind password (for simple authentication)\n"),
173 N_("  -W         prompt for bind password\n"),
174 N_("  -x         Simple authentication\n"),
175 N_("  -X authzid SASL authorization identity (\"dn:<dn>\" or \"u:<user>\")\n"),
176 N_("  -y file    Read password from file\n"),
177 N_("  -Y mech    SASL mechanism\n"),
178 N_("  -Z         Start TLS request (-ZZ to require successful response)\n"),
179 NULL
180         };
181         const char *const *cpp;
182
183         fputs( _("Common options:\n"), stderr );
184         for( cpp = descriptions; *cpp != NULL; cpp++ ) {
185                 if( strchr( options, (*cpp)[3] ) || (*cpp)[3] == ' ' ) {
186                         fputs( _(*cpp), stderr );
187                 }
188         }
189 }
190
191 void tool_perror(
192         char *func,
193         int err,
194         char *extra,
195         char *matched,
196         char *info,
197         char **refs )
198 {
199         fprintf( stderr, "%s: %s (%d)%s\n",
200                 func, ldap_err2string( err ), err, extra ? extra : "" );
201
202         if ( matched && *matched ) {
203                 fprintf( stderr, _("\tmatched DN: %s\n"), matched );
204         }
205
206         if ( info && *info ) {
207                 fprintf( stderr, _("\tadditional info: %s\n"), info );
208         }
209
210         if ( refs && *refs ) {
211                 int i;
212                 fprintf( stderr, _("\treferrals:\n") );
213                 for( i=0; refs[i]; i++ ) {
214                         fprintf( stderr, "\t\t%s\n", refs[i] );
215                 }
216         }
217 }
218
219
220 void
221 tool_args( int argc, char **argv )
222 {
223         int i;
224
225         while (( i = getopt( argc, argv, options )) != EOF ) {
226                 int crit, ival;
227                 char *control, *cvalue, *next;
228                 switch( i ) {
229                 case 'c':       /* continuous operation mode */
230                         contoper++;
231                         break;
232                 case 'C':
233                         referrals++;
234                         break;
235                 case 'd':
236                         ival = strtol( optarg, &next, 10 );
237                         if (next == NULL || next[0] != '\0') {
238                                 fprintf( stderr, "%s: unable to parse debug value \"%s\"\n", prog, optarg);
239                                 exit(EXIT_FAILURE);
240                         }
241                         debug |= ival;
242                         break;
243                 case 'D':       /* bind DN */
244                         if( binddn != NULL ) {
245                                 fprintf( stderr, "%s: -D previously specified\n", prog );
246                                 exit( EXIT_FAILURE );
247                         }
248                         binddn = ber_strdup( optarg );
249                         break;
250                 case 'e': /* general extensions (controls and such) */
251                         /* should be extended to support comma separated list of
252                          *      [!]key[=value] parameters, e.g.  -e !foo,bar=567
253                          */
254
255                         crit = 0;
256                         cvalue = NULL;
257                         if( optarg[0] == '!' ) {
258                                 crit = 1;
259                                 optarg++;
260                         }
261
262                         control = ber_strdup( optarg );
263                         if ( (cvalue = strchr( control, '=' )) != NULL ) {
264                                 *cvalue++ = '\0';
265                         }
266
267                         if ( strcasecmp( control, "assert" ) == 0 ) {
268                                 if( assertctl ) {
269                                         fprintf( stderr, "assert control previously specified\n");
270                                         exit( EXIT_FAILURE );
271                                 }
272                                 if( cvalue == NULL ) {
273                                         fprintf( stderr, "assert: control value expected\n" );
274                                         usage();
275                                 }
276
277                                 assertctl = 1 + crit;
278
279                                 assert( assertion == NULL );
280                                 assertion = cvalue;
281
282                         } else if ( strcasecmp( control, "authzid" ) == 0 ) {
283                                 if( authzid != NULL ) {
284                                         fprintf( stderr, "authzid control previously specified\n");
285                                         exit( EXIT_FAILURE );
286                                 }
287                                 if( cvalue == NULL ) {
288                                         fprintf( stderr, "authzid: control value expected\n" );
289                                         usage();
290                                 }
291                                 if( !crit ) {
292                                         fprintf( stderr, "authzid: must be marked critical\n" );
293                                         usage();
294                                 }
295
296                                 assert( authzid == NULL );
297                                 authzid = cvalue;
298
299                         } else if ( strcasecmp( control, "manageDIT" ) == 0 ) {
300                                 if( manageDIT ) {
301                                         fprintf( stderr,
302                                                 "manageDIT control previously specified\n");
303                                         exit( EXIT_FAILURE );
304                                 }
305                                 if( cvalue != NULL ) {
306                                         fprintf( stderr,
307                                                 "manageDIT: no control value expected\n" );
308                                         usage();
309                                 }
310
311                                 manageDIT = 1 + crit;
312
313                         } else if ( strcasecmp( control, "manageDSAit" ) == 0 ) {
314                                 if( manageDSAit ) {
315                                         fprintf( stderr,
316                                                 "manageDSAit control previously specified\n");
317                                         exit( EXIT_FAILURE );
318                                 }
319                                 if( cvalue != NULL ) {
320                                         fprintf( stderr,
321                                                 "manageDSAit: no control value expected\n" );
322                                         usage();
323                                 }
324
325                                 manageDSAit = 1 + crit;
326
327                         } else if ( strcasecmp( control, "noop" ) == 0 ) {
328                                 if( noop ) {
329                                         fprintf( stderr, "noop control previously specified\n");
330                                         exit( EXIT_FAILURE );
331                                 }
332                                 if( cvalue != NULL ) {
333                                         fprintf( stderr, "noop: no control value expected\n" );
334                                         usage();
335                                 }
336
337                                 noop = 1 + crit;
338
339 #ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
340                         } else if ( strcasecmp( control, "ppolicy" ) == 0 ) {
341                                 if( ppolicy ) {
342                                         fprintf( stderr, "ppolicy control previously specified\n");
343                                         exit( EXIT_FAILURE );
344                                 }
345                                 if( cvalue != NULL ) {
346                                         fprintf( stderr, "ppolicy: no control value expected\n" );
347                                         usage();
348                                 }
349                                 if( crit ) {
350                                         fprintf( stderr, "ppolicy: critical flag not allowed\n" );
351                                         usage();
352                                 }
353
354                                 ppolicy = 1;
355 #endif
356
357                         } else if ( strcasecmp( control, "preread" ) == 0 ) {
358                                 if( preread ) {
359                                         fprintf( stderr, "preread control previously specified\n");
360                                         exit( EXIT_FAILURE );
361                                 }
362
363                                 preread = 1 + crit;
364                                 preread_attrs = cvalue;
365
366                         } else if ( strcasecmp( control, "postread" ) == 0 ) {
367                                 if( postread ) {
368                                         fprintf( stderr, "postread control previously specified\n");
369                                         exit( EXIT_FAILURE );
370                                 }
371
372                                 postread = 1 + crit;
373                                 postread_attrs = cvalue;
374
375 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
376                         } else if ( strcasecmp( control, "chaining" ) == 0 ) {
377                                 chaining = 1 + crit;
378
379                                 if ( cvalue != NULL ) {
380                                         char    *continuation;
381
382                                         continuation = strchr( cvalue, '/' );
383                                         if ( continuation ) {
384                                                 /* FIXME: this makes sense only in searches */
385                                                 *continuation++ = '\0';
386                                                 if ( strcasecmp( continuation, "chainingPreferred" ) == 0 ) {
387                                                         chainingContinuation = LDAP_CHAINING_PREFERRED;
388                                                 } else if ( strcasecmp( continuation, "chainingRequired" ) == 0 ) {
389                                                         chainingContinuation = LDAP_CHAINING_REQUIRED;
390                                                 } else if ( strcasecmp( continuation, "referralsPreferred" ) == 0 ) {
391                                                         chainingContinuation = LDAP_REFERRALS_PREFERRED;
392                                                 } else if ( strcasecmp( continuation, "referralsRequired" ) == 0 ) {
393                                                         chainingContinuation = LDAP_REFERRALS_REQUIRED;
394                                                 } else {
395                                                         fprintf( stderr,
396                                                                 "chaining behavior control "
397                                                                 "continuation value \"%s\" invalid\n",
398                                                                 continuation );
399                                                         exit( EXIT_FAILURE );
400                                                 }
401                                         }
402         
403                                         if ( strcasecmp( cvalue, "chainingPreferred" ) == 0 ) {
404                                                 chainingResolve = LDAP_CHAINING_PREFERRED;
405                                         } else if ( strcasecmp( cvalue, "chainingRequired" ) == 0 ) {
406                                                 chainingResolve = LDAP_CHAINING_REQUIRED;
407                                         } else if ( strcasecmp( cvalue, "referralsPreferred" ) == 0 ) {
408                                                 chainingResolve = LDAP_REFERRALS_PREFERRED;
409                                         } else if ( strcasecmp( cvalue, "referralsRequired" ) == 0 ) {
410                                                 chainingResolve = LDAP_REFERRALS_REQUIRED;
411                                         } else {
412                                                 fprintf( stderr,
413                                                         "chaining behavior control "
414                                                         "resolve value \"%s\" invalid\n",
415                                                         cvalue);
416                                                 exit( EXIT_FAILURE );
417                                         }
418                                 }
419 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
420
421                         /* this shouldn't go here, really; but it's a feature... */
422                         } else if ( strcasecmp( control, "abandon" ) == 0 ) {
423                                 abcan = LDAP_REQ_ABANDON;
424
425                         } else if ( strcasecmp( control, "cancel" ) == 0 ) {
426                                 abcan = LDAP_REQ_EXTENDED;
427
428                         } else {
429                                 fprintf( stderr, "Invalid general control name: %s\n",
430                                         control );
431                                 usage();
432                         }
433                         break;
434                 case 'f':       /* read from file */
435                         if( infile != NULL ) {
436                                 fprintf( stderr, "%s: -f previously specified\n", prog );
437                                 exit( EXIT_FAILURE );
438                         }
439                         infile = ber_strdup( optarg );
440                         break;
441                 case 'h':       /* ldap host */
442                         if( ldaphost != NULL ) {
443                                 fprintf( stderr, "%s: -h previously specified\n", prog );
444                                 exit( EXIT_FAILURE );
445                         }
446                         ldaphost = ber_strdup( optarg );
447                         break;
448                 case 'H':       /* ldap URI */
449                         if( ldapuri != NULL ) {
450                                 fprintf( stderr, "%s: -H previously specified\n", prog );
451                                 exit( EXIT_FAILURE );
452                         }
453                         ldapuri = ber_strdup( optarg );
454                         break;
455                 case 'I':
456 #ifdef HAVE_CYRUS_SASL
457                         if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
458                                 fprintf( stderr, "%s: incompatible previous "
459                                         "authentication choice\n",
460                                         prog );
461                                 exit( EXIT_FAILURE );
462                         }
463                         authmethod = LDAP_AUTH_SASL;
464                         sasl_flags = LDAP_SASL_INTERACTIVE;
465                         break;
466 #else
467                         fprintf( stderr, "%s: was not compiled with SASL support\n",
468                                 prog );
469                         exit( EXIT_FAILURE );
470 #endif
471                 case 'k':       /* kerberos bind */
472 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_KBIND
473                         if( authmethod != -1 ) {
474                                 fprintf( stderr, "%s: -k incompatible with previous "
475                                         "authentication choice\n", prog );
476                                 exit( EXIT_FAILURE );
477                         }
478                         authmethod = LDAP_AUTH_KRBV4;
479 #else
480                         fprintf( stderr, "%s: not compiled with Kerberos support\n", prog );
481                         exit( EXIT_FAILURE );
482 #endif
483                         break;
484                 case 'K':       /* kerberos bind, part one only */
485 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_KBIND
486                         if( authmethod != -1 ) {
487                                 fprintf( stderr, "%s: incompatible with previous "
488                                         "authentication choice\n", prog );
489                                 exit( EXIT_FAILURE );
490                         }
491                         authmethod = LDAP_AUTH_KRBV41;
492 #else
493                         fprintf( stderr, "%s: not compiled with Kerberos support\n", prog );
494                         exit( EXIT_FAILURE );
495 #endif
496                         break;
497                 case 'M':
498                         /* enable Manage DSA IT */
499                         manageDSAit++;
500                         break;
501                 case 'n':       /* print operations, don't actually do them */
502                         not++;
503                         break;
504                 case 'O':
505 #ifdef HAVE_CYRUS_SASL
506                         if( sasl_secprops != NULL ) {
507                                 fprintf( stderr, "%s: -O previously specified\n", prog );
508                                 exit( EXIT_FAILURE );
509                         }
510                         if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
511                                 fprintf( stderr, "%s: incompatible previous "
512                                         "authentication choice\n", prog );
513                                 exit( EXIT_FAILURE );
514                         }
515                         authmethod = LDAP_AUTH_SASL;
516                         sasl_secprops = ber_strdup( optarg );
517 #else
518                         fprintf( stderr, "%s: not compiled with SASL support\n", prog );
519                         exit( EXIT_FAILURE );
520 #endif
521                         break;
522                 case 'p':
523                         if( ldapport ) {
524                                 fprintf( stderr, "%s: -p previously specified\n", prog );
525                                 exit( EXIT_FAILURE );
526                         }
527                         ival = strtol( optarg, &next, 10 );
528                         if ( next == NULL || next[0] != '\0' ) {
529                                 fprintf( stderr, "%s: unable to parse port number \"%s\"\n", prog, optarg );
530                                 exit( EXIT_FAILURE );
531                         }
532                         ldapport = ival;
533                         break;
534                 case 'P':
535                         ival = strtol( optarg, &next, 10 );
536                         if ( next == NULL || next[0] != '\0' ) {
537                                 fprintf( stderr, "%s: unabel to parse protocol version \"%s\"\n", prog, optarg );
538                                 exit( EXIT_FAILURE );
539                         }
540                         switch( ival ) {
541                         case 2:
542                                 if( protocol == LDAP_VERSION3 ) {
543                                         fprintf( stderr, "%s: -P 2 incompatible with version %d\n",
544                                                 prog, protocol );
545                                         exit( EXIT_FAILURE );
546                                 }
547                                 protocol = LDAP_VERSION2;
548                                 break;
549                         case 3:
550                                 if( protocol == LDAP_VERSION2 ) {
551                                         fprintf( stderr, "%s: -P 2 incompatible with version %d\n",
552                                                 prog, protocol );
553                                         exit( EXIT_FAILURE );
554                                 }
555                                 protocol = LDAP_VERSION3;
556                                 break;
557                         default:
558                                 fprintf( stderr, "%s: protocol version should be 2 or 3\n",
559                                         prog );
560                                 usage();
561                         }
562                         break;
563                 case 'Q':
564 #ifdef HAVE_CYRUS_SASL
565                         if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
566                                 fprintf( stderr, "%s: incompatible previous "
567                                         "authentication choice\n",
568                                         prog );
569                                 exit( EXIT_FAILURE );
570                         }
571                         authmethod = LDAP_AUTH_SASL;
572                         sasl_flags = LDAP_SASL_QUIET;
573                         break;
574 #else
575                         fprintf( stderr, "%s: not compiled with SASL support\n",
576                                 prog );
577                         exit( EXIT_FAILURE );
578 #endif
579                 case 'R':
580 #ifdef HAVE_CYRUS_SASL
581                         if( sasl_realm != NULL ) {
582                                 fprintf( stderr, "%s: -R previously specified\n", prog );
583                                 exit( EXIT_FAILURE );
584                         }
585                         if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
586                                 fprintf( stderr, "%s: incompatible previous "
587                                         "authentication choice\n",
588                                         prog );
589                                 exit( EXIT_FAILURE );
590                         }
591                         authmethod = LDAP_AUTH_SASL;
592                         sasl_realm = ber_strdup( optarg );
593 #else
594                         fprintf( stderr, "%s: not compiled with SASL support\n",
595                                 prog );
596                         exit( EXIT_FAILURE );
597 #endif
598                         break;
599                 case 'U':
600 #ifdef HAVE_CYRUS_SASL
601                         if( sasl_authc_id != NULL ) {
602                                 fprintf( stderr, "%s: -U previously specified\n", prog );
603                                 exit( EXIT_FAILURE );
604                         }
605                         if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
606                                 fprintf( stderr, "%s: incompatible previous "
607                                         "authentication choice\n",
608                                         prog );
609                                 exit( EXIT_FAILURE );
610                         }
611                         authmethod = LDAP_AUTH_SASL;
612                         sasl_authc_id = ber_strdup( optarg );
613 #else
614                         fprintf( stderr, "%s: not compiled with SASL support\n",
615                                 prog );
616                         exit( EXIT_FAILURE );
617 #endif
618                         break;
619                 case 'v':       /* verbose mode */
620                         verbose++;
621                         break;
622                 case 'V':       /* version */
623                         version++;
624                         break;
625                 case 'w':       /* password */
626                         passwd.bv_val = ber_strdup( optarg );
627                         {
628                                 char* p;
629
630                                 for( p = optarg; *p != '\0'; p++ ) {
631                                         *p = '\0';
632                                 }
633                         }
634                         passwd.bv_len = strlen( passwd.bv_val );
635                         break;
636                 case 'W':
637                         want_bindpw++;
638                         break;
639                 case 'y':
640                         pw_file = optarg;
641                         break;
642                 case 'Y':
643 #ifdef HAVE_CYRUS_SASL
644                         if( sasl_mech != NULL ) {
645                                 fprintf( stderr, "%s: -Y previously specified\n", prog );
646                                 exit( EXIT_FAILURE );
647                         }
648                         if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
649                                 fprintf( stderr,
650                                         "%s: incompatible with authentication choice\n", prog );
651                                 exit( EXIT_FAILURE );
652                         }
653                         authmethod = LDAP_AUTH_SASL;
654                         sasl_mech = ber_strdup( optarg );
655 #else
656                         fprintf( stderr, "%s: not compiled with SASL support\n", prog );
657                         exit( EXIT_FAILURE );
658 #endif
659                         break;
660                 case 'x':
661                         if( authmethod != -1 && authmethod != LDAP_AUTH_SIMPLE ) {
662                                 fprintf( stderr, "%s: incompatible with previous "
663                                         "authentication choice\n", prog );
664                                 exit( EXIT_FAILURE );
665                         }
666                         authmethod = LDAP_AUTH_SIMPLE;
667                         break;
668                 case 'X':
669 #ifdef HAVE_CYRUS_SASL
670                         if( sasl_authz_id != NULL ) {
671                                 fprintf( stderr, "%s: -X previously specified\n", prog );
672                                 exit( EXIT_FAILURE );
673                         }
674                         if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
675                                 fprintf( stderr, "%s: -X incompatible with "
676                                         "authentication choice\n", prog );
677                                 exit( EXIT_FAILURE );
678                         }
679                         authmethod = LDAP_AUTH_SASL;
680                         sasl_authz_id = ber_strdup( optarg );
681 #else
682                         fprintf( stderr, "%s: not compiled with SASL support\n", prog );
683                         exit( EXIT_FAILURE );
684 #endif
685                         break;
686                 case 'Z':
687 #ifdef HAVE_TLS
688                         use_tls++;
689 #else
690                         fprintf( stderr, "%s: not compiled with TLS support\n", prog );
691                         exit( EXIT_FAILURE );
692 #endif
693                         break;
694                 default:
695                         if( handle_private_option( i ) ) break;
696                         fprintf( stderr, "%s: unrecognized option -%c\n",
697                                 prog, optopt );
698                         usage();
699                 }
700         }
701
702         {
703                 /* prevent bad linking */
704                 LDAPAPIInfo api;
705                 api.ldapai_info_version = LDAP_API_INFO_VERSION;
706
707                 if ( ldap_get_option(NULL, LDAP_OPT_API_INFO, &api)
708                         != LDAP_OPT_SUCCESS )
709                 {
710                         fprintf( stderr, "%s: ldap_get_option(API_INFO) failed\n", prog );
711                         exit( EXIT_FAILURE );
712                 }
713
714                 if (api.ldapai_info_version != LDAP_API_INFO_VERSION) {
715                         fprintf( stderr, "LDAP APIInfo version mismatch: "
716                                 "library %d, header %d\n",
717                                 api.ldapai_info_version, LDAP_API_INFO_VERSION );
718                         exit( EXIT_FAILURE );
719                 }
720
721                 if( api.ldapai_api_version != LDAP_API_VERSION ) {
722                         fprintf( stderr, "LDAP API version mismatch: "
723                                 "library %d, header %d\n",
724                                 api.ldapai_api_version, LDAP_API_VERSION );
725                         exit( EXIT_FAILURE );
726                 }
727
728                 if( strcmp(api.ldapai_vendor_name, LDAP_VENDOR_NAME ) != 0 ) {
729                         fprintf( stderr, "LDAP vendor name mismatch: "
730                                 "library %s, header %s\n",
731                                 api.ldapai_vendor_name, LDAP_VENDOR_NAME );
732                         exit( EXIT_FAILURE );
733                 }
734
735                 if( api.ldapai_vendor_version != LDAP_VENDOR_VERSION ) {
736                         fprintf( stderr, "LDAP vendor version mismatch: "
737                                 "library %d, header %d\n",
738                                 api.ldapai_vendor_version, LDAP_VENDOR_VERSION );
739                         exit( EXIT_FAILURE );
740                 }
741
742                 if (version) {
743                         fprintf( stderr, "%s: %s\t(LDAP library: %s %d)\n",
744                                 prog, __Version,
745                                 LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION );
746                         if (version > 1) exit( EXIT_SUCCESS );
747                 }
748
749                 ldap_memfree( api.ldapai_vendor_name );
750                 ldap_value_free( api.ldapai_extensions );
751         }
752
753         if (protocol == -1)
754                 protocol = LDAP_VERSION3;
755
756         if (authmethod == -1 && protocol > LDAP_VERSION2) {
757 #ifdef HAVE_CYRUS_SASL
758                 authmethod = LDAP_AUTH_SASL;
759 #else
760                 authmethod = LDAP_AUTH_SIMPLE;
761 #endif
762         }
763
764         if( ldapuri == NULL ) {
765                 if( ldapport && ( ldaphost == NULL )) {
766                         fprintf( stderr, "%s: -p without -h is invalid.\n", prog );
767                         exit( EXIT_FAILURE );
768                 }
769         } else {
770                 if( ldaphost != NULL ) {
771                         fprintf( stderr, "%s: -H incompatible with -h\n", prog );
772                         exit( EXIT_FAILURE );
773                 }
774                 if( ldapport ) {
775                         fprintf( stderr, "%s: -H incompatible with -p\n", prog );
776                         exit( EXIT_FAILURE );
777                 }
778         }
779         if( protocol == LDAP_VERSION2 ) {
780                 if( assertctl || authzid || manageDIT || manageDSAit ||
781 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
782                         chaining ||
783 #endif
784                         noop || ppolicy || preread || postread )
785                 {
786                         fprintf( stderr, "%s: -e/-M incompatible with LDAPv2\n", prog );
787                         exit( EXIT_FAILURE );
788                 }
789 #ifdef HAVE_TLS
790                 if( use_tls ) {
791                         fprintf( stderr, "%s: -Z incompatible with LDAPv2\n", prog );
792                         exit( EXIT_FAILURE );
793                 }
794 #endif
795 #ifdef HAVE_CYRUS_SASL
796                 if( authmethod == LDAP_AUTH_SASL ) {
797                         fprintf( stderr, "%s: -[IOQRUXY] incompatible with LDAPv2\n",
798                                 prog );
799                         exit( EXIT_FAILURE );
800                 }
801 #endif
802         } else {
803 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_KBIND
804                 if ( authmethod == LDAP_AUTH_KRBV4 || authmethod == LDAP_AUTH_KRBV41 ) {
805                         fprintf( stderr, "%s: -k/-K incompatible with LDAPv%d\n",
806                                 prog, protocol );
807                         exit( EXIT_FAILURE );
808                 }
809 #endif
810         }
811 }
812
813
814 LDAP *
815 tool_conn_setup( int not, void (*private_setup)( LDAP * ) )
816 {
817         LDAP *ld = NULL;
818
819         if ( debug ) {
820                 if( ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &debug )
821                         != LBER_OPT_SUCCESS )
822                 {
823                         fprintf( stderr,
824                                 "Could not set LBER_OPT_DEBUG_LEVEL %d\n", debug );
825                 }
826                 if( ldap_set_option( NULL, LDAP_OPT_DEBUG_LEVEL, &debug )
827                         != LDAP_OPT_SUCCESS )
828                 {
829                         fprintf( stderr,
830                                 "Could not set LDAP_OPT_DEBUG_LEVEL %d\n", debug );
831                 }
832         }
833
834 #ifdef SIGPIPE
835         (void) SIGNAL( SIGPIPE, SIG_IGN );
836 #endif
837
838         if ( abcan ) {
839                 SIGNAL( SIGINT, do_sig );
840         }
841
842         if ( !not ) {
843                 int rc;
844
845                 if( ( ldaphost != NULL || ldapport ) && ( ldapuri == NULL ) ) {
846                         /* construct URL */
847                         LDAPURLDesc url;
848                         memset( &url, 0, sizeof(url));
849
850                         url.lud_scheme = "ldap";
851                         url.lud_host = ldaphost;
852                         url.lud_port = ldapport;
853                         url.lud_scope = LDAP_SCOPE_DEFAULT;
854
855                         ldapuri = ldap_url_desc2str( &url );
856                 }
857
858                 if ( verbose ) {
859                         fprintf( stderr, "ldap_initialize( %s )\n",
860                                 ldapuri != NULL ? ldapuri : "<DEFAULT>" );
861                 }
862                 rc = ldap_initialize( &ld, ldapuri );
863                 if( rc != LDAP_SUCCESS ) {
864                         fprintf( stderr,
865                                 "Could not create LDAP session handle for URI=%s (%d): %s\n",
866                                 ldapuri, rc, ldap_err2string(rc) );
867                         exit( EXIT_FAILURE );
868                 }
869
870                 if( private_setup ) private_setup( ld );
871
872                 /* referrals */
873                 if( ldap_set_option( ld, LDAP_OPT_REFERRALS,
874                         referrals ? LDAP_OPT_ON : LDAP_OPT_OFF ) != LDAP_OPT_SUCCESS )
875                 {
876                         fprintf( stderr, "Could not set LDAP_OPT_REFERRALS %s\n",
877                                 referrals ? "on" : "off" );
878                         exit( EXIT_FAILURE );
879                 }
880
881                 if( ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &protocol )
882                         != LDAP_OPT_SUCCESS )
883                 {
884                         fprintf( stderr, "Could not set LDAP_OPT_PROTOCOL_VERSION %d\n",
885                                 protocol );
886                         exit( EXIT_FAILURE );
887                 }
888
889                 if ( use_tls &&
890                         ( ldap_start_tls_s( ld, NULL, NULL ) != LDAP_SUCCESS ))
891                 {
892                         ldap_perror( ld, "ldap_start_tls" );
893                         if ( use_tls > 1 ) {
894                                 exit( EXIT_FAILURE );
895                         }
896                 }
897         }
898
899         return ld;
900 }
901
902
903 void
904 tool_bind( LDAP *ld )
905 {
906 #ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
907         if ( ppolicy ) {
908                 LDAPControl *ctrls[2], c;
909                 c.ldctl_oid = LDAP_CONTROL_PASSWORDPOLICYREQUEST;
910                 c.ldctl_value.bv_val = NULL;
911                 c.ldctl_value.bv_len = 0;
912                 c.ldctl_iscritical = 0;
913                 ctrls[0] = &c;
914                 ctrls[1] = NULL;
915                 ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, ctrls );
916         }
917 #endif
918
919         if ( authmethod == LDAP_AUTH_SASL ) {
920 #ifdef HAVE_CYRUS_SASL
921                 void *defaults;
922                 int rc;
923
924                 if( sasl_secprops != NULL ) {
925                         rc = ldap_set_option( ld, LDAP_OPT_X_SASL_SECPROPS,
926                                 (void *) sasl_secprops );
927
928                         if( rc != LDAP_OPT_SUCCESS ) {
929                                 fprintf( stderr,
930                                         "Could not set LDAP_OPT_X_SASL_SECPROPS: %s\n",
931                                         sasl_secprops );
932                                 exit( EXIT_FAILURE );
933                         }
934                 }
935
936                 defaults = lutil_sasl_defaults( ld,
937                         sasl_mech,
938                         sasl_realm,
939                         sasl_authc_id,
940                         passwd.bv_val,
941                         sasl_authz_id );
942
943                 rc = ldap_sasl_interactive_bind_s( ld, binddn,
944                         sasl_mech, NULL, NULL,
945                         sasl_flags, lutil_sasl_interact, defaults );
946
947                 lutil_sasl_freedefs( defaults );
948                 if( rc != LDAP_SUCCESS ) {
949                         ldap_perror( ld, "ldap_sasl_interactive_bind_s" );
950                         exit( EXIT_FAILURE );
951                 }
952 #else
953                 fprintf( stderr, "%s: not compiled with SASL support\n",
954                         prog );
955                 exit( EXIT_FAILURE );
956 #endif
957         } else {
958                 int msgid, err;
959                 LDAPMessage *result;
960                 LDAPControl **ctrls;
961                 char msgbuf[256];
962                 char *matched = NULL;
963                 char *info = NULL;
964                 char **refs = NULL;
965
966                 msgbuf[0] = 0;
967
968                 msgid = ldap_bind( ld, binddn, passwd.bv_val, authmethod );
969                 if ( msgid == -1 ) {
970                         ldap_perror( ld, "ldap_bind" );
971                         exit( EXIT_FAILURE );
972                 }
973
974                 if ( ldap_result( ld, msgid, 1, NULL, &result ) == -1 ) {
975                         ldap_perror( ld, "ldap_result" );
976                         exit( EXIT_FAILURE );
977                 }
978
979                 if ( ldap_parse_result( ld, result, &err, &matched, &info, &refs,
980                         &ctrls, 1 ) != LDAP_SUCCESS )
981                 {
982                         ldap_perror( ld, "ldap_bind parse result" );
983                         exit( EXIT_FAILURE );
984                 }
985
986 #ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
987                 if ( ctrls && ppolicy ) {
988                         LDAPControl *ctrl;
989                         int expire, grace, len = 0;
990                         LDAPPasswordPolicyError pErr = -1;
991                         
992                         ctrl = ldap_find_control( LDAP_CONTROL_PASSWORDPOLICYRESPONSE,
993                                 ctrls );
994
995                         if ( ctrl && ldap_parse_passwordpolicy_control( ld, ctrl,
996                                 &expire, &grace, &pErr ) == LDAP_SUCCESS )
997                         {
998                                 if ( pErr != PP_noError ){
999                                         msgbuf[0] = ';';
1000                                         msgbuf[1] = ' ';
1001                                         strcpy( msgbuf+2, ldap_passwordpolicy_err2txt( pErr ));
1002                                         len = strlen( msgbuf );
1003                                 }
1004                                 if ( expire >= 0 ) {
1005                                         sprintf( msgbuf+len,
1006                                                 " (Password expires in %d seconds)",
1007                                                 expire );
1008                                 } else if ( grace >= 0 ) {
1009                                         sprintf( msgbuf+len,
1010                                                 " (Password expired, %d grace logins remain)",
1011                                                 grace );
1012                                 }
1013                         }
1014                 }
1015 #endif
1016
1017                 if ( ctrls ) {
1018                         ldap_controls_free( ctrls );
1019                 }
1020
1021                 if ( err != LDAP_SUCCESS
1022                         || msgbuf[0]
1023                         || ( matched && matched[ 0 ] )
1024                         || ( info && info[ 0 ] )
1025                         || refs )
1026                 {
1027                         tool_perror( "ldap_bind", err, msgbuf, matched, info, refs );
1028
1029                         if( matched ) ber_memfree( matched );
1030                         if( info ) ber_memfree( info );
1031                         if( refs ) ber_memvfree( (void **)refs );
1032
1033                         if ( err != LDAP_SUCCESS ) exit( EXIT_FAILURE );
1034                 }
1035         }
1036 }
1037
1038 void
1039 tool_unbind( LDAP *ld )
1040 {
1041         int err = ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, NULL );
1042
1043         if ( err != LDAP_OPT_SUCCESS ) {
1044                 fprintf( stderr, "Could not unset controls\n");
1045         }
1046
1047         (void) ldap_unbind_ext( ld, NULL, NULL );
1048 }
1049
1050
1051 /* Set server controls.  Add controls extra_c[0..count-1], if set. */
1052 void
1053 tool_server_controls( LDAP *ld, LDAPControl *extra_c, int count )
1054 {
1055         int i = 0, j, crit = 0, err;
1056         LDAPControl c[10], **ctrls;
1057
1058         ctrls = (LDAPControl**) malloc(sizeof(c) + (count+1)*sizeof(LDAPControl*));
1059         if ( ctrls == NULL ) {
1060                 fprintf( stderr, "No memory\n" );
1061                 exit( EXIT_FAILURE );
1062         }
1063
1064         if ( assertctl ) {
1065                 BerElementBuffer berbuf;
1066                 BerElement *ber = (BerElement *)&berbuf;
1067                 
1068                 if( assertion == NULL || *assertion == '\0' ) {
1069                         fprintf( stderr, "Assertion=<empty>\n" );
1070                         exit( EXIT_FAILURE );
1071                 }
1072
1073                 ber_init2( ber, NULL, LBER_USE_DER );
1074
1075                 err = ldap_pvt_put_filter( ber, assertion );
1076                 if( err < 0 ) {
1077                         fprintf( stderr, "assertion encode failed (%d)\n", err );
1078                         exit( EXIT_FAILURE );
1079                 }
1080
1081                 err = ber_flatten2( ber, &c[i].ldctl_value, 0 );
1082                 if( err < 0 ) {
1083                         fprintf( stderr, "assertion flatten failed (%d)\n", err );
1084                         exit( EXIT_FAILURE );
1085                 }
1086
1087                 c[i].ldctl_oid = LDAP_CONTROL_ASSERT;
1088                 c[i].ldctl_iscritical = assertctl > 1;
1089                 ctrls[i] = &c[i];
1090                 i++;
1091         }
1092
1093         if ( authzid ) {
1094                 c[i].ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
1095                 c[i].ldctl_value.bv_val = authzid;
1096                 c[i].ldctl_value.bv_len = strlen( authzid );
1097                 c[i].ldctl_iscritical = 1;
1098                 ctrls[i] = &c[i];
1099                 i++;
1100         }
1101
1102         if ( manageDIT ) {
1103                 c[i].ldctl_oid = LDAP_CONTROL_MANAGEDIT;
1104                 BER_BVZERO( &c[i].ldctl_value );
1105                 c[i].ldctl_iscritical = manageDIT > 1;
1106                 ctrls[i] = &c[i];
1107                 i++;
1108         }
1109
1110         if ( manageDSAit ) {
1111                 c[i].ldctl_oid = LDAP_CONTROL_MANAGEDSAIT;
1112                 BER_BVZERO( &c[i].ldctl_value );
1113                 c[i].ldctl_iscritical = manageDSAit > 1;
1114                 ctrls[i] = &c[i];
1115                 i++;
1116         }
1117
1118         if ( noop ) {
1119                 c[i].ldctl_oid = LDAP_CONTROL_NOOP;
1120                 BER_BVZERO( &c[i].ldctl_value );
1121                 c[i].ldctl_iscritical = noop > 1;
1122                 ctrls[i] = &c[i];
1123                 i++;
1124         }
1125
1126         if ( preread ) {
1127                 char berbuf[LBER_ELEMENT_SIZEOF];
1128                 BerElement *ber = (BerElement *)berbuf;
1129                 char **attrs = NULL;
1130
1131                 if( preread_attrs ) {
1132                         attrs = ldap_str2charray( preread_attrs, "," );
1133                 }
1134
1135                 ber_init2( ber, NULL, LBER_USE_DER );
1136
1137                 if( ber_printf( ber, "{v}", attrs ) == -1 ) {
1138                         fprintf( stderr, "preread attrs encode failed.\n" );
1139                         exit( EXIT_FAILURE );
1140                 }
1141
1142                 err = ber_flatten2( ber, &c[i].ldctl_value, 0 );
1143                 if( err < 0 ) {
1144                         fprintf( stderr, "preread flatten failed (%d)\n", err );
1145                         exit( EXIT_FAILURE );
1146                 }
1147
1148                 c[i].ldctl_oid = LDAP_CONTROL_PRE_READ;
1149                 c[i].ldctl_iscritical = preread > 1;
1150                 ctrls[i] = &c[i];
1151                 i++;
1152
1153                 if( attrs ) ldap_charray_free( attrs );
1154         }
1155
1156         if ( postread ) {
1157                 char berbuf[LBER_ELEMENT_SIZEOF];
1158                 BerElement *ber = (BerElement *)berbuf;
1159                 char **attrs = NULL;
1160
1161                 if( postread_attrs ) {
1162                         attrs = ldap_str2charray( postread_attrs, "," );
1163                 }
1164
1165                 ber_init2( ber, NULL, LBER_USE_DER );
1166
1167                 if( ber_printf( ber, "{v}", attrs ) == -1 ) {
1168                         fprintf( stderr, "postread attrs encode failed.\n" );
1169                         exit( EXIT_FAILURE );
1170                 }
1171
1172                 err = ber_flatten2( ber, &c[i].ldctl_value, 0 );
1173                 if( err < 0 ) {
1174                         fprintf( stderr, "postread flatten failed (%d)\n", err );
1175                         exit( EXIT_FAILURE );
1176                 }
1177
1178                 c[i].ldctl_oid = LDAP_CONTROL_POST_READ;
1179                 c[i].ldctl_iscritical = postread > 1;
1180                 ctrls[i] = &c[i];
1181                 i++;
1182
1183                 if( attrs ) ldap_charray_free( attrs );
1184         }
1185
1186 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1187         if ( chaining ) {
1188                 if ( chainingResolve > -1 ) {
1189                         BerElementBuffer berbuf;
1190                         BerElement *ber = (BerElement *)&berbuf;
1191
1192                         ber_init2( ber, NULL, LBER_USE_DER );
1193
1194                         err = ber_printf( ber, "{e" /* } */, chainingResolve );
1195                         if ( err == -1 ) {
1196                                 ber_free( ber, 1 );
1197                                 fprintf( stderr, _("Chaining behavior control encoding error!\n") );
1198                                 exit( EXIT_FAILURE );
1199                         }
1200
1201                         if ( chainingContinuation > -1 ) {
1202                                 err = ber_printf( ber, "e", chainingContinuation );
1203                                 if ( err == -1 ) {
1204                                         ber_free( ber, 1 );
1205                                         fprintf( stderr, _("Chaining behavior control encoding error!\n") );
1206                                         exit( EXIT_FAILURE );
1207                                 }
1208                         }
1209
1210                         err = ber_printf( ber, /* { */ "N}" );
1211                         if ( err == -1 ) {
1212                                 ber_free( ber, 1 );
1213                                 fprintf( stderr, _("Chaining behavior control encoding error!\n") );
1214                                 exit( EXIT_FAILURE );
1215                         }
1216
1217                         if ( ber_flatten2( ber, &c[i].ldctl_value, 0 ) == -1 ) {
1218                                 exit( EXIT_FAILURE );
1219                         }
1220
1221                 } else {
1222                         BER_BVZERO( &c[i].ldctl_value );
1223                 }
1224
1225                 c[i].ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1226                 c[i].ldctl_iscritical = chaining > 1;
1227                 ctrls[i] = &c[i];
1228                 i++;
1229         }
1230 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1231
1232         while ( count-- ) {
1233                 ctrls[i++] = extra_c++;
1234         }
1235         ctrls[i] = NULL;
1236
1237         err = ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, ctrls );
1238
1239         if ( err != LDAP_OPT_SUCCESS ) {
1240                 for ( j = 0; j < i; j++ ) {
1241                         if ( ctrls[j]->ldctl_iscritical ) crit = 1;
1242                 }
1243                 fprintf( stderr, "Could not set %scontrols\n",
1244                         crit ? "critical " : "" );
1245         }
1246
1247         free( ctrls );
1248         if ( crit ) {
1249                 exit( EXIT_FAILURE );
1250         }
1251 }
1252
1253 int
1254 tool_check_abandon( LDAP *ld, int msgid )
1255 {
1256         int     rc;
1257
1258         switch ( gotintr ) {
1259         case LDAP_REQ_EXTENDED:
1260                 rc = ldap_cancel_s( ld, msgid, NULL, NULL );
1261                 fprintf( stderr, "got interrupt, cancel got %d: %s\n",
1262                                 rc, ldap_err2string( rc ) );
1263                 return -1;
1264
1265         case LDAP_REQ_ABANDON:
1266                 rc = ldap_abandon( ld, msgid );
1267                 fprintf( stderr, "got interrupt, abandon got %d: %s\n",
1268                                 rc, ldap_err2string( rc ) );
1269                 return -1;
1270         }
1271
1272         return 0;
1273 }
1274