1 /*
2  * The contents of this file are subject to the Netscape Public
3  * License Version 1.1 (the "License"); you may not use this file
4  * except in compliance with the License. You may obtain a copy of
5  * the License at http://www.mozilla.org/NPL/
6  *
7  * Software distributed under the License is distributed on an "AS
8  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
9  * implied. See the License for the specific language governing
10  * rights and limitations under the License.
11  *
12  * The Original Code is Mozilla Communicator client code, released
13  * March 31, 1998.
14  *
15  * The Initial Developer of the Original Code is Netscape
16  * Communications Corporation. Portions created by Netscape are
17  * Copyright (C) 1998-1999 Netscape Communications Corporation. All
18  * Rights Reserved.
19  *
20  * Contributor(s):
21  */
22 
23 /*
24  *  Copyright (c) 1990 Regents of the University of Michigan.
25  *  All rights reserved.
26  */
27 /*
28  *  abandon.c
29  */
30 
31 #if 0
32 #ifndef lint
33 static char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n";
34 #endif
35 #endif
36 
37 #include "ldap-int.h"
38 
39 static int do_abandon( LDAP *ld, int origid, int msgid,
40     LDAPControl **serverctrls, LDAPControl **clientctrls );
41 
42 /*
43  * ldap_abandon - perform an ldap abandon operation. Parameters:
44  *
45  *	ld		LDAP descriptor
46  *	msgid		The message id of the operation to abandon
47  *
48  * ldap_abandon returns 0 if everything went ok, -1 otherwise.
49  *
50  * Example:
51  *	ldap_abandon( ld, msgid );
52  */
53 int
54 LDAP_CALL
ldap_abandon(LDAP * ld,int msgid)55 ldap_abandon( LDAP *ld, int msgid )
56 {
57 	LDAPDebug( LDAP_DEBUG_TRACE, "ldap_abandon %d\n", msgid, 0, 0 );
58 
59 	if ( ldap_abandon_ext( ld, msgid, NULL, NULL ) == LDAP_SUCCESS ) {
60 	    return( 0 );
61 	}
62 
63 	return( -1 );
64 }
65 
66 
67 /*
68  * LDAPv3 extended abandon.
69  * Returns an LDAP error code.
70  */
71 int
72 LDAP_CALL
ldap_abandon_ext(LDAP * ld,int msgid,LDAPControl ** serverctrls,LDAPControl ** clientctrls)73 ldap_abandon_ext( LDAP *ld, int msgid, LDAPControl **serverctrls,
74 	LDAPControl **clientctrls )
75 {
76 	int	rc;
77 
78 	LDAPDebug( LDAP_DEBUG_TRACE, "ldap_abandon_ext %d\n", msgid, 0, 0 );
79 
80 	if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
81 		return( LDAP_PARAM_ERROR );
82 	}
83 
84 	LDAP_MUTEX_LOCK( ld, LDAP_CONN_LOCK );
85 	LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK );
86 	rc = do_abandon( ld, msgid, msgid, serverctrls, clientctrls );
87 
88 	/*
89 	 * XXXmcs should use cache function pointers to hook in memcache
90 	 */
91 	ldap_memcache_abandon( ld, msgid );
92 
93 	LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK );
94 	LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK );
95 
96 	return( rc );
97 }
98 
99 
100 /*
101  * Abandon all outstanding requests for msgid (included child requests
102  * spawned when chasing referrals).  This function calls itself recursively.
103  * No locking is done is this function so it must be done by the caller.
104  * Returns an LDAP error code and sets it in LDAP *ld as well
105  */
106 static int
do_abandon(LDAP * ld,int origid,int msgid,LDAPControl ** serverctrls,LDAPControl ** clientctrls)107 do_abandon( LDAP *ld, int origid, int msgid, LDAPControl **serverctrls,
108     LDAPControl **clientctrls )
109 {
110 	BerElement	*ber;
111 	int		i, bererr, lderr, sendabandon;
112 	Sockbuf		*sb;
113 	LDAPRequest	*lr = NULL;
114 
115 	/*
116 	 * An abandon request looks like this:
117 	 *	AbandonRequest ::= MessageID
118 	 */
119 	LDAPDebug( LDAP_DEBUG_TRACE, "do_abandon origid %d, msgid %d\n",
120 		origid, msgid, 0 );
121 
122 	/* optimistic */
123 	lderr = LDAP_SUCCESS;
124 
125 /*
126  * this is not the best implementation...
127  * the code special cases the when async io is enabled.
128  * The logic is clear this way, at the cost of code bloat.
129  * This logic should be cleaned up post nova 4.5 rtm
130  */
131     if (ld->ld_options & LDAP_BITOPT_ASYNC)
132     {
133         /* Don't send an abandon message unless there is something to abandon. */
134         sendabandon = 0;
135 
136         /* Find the request that we are abandoning. */
137         if (ld->ld_requests != NULL) {
138             for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) {
139                 if ( lr->lr_msgid == msgid ) {	/* this message */
140                     if ( origid == msgid && lr->lr_parent != NULL ) {
141                         /* don't let caller abandon child requests! */
142                         lderr = LDAP_PARAM_ERROR;
143                         goto set_errorcode_and_return;
144                     }
145                     if ( lr->lr_status == LDAP_REQST_INPROGRESS ) {
146                         /* We only need to send an abandon message if the request
147                          * is in progress.
148                          */
149                         sendabandon = 1;
150                     }
151                     break;
152                 }
153                 if ( lr->lr_origid == msgid ) {	/* child:  abandon it */
154                     (void)do_abandon( ld, msgid, lr->lr_msgid,
155                                       serverctrls, clientctrls );
156                     /* we ignore errors from child abandons... */
157                 }
158             }
159         }
160     }
161     else
162     {
163         sendabandon = 1;
164         /* find the request that we are abandoning */
165         for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) {
166             if ( lr->lr_msgid == msgid ) {	/* this message */
167                 break;
168             }
169             if ( lr->lr_origid == msgid ) {	/* child:  abandon it */
170                 (void)do_abandon( ld, msgid, lr->lr_msgid,
171                                   serverctrls, clientctrls );
172                 /* we ignore errors from child abandons... */
173             }
174         }
175 
176         if ( lr != NULL ) {
177             if ( origid == msgid && lr->lr_parent != NULL ) {
178                 /* don't let caller abandon child requests! */
179                 lderr = LDAP_PARAM_ERROR;
180                 goto set_errorcode_and_return;
181             }
182             if ( lr->lr_status != LDAP_REQST_INPROGRESS ) {
183                 /* no need to send abandon message */
184                 sendabandon = 0;
185             }
186         }
187     }
188 	if ( ldap_msgdelete( ld, msgid ) == 0 ) {
189 		/* we had all the results and deleted them */
190 		goto set_errorcode_and_return;
191 	}
192 
193 	if ( sendabandon ) {
194 		/* create a message to send */
195 		if (( lderr = nsldapi_alloc_ber_with_options( ld, &ber )) ==
196 		    LDAP_SUCCESS ) {
197 			LDAP_MUTEX_LOCK( ld, LDAP_MSGID_LOCK );
198 #ifdef CLDAP
199 			if ( ld->ld_dbp->sb_naddr > 0 ) {
200 				bererr = ber_printf( ber, "{isti",
201 				    ++ld->ld_msgid, ld->ld_cldapdn,
202 				    LDAP_REQ_ABANDON, msgid );
203 			} else {
204 #endif /* CLDAP */
205 				bererr = ber_printf( ber, "{iti",
206 				    ++ld->ld_msgid, LDAP_REQ_ABANDON, msgid );
207 #ifdef CLDAP
208 			}
209 #endif /* CLDAP */
210 			LDAP_MUTEX_UNLOCK( ld, LDAP_MSGID_LOCK );
211 
212 			if ( bererr == -1 ||
213 			    ( lderr = nsldapi_put_controls( ld, serverctrls,
214 			    1, ber )) != LDAP_SUCCESS ) {
215 				lderr = LDAP_ENCODING_ERROR;
216 				ber_free( ber, 1 );
217 			} else {
218 				/* send the message */
219 				if ( lr != NULL ) {
220 					sb = lr->lr_conn->lconn_sb;
221 				} else {
222 					sb = ld->ld_sbp;
223 				}
224 				if ( nsldapi_ber_flush( ld, sb, ber, 1, 0 )
225 				    != 0 ) {
226 					lderr = LDAP_SERVER_DOWN;
227 				}
228 			}
229 		}
230 	}
231 
232 	if ( lr != NULL ) {
233 		if ( sendabandon ) {
234 			nsldapi_free_connection( ld, lr->lr_conn, NULL, NULL,
235 			    0, 1 );
236 		}
237 		if ( origid == msgid ) {
238 			nsldapi_free_request( ld, lr, 0 );
239 		}
240 	}
241 
242 
243 	LDAP_MUTEX_LOCK( ld, LDAP_ABANDON_LOCK );
244 	if ( ld->ld_abandoned == NULL ) {
245 		if ( (ld->ld_abandoned = (int *)NSLDAPI_MALLOC( 2
246 		    * sizeof(int) )) == NULL ) {
247 			lderr = LDAP_NO_MEMORY;
248 			LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK );
249 			goto set_errorcode_and_return;
250 		}
251 		i = 0;
252 	} else {
253 		for ( i = 0; ld->ld_abandoned[i] != -1; i++ )
254 			;	/* NULL */
255 		if ( (ld->ld_abandoned = (int *)NSLDAPI_REALLOC( (char *)
256 		    ld->ld_abandoned, (i + 2) * sizeof(int) )) == NULL ) {
257 			lderr = LDAP_NO_MEMORY;
258 			LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK );
259 			goto set_errorcode_and_return;
260 		}
261 	}
262 	ld->ld_abandoned[i] = msgid;
263 	ld->ld_abandoned[i + 1] = -1;
264 	LDAP_MUTEX_UNLOCK( ld, LDAP_ABANDON_LOCK );
265 
266 set_errorcode_and_return:
267 	LDAP_SET_LDERRNO( ld, lderr, NULL, NULL );
268 	return( lderr );
269 }
270