diff options
author | Christian Grothoff <christian@grothoff.org> | 2013-10-05 13:10:53 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2013-10-05 13:10:53 +0000 |
commit | ac3a1c494fd48fc1d01fcc4eafa0b3f4878f5441 (patch) | |
tree | a21b54452171f582444a199e70426b4c4266152b /src/conversation/conversation_api.c | |
parent | 45c9ba99503eaf130b3d298062c612e281893d6a (diff) | |
download | gnunet-ac3a1c494fd48fc1d01fcc4eafa0b3f4878f5441.tar.gz gnunet-ac3a1c494fd48fc1d01fcc4eafa0b3f4878f5441.zip |
-rename fest: new to default
Diffstat (limited to 'src/conversation/conversation_api.c')
-rw-r--r-- | src/conversation/conversation_api.c | 1498 |
1 files changed, 930 insertions, 568 deletions
diff --git a/src/conversation/conversation_api.c b/src/conversation/conversation_api.c index 5bd88c01f..6ce198e64 100644 --- a/src/conversation/conversation_api.c +++ b/src/conversation/conversation_api.c | |||
@@ -1,770 +1,1132 @@ | |||
1 | /* | 1 | /* |
2 | This file is part of GNUnet. | 2 | This file is part of GNUnet |
3 | (C 2013 Christian Grothoff (and other contributing authors) | 3 | (C) 2013 Christian Grothoff (and other contributing authors) |
4 | 4 | ||
5 | GNUnet is free software; you can redistribute it and/or modify | 5 | GNUnet is free software; you can redistribute it and/or modify |
6 | it under the terms of the GNU General Public License as published | 6 | it under the terms of the GNU General Public License as published |
7 | by the Free Software Foundation; either version 3, or (at your | 7 | by the Free Software Foundation; either version 3, or (at your |
8 | option) any later version. | 8 | option) any later version. |
9 | 9 | ||
10 | GNUnet is distributed in the hope that it will be useful, but | 10 | GNUnet is distributed in the hope that it will be useful, but |
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | 11 | WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | General Public License for more details. | 13 | General Public License for more details. |
14 | 14 | ||
15 | You should have received a copy of the GNU General Public License | 15 | You should have received a copy of the GNU General Public License |
16 | along with GNUnet; see the file COPYING. If not, write to the | 16 | along with GNUnet; see the file COPYING. If not, write to the |
17 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, | 17 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
18 | Boston, MA 02111-1307, USA. | 18 | Boston, MA 02111-1307, USA. |
19 | */ | 19 | */ |
20 | 20 | ||
21 | /** | 21 | /** |
22 | * @file conversation/conversation_api.c | 22 | * @file conversation/conversation_api2.c |
23 | * @brief API for conversation | 23 | * @brief API to the conversation service |
24 | * @author Simon Dieterle | 24 | * @author Simon Dieterle |
25 | * @author Andreas Fuchs | 25 | * @author Andreas Fuchs |
26 | * STRUCTURE: | 26 | * @author Christian Grothoff |
27 | * - DATA STRUCTURES | ||
28 | * - DECLARATIONS | ||
29 | * - AUXILIARY FUNCTIONS | ||
30 | * - RECEIVE HANDLERS | ||
31 | * - SEND FUNCTIONS | ||
32 | * - API CALL DEFINITIONS | ||
33 | * | ||
34 | */ | 27 | */ |
35 | |||
36 | #include "platform.h" | 28 | #include "platform.h" |
37 | #include "gnunet_util_lib.h" | 29 | #include "gnunet_conversation_service.h" |
38 | #include "gnunet_dnsparser_lib.h" | ||
39 | #include "gnunet_gns_service.h" | 30 | #include "gnunet_gns_service.h" |
40 | #include "gnunet_protocols.h" | ||
41 | #include "conversation.h" | 31 | #include "conversation.h" |
42 | #include "gnunet_conversation_service.h" | ||
43 | |||
44 | #define MAX_TRANSMIT_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60) | ||
45 | 32 | ||
46 | /******************************************************************************/ | ||
47 | /************************ DATA STRUCTURES ****************************/ | ||
48 | /******************************************************************************/ | ||
49 | 33 | ||
50 | enum GNUNET_CONVERSATION_CallType | 34 | /** |
35 | * A phone record specifies which peer is hosting a given user and | ||
36 | * may also specify the phone line that is used (typically zero). | ||
37 | * The version is also right now always zero. | ||
38 | */ | ||
39 | struct PhoneRecord | ||
51 | { | 40 | { |
52 | CALLER = 0, | 41 | |
53 | CALLEE | 42 | /** |
43 | * Version of the phone record, for now always zero. We may | ||
44 | * use other versions for anonymously hosted phone lines in | ||
45 | * the future. | ||
46 | */ | ||
47 | uint32_t version GNUNET_PACKED; | ||
48 | |||
49 | /** | ||
50 | * Phone line to use at the peer. | ||
51 | */ | ||
52 | uint32_t line GNUNET_PACKED; | ||
53 | |||
54 | /** | ||
55 | * Identity of the peer hosting the phone service. | ||
56 | */ | ||
57 | struct GNUNET_PeerIdentity peer; | ||
58 | |||
54 | }; | 59 | }; |
55 | 60 | ||
61 | |||
56 | /** | 62 | /** |
57 | * Information about a call | 63 | * Possible states of the phone. |
58 | */ | 64 | */ |
59 | struct GNUNET_CONVERSATION_CallInformation | 65 | enum PhoneState |
60 | { | 66 | { |
67 | /** | ||
68 | * We still need to register the phone. | ||
69 | */ | ||
70 | PS_REGISTER = 0, | ||
61 | 71 | ||
62 | /** | 72 | /** |
63 | * Peer interacting with | 73 | * We are waiting for a call. |
64 | */ | 74 | */ |
65 | struct GNUNET_PeerIdentity peer; | 75 | PS_WAITING, |
66 | 76 | ||
67 | /** | 77 | /** |
68 | * Type of call (incoming or outgoing) | 78 | * The phone is ringing. |
69 | */ | 79 | */ |
70 | int type; | 80 | PS_RINGING, |
71 | 81 | ||
72 | /** | 82 | /** |
73 | * Shows if the call ist fully established | 83 | * The phone is in an active conversation. |
74 | */ | 84 | */ |
75 | int established; | 85 | PS_ACTIVE |
76 | }; | 86 | }; |
77 | 87 | ||
88 | |||
78 | /** | 89 | /** |
79 | * Opaque handle to the service. | 90 | * A phone is a device that can ring to signal an incoming call and |
91 | * that you can pick up to answer the call and hang up to terminate | ||
92 | * the call. You can also hang up a ringing phone immediately | ||
93 | * (without picking it up) to stop it from ringing. Phones have | ||
94 | * caller ID. You can ask the phone for its record and make that | ||
95 | * record available (via GNS) to enable others to call you. | ||
96 | * Multiple phones maybe connected to the same line (the line is | ||
97 | * something rather internal to a phone and not obvious from it). | ||
98 | * You can only have one conversation per phone at any time. | ||
80 | */ | 99 | */ |
81 | struct GNUNET_CONVERSATION_Handle | 100 | struct GNUNET_CONVERSATION_Phone |
82 | { | 101 | { |
83 | |||
84 | /** | 102 | /** |
85 | * Our configuration. | 103 | * Our configuration. |
86 | */ | 104 | */ |
87 | const struct GNUNET_CONFIGURATION_Handle *cfg; | 105 | const struct GNUNET_CONFIGURATION_Handle *cfg; |
88 | 106 | ||
89 | /** | 107 | /** |
90 | * Handle to the server connection, to send messages later | 108 | * Handle to talk with CONVERSATION service. |
91 | */ | 109 | */ |
92 | struct GNUNET_CLIENT_Connection *client; | 110 | struct GNUNET_CLIENT_Connection *client; |
93 | 111 | ||
94 | /** | 112 | /** |
95 | * GNS handle | 113 | * Function to call for phone events. |
96 | */ | 114 | */ |
97 | struct GNUNET_GNS_Handle *gns; | 115 | GNUNET_CONVERSATION_EventHandler event_handler; |
98 | 116 | ||
99 | /** | 117 | /** |
100 | * Namestore handle | 118 | * Closure for @e event_handler |
101 | */ | 119 | */ |
102 | struct GNUNET_NAMESTORE_Handle *namestore; | 120 | void *event_handler_cls; |
103 | 121 | ||
104 | /** | 122 | /** |
105 | * TXT record for gns | 123 | * Speaker, or NULL if none is attached. |
106 | */ | 124 | */ |
107 | int txt_record_set; | 125 | struct GNUNET_SPEAKER_Handle *speaker; |
108 | 126 | ||
109 | /** | 127 | /** |
110 | * Callback for incoming calls | 128 | * Microphone, or NULL if none is attached. |
111 | */ | 129 | */ |
112 | GNUNET_CONVERSATION_CallHandler *call_handler; | 130 | struct GNUNET_MICROPHONE_Handle *mic; |
131 | |||
132 | /** | ||
133 | * Connection to NAMESTORE (for reverse lookup). | ||
134 | */ | ||
135 | struct GNUNET_NAMESTORE_Handle *ns; | ||
113 | 136 | ||
114 | /** | 137 | /** |
115 | * Callback for rejected calls | 138 | * Active NAMESTORE lookup (or NULL). |
116 | */ | 139 | */ |
117 | GNUNET_CONVERSATION_RejectHandler *reject_handler; | 140 | struct GNUNET_NAMESTORE_QueueEntry *qe; |
118 | 141 | ||
142 | /** | ||
143 | * Handle for transmitting to the CONVERSATION service. | ||
144 | */ | ||
145 | struct GNUNET_MQ_Handle *mq; | ||
146 | |||
147 | /** | ||
148 | * This phone's record. | ||
149 | */ | ||
150 | struct PhoneRecord my_record; | ||
151 | |||
119 | /** | 152 | /** |
120 | * Callback for notifications | 153 | * My GNS zone. |
121 | */ | 154 | */ |
122 | GNUNET_CONVERSATION_NotificationHandler *notification_handler; | 155 | struct GNUNET_CRYPTO_EccPrivateKey my_zone; |
123 | 156 | ||
124 | /** | 157 | /** |
125 | * Callback for missed calls | 158 | * Identity of the person calling us (valid while in state #PS_RINGING). |
126 | */ | 159 | */ |
127 | GNUNET_CONVERSATION_MissedCallHandler *missed_call_handler; | 160 | struct GNUNET_CRYPTO_EccPublicSignKey caller_id; |
128 | 161 | ||
129 | /** | 162 | /** |
130 | * The pointer to the call | 163 | * State machine for the phone. |
131 | */ | 164 | */ |
132 | struct GNUNET_CONVERSATION_CallInformation *call; | 165 | enum PhoneState state; |
166 | |||
133 | }; | 167 | }; |
134 | 168 | ||
135 | /******************************************************************************/ | ||
136 | /*********************** AUXILIARY FUNCTIONS *************************/ | ||
137 | /******************************************************************************/ | ||
138 | 169 | ||
139 | /** | 170 | /** |
140 | * Initialize the conversation txt record in GNS | 171 | * The phone got disconnected, reconnect to the service. |
141 | */ | 172 | * |
173 | * @param phone phone to reconnect | ||
174 | */ | ||
142 | static void | 175 | static void |
143 | setup_gns_txt (struct GNUNET_CONVERSATION_Handle *handle) | 176 | reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone); |
144 | { | ||
145 | struct GNUNET_CRYPTO_EccPublicSignKey zone_pkey; | ||
146 | struct GNUNET_CRYPTO_EccPrivateKey *zone_key; | ||
147 | struct GNUNET_CRYPTO_EccPrivateKey *peer_key; | ||
148 | struct GNUNET_NAMESTORE_RecordData rd; | ||
149 | struct GNUNET_PeerIdentity peer; | ||
150 | 177 | ||
151 | char *zone_keyfile; | ||
152 | char *peer_keyfile; | ||
153 | 178 | ||
154 | rd.expiration_time = UINT64_MAX; | 179 | /** |
180 | * We have resolved the caller ID using our name service. | ||
181 | * | ||
182 | * @param cls the `struct GNUNET_CONVERSATION_Phone` | ||
183 | * @param zone our zone used for resolution | ||
184 | * @param label name of the caller | ||
185 | * @param rd_count number of records we have in @a rd | ||
186 | * @param rd records we have for the caller's label | ||
187 | */ | ||
188 | static void | ||
189 | handle_caller_name (void *cls, | ||
190 | const struct GNUNET_CRYPTO_EccPrivateKey *zone, | ||
191 | const char *label, | ||
192 | unsigned int rd_count, | ||
193 | const struct GNUNET_NAMESTORE_RecordData *rd) | ||
194 | { | ||
195 | struct GNUNET_CONVERSATION_Phone *phone = cls; | ||
196 | char *name; | ||
155 | 197 | ||
156 | if (GNUNET_OK != | 198 | phone->qe = NULL; |
157 | GNUNET_CONFIGURATION_get_value_filename (handle->cfg, "gns", "ZONEKEY", | 199 | if (NULL == label) |
158 | &zone_keyfile)) | 200 | name = GNUNET_strdup (GNUNET_NAMESTORE_pkey_to_zkey (&phone->caller_id)); |
159 | { | 201 | else |
160 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to get key from cfg\n"); | 202 | GNUNET_asprintf (&name, "%.gnu", label); |
161 | return; | 203 | phone->event_handler (phone->event_handler_cls, |
162 | } | 204 | GNUNET_CONVERSATION_EC_RING, |
205 | name); | ||
206 | GNUNET_free (name); | ||
207 | } | ||
163 | 208 | ||
164 | if (GNUNET_OK != | ||
165 | GNUNET_CONFIGURATION_get_value_filename (handle->cfg, "PEER", | ||
166 | "PRIVATE_KEY", &peer_keyfile)) | ||
167 | { | ||
168 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to get key from cfg\n"); | ||
169 | return; | ||
170 | } | ||
171 | 209 | ||
172 | zone_key = GNUNET_CRYPTO_ecc_key_create_from_file (zone_keyfile); | 210 | /** |
173 | GNUNET_CRYPTO_ecc_key_get_public_for_signature (zone_key, &zone_pkey); | 211 | * We received a `struct ClientPhoneRingMessage` |
174 | peer_key = GNUNET_CRYPTO_ecc_key_create_from_file (peer_keyfile); | 212 | * |
175 | GNUNET_CRYPTO_ecc_key_get_public_for_signature (peer_key, | 213 | * @param cls the `struct GNUNET_CONVERSATION_Phone` |
176 | &peer.public_key); | 214 | * @param msg the message |
177 | const char *h = GNUNET_i2s_full (&peer); | 215 | */ |
178 | 216 | static void | |
179 | rd.data_size = strlen (h) + 1; | 217 | handle_phone_ring (void *cls, |
180 | rd.data = h; | 218 | const struct GNUNET_MessageHeader *msg) |
181 | rd.record_type = GNUNET_DNSPARSER_TYPE_TXT; | 219 | { |
182 | rd.flags = GNUNET_NAMESTORE_RF_NONE; | 220 | struct GNUNET_CONVERSATION_Phone *phone = cls; |
183 | 221 | const struct ClientPhoneRingMessage *ring; | |
184 | /* FIXME: continuation? return value? */ | 222 | |
185 | GNUNET_NAMESTORE_records_store (handle->namestore, | 223 | ring = (const struct ClientPhoneRingMessage *) msg; |
186 | zone_key, | 224 | switch (phone->state) |
187 | "conversation", | 225 | { |
188 | 1, &rd, | 226 | case PS_REGISTER: |
189 | NULL, NULL); | 227 | GNUNET_assert (0); |
228 | break; | ||
229 | case PS_WAITING: | ||
230 | phone->state = PS_RINGING; | ||
231 | phone->caller_id = ring->caller_id; | ||
232 | phone->qe = GNUNET_NAMESTORE_zone_to_name (phone->ns, | ||
233 | &phone->my_zone, | ||
234 | &ring->caller_id, | ||
235 | &handle_caller_name, | ||
236 | phone); | ||
237 | break; | ||
238 | case PS_RINGING: | ||
239 | GNUNET_break (0); | ||
240 | reconnect_phone (phone); | ||
241 | break; | ||
242 | case PS_ACTIVE: | ||
243 | GNUNET_break (0); | ||
244 | reconnect_phone (phone); | ||
245 | break; | ||
246 | } | ||
190 | } | 247 | } |
191 | 248 | ||
249 | |||
192 | /** | 250 | /** |
193 | * Callback for checking the conversation txt gns record | 251 | * We received a `struct ClientPhoneHangupMessage`. |
194 | * | 252 | * |
195 | * @param cls closure | 253 | * @param cls the `struct GNUNET_CONVERSATION_Phone` |
196 | * @param rd_count | 254 | * @param msg the message |
197 | * @param rd | 255 | */ |
198 | */ | ||
199 | static void | 256 | static void |
200 | check_gns_cb (void *cls, uint32_t rd_count, | 257 | handle_phone_hangup (void *cls, |
201 | const struct GNUNET_NAMESTORE_RecordData *rd) | 258 | const struct GNUNET_MessageHeader *msg) |
202 | { | 259 | { |
203 | struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls; | 260 | struct GNUNET_CONVERSATION_Phone *phone = cls; |
204 | 261 | const struct ClientPhoneHangupMessage *hang; | |
205 | if (0 == rd_count) | 262 | size_t len; |
206 | { | 263 | const char *reason; |
207 | setup_gns_txt (h); | 264 | |
208 | } | 265 | hang = (const struct ClientPhoneHangupMessage *) msg; |
209 | else | 266 | reason = (const char *) &hang[1]; |
267 | len = htons (hang->header.size) - sizeof (struct ClientPhoneHangupMessage); | ||
268 | if ( (0 == len) || | ||
269 | ('\0' != reason[len-1]) ) | ||
270 | { | ||
271 | GNUNET_break (0); | ||
272 | reconnect_phone (phone); | ||
273 | return; | ||
274 | } | ||
275 | switch (phone->state) | ||
276 | { | ||
277 | case PS_REGISTER: | ||
278 | GNUNET_assert (0); | ||
279 | break; | ||
280 | case PS_WAITING: | ||
281 | GNUNET_break (0); | ||
282 | reconnect_phone (phone); | ||
283 | break; | ||
284 | case PS_RINGING: | ||
285 | if (NULL != phone->qe) | ||
210 | { | 286 | { |
211 | h->txt_record_set = GNUNET_YES; | 287 | GNUNET_NAMESTORE_cancel (phone->qe); |
288 | phone->qe = NULL; | ||
289 | phone->state = PS_WAITING; | ||
290 | break; | ||
212 | } | 291 | } |
292 | phone->state = PS_WAITING; | ||
293 | phone->event_handler (phone->event_handler_cls, | ||
294 | GNUNET_CONVERSATION_EC_TERMINATED, | ||
295 | reason); | ||
296 | break; | ||
297 | case PS_ACTIVE: | ||
298 | GNUNET_break (NULL == phone->qe); | ||
299 | phone->state = PS_WAITING; | ||
300 | phone->event_handler (phone->event_handler_cls, | ||
301 | GNUNET_CONVERSATION_EC_TERMINATED, | ||
302 | reason); | ||
303 | break; | ||
304 | } | ||
213 | } | 305 | } |
214 | 306 | ||
215 | 307 | ||
216 | /** | 308 | /** |
217 | * Check if the gns txt record for conversation exits | 309 | * We received a `struct ClientAudioMessage` |
310 | * | ||
311 | * @param cls the `struct GNUNET_CONVERSATION_Phone` | ||
312 | * @param msg the message | ||
218 | */ | 313 | */ |
219 | static void | 314 | static void |
220 | check_gns (struct GNUNET_CONVERSATION_Handle *h) | 315 | handle_phone_audio_message (void *cls, |
316 | const struct GNUNET_MessageHeader *msg) | ||
221 | { | 317 | { |
222 | GNUNET_GNS_lookup (h->gns, "conversation.gns", | 318 | struct GNUNET_CONVERSATION_Phone *phone = cls; |
223 | NULL /* FIXME_ZONE */, | 319 | const struct ClientAudioMessage *am; |
224 | GNUNET_DNSPARSER_TYPE_TXT, | ||
225 | GNUNET_NO, | ||
226 | NULL, | ||
227 | &check_gns_cb, h); | ||
228 | } | ||
229 | 320 | ||
321 | am = (const struct ClientAudioMessage *) msg; | ||
322 | switch (phone->state) | ||
323 | { | ||
324 | case PS_REGISTER: | ||
325 | GNUNET_assert (0); | ||
326 | break; | ||
327 | case PS_WAITING: | ||
328 | GNUNET_break (0); | ||
329 | reconnect_phone (phone); | ||
330 | break; | ||
331 | case PS_RINGING: | ||
332 | GNUNET_break (0); | ||
333 | reconnect_phone (phone); | ||
334 | break; | ||
335 | case PS_ACTIVE: | ||
336 | phone->speaker->play (phone->speaker->cls, | ||
337 | ntohs (msg->size) - sizeof (struct ClientAudioMessage), | ||
338 | &am[1]); | ||
339 | break; | ||
340 | } | ||
341 | } | ||
230 | 342 | ||
231 | /******************************************************************************/ | ||
232 | /*********************** RECEIVE HANDLERS ****************************/ | ||
233 | /******************************************************************************/ | ||
234 | 343 | ||
235 | /** | 344 | /** |
236 | * Function to process all messages received from the service | 345 | * We encountered an error talking with the conversation service. |
237 | * | 346 | * |
238 | * @param cls closure | 347 | * @param cls the `struct GNUNET_CONVERSATION_Phone` |
239 | * @param msg message received, NULL on timeout or fatal error | 348 | * @param error details about the error |
240 | */ | 349 | */ |
241 | static void | 350 | static void |
242 | receive_message_cb (void *cls, | 351 | phone_error_handler (void *cls, |
243 | const struct GNUNET_MessageHeader *msg) | 352 | enum GNUNET_MQ_Error error) |
244 | { | 353 | { |
245 | struct GNUNET_CONVERSATION_Handle *h = cls; | 354 | struct GNUNET_CONVERSATION_Phone *phone = cls; |
246 | struct ServerClientSessionInitiateMessage *imsg; | ||
247 | struct ServerClientSessionRejectMessage *rmsg; | ||
248 | struct GNUNET_CONVERSATION_MissedCallNotification *missed_calls; | ||
249 | |||
250 | if (NULL != msg) | ||
251 | { | ||
252 | switch (ntohs (msg->type)) | ||
253 | { | ||
254 | case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_ACCEPT: | ||
255 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
256 | _("%s has accepted your call.\n"), | ||
257 | GNUNET_i2s_full (&(h->call->peer))); | ||
258 | |||
259 | h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_CALL_ACCEPTED, | ||
260 | &(h->call->peer)); | ||
261 | h->call->type = CALLEE; | ||
262 | |||
263 | break; | ||
264 | |||
265 | case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_REJECT: | ||
266 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
267 | _("%s has rejected your call.\n"), | ||
268 | GNUNET_i2s_full (&(h->call->peer))); | ||
269 | |||
270 | rmsg = (struct ServerClientSessionRejectMessage *) msg; | ||
271 | h->reject_handler (NULL, h, ntohs (rmsg->reason), &(h->call->peer)); | ||
272 | GNUNET_free (h->call); | ||
273 | h->call = NULL; | ||
274 | |||
275 | break; | ||
276 | |||
277 | case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_TERMINATE: | ||
278 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
279 | _("%s has terminated the call.\n"), | ||
280 | GNUNET_i2s_full (&(h->call->peer))); | ||
281 | |||
282 | h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_CALL_TERMINATED, | ||
283 | &(h->call->peer)); | ||
284 | GNUNET_free (h->call); | ||
285 | h->call = NULL; | ||
286 | |||
287 | break; | ||
288 | |||
289 | case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_INITIATE: | ||
290 | imsg = (struct ServerClientSessionInitiateMessage *) msg; | ||
291 | |||
292 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("%s wants to call you.\n"), | ||
293 | GNUNET_i2s_full (&(imsg->peer))); | ||
294 | |||
295 | h->call = | ||
296 | (struct GNUNET_CONVERSATION_CallInformation *) | ||
297 | GNUNET_malloc (sizeof (struct GNUNET_CONVERSATION_CallInformation)); | ||
298 | memcpy (&(h->call->peer), &(imsg->peer), | ||
299 | sizeof (struct GNUNET_PeerIdentity)); | ||
300 | h->call_handler (NULL, h, &(h->call->peer)); | ||
301 | h->call->type = CALLEE; | ||
302 | |||
303 | break; | ||
304 | |||
305 | case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_MISSED_CALL: | ||
306 | missed_calls = | ||
307 | (struct GNUNET_CONVERSATION_MissedCallNotification *) (msg + | ||
308 | (sizeof | ||
309 | (struct | ||
310 | GNUNET_MessageHeader))); | ||
311 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
312 | _("You &d have missed a calls.\n"), | ||
313 | missed_calls->number); | ||
314 | h->missed_call_handler (NULL, h, missed_calls); | ||
315 | break; | ||
316 | |||
317 | case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SERVICE_BLOCKED: | ||
318 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("The service is blocked.\n")); | ||
319 | h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_SERVICE_BLOCKED, | ||
320 | NULL); | ||
321 | break; | ||
322 | |||
323 | case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_PEER_NOT_CONNECTED: | ||
324 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
325 | _("The peer you are calling is not connected.\n")); | ||
326 | h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_NO_PEER, NULL); | ||
327 | break; | ||
328 | |||
329 | case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_NO_ANSWER: | ||
330 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
331 | _("The peer you are calling does not answer.\n")); | ||
332 | h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_NO_ANSWER, | ||
333 | &(h->call->peer)); | ||
334 | break; | ||
335 | |||
336 | case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_ERROR: | ||
337 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Generic error occured.\n")); | ||
338 | break; | ||
339 | |||
340 | default: | ||
341 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
342 | _("Got unknown message type.\n")); | ||
343 | break; | ||
344 | } | ||
345 | 355 | ||
346 | } | 356 | GNUNET_break (0); |
347 | 357 | reconnect_phone (phone); | |
348 | GNUNET_CLIENT_receive (h->client, &receive_message_cb, h, | ||
349 | GNUNET_TIME_UNIT_FOREVER_REL); | ||
350 | } | 358 | } |
351 | 359 | ||
352 | /******************************************************************************/ | ||
353 | /************************ SEND FUNCTIONS ****************************/ | ||
354 | /******************************************************************************/ | ||
355 | 360 | ||
356 | /** | 361 | /** |
357 | * Function called to send a session initiate message to the service. | 362 | * The phone got disconnected, reconnect to the service. |
358 | * "buf" will be NULL and "size" zero if the socket was closed for writing in | ||
359 | * the meantime. | ||
360 | * | 363 | * |
361 | * @param cls closure, NULL | 364 | * @param phone phone to reconnect |
362 | * @param size number of bytes available in buf | ||
363 | * @param buf where the callee should write the initiate message | ||
364 | * @return number of bytes written to buf | ||
365 | */ | 365 | */ |
366 | static size_t | 366 | static void |
367 | transmit_session_initiate_message (void *cls, size_t size, void *buf) | 367 | reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone) |
368 | { | 368 | { |
369 | size_t msg_size; | 369 | static struct GNUNET_MQ_MessageHandler handlers[] = |
370 | struct ClientServerSessionInitiateMessage *msg; | 370 | { |
371 | struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls; | 371 | { &handle_phone_ring, |
372 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING, | ||
373 | sizeof (struct ClientPhoneRingMessage) }, | ||
374 | { &handle_phone_hangup, | ||
375 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP, | ||
376 | 0 }, | ||
377 | { &handle_phone_audio_message, | ||
378 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO, | ||
379 | 0 }, | ||
380 | { NULL, 0, 0 } | ||
381 | }; | ||
382 | struct GNUNET_MQ_Envelope *e; | ||
383 | struct ClientPhoneRegisterMessage *reg; | ||
384 | |||
385 | if (NULL != phone->mq) | ||
386 | { | ||
387 | GNUNET_MQ_destroy (phone->mq); | ||
388 | phone->mq = NULL; | ||
389 | } | ||
390 | if (NULL != phone->client) | ||
391 | { | ||
392 | GNUNET_CLIENT_disconnect (phone->client); | ||
393 | phone->client = NULL; | ||
394 | } | ||
395 | phone->state = PS_REGISTER; | ||
396 | phone->client = GNUNET_CLIENT_connect ("conversation", phone->cfg); | ||
397 | if (NULL == phone->client) | ||
398 | return; | ||
399 | phone->mq = GNUNET_MQ_queue_for_connection_client (phone->client, | ||
400 | handlers, | ||
401 | &phone_error_handler, | ||
402 | phone); | ||
403 | e = GNUNET_MQ_msg (reg, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER); | ||
404 | reg->line = phone->my_record.line; | ||
405 | GNUNET_MQ_send (phone->mq, e); | ||
406 | phone->state = PS_WAITING; | ||
407 | } | ||
372 | 408 | ||
373 | msg_size = sizeof (struct ClientServerSessionInitiateMessage); | ||
374 | 409 | ||
375 | GNUNET_assert (size >= msg_size); | 410 | /** |
376 | msg = buf; | 411 | * Create a new phone. |
377 | msg->header.size = htons (msg_size); | 412 | * |
378 | msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_INITIATE); | 413 | * @param cfg configuration for the phone; specifies the phone service and |
379 | memcpy (&msg->peer, &(h->call->peer), sizeof (struct GNUNET_PeerIdentity)); | 414 | * which line the phone is to be connected to |
415 | * @param ego ego to use for name resolution (when determining caller ID) | ||
416 | * @param event_handler how to notify the owner of the phone about events | ||
417 | * @param event_handler_cls closure for @a event_handler | ||
418 | */ | ||
419 | struct GNUNET_CONVERSATION_Phone * | ||
420 | GNUNET_CONVERSATION_phone_create (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
421 | const struct GNUNET_IDENTITY_Ego *ego, | ||
422 | GNUNET_CONVERSATION_EventHandler event_handler, | ||
423 | void *event_handler_cls) | ||
424 | { | ||
425 | struct GNUNET_CONVERSATION_Phone *phone; | ||
426 | unsigned long long line; | ||
380 | 427 | ||
381 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | 428 | if (GNUNET_OK != |
382 | _ | 429 | GNUNET_CONFIGURATION_get_value_number (cfg, |
383 | ("Sending ClientServerSessionInitiateMessage to the service for peer: %s\n"), | 430 | "CONVERSATION", |
384 | GNUNET_i2s_full (&(h->call->peer))); | 431 | "LINE", |
432 | &line)) | ||
433 | return NULL; | ||
434 | phone = GNUNET_new (struct GNUNET_CONVERSATION_Phone); | ||
435 | if (GNUNET_OK != | ||
436 | GNUNET_CRYPTO_get_host_identity (cfg, | ||
437 | &phone->my_record.peer)) | ||
438 | { | ||
439 | GNUNET_break (0); | ||
440 | GNUNET_free (phone); | ||
441 | return NULL; | ||
442 | } | ||
443 | phone->cfg = cfg; | ||
444 | phone->my_zone = *GNUNET_IDENTITY_ego_get_private_key (ego); | ||
445 | phone->event_handler = event_handler; | ||
446 | phone->event_handler_cls = event_handler_cls; | ||
447 | phone->ns = GNUNET_NAMESTORE_connect (cfg); | ||
448 | phone->my_record.line = htonl ((uint32_t) line); | ||
449 | phone->my_record.version = htonl (0); | ||
450 | reconnect_phone (phone); | ||
451 | if ( (NULL == phone->client) || | ||
452 | (NULL == phone->ns) ) | ||
453 | { | ||
454 | GNUNET_break (0); | ||
455 | GNUNET_CONVERSATION_phone_destroy (phone); | ||
456 | return NULL; | ||
457 | } | ||
458 | return phone; | ||
459 | } | ||
385 | 460 | ||
386 | h->call->type = CALLER; | ||
387 | 461 | ||
388 | return msg_size; | 462 | /** |
463 | * Fill in a namestore record with the contact information | ||
464 | * for this phone. Note that the filled in "data" value | ||
465 | * is only valid until the phone is destroyed. | ||
466 | * | ||
467 | * @param phone phone to create a record for | ||
468 | * @param rd namestore record to fill in | ||
469 | */ | ||
470 | void | ||
471 | GNUNET_CONVERSATION_phone_get_record (struct GNUNET_CONVERSATION_Phone *phone, | ||
472 | struct GNUNET_NAMESTORE_RecordData *rd) | ||
473 | { | ||
474 | rd->data = &phone->my_record; | ||
475 | rd->expiration_time = 0; | ||
476 | rd->data_size = sizeof (struct PhoneRecord); | ||
477 | rd->record_type = GNUNET_NAMESTORE_TYPE_PHONE; | ||
478 | rd->flags = GNUNET_NAMESTORE_RF_NONE; | ||
389 | } | 479 | } |
390 | 480 | ||
481 | |||
391 | /** | 482 | /** |
392 | * Function called to send a session accept message to the service. | 483 | * Process recorded audio data. |
393 | * "buf" will be NULL and "size" zero if the socket was closed for writing in | ||
394 | * the meantime. | ||
395 | * | 484 | * |
396 | * @param cls closure, NULL | 485 | * @param cls closure with the `struct GNUNET_CONVERSATION_Phone` |
397 | * @param size number of bytes available in buf | 486 | * @param data_size number of bytes in @a data |
398 | * @param buf where the callee should write the accept message | 487 | * @param data audio data to play |
399 | * @return number of bytes written to buf | ||
400 | */ | 488 | */ |
401 | static size_t | 489 | static void |
402 | transmit_session_accept_message (void *cls, size_t size, void *buf) | 490 | transmit_phone_audio (void *cls, |
491 | size_t data_size, | ||
492 | const void *data) | ||
403 | { | 493 | { |
404 | size_t msg_size; | 494 | struct GNUNET_CONVERSATION_Phone *phone = cls; |
405 | struct ClientServerSessionAcceptMessage *msg; | 495 | struct GNUNET_MQ_Envelope *e; |
406 | struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls; | 496 | struct ClientAudioMessage *am; |
407 | 497 | ||
408 | msg_size = sizeof (struct ClientServerSessionAcceptMessage); | 498 | GNUNET_assert (PS_ACTIVE == phone->state); |
499 | e = GNUNET_MQ_msg_extra (am, | ||
500 | data_size, | ||
501 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO); | ||
502 | memcpy (&am[1], data, data_size); | ||
503 | GNUNET_MQ_send (phone->mq, e); | ||
504 | } | ||
409 | 505 | ||
410 | GNUNET_assert (size >= msg_size); | ||
411 | msg = buf; | ||
412 | msg->header.size = htons (msg_size); | ||
413 | msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_ACCEPT); | ||
414 | 506 | ||
415 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | 507 | /** |
416 | _ | 508 | * Picks up a (ringing) phone. This will connect the speaker |
417 | ("Sending ClienServerSessionAcceptMessage to the service for peer: %s\n"), | 509 | * to the microphone of the other party, and vice versa. |
418 | GNUNET_i2s_full (&(h->call->peer))); | 510 | * |
511 | * @param phone phone to pick up | ||
512 | * @param metadata meta data to give to the other user about the pick up event | ||
513 | * @param speaker speaker to use | ||
514 | * @param mic microphone to use | ||
515 | */ | ||
516 | void | ||
517 | GNUNET_CONVERSATION_phone_pick_up (struct GNUNET_CONVERSATION_Phone *phone, | ||
518 | const char *metadata, | ||
519 | struct GNUNET_SPEAKER_Handle *speaker, | ||
520 | struct GNUNET_MICROPHONE_Handle *mic) | ||
521 | { | ||
522 | struct GNUNET_MQ_Envelope *e; | ||
523 | struct ClientPhonePickupMessage *pick; | ||
524 | size_t slen; | ||
525 | |||
526 | GNUNET_assert (PS_RINGING == phone->state); | ||
527 | phone->speaker = speaker; | ||
528 | phone->mic = mic; | ||
529 | slen = strlen (metadata) + 1; | ||
530 | e = GNUNET_MQ_msg_extra (pick, slen, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP); | ||
531 | memcpy (&pick[1], metadata, slen); | ||
532 | GNUNET_MQ_send (phone->mq, e); | ||
533 | phone->state = PS_ACTIVE; | ||
534 | phone->speaker->enable_speaker (phone->speaker->cls); | ||
535 | phone->mic->enable_microphone (phone->mic->cls, | ||
536 | &transmit_phone_audio, | ||
537 | phone); | ||
538 | } | ||
419 | 539 | ||
420 | h->call->established = GNUNET_YES; | ||
421 | 540 | ||
422 | return msg_size; | 541 | /** |
542 | * Hang up up a (possibly ringing) phone. This will notify the other | ||
543 | * party that we are no longer interested in talking with them. | ||
544 | * | ||
545 | * @param phone phone to pick up | ||
546 | * @param reason text we give to the other party about why we terminated the conversation | ||
547 | */ | ||
548 | void | ||
549 | GNUNET_CONVERSATION_phone_hang_up (struct GNUNET_CONVERSATION_Phone *phone, | ||
550 | const char *reason) | ||
551 | { | ||
552 | struct GNUNET_MQ_Envelope *e; | ||
553 | struct ClientPhoneHangupMessage *hang; | ||
554 | size_t slen; | ||
555 | |||
556 | GNUNET_assert ( (PS_RINGING == phone->state) || | ||
557 | (PS_ACTIVE == phone->state) ); | ||
558 | phone->speaker->disable_speaker (phone->speaker->cls); | ||
559 | phone->mic->disable_microphone (phone->mic->cls); | ||
560 | phone->speaker = NULL; | ||
561 | phone->mic = NULL; | ||
562 | slen = strlen (reason) + 1; | ||
563 | e = GNUNET_MQ_msg_extra (hang, slen, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP); | ||
564 | memcpy (&hang[1], reason, slen); | ||
565 | GNUNET_MQ_send (phone->mq, e); | ||
566 | phone->state = PS_WAITING; | ||
423 | } | 567 | } |
424 | 568 | ||
569 | |||
425 | /** | 570 | /** |
426 | * Function called to send a session reject message to the service. | 571 | * Destroys a phone. |
427 | * "buf" will be NULL and "size" zero if the socket was closed for writing in | ||
428 | * the meantime. | ||
429 | * | 572 | * |
430 | * @param cls closure, NULL | 573 | * @param phone phone to destroy |
431 | * @param size number of bytes available in buf | ||
432 | * @param buf where the callee should write the reject message | ||
433 | * @return number of bytes written to buf | ||
434 | */ | 574 | */ |
435 | static size_t | 575 | void |
436 | transmit_session_reject_message (void *cls, size_t size, void *buf) | 576 | GNUNET_CONVERSATION_phone_destroy (struct GNUNET_CONVERSATION_Phone *phone) |
437 | { | 577 | { |
438 | size_t msg_size; | 578 | if (NULL != phone->speaker) |
439 | struct ClientServerSessionRejectMessage *msg; | 579 | { |
440 | struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls; | 580 | phone->speaker->disable_speaker (phone->speaker->cls); |
581 | phone->speaker = NULL; | ||
582 | } | ||
583 | if (NULL != phone->mic) | ||
584 | { | ||
585 | phone->mic->disable_microphone (phone->mic->cls); | ||
586 | phone->mic = NULL; | ||
587 | } | ||
588 | if (NULL != phone->qe) | ||
589 | { | ||
590 | GNUNET_NAMESTORE_cancel (phone->qe); | ||
591 | phone->qe = NULL; | ||
592 | } | ||
593 | if (NULL != phone->ns) | ||
594 | { | ||
595 | GNUNET_NAMESTORE_disconnect (phone->ns); | ||
596 | phone->ns = NULL; | ||
597 | } | ||
598 | if (NULL != phone->mq) | ||
599 | { | ||
600 | GNUNET_MQ_destroy (phone->mq); | ||
601 | phone->mq = NULL; | ||
602 | } | ||
603 | if (NULL != phone->client) | ||
604 | { | ||
605 | GNUNET_CLIENT_disconnect (phone->client); | ||
606 | phone->client = NULL; | ||
607 | } | ||
608 | GNUNET_free (phone); | ||
609 | } | ||
610 | |||
611 | |||
612 | /* ******************************* Call API *************************** */ | ||
441 | 613 | ||
442 | msg_size = sizeof (struct ClientServerSessionRejectMessage); | 614 | /** |
615 | * Possible states of the phone. | ||
616 | */ | ||
617 | enum CallState | ||
618 | { | ||
619 | /** | ||
620 | * We still need to lookup the callee. | ||
621 | */ | ||
622 | CS_LOOKUP = 0, | ||
443 | 623 | ||
444 | GNUNET_assert (size >= msg_size); | 624 | /** |
445 | msg = buf; | 625 | * The call is ringing. |
446 | msg->header.size = htons (msg_size); | 626 | */ |
447 | msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_REJECT); | 627 | CS_RINGING, |
448 | msg->reason = htons (GNUNET_CONVERSATION_REJECT_REASON_NOT_WANTED); | ||
449 | 628 | ||
450 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | 629 | /** |
451 | _ | 630 | * The call is in an active conversation. |
452 | ("Sending ClientServerSessionRejectMessage to the service for peer: %s\n"), | 631 | */ |
453 | GNUNET_i2s_full (&(h->call->peer))); | 632 | CS_ACTIVE, |
454 | 633 | ||
455 | GNUNET_free (h->call); | 634 | /** |
456 | h->call = NULL; | 635 | * The call is in termination. |
636 | */ | ||
637 | CS_SHUTDOWN | ||
638 | }; | ||
457 | 639 | ||
458 | return msg_size; | ||
459 | } | ||
460 | 640 | ||
461 | /** | 641 | /** |
462 | * Function called to send a session terminate message to the service. | 642 | * Handle for an outgoing call. |
463 | * "buf" will be NULL and "size" zero if the socket was closed for writing in | ||
464 | * the meantime. | ||
465 | * | ||
466 | * @param cls closure, NULL | ||
467 | * @param size number of bytes available in buf | ||
468 | * @param buf where the callee should write the terminate message | ||
469 | * @return number of bytes written to buf | ||
470 | */ | 643 | */ |
471 | static size_t | 644 | struct GNUNET_CONVERSATION_Call |
472 | transmit_session_terminate_message (void *cls, size_t size, void *buf) | ||
473 | { | 645 | { |
474 | size_t msg_size; | ||
475 | struct ClientServerSessionTerminateMessage *msg; | ||
476 | struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls; | ||
477 | 646 | ||
478 | msg_size = sizeof (struct ClientServerSessionTerminateMessage); | 647 | /** |
648 | * Our configuration. | ||
649 | */ | ||
650 | const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
651 | |||
652 | /** | ||
653 | * Handle to talk with CONVERSATION service. | ||
654 | */ | ||
655 | struct GNUNET_CLIENT_Connection *client; | ||
656 | |||
657 | /** | ||
658 | * Our caller identity. | ||
659 | */ | ||
660 | struct GNUNET_IDENTITY_Ego *caller_id; | ||
661 | |||
662 | /** | ||
663 | * Target callee as a GNS address/name. | ||
664 | */ | ||
665 | char *callee; | ||
479 | 666 | ||
480 | GNUNET_assert (size >= msg_size); | 667 | /** |
481 | msg = buf; | 668 | * Our speaker. |
482 | msg->header.size = htons (msg_size); | 669 | */ |
483 | msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_TERMINATE); | 670 | struct GNUNET_SPEAKER_Handle *speaker; |
484 | 671 | ||
485 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | 672 | /** |
486 | _ | 673 | * Our microphone. |
487 | ("Sending ClientServerSessionTerminateMessage to the service for peer: %s\n"), | 674 | */ |
488 | GNUNET_i2s_full (&(h->call->peer))); | 675 | struct GNUNET_MICROPHONE_Handle *mic; |
676 | |||
677 | /** | ||
678 | * Function to call with events. | ||
679 | */ | ||
680 | GNUNET_CONVERSATION_EventHandler event_handler; | ||
489 | 681 | ||
490 | GNUNET_free (h->call); | 682 | /** |
491 | h->call = NULL; | 683 | * Closure for @e event_handler |
684 | */ | ||
685 | void *event_handler_cls; | ||
686 | |||
687 | /** | ||
688 | * Handle for transmitting to the CONVERSATION service. | ||
689 | */ | ||
690 | struct GNUNET_MQ_Handle *mq; | ||
691 | |||
692 | /** | ||
693 | * Connection to GNS (can be NULL). | ||
694 | */ | ||
695 | struct GNUNET_GNS_Handle *gns; | ||
696 | |||
697 | /** | ||
698 | * Active GNS lookup (or NULL). | ||
699 | */ | ||
700 | struct GNUNET_GNS_LookupRequest *gns_lookup; | ||
701 | |||
702 | /** | ||
703 | * Target phone record, only valid after the lookup is done. | ||
704 | */ | ||
705 | struct PhoneRecord phone_record; | ||
706 | |||
707 | /** | ||
708 | * State machine for the call. | ||
709 | */ | ||
710 | enum CallState state; | ||
711 | |||
712 | }; | ||
492 | 713 | ||
493 | return msg_size; | ||
494 | } | ||
495 | 714 | ||
496 | /** | 715 | /** |
497 | * Auxiliary function to call a peer. | 716 | * The call got disconnected, reconnect to the service. |
498 | * | 717 | * |
499 | * @param h conversation handle | 718 | * @param call call to reconnect |
500 | * @return | ||
501 | */ | 719 | */ |
502 | static void | 720 | static void |
503 | initiate_call (struct GNUNET_CONVERSATION_Handle *h, struct GNUNET_PeerIdentity peer) | 721 | reconnect_call (struct GNUNET_CONVERSATION_Call *call); |
504 | { | 722 | |
505 | h->call = | ||
506 | (struct GNUNET_CONVERSATION_CallInformation *) | ||
507 | GNUNET_malloc (sizeof (struct GNUNET_CONVERSATION_CallInformation)); | ||
508 | memcpy (&(h->call->peer), &peer, sizeof (struct GNUNET_PeerIdentity)); | ||
509 | |||
510 | GNUNET_CLIENT_notify_transmit_ready (h->client, | ||
511 | sizeof (struct | ||
512 | ClientServerSessionInitiateMessage), | ||
513 | MAX_TRANSMIT_DELAY, GNUNET_YES, | ||
514 | &transmit_session_initiate_message, h); | ||
515 | |||
516 | return; | ||
517 | } | ||
518 | 723 | ||
519 | /** | 724 | /** |
520 | * Auxiliary function to accept a call. | 725 | * We received a `struct ClientPhoneBusyMessage` |
521 | * | 726 | * |
522 | * @param h conversation handle | 727 | * @param cls the `struct GNUNET_CONVERSATION_Call` |
728 | * @param msg the message | ||
523 | */ | 729 | */ |
524 | static void | 730 | static void |
525 | accept_call (struct GNUNET_CONVERSATION_Handle *h) | 731 | handle_call_busy (void *cls, |
732 | const struct GNUNET_MessageHeader *msg) | ||
526 | { | 733 | { |
527 | GNUNET_CLIENT_notify_transmit_ready (h->client, | 734 | struct GNUNET_CONVERSATION_Call *call = cls; |
528 | sizeof (struct | 735 | |
529 | ClientServerSessionAcceptMessage), | 736 | switch (call->state) |
530 | MAX_TRANSMIT_DELAY, GNUNET_YES, | 737 | { |
531 | &transmit_session_accept_message, h); | 738 | case CS_LOOKUP: |
739 | GNUNET_break (0); | ||
740 | reconnect_call (call); | ||
741 | break; | ||
742 | case CS_RINGING: | ||
743 | call->event_handler (call->event_handler_cls, | ||
744 | GNUNET_CONVERSATION_EC_BUSY); | ||
745 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
746 | break; | ||
747 | case CS_ACTIVE: | ||
748 | GNUNET_break (0); | ||
749 | reconnect_call (call); | ||
750 | break; | ||
751 | case CS_SHUTDOWN: | ||
752 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
753 | break; | ||
754 | } | ||
532 | } | 755 | } |
533 | 756 | ||
534 | 757 | ||
535 | /** | 758 | /** |
536 | * Auxiliary function to reject a call. | 759 | * Process recorded audio data. |
537 | * | 760 | * |
538 | * @param h conversation handle | 761 | * @param cls closure with the `struct GNUNET_CONVERSATION_Call` |
762 | * @param data_size number of bytes in @a data | ||
763 | * @param data audio data to play | ||
539 | */ | 764 | */ |
540 | static void | 765 | static void |
541 | reject_call (struct GNUNET_CONVERSATION_Handle *h) | 766 | transmit_call_audio (void *cls, |
767 | size_t data_size, | ||
768 | const void *data) | ||
542 | { | 769 | { |
543 | GNUNET_CLIENT_notify_transmit_ready (h->client, | 770 | struct GNUNET_CONVERSATION_Call *call = cls; |
544 | sizeof (struct | 771 | struct GNUNET_MQ_Envelope *e; |
545 | ClientServerSessionRejectMessage), | 772 | struct ClientAudioMessage *am; |
546 | MAX_TRANSMIT_DELAY, GNUNET_YES, | 773 | |
547 | &transmit_session_reject_message, h); | 774 | GNUNET_assert (CS_ACTIVE == call->state); |
775 | e = GNUNET_MQ_msg_extra (am, | ||
776 | data_size, | ||
777 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO); | ||
778 | memcpy (&am[1], data, data_size); | ||
779 | GNUNET_MQ_send (call->mq, e); | ||
548 | } | 780 | } |
549 | 781 | ||
550 | 782 | ||
551 | /** | 783 | /** |
552 | * Auxiliary function to terminate a call. | 784 | * We received a `struct ClientPhonePickedupMessage` |
553 | * | 785 | * |
554 | * @param h conversation handle | 786 | * @param cls the `struct GNUNET_CONVERSATION_Call` |
787 | * @param msg the message | ||
555 | */ | 788 | */ |
556 | static void | 789 | static void |
557 | terminate_call (struct GNUNET_CONVERSATION_Handle *h) | 790 | handle_call_picked_up (void *cls, |
791 | const struct GNUNET_MessageHeader *msg) | ||
558 | { | 792 | { |
559 | GNUNET_CLIENT_notify_transmit_ready (h->client, | 793 | struct GNUNET_CONVERSATION_Call *call = cls; |
560 | sizeof (struct | 794 | const struct ClientPhonePickedupMessage *am; |
561 | ClientServerSessionTerminateMessage), | 795 | const char *metadata; |
562 | MAX_TRANSMIT_DELAY, GNUNET_YES, | 796 | size_t size; |
563 | &transmit_session_terminate_message, | 797 | |
564 | h); | 798 | am = (const struct ClientPhonePickedupMessage *) msg; |
799 | size = ntohs (am->header.size) - sizeof (struct ClientPhonePickedupMessage); | ||
800 | metadata = (const char *) &am[1]; | ||
801 | if ( (0 == size) || | ||
802 | ('\0' != metadata[size - 1]) ) | ||
803 | metadata = NULL; | ||
804 | switch (call->state) | ||
805 | { | ||
806 | case CS_LOOKUP: | ||
807 | GNUNET_break (0); | ||
808 | reconnect_call (call); | ||
809 | break; | ||
810 | case CS_RINGING: | ||
811 | call->state = CS_ACTIVE; | ||
812 | call->event_handler (call->event_handler_cls, | ||
813 | GNUNET_CONVERSATION_EC_READY, | ||
814 | metadata); | ||
815 | call->speaker->enable_speaker (call->speaker->cls); | ||
816 | call->mic->enable_microphone (call->mic->cls, | ||
817 | &transmit_call_audio, | ||
818 | call); | ||
819 | break; | ||
820 | case CS_ACTIVE: | ||
821 | GNUNET_break (0); | ||
822 | reconnect_call (call); | ||
823 | break; | ||
824 | case CS_SHUTDOWN: | ||
825 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
826 | break; | ||
827 | } | ||
565 | } | 828 | } |
566 | 829 | ||
567 | 830 | ||
568 | /** | 831 | /** |
832 | * We received a `struct ClientPhoneHangupMessage` | ||
569 | * | 833 | * |
834 | * @param cls the `struct GNUNET_CONVERSATION_Call` | ||
835 | * @param msg the message | ||
570 | */ | 836 | */ |
571 | static void | 837 | static void |
572 | gns_call_cb (void *cls, uint32_t rd_count, | 838 | handle_call_hangup (void *cls, |
573 | const struct GNUNET_NAMESTORE_RecordData *rd) | 839 | const struct GNUNET_MessageHeader *msg) |
574 | { | 840 | { |
575 | struct GNUNET_CONVERSATION_Handle *handle = cls; | 841 | struct GNUNET_CONVERSATION_Call *call = cls; |
576 | struct GNUNET_PeerIdentity peer; | 842 | const struct ClientPhoneHangupMessage *am; |
577 | unsigned int i; | 843 | const char *reason; |
578 | 844 | size_t size; | |
579 | for (i=0;i<rd_count;i++) | 845 | |
846 | am = (const struct ClientPhoneHangupMessage *) msg; | ||
847 | size = ntohs (am->header.size) - sizeof (struct ClientPhoneHangupMessage); | ||
848 | reason = (const char *) &am[1]; | ||
849 | if ( (0 == size) || | ||
850 | ('\0' != reason[size - 1]) ) | ||
851 | reason = NULL; | ||
852 | switch (call->state) | ||
580 | { | 853 | { |
581 | switch (rd[i].record_type) | 854 | case CS_LOOKUP: |
582 | { | 855 | GNUNET_break (0); |
583 | case GNUNET_DNSPARSER_TYPE_TXT: /* FIXME: use fresh record type for voide... */ | 856 | reconnect_call (call); |
584 | if (GNUNET_OK != | 857 | break; |
585 | GNUNET_CRYPTO_ecc_public_sign_key_from_string (rd[i].data, | 858 | case CS_RINGING: |
586 | rd[i].data_size, | 859 | call->event_handler (call->event_handler_cls, |
587 | &peer.public_key)) | 860 | GNUNET_CONVERSATION_EC_TERMINATED, |
588 | { | 861 | reason); |
589 | GNUNET_break_op (0); | 862 | GNUNET_CONVERSATION_call_stop (call, NULL); |
590 | continue; | 863 | return; |
591 | } | 864 | case CS_ACTIVE: |
592 | initiate_call (handle, peer); | 865 | call->event_handler (call->event_handler_cls, |
593 | return; | 866 | GNUNET_CONVERSATION_EC_TERMINATED, |
594 | default: | 867 | reason); |
595 | break; | 868 | GNUNET_CONVERSATION_call_stop (call, NULL); |
596 | } | 869 | return; |
870 | case CS_SHUTDOWN: | ||
871 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
872 | break; | ||
597 | } | 873 | } |
598 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
599 | "Lookup failed\n"); | ||
600 | handle->notification_handler (NULL, handle, | ||
601 | GNUNET_CONVERSATION_NT_NO_PEER, | ||
602 | NULL); | ||
603 | } | 874 | } |
604 | 875 | ||
605 | 876 | ||
606 | /** | 877 | /** |
607 | * GNS lookup | 878 | * We received a `struct ClientAudioMessage` |
608 | */ | 879 | * |
880 | * @param cls the `struct GNUNET_CONVERSATION_Call` | ||
881 | * @param msg the message | ||
882 | */ | ||
609 | static void | 883 | static void |
610 | gns_lookup_and_call (struct GNUNET_CONVERSATION_Handle *h, const char *callee) | 884 | handle_call_audio_message (void *cls, |
611 | { | 885 | const struct GNUNET_MessageHeader *msg) |
612 | char domain[GNUNET_DNSPARSER_MAX_NAME_LENGTH]; | ||
613 | char *pos; | ||
614 | |||
615 | pos = domain; | ||
616 | strcpy (pos, "conversation"); | ||
617 | pos += strlen ("conversation"); | ||
618 | strcpy (pos, "."); | ||
619 | pos++; | ||
620 | strcpy (pos, callee); | ||
621 | pos += strlen (callee); | ||
622 | |||
623 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Lookup for %s\n", domain); | ||
624 | |||
625 | GNUNET_GNS_lookup (h->gns, | ||
626 | domain, | ||
627 | NULL /* FIXME: ZONE! */, | ||
628 | GNUNET_DNSPARSER_TYPE_TXT, | ||
629 | GNUNET_NO, | ||
630 | NULL, | ||
631 | &gns_call_cb, h); | ||
632 | } | ||
633 | |||
634 | |||
635 | /******************************************************************************/ | ||
636 | /********************** API CALL DEFINITIONS *************************/ | ||
637 | /******************************************************************************/ | ||
638 | |||
639 | struct GNUNET_CONVERSATION_Handle * | ||
640 | GNUNET_CONVERSATION_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
641 | void *cls, | ||
642 | GNUNET_CONVERSATION_CallHandler call_handler, | ||
643 | GNUNET_CONVERSATION_RejectHandler reject_handler, | ||
644 | GNUNET_CONVERSATION_NotificationHandler notification_handler, | ||
645 | GNUNET_CONVERSATION_MissedCallHandler missed_call_handler) | ||
646 | { | 886 | { |
647 | struct GNUNET_CONVERSATION_Handle *h; | 887 | struct GNUNET_CONVERSATION_Call *call = cls; |
648 | 888 | const struct ClientAudioMessage *am; | |
649 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
650 | "GNUNET_CONVERSATION_connect()\n"); | ||
651 | h = GNUNET_malloc (sizeof (struct GNUNET_CONVERSATION_Handle)); | ||
652 | |||
653 | h->cfg = cfg; | ||
654 | h->call_handler = call_handler; | ||
655 | h->reject_handler = reject_handler; | ||
656 | h->notification_handler = notification_handler; | ||
657 | h->missed_call_handler = missed_call_handler; | ||
658 | 889 | ||
659 | if (NULL == (h->client = GNUNET_CLIENT_connect ("conversation", cfg))) | 890 | am = (const struct ClientAudioMessage *) msg; |
660 | { | 891 | switch (call->state) |
661 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not access CONVERSATION service\n"); | 892 | { |
662 | GNUNET_break (0); | 893 | case CS_LOOKUP: |
663 | GNUNET_free (h); | 894 | GNUNET_break (0); |
895 | reconnect_call (call); | ||
896 | break; | ||
897 | case CS_RINGING: | ||
898 | GNUNET_break (0); | ||
899 | reconnect_call (call); | ||
900 | break; | ||
901 | case CS_ACTIVE: | ||
902 | call->speaker->play (call->speaker->cls, | ||
903 | ntohs (msg->size) - sizeof (struct ClientAudioMessage), | ||
904 | &am[1]); | ||
905 | break; | ||
906 | case CS_SHUTDOWN: | ||
907 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
908 | break; | ||
664 | 909 | ||
665 | return NULL; | 910 | } |
666 | } | 911 | } |
667 | 912 | ||
668 | if (NULL == (h->gns = GNUNET_GNS_connect (cfg))) | ||
669 | { | ||
670 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not access GNS service\n"); | ||
671 | GNUNET_break (0); | ||
672 | GNUNET_CLIENT_disconnect (h->client); | ||
673 | GNUNET_free (h); | ||
674 | 913 | ||
675 | return NULL; | 914 | /** |
676 | } | 915 | * Iterator called on obtained result for a GNS lookup. |
916 | * | ||
917 | * @param cls closure with the `struct GNUNET_CONVERSATION_Call` | ||
918 | * @param rd_count number of records in @a rd | ||
919 | * @param rd the records in reply | ||
920 | */ | ||
921 | static void | ||
922 | handle_gns_response (void *cls, | ||
923 | uint32_t rd_count, | ||
924 | const struct GNUNET_NAMESTORE_RecordData *rd) | ||
925 | { | ||
926 | struct GNUNET_CONVERSATION_Call *call = cls; | ||
927 | uint32_t i; | ||
928 | struct GNUNET_MQ_Envelope *e; | ||
929 | struct ClientCallMessage *ccm; | ||
677 | 930 | ||
678 | if (NULL == (h->namestore = GNUNET_NAMESTORE_connect (cfg))) | 931 | for (i=0;i<rd_count;i++) |
932 | { | ||
933 | if (GNUNET_NAMESTORE_TYPE_PHONE == rd[i].record_type) | ||
679 | { | 934 | { |
680 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | 935 | if (rd[i].data_size != sizeof (struct PhoneRecord)) |
681 | "Could not access NAMESTORE service\n"); | 936 | { |
682 | GNUNET_break (0); | 937 | GNUNET_break_op (0); |
683 | GNUNET_CLIENT_disconnect (h->client); | 938 | continue; |
684 | GNUNET_GNS_disconnect (h->gns); | 939 | } |
685 | GNUNET_free (h); | 940 | memcpy (&call->phone_record, |
686 | 941 | rd[i].data, | |
687 | return NULL; | 942 | rd[i].data_size); |
943 | e = GNUNET_MQ_msg (ccm, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL); | ||
944 | ccm->line = call->phone_record.line; | ||
945 | ccm->target = call->phone_record.peer; | ||
946 | ccm->caller_id = *GNUNET_IDENTITY_ego_get_private_key (call->caller_id); | ||
947 | GNUNET_MQ_send (call->mq, e); | ||
948 | call->state = CS_RINGING; | ||
949 | call->event_handler (call->event_handler_cls, | ||
950 | GNUNET_CONVERSATION_EC_RINGING); | ||
951 | return; | ||
688 | } | 952 | } |
689 | 953 | } | |
690 | check_gns (h); | 954 | /* not found */ |
691 | GNUNET_CLIENT_receive (h->client, &receive_message_cb, h, | 955 | call->event_handler (call->event_handler_cls, |
692 | GNUNET_TIME_UNIT_FOREVER_REL); | 956 | GNUNET_CONVERSATION_EC_GNS_FAIL); |
693 | 957 | GNUNET_CONVERSATION_call_stop (call, NULL); | |
694 | return h; | ||
695 | } | 958 | } |
696 | 959 | ||
697 | 960 | ||
698 | void | 961 | /** |
699 | GNUNET_CONVERSATION_disconnect (struct GNUNET_CONVERSATION_Handle *handle) | 962 | * We encountered an error talking with the conversation service. |
963 | * | ||
964 | * @param cls the `struct GNUNET_CONVERSATION_Call` | ||
965 | * @param error details about the error | ||
966 | */ | ||
967 | static void | ||
968 | call_error_handler (void *cls, | ||
969 | enum GNUNET_MQ_Error error) | ||
700 | { | 970 | { |
701 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CONVERSATION DISCONNECT\n"); | 971 | struct GNUNET_CONVERSATION_Call *call = cls; |
702 | |||
703 | GNUNET_CLIENT_disconnect (handle->client); | ||
704 | GNUNET_GNS_disconnect (handle->gns); | ||
705 | 972 | ||
706 | GNUNET_free (handle); | 973 | GNUNET_break (0); |
707 | handle = NULL; | 974 | reconnect_call (call); |
708 | } | 975 | } |
709 | 976 | ||
710 | 977 | ||
711 | void | 978 | /** |
712 | GNUNET_CONVERSATION_call (struct GNUNET_CONVERSATION_Handle *h, | 979 | * The call got disconnected, reconnect to the service. |
713 | const char *callee, | 980 | * |
714 | int doGnsLookup) | 981 | * @param call call to reconnect |
982 | */ | ||
983 | static void | ||
984 | reconnect_call (struct GNUNET_CONVERSATION_Call *call) | ||
715 | { | 985 | { |
716 | struct GNUNET_PeerIdentity peer; | 986 | static struct GNUNET_MQ_MessageHandler handlers[] = |
717 | |||
718 | if (NULL == h || NULL == h->client) | ||
719 | return; | ||
720 | |||
721 | if (GNUNET_YES == doGnsLookup) | ||
722 | { | 987 | { |
723 | gns_lookup_and_call (h, callee); | 988 | { &handle_call_busy, |
724 | return; | 989 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_BUSY, |
990 | sizeof (struct ClientPhoneBusyMessage) }, | ||
991 | { &handle_call_picked_up, | ||
992 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP, | ||
993 | 0 }, | ||
994 | { &handle_call_hangup, | ||
995 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP, | ||
996 | 0 }, | ||
997 | { &handle_call_audio_message, | ||
998 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO, | ||
999 | 0 }, | ||
1000 | { NULL, 0, 0 } | ||
1001 | }; | ||
1002 | if (NULL != call->mq) | ||
1003 | { | ||
1004 | GNUNET_MQ_destroy (call->mq); | ||
1005 | call->mq = NULL; | ||
725 | } | 1006 | } |
726 | if (GNUNET_OK != | 1007 | if (NULL != call->client) |
727 | GNUNET_CRYPTO_ecc_public_sign_key_from_string (callee, | ||
728 | strlen (callee), | ||
729 | &peer.public_key)) | ||
730 | { | 1008 | { |
731 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | 1009 | GNUNET_CLIENT_disconnect (call->client); |
732 | _("`%s' is not a valid public key\n"), | 1010 | call->client = NULL; |
733 | callee); | 1011 | } |
734 | h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_NO_PEER, NULL); | 1012 | call->client = GNUNET_CLIENT_connect ("conversation", call->cfg); |
1013 | if (NULL == call->client) | ||
735 | return; | 1014 | return; |
736 | } | 1015 | call->mq = GNUNET_MQ_queue_for_connection_client (call->client, |
737 | initiate_call (h, peer); | 1016 | handlers, |
1017 | &call_error_handler, | ||
1018 | call); | ||
738 | } | 1019 | } |
739 | 1020 | ||
740 | 1021 | ||
741 | void | 1022 | /** |
742 | GNUNET_CONVERSATION_hangup (struct GNUNET_CONVERSATION_Handle *h) | 1023 | * Call the phone of another user. |
1024 | * | ||
1025 | * @param cfg configuration to use, specifies our phone service | ||
1026 | * @param caller_id identity of the caller | ||
1027 | * @param callee GNS name of the callee (used to locate the callee's record) | ||
1028 | * @param speaker speaker to use (will be used automatically immediately once the | ||
1029 | * #GNUNET_CONVERSATION_EC_READY event is generated); we will NOT generate | ||
1030 | * a ring tone on the speaker | ||
1031 | * @param mic microphone to use (will be used automatically immediately once the | ||
1032 | * #GNUNET_CONVERSATION_EC_READY event is generated) | ||
1033 | * @param event_handler how to notify the owner of the phone about events | ||
1034 | * @param event_handler_cls closure for @a event_handler | ||
1035 | */ | ||
1036 | struct GNUNET_CONVERSATION_Call * | ||
1037 | GNUNET_CONVERSATION_call_start (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
1038 | struct GNUNET_IDENTITY_Ego *caller_id, | ||
1039 | const char *callee, | ||
1040 | struct GNUNET_SPEAKER_Handle *speaker, | ||
1041 | struct GNUNET_MICROPHONE_Handle *mic, | ||
1042 | GNUNET_CONVERSATION_EventHandler event_handler, | ||
1043 | void *event_handler_cls) | ||
743 | { | 1044 | { |
744 | if (NULL == h || NULL == h->client) | 1045 | struct GNUNET_CONVERSATION_Call *call; |
745 | return; | 1046 | struct GNUNET_CRYPTO_EccPublicSignKey my_zone; |
746 | 1047 | ||
747 | terminate_call (h); | 1048 | GNUNET_IDENTITY_ego_get_public_key (caller_id, |
1049 | &my_zone); | ||
1050 | call = GNUNET_new (struct GNUNET_CONVERSATION_Call); | ||
1051 | call->cfg = cfg; | ||
1052 | call->caller_id = caller_id; | ||
1053 | call->callee = GNUNET_strdup (callee); | ||
1054 | call->speaker = speaker; | ||
1055 | call->mic = mic; | ||
1056 | call->event_handler = event_handler; | ||
1057 | call->event_handler_cls = event_handler_cls; | ||
1058 | call->gns = GNUNET_GNS_connect (cfg); | ||
1059 | reconnect_call (call); | ||
1060 | |||
1061 | if ( (NULL == call->client) || | ||
1062 | (NULL == call->gns) ) | ||
1063 | { | ||
1064 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
1065 | return NULL; | ||
1066 | } | ||
1067 | call->gns_lookup = GNUNET_GNS_lookup (call->gns, callee, | ||
1068 | &my_zone, | ||
1069 | GNUNET_NAMESTORE_TYPE_PHONE, | ||
1070 | GNUNET_NO, | ||
1071 | NULL /* FIXME: add shortening support */, | ||
1072 | &handle_gns_response, call); | ||
1073 | GNUNET_assert (NULL != call->gns_lookup); | ||
1074 | return call; | ||
748 | } | 1075 | } |
749 | 1076 | ||
750 | 1077 | ||
1078 | /** | ||
1079 | * Terminate a call. The call may be ringing or ready at this time. | ||
1080 | * | ||
1081 | * @param call call to terminate | ||
1082 | * @param reason if the call was active (ringing or ready) this will be the | ||
1083 | * reason given to the other user for why we hung up | ||
1084 | */ | ||
751 | void | 1085 | void |
752 | GNUNET_CONVERSATION_accept (struct GNUNET_CONVERSATION_Handle *h) | 1086 | GNUNET_CONVERSATION_call_stop (struct GNUNET_CONVERSATION_Call *call, |
1087 | const char *reason) | ||
753 | { | 1088 | { |
754 | if (NULL == h || NULL == h->client) | 1089 | if (NULL != reason) |
755 | return; | 1090 | { |
1091 | // FIXME: transmit reason to service... (not implemented!) | ||
1092 | GNUNET_break (0); | ||
1093 | // return; | ||
1094 | } | ||
1095 | if (NULL != call->speaker) | ||
1096 | { | ||
1097 | if (CS_ACTIVE == call->state) | ||
1098 | call->speaker->disable_speaker (call->speaker->cls); | ||
1099 | call->speaker = NULL; | ||
1100 | } | ||
1101 | if (NULL != call->mic) | ||
1102 | { | ||
1103 | if (CS_ACTIVE == call->state) | ||
1104 | call->mic->disable_microphone (call->mic->cls); | ||
1105 | call->mic =NULL; | ||
1106 | } | ||
1107 | if (NULL != call->mq) | ||
1108 | { | ||
1109 | GNUNET_MQ_destroy (call->mq); | ||
1110 | call->mq = NULL; | ||
1111 | } | ||
1112 | if (NULL != call->client) | ||
1113 | { | ||
1114 | GNUNET_CLIENT_disconnect (call->client); | ||
1115 | call->client = NULL; | ||
1116 | } | ||
1117 | if (NULL != call->gns_lookup) | ||
1118 | { | ||
1119 | GNUNET_GNS_lookup_cancel (call->gns_lookup); | ||
1120 | call->gns_lookup = NULL; | ||
1121 | } | ||
1122 | if (NULL != call->gns) | ||
1123 | { | ||
1124 | GNUNET_GNS_disconnect (call->gns); | ||
1125 | call->gns = NULL; | ||
1126 | } | ||
756 | 1127 | ||
757 | accept_call (h); | 1128 | GNUNET_free (call); |
758 | } | 1129 | } |
759 | 1130 | ||
760 | 1131 | ||
761 | void | ||
762 | GNUNET_CONVERSATION_reject (struct GNUNET_CONVERSATION_Handle *h) | ||
763 | { | ||
764 | if (NULL == h || NULL == h->client) | ||
765 | return; | ||
766 | |||
767 | reject_call (h); | ||
768 | } | ||
769 | |||
770 | /* end of conversation_api.c */ | 1132 | /* end of conversation_api.c */ |