7ec258c9fab20936462d647e1df1dccff642af5f
[gpgme.git] / lang / cpp / src / eventloopinteractor.cpp
1 /*
2   eventloopinteractor.cpp
3   Copyright (C) 2003,2004 Klarälvdalens Datakonsult AB
4
5   This file is part of GPGME++.
6
7   GPGME++ is free software; you can redistribute it and/or
8   modify it under the terms of the GNU Library General Public
9   License as published by the Free Software Foundation; either
10   version 2 of the License, or (at your option) any later version.
11
12   GPGME++ is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU Library General Public License for more details.
16
17   You should have received a copy of the GNU Library General Public License
18   along with GPGME++; see the file COPYING.LIB.  If not, write to the
19   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20   Boston, MA 02110-1301, USA.
21 */
22
23 #include <eventloopinteractor.h>
24
25 #include <context.h>
26 #include "context_p.h"
27 #include <key.h>
28 #include <trustitem.h>
29
30 #include <gpgme.h>
31
32 #include <vector>
33 using std::vector;
34 #ifndef NDEBUG
35 # include <iostream>
36 #endif
37 #include <cassert>
38
39 namespace GpgME
40 {
41
42 //
43 // EventLoopInteractor::Private Declaration
44 //
45
46 class EventLoopInteractor::Private
47 {
48 public:
49     struct OneFD {
50         OneFD(int aFd, int aDir, gpgme_io_cb_t aFnc,
51               void *aFncData, void *aExternalTag)
52             : fd(aFd), dir(aDir), fnc(aFnc),
53               fncData(aFncData), externalTag(aExternalTag) {}
54         int fd;
55         int dir;
56         gpgme_io_cb_t fnc;
57         void *fncData;
58         void *externalTag;
59     };
60
61     vector<OneFD *> mCallbacks;
62
63     static void removeIOCb(void *tag);
64     static gpgme_error_t registerIOCb(void *data, int fd, int dir,
65                                       gpgme_io_cb_t fnc, void *fnc_data,
66                                       void **r_tag);
67     static void eventIOCb(void *, gpgme_event_io_t type, void *type_data);
68
69     static const gpgme_io_cbs iocbs;
70 };
71
72 const gpgme_io_cbs EventLoopInteractor::Private::iocbs = {
73     &EventLoopInteractor::Private::registerIOCb,
74     0,
75     &EventLoopInteractor::Private::removeIOCb,
76     &EventLoopInteractor::Private::eventIOCb,
77     0
78 };
79
80 //
81 // EventLoopInteractor::Private IO Callback Implementations
82 //
83
84 gpgme_error_t EventLoopInteractor::Private::registerIOCb(void *, int fd, int dir,
85         gpgme_io_cb_t fnc, void *fnc_data,
86         void **r_tag)
87 {
88     assert(instance()); assert(instance()->d);
89     bool ok = false;
90     void *etag = instance()->registerWatcher(fd, dir ? Read : Write, ok);
91     if (!ok) {
92         return gpgme_error(GPG_ERR_GENERAL);
93     }
94     instance()->d->mCallbacks.push_back(new OneFD(fd, dir, fnc, fnc_data, etag));
95     if (r_tag) {
96         *r_tag = instance()->d->mCallbacks.back();
97     }
98     return GPG_ERR_NO_ERROR;
99 }
100
101 void EventLoopInteractor::Private::removeIOCb(void *tag)
102 {
103
104     if (!instance() || !instance()->d) {
105         return;
106     }
107     for (vector<OneFD *>::iterator it = instance()->d->mCallbacks.begin();
108             it != instance()->d->mCallbacks.end() ; ++it) {
109         if (*it == tag) {
110             instance()->unregisterWatcher((*it)->externalTag);
111             delete *it; *it = 0;
112             instance()->d->mCallbacks.erase(it);
113             return;
114         }
115     }
116 }
117
118 void EventLoopInteractor::Private::eventIOCb(void *data, gpgme_event_io_t type, void *type_data)
119 {
120     assert(instance());
121     Context *ctx = static_cast<Context *>(data);
122     switch (type) {
123     case GPGME_EVENT_START: {
124         instance()->operationStartEvent(ctx);
125         // TODO: what's in type_data?
126     }
127     break;
128     case GPGME_EVENT_DONE: {
129         gpgme_error_t e = *static_cast<gpgme_error_t *>(type_data);
130         if (ctx && ctx->impl()) {
131             ctx->impl()->lasterr = e;
132         }
133         instance()->operationDoneEvent(ctx, Error(e));
134     }
135     break;
136     case GPGME_EVENT_NEXT_KEY: {
137         gpgme_key_t key = static_cast<gpgme_key_t>(type_data);
138         instance()->nextKeyEvent(ctx, Key(key, false));
139     }
140     break;
141     case GPGME_EVENT_NEXT_TRUSTITEM: {
142         gpgme_trust_item_t item = static_cast<gpgme_trust_item_t>(type_data);
143         instance()->nextTrustItemEvent(ctx, TrustItem(item));
144         gpgme_trust_item_unref(item);
145     }
146     break;
147     default: // warn
148         ;
149     }
150 }
151
152 //
153 // EventLoopInteractor Implementation
154 //
155
156 EventLoopInteractor *EventLoopInteractor::mSelf = 0;
157
158 EventLoopInteractor::EventLoopInteractor() : d(new Private)
159 {
160     assert(!mSelf);
161     mSelf = this;
162 }
163
164 EventLoopInteractor::~EventLoopInteractor()
165 {
166     // warn if there are still callbacks registered
167     mSelf = 0;
168     delete d;
169 }
170
171 void EventLoopInteractor::manage(Context *context)
172 {
173     if (!context || context->managedByEventLoopInteractor()) {
174         return;
175     }
176     gpgme_io_cbs *iocbs = new gpgme_io_cbs(Private::iocbs);
177     iocbs->event_priv = context;
178     context->installIOCallbacks(iocbs);
179 }
180
181 void EventLoopInteractor::unmanage(Context *context)
182 {
183     if (context) {
184         context->uninstallIOCallbacks();
185     }
186 }
187
188 void EventLoopInteractor::actOn(int fd, Direction dir)
189 {
190     for (vector<Private::OneFD *>::const_iterator it = d->mCallbacks.begin();
191             it != d->mCallbacks.end() ; ++it) {
192         if ((*it)->fd == fd && ((*it)->dir ? Read : Write) == dir) {
193             (*((*it)->fnc))((*it)->fncData, fd);
194             break;
195         }
196     }
197 }
198
199 } // namespace GpgME