1// $OpenLDAP$
2/*
3 * Copyright 2000-2021 The OpenLDAP Foundation, All Rights Reserved.
4 * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5 */
6
7
8#include "config.h"
9#include "debug.h"
10#include "LDAPMessageQueue.h"
11#include "LDAPRequest.h"
12#include "LDAPResult.h"
13#include "LDAPSearchReference.h"
14#include "LDAPSearchRequest.h"
15#include "LDAPUrl.h"
16#include "LDAPUrlList.h"
17#include "LDAPException.h"
18
19using namespace std;
20
21// TODO: How to handle unsolicited notifications, like notice of
22//       disconnection
23
24LDAPMessageQueue::LDAPMessageQueue(LDAPRequest *req){
25    DEBUG(LDAP_DEBUG_CONSTRUCT, "LDAPMessageQueue::LDAPMessageQueue()" << endl);
26	m_activeReq.push(req);
27    m_issuedReq.push_back(req);
28}
29
30LDAPMessageQueue::~LDAPMessageQueue(){
31    DEBUG(LDAP_DEBUG_DESTROY, "LDAPMessageQueue::~LDAPMessageQueue()" << endl);
32    for(LDAPRequestList::iterator i=m_issuedReq.begin();
33            i != m_issuedReq.end(); i++){
34        delete *i;
35    }
36    m_issuedReq.clear();
37}
38
39
40LDAPMsg *LDAPMessageQueue::getNext(){
41    DEBUG(LDAP_DEBUG_TRACE,"LDAPMessageQueue::getNext()" << endl);
42
43    if ( m_activeReq.empty() ) {
44        return 0;
45    }
46
47    LDAPRequest *req=m_activeReq.top();
48    LDAPMsg *ret=0;
49
50    try{
51        ret = req->getNextMessage();
52    }catch(LDAPException e){
53        //do some clean up
54        m_activeReq.pop();
55        throw;
56    }
57
58    const LDAPConstraints *constr=req->getConstraints();
59    switch (ret->getMessageType()) {
60        case LDAPMsg::SEARCH_REFERENCE :
61            if (constr->getReferralChase() ){
62                //throws Exception (limit Exceeded)
63                LDAPRequest *refReq=chaseReferral(ret);
64                if(refReq != 0){
65                    m_activeReq.push(refReq);
66                    m_issuedReq.push_back(refReq);
67                    delete ret;
68                    return getNext();
69                }
70            }
71            return ret;
72        break;
73        case LDAPMsg::SEARCH_ENTRY :
74            return ret;
75        break;
76        case LDAPMsg::SEARCH_DONE :
77            if(req->isReferral()){
78                req->unbind();
79            }
80            switch ( ((LDAPResult*)ret)->getResultCode()) {
81                case LDAPResult::REFERRAL :
82                    if(constr->getReferralChase()){
83                        //throws Exception (limit Exceeded)
84                        LDAPRequest *refReq=chaseReferral(ret);
85                        if(refReq != 0){
86                            m_activeReq.pop();
87                            m_activeReq.push(refReq);
88                            m_issuedReq.push_back(refReq);
89                            delete ret;
90                            return getNext();
91                        }
92                    }
93                    return ret;
94                break;
95                case LDAPResult::SUCCESS :
96                    if(req->isReferral()){
97                        delete ret;
98                        m_activeReq.pop();
99                        return getNext();
100                    }else{
101                        m_activeReq.pop();
102                        return ret;
103                    }
104                break;
105                default:
106                    m_activeReq.pop();
107                    return ret;
108                break;
109            }
110        break;
111        //must be some kind of LDAPResultMessage
112        default:
113            if(req->isReferral()){
114                req->unbind();
115            }
116            LDAPResult* res_p=(LDAPResult*)ret;
117            switch (res_p->getResultCode()) {
118                case LDAPResult::REFERRAL :
119                    if(constr->getReferralChase()){
120                        //throws Exception (limit Exceeded)
121                        LDAPRequest *refReq=chaseReferral(ret);
122                        if(refReq != 0){
123                            m_activeReq.pop();
124                            m_activeReq.push(refReq);
125                            m_issuedReq.push_back(refReq);
126                            delete ret;
127                            return getNext();
128                        }
129                    }
130                    return ret;
131                break;
132                default:
133                    m_activeReq.pop();
134                    return ret;
135            }
136        break;
137    }
138}
139
140// TODO Maybe moved to LDAPRequest::followReferral seems more reasonable
141//there
142LDAPRequest* LDAPMessageQueue::chaseReferral(LDAPMsg* ref){
143    DEBUG(LDAP_DEBUG_TRACE,"LDAPMessageQueue::chaseReferral()" << endl);
144    LDAPRequest *req=m_activeReq.top();
145    LDAPRequest *refReq=req->followReferral(ref);
146    if(refReq !=0){
147        if(refReq->getConstraints()->getHopLimit() < refReq->getHopCount()){
148            delete(refReq);
149            throw LDAPException(LDAP_REFERRAL_LIMIT_EXCEEDED);
150        }
151        if(refReq->isCycle()){
152            delete(refReq);
153            throw LDAPException(LDAP_CLIENT_LOOP);
154        }
155        try {
156            refReq->sendRequest();
157            return refReq;
158        }catch (LDAPException e){
159            DEBUG(LDAP_DEBUG_TRACE,"   caught exception" << endl);
160            return 0;
161        }
162    }else{
163        return 0;
164    }
165}
166
167LDAPRequestStack* LDAPMessageQueue::getRequestStack(){
168    DEBUG(LDAP_DEBUG_TRACE,"LDAPMessageQueue::getRequestStack()" << endl);
169    return &m_activeReq;
170}
171
172