aboutsummaryrefslogtreecommitdiff
path: root/src/conversation
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2013-11-15 19:06:55 +0000
committerChristian Grothoff <christian@grothoff.org>2013-11-15 19:06:55 +0000
commitdd594a4b0a7dbc57ea49f8a797a7a412e53baa53 (patch)
tree6a651b061395ebd9370cef01f14655ff50e81a66 /src/conversation
parent1c405f3fb76f509113526678dbe9a5736dfce394 (diff)
downloadgnunet-dd594a4b0a7dbc57ea49f8a797a7a412e53baa53.tar.gz
gnunet-dd594a4b0a7dbc57ea49f8a797a7a412e53baa53.zip
-towards enabling call waiting in conversation -- creates FTBFS
Diffstat (limited to 'src/conversation')
-rw-r--r--src/conversation/Makefile.am1
-rw-r--r--src/conversation/conversation.h77
-rw-r--r--src/conversation/conversation_api.c1115
-rw-r--r--src/conversation/conversation_api_call.c678
-rw-r--r--src/conversation/gnunet-conversation.c201
5 files changed, 1267 insertions, 805 deletions
diff --git a/src/conversation/Makefile.am b/src/conversation/Makefile.am
index b9e0c99b6..010e87e6c 100644
--- a/src/conversation/Makefile.am
+++ b/src/conversation/Makefile.am
@@ -142,6 +142,7 @@ gnunet_conversation_test_LDFLAGS = \
142 142
143 143
144test_conversation_api_SOURCES = \ 144test_conversation_api_SOURCES = \
145 test_conversation_api_call.c \
145 test_conversation_api.c 146 test_conversation_api.c
146test_conversation_api_LDADD = \ 147test_conversation_api_LDADD = \
147 libgnunetconversation.la \ 148 libgnunetconversation.la \
diff --git a/src/conversation/conversation.h b/src/conversation/conversation.h
index e2316984a..91afbbce4 100644
--- a/src/conversation/conversation.h
+++ b/src/conversation/conversation.h
@@ -82,9 +82,10 @@ struct ClientPhoneRingMessage
82 struct GNUNET_MessageHeader header; 82 struct GNUNET_MessageHeader header;
83 83
84 /** 84 /**
85 * Always zero. 85 * CID, internal caller ID to identify which active call we are
86 * talking about.
86 */ 87 */
87 uint32_t reserved GNUNET_PACKED; 88 uint32_t cid GNUNET_PACKED;
88 89
89 /** 90 /**
90 * Who is calling us? 91 * Who is calling us?
@@ -95,15 +96,40 @@ struct ClientPhoneRingMessage
95 96
96 97
97/** 98/**
98 * Service -> Client message for phone is busy. 99 * Service <-> Client message for phone was suspended.
99 */ 100 */
100struct ClientPhoneBusyMessage 101struct ClientPhoneSuspendMessage
101{ 102{
102 /** 103 /**
103 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_BUSY 104 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND
104 */ 105 */
105 struct GNUNET_MessageHeader header; 106 struct GNUNET_MessageHeader header;
106 107
108 /**
109 * CID, internal caller ID to identify which active call we are
110 * talking about.
111 */
112 uint32_t cid GNUNET_PACKED;
113
114};
115
116
117/**
118 * Service <-> Client message for phone was resumed.
119 */
120struct ClientPhoneResumeMessage
121{
122 /**
123 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME
124 */
125 struct GNUNET_MessageHeader header;
126
127 /**
128 * CID, internal caller ID to identify which active call we are
129 * talking about.
130 */
131 uint32_t cid GNUNET_PACKED;
132
107}; 133};
108 134
109 135
@@ -117,7 +143,11 @@ struct ClientPhonePickupMessage
117 */ 143 */
118 struct GNUNET_MessageHeader header; 144 struct GNUNET_MessageHeader header;
119 145
120 /* followed by variable length 0-terminated string with meta data */ 146 /**
147 * CID, internal caller ID to identify which active call we are
148 * talking about.
149 */
150 uint32_t cid GNUNET_PACKED;
121 151
122}; 152};
123 153
@@ -133,7 +163,11 @@ struct ClientPhoneHangupMessage
133 */ 163 */
134 struct GNUNET_MessageHeader header; 164 struct GNUNET_MessageHeader header;
135 165
136 /* followed by variable length 0-terminated string with meta data */ 166 /**
167 * CID, internal caller ID to identify which active call we are
168 * talking about.
169 */
170 uint32_t cid GNUNET_PACKED;
137 171
138}; 172};
139 173
@@ -148,6 +182,12 @@ struct ClientAudioMessage
148 */ 182 */
149 struct GNUNET_MessageHeader header; 183 struct GNUNET_MessageHeader header;
150 184
185 /**
186 * CID, internal caller ID to identify which active call we are
187 * sending data to.
188 */
189 uint32_t cid GNUNET_PACKED;
190
151 /* followed by audio data */ 191 /* followed by audio data */
152 192
153}; 193};
@@ -191,8 +231,6 @@ struct ClientPhonePickedupMessage
191 */ 231 */
192 struct GNUNET_MessageHeader header; 232 struct GNUNET_MessageHeader header;
193 233
194 /* followed by variable length 0-terminated string with meta data */
195
196}; 234};
197 235
198 236
@@ -259,7 +297,6 @@ struct MeshPhoneHangupMessage
259 */ 297 */
260 struct GNUNET_MessageHeader header; 298 struct GNUNET_MessageHeader header;
261 299
262 /* followed by variable-size 0-terminated reason string */
263}; 300};
264 301
265 302
@@ -273,17 +310,29 @@ struct MeshPhonePickupMessage
273 */ 310 */
274 struct GNUNET_MessageHeader header; 311 struct GNUNET_MessageHeader header;
275 312
276 /* followed by variable-size 0-terminated metadata string */
277}; 313};
278 314
279 315
280/** 316/**
281 * Mesh message for phone busy. 317 * Mesh message for phone suspended.
318 */
319struct MeshPhoneSuspendMessage
320{
321 /**
322 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_SUSPEND
323 */
324 struct GNUNET_MessageHeader header;
325
326};
327
328
329/**
330 * Mesh message for phone resumed.
282 */ 331 */
283struct MeshPhoneBusyMessage 332struct MeshPhoneResumeMessage
284{ 333{
285 /** 334 /**
286 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_BUSY 335 * Type is: #GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RESUME
287 */ 336 */
288 struct GNUNET_MessageHeader header; 337 struct GNUNET_MessageHeader header;
289 338
diff --git a/src/conversation/conversation_api.c b/src/conversation/conversation_api.c
index 63b072fc0..9dfc6e913 100644
--- a/src/conversation/conversation_api.c
+++ b/src/conversation/conversation_api.c
@@ -20,7 +20,7 @@
20 20
21/** 21/**
22 * @file conversation/conversation_api.c 22 * @file conversation/conversation_api.c
23 * @brief API to the conversation service 23 * @brief phone and caller API to the conversation service
24 * @author Simon Dieterle 24 * @author Simon Dieterle
25 * @author Andreas Fuchs 25 * @author Andreas Fuchs
26 * @author Christian Grothoff 26 * @author Christian Grothoff
@@ -33,29 +33,127 @@
33 33
34 34
35/** 35/**
36 * Possible states of the phone. 36 * Possible states of a caller.
37 */ 37 */
38enum PhoneState 38enum CallerState
39{ 39{
40 /** 40 /**
41 * We still need to register the phone. 41 * We still need to reverse lookup the caller ID.
42 */ 42 */
43 PS_REGISTER = 0, 43 CS_RESOLVE,
44 44
45 /** 45 /**
46 * We are waiting for a call. 46 * The phone is ringing (user knows about incoming call).
47 */ 47 */
48 PS_WAITING, 48 CS_RINGING,
49 49
50 /** 50 /**
51 * The phone is ringing. 51 * The phone is in an active conversation.
52 */ 52 */
53 PS_RINGING, 53 CS_ACTIVE,
54 54
55 /** 55 /**
56 * The phone is in an active conversation. 56 * We suspended the conversation.
57 */
58 CS_CALLEE_SUSPENDED,
59
60 /**
61 * Caller suspended the conversation.
62 */
63 CS_CALLER_SUSPENDED,
64
65 /**
66 * Both sides suspended the conversation.
57 */ 67 */
58 PS_ACTIVE 68 CS_BOTH_SUSPENDED
69};
70
71
72
73/**
74 * A caller is the handle we have for an incoming call.
75 */
76struct GNUNET_CONVERSATION_Caller
77{
78
79 /**
80 * We keep all callers in a DLL.
81 */
82 struct GNUNET_CONVERSATION_Caller *next;
83
84 /**
85 * We keep all callers in a DLL.
86 */
87 struct GNUNET_CONVERSATION_Caller *prev;
88
89 /**
90 * Our phone.
91 */
92 struct GNUNET_CONVERSATION_Phone *phone;
93
94 /**
95 * Function to call for phone events.
96 */
97 GNUNET_CONVERSATION_CallerEventHandler event_handler;
98
99 /**
100 * Closure for @e event_handler
101 */
102 void *event_handler_cls;
103
104 /**
105 * Speaker, or NULL if none is attached.
106 */
107 struct GNUNET_SPEAKER_Handle *speaker;
108
109 /**
110 * Microphone, or NULL if none is attached.
111 */
112 struct GNUNET_MICROPHONE_Handle *mic;
113
114 /**
115 * Active NAMESTORE lookup (or NULL).
116 */
117 struct GNUNET_NAMESTORE_QueueEntry *qe;
118
119 /**
120 * Identity of the person calling us (valid while in state #PS_RINGING).
121 */
122 struct GNUNET_CRYPTO_EcdsaPublicKey caller_id;
123
124 /**
125 * Caller ID of the person calling us as a string.
126 */
127 char *caller_id_str;
128
129 /**
130 * Internal handle to identify the caller with the service.
131 */
132 uint32_t cid;
133
134 /**
135 * State machine for the phone.
136 */
137 enum CallerState state;
138
139};
140
141
142/**
143 * Possible states of a phone.
144 */
145enum PhoneState
146{
147 /**
148 * We still need to register the phone.
149 */
150 PS_REGISTER = 0,
151
152 /**
153 * We are waiting for calls.
154 */
155 PS_READY
156
59}; 157};
60 158
61 159
@@ -83,24 +181,24 @@ struct GNUNET_CONVERSATION_Phone
83 struct GNUNET_CLIENT_Connection *client; 181 struct GNUNET_CLIENT_Connection *client;
84 182
85 /** 183 /**
86 * Function to call for phone events. 184 * We keep all callers in a DLL.
87 */ 185 */
88 GNUNET_CONVERSATION_EventHandler event_handler; 186 struct GNUNET_CONVERSATION_Caller *caller_head;
89 187
90 /** 188 /**
91 * Closure for @e event_handler 189 * We keep all callers in a DLL.
92 */ 190 */
93 void *event_handler_cls; 191 struct GNUNET_CONVERSATION_Caller *caller_tail;
94 192
95 /** 193 /**
96 * Speaker, or NULL if none is attached. 194 * Function to call for phone events.
97 */ 195 */
98 struct GNUNET_SPEAKER_Handle *speaker; 196 GNUNET_CONVERSATION_PhoneEventHandler event_handler;
99 197
100 /** 198 /**
101 * Microphone, or NULL if none is attached. 199 * Closure for @e event_handler
102 */ 200 */
103 struct GNUNET_MICROPHONE_Handle *mic; 201 void *event_handler_cls;
104 202
105 /** 203 /**
106 * Connection to NAMESTORE (for reverse lookup). 204 * Connection to NAMESTORE (for reverse lookup).
@@ -108,11 +206,6 @@ struct GNUNET_CONVERSATION_Phone
108 struct GNUNET_NAMESTORE_Handle *ns; 206 struct GNUNET_NAMESTORE_Handle *ns;
109 207
110 /** 208 /**
111 * Active NAMESTORE lookup (or NULL).
112 */
113 struct GNUNET_NAMESTORE_QueueEntry *qe;
114
115 /**
116 * Handle for transmitting to the CONVERSATION service. 209 * Handle for transmitting to the CONVERSATION service.
117 */ 210 */
118 struct GNUNET_MQ_Handle *mq; 211 struct GNUNET_MQ_Handle *mq;
@@ -128,11 +221,6 @@ struct GNUNET_CONVERSATION_Phone
128 struct GNUNET_CRYPTO_EcdsaPrivateKey my_zone; 221 struct GNUNET_CRYPTO_EcdsaPrivateKey my_zone;
129 222
130 /** 223 /**
131 * Identity of the person calling us (valid while in state #PS_RINGING).
132 */
133 struct GNUNET_CRYPTO_EcdsaPublicKey caller_id;
134
135 /**
136 * State machine for the phone. 224 * State machine for the phone.
137 */ 225 */
138 enum PhoneState state; 226 enum PhoneState state;
@@ -152,7 +240,7 @@ reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone);
152/** 240/**
153 * We have resolved the caller ID using our name service. 241 * We have resolved the caller ID using our name service.
154 * 242 *
155 * @param cls the `struct GNUNET_CONVERSATION_Phone` 243 * @param cls the `struct GNUNET_CONVERSATION_Caller`
156 * @param zone our zone used for resolution 244 * @param zone our zone used for resolution
157 * @param label name of the caller 245 * @param label name of the caller
158 * @param rd_count number of records we have in @a rd 246 * @param rd_count number of records we have in @a rd
@@ -165,18 +253,47 @@ handle_caller_name (void *cls,
165 unsigned int rd_count, 253 unsigned int rd_count,
166 const struct GNUNET_GNSRECORD_Data *rd) 254 const struct GNUNET_GNSRECORD_Data *rd)
167{ 255{
168 struct GNUNET_CONVERSATION_Phone *phone = cls; 256 struct GNUNET_CONVERSATION_Caller *caller = cls;
257 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
169 char *name; 258 char *name;
170 259
171 phone->qe = NULL; 260 caller->qe = NULL;
172 if (NULL == label) 261 if (NULL == label)
173 name = GNUNET_strdup (GNUNET_GNSRECORD_pkey_to_zkey (&phone->caller_id)); 262 name = GNUNET_strdup (GNUNET_GNSRECORD_pkey_to_zkey (&caller->caller_id));
174 else 263 else
175 GNUNET_asprintf (&name, "%.gnu", label); 264 GNUNET_asprintf (&name, "%.gnu", label);
265 caller->caller_id_str = name;
266 caller->state = CS_RINGING;
176 phone->event_handler (phone->event_handler_cls, 267 phone->event_handler (phone->event_handler_cls,
177 GNUNET_CONVERSATION_EC_RING, 268 GNUNET_CONVERSATION_EC_PHONE_RING,
269 caller,
178 name); 270 name);
179 GNUNET_free (name); 271}
272
273
274/**
275 * Process recorded audio data.
276 *
277 * @param cls closure with the `struct GNUNET_CONVERSATION_Caller`
278 * @param data_size number of bytes in @a data
279 * @param data audio data to play
280 */
281static void
282transmit_phone_audio (void *cls,
283 size_t data_size,
284 const void *data)
285{
286 struct GNUNET_CONVERSATION_Caller *caller = cls;
287 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
288 struct GNUNET_MQ_Envelope *e;
289 struct ClientAudioMessage *am;
290
291 e = GNUNET_MQ_msg_extra (am,
292 data_size,
293 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
294 am->cid = caller->cid;
295 memcpy (&am[1], data, data_size);
296 GNUNET_MQ_send (phone->mq, e);
180} 297}
181 298
182 299
@@ -192,6 +309,7 @@ handle_phone_ring (void *cls,
192{ 309{
193 struct GNUNET_CONVERSATION_Phone *phone = cls; 310 struct GNUNET_CONVERSATION_Phone *phone = cls;
194 const struct ClientPhoneRingMessage *ring; 311 const struct ClientPhoneRingMessage *ring;
312 struct GNUNET_CONVERSATION_Caller *caller;
195 313
196 ring = (const struct ClientPhoneRingMessage *) msg; 314 ring = (const struct ClientPhoneRingMessage *) msg;
197 switch (phone->state) 315 switch (phone->state)
@@ -199,22 +317,20 @@ handle_phone_ring (void *cls,
199 case PS_REGISTER: 317 case PS_REGISTER:
200 GNUNET_assert (0); 318 GNUNET_assert (0);
201 break; 319 break;
202 case PS_WAITING: 320 case PS_READY:
203 phone->state = PS_RINGING; 321 caller = GNUNET_new (struct GNUNET_CONVERSATION_Caller);
204 phone->caller_id = ring->caller_id; 322 caller->phone = phone;
205 phone->qe = GNUNET_NAMESTORE_zone_to_name (phone->ns, 323 GNUNET_CONTAINER_DLL_insert (phone->caller_head,
206 &phone->my_zone, 324 phone->caller_tail,
207 &ring->caller_id, 325 caller);
208 &handle_caller_name, 326 caller->state = CS_RESOLVE;
209 phone); 327 caller->caller_id = ring->caller_id;
210 break; 328 caller->cid = ring->cid;
211 case PS_RINGING: 329 caller->qe = GNUNET_NAMESTORE_zone_to_name (phone->ns,
212 GNUNET_break (0); 330 &phone->my_zone,
213 reconnect_phone (phone); 331 &ring->caller_id,
214 break; 332 &handle_caller_name,
215 case PS_ACTIVE: 333 caller);
216 GNUNET_break (0);
217 reconnect_phone (phone);
218 break; 334 break;
219 } 335 }
220} 336}
@@ -232,49 +348,144 @@ handle_phone_hangup (void *cls,
232{ 348{
233 struct GNUNET_CONVERSATION_Phone *phone = cls; 349 struct GNUNET_CONVERSATION_Phone *phone = cls;
234 const struct ClientPhoneHangupMessage *hang; 350 const struct ClientPhoneHangupMessage *hang;
235 size_t len; 351 struct GNUNET_CONVERSATION_Caller *caller;
236 const char *reason;
237 352
238 hang = (const struct ClientPhoneHangupMessage *) msg; 353 hang = (const struct ClientPhoneHangupMessage *) msg;
239 reason = (const char *) &hang[1]; 354 for (caller = phone->caller_head; NULL != caller; caller = caller->next)
240 len = htons (hang->header.size) - sizeof (struct ClientPhoneHangupMessage); 355 if (hang->cid == caller->cid)
241 if ( (0 == len) || 356 break;
242 ('\0' != reason[len-1]) ) 357 if (NULL == caller)
243 {
244 GNUNET_break (0);
245 reconnect_phone (phone);
246 return; 358 return;
247 } 359
248 switch (phone->state) 360 switch (caller->state)
249 { 361 {
250 case PS_REGISTER: 362 case CS_RESOLVE:
251 GNUNET_assert (0); 363 GNUNET_NAMESTORE_cancel (caller->qe);
364 caller->qe = NULL;
252 break; 365 break;
253 case PS_WAITING: 366 case CS_RINGING:
254 GNUNET_break (0); 367 phone->event_handler (phone->event_handler_cls,
255 reconnect_phone (phone); 368 GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
369 caller,
370 caller->caller_id_str);
256 break; 371 break;
257 case PS_RINGING: 372 case CS_ACTIVE:
258 if (NULL != phone->qe) 373 caller->speaker->disable_speaker (caller->speaker->cls);
259 { 374 caller->mic->disable_microphone (caller->mic->cls);
260 GNUNET_NAMESTORE_cancel (phone->qe);
261 phone->qe = NULL;
262 phone->state = PS_WAITING;
263 break;
264 }
265 phone->state = PS_WAITING;
266 phone->event_handler (phone->event_handler_cls, 375 phone->event_handler (phone->event_handler_cls,
267 GNUNET_CONVERSATION_EC_TERMINATED, 376 GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
268 reason); 377 caller,
378 caller->caller_id_str);
269 break; 379 break;
270 case PS_ACTIVE: 380 case CS_CALLEE_SUSPENDED:
271 GNUNET_break (NULL == phone->qe); 381 case CS_CALLER_SUSPENDED:
272 phone->state = PS_WAITING; 382 case CS_BOTH_SUSPENDED:
273 phone->event_handler (phone->event_handler_cls, 383 phone->event_handler (phone->event_handler_cls,
274 GNUNET_CONVERSATION_EC_TERMINATED, 384 GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
275 reason); 385 caller,
276 phone->speaker->disable_speaker (phone->speaker->cls); 386 caller->caller_id_str);
277 phone->mic->disable_microphone (phone->mic->cls); 387 break;
388 }
389 GNUNET_CONTAINER_DLL_remove (phone->caller_head,
390 phone->caller_tail,
391 caller);
392 GNUNET_free (caller);
393}
394
395
396/**
397 * We received a `struct ClientPhoneSuspendMessage`.
398 *
399 * @param cls the `struct GNUNET_CONVERSATION_Phone`
400 * @param msg the message
401 */
402static void
403handle_phone_suspend (void *cls,
404 const struct GNUNET_MessageHeader *msg)
405{
406 struct GNUNET_CONVERSATION_Phone *phone = cls;
407 struct GNUNET_CONVERSATION_Caller *caller;
408 const struct ClientPhoneSuspendMessage *suspend;
409
410 suspend = (const struct ClientPhoneSuspendMessage *) msg;
411 for (caller = phone->caller_head; NULL != caller; caller = caller->next)
412 if (suspend->cid == caller->cid)
413 break;
414 if (NULL == caller)
415 return;
416 switch (caller->state)
417 {
418 case CS_RESOLVE:
419 GNUNET_break_op (0);
420 break;
421 case CS_RINGING:
422 GNUNET_break_op (0);
423 break;
424 case CS_ACTIVE:
425 caller->state = CS_CALLER_SUSPENDED;
426 caller->speaker->disable_speaker (caller->speaker->cls);
427 caller->mic->disable_microphone (caller->mic->cls);
428 caller->event_handler (caller->event_handler_cls,
429 GNUNET_CONVERSATION_EC_CALLER_SUSPEND);
430 break;
431 case CS_CALLEE_SUSPENDED:
432 caller->state = CS_BOTH_SUSPENDED;
433 caller->event_handler (caller->event_handler_cls,
434 GNUNET_CONVERSATION_EC_CALLER_SUSPEND);
435 break;
436 case CS_CALLER_SUSPENDED:
437 case CS_BOTH_SUSPENDED:
438 GNUNET_break_op (0);
439 break;
440 }
441}
442
443
444/**
445 * We received a `struct ClientPhoneResumeMessage`.
446 *
447 * @param cls the `struct GNUNET_CONVERSATION_Phone`
448 * @param msg the message
449 */
450static void
451handle_phone_resume (void *cls,
452 const struct GNUNET_MessageHeader *msg)
453{
454 struct GNUNET_CONVERSATION_Phone *phone = cls;
455 struct GNUNET_CONVERSATION_Caller *caller;
456 const struct ClientPhoneResumeMessage *resume;
457
458 resume = (const struct ClientPhoneResumeMessage *) msg;
459 for (caller = phone->caller_head; NULL != caller; caller = caller->next)
460 if (resume->cid == caller->cid)
461 break;
462 if (NULL == caller)
463 return;
464 switch (caller->state)
465 {
466 case CS_RESOLVE:
467 GNUNET_break_op (0);
468 break;
469 case CS_RINGING:
470 GNUNET_break_op (0);
471 break;
472 case CS_ACTIVE:
473 case CS_CALLEE_SUSPENDED:
474 GNUNET_break_op (0);
475 break;
476 case CS_CALLER_SUSPENDED:
477 caller->state = CS_ACTIVE;
478 caller->speaker->enable_speaker (caller->speaker->cls);
479 caller->mic->enable_microphone (caller->mic->cls,
480 &transmit_phone_audio,
481 caller);
482 caller->event_handler (caller->event_handler_cls,
483 GNUNET_CONVERSATION_EC_CALLER_RESUME);
484 break;
485 case CS_BOTH_SUSPENDED:
486 caller->state = CS_CALLEE_SUSPENDED;
487 caller->event_handler (caller->event_handler_cls,
488 GNUNET_CONVERSATION_EC_CALLER_RESUME);
278 break; 489 break;
279 } 490 }
280} 491}
@@ -292,25 +503,30 @@ handle_phone_audio_message (void *cls,
292{ 503{
293 struct GNUNET_CONVERSATION_Phone *phone = cls; 504 struct GNUNET_CONVERSATION_Phone *phone = cls;
294 const struct ClientAudioMessage *am; 505 const struct ClientAudioMessage *am;
506 struct GNUNET_CONVERSATION_Caller *caller;
295 507
296 am = (const struct ClientAudioMessage *) msg; 508 am = (const struct ClientAudioMessage *) msg;
297 switch (phone->state) 509 for (caller = phone->caller_head; NULL != caller; caller = caller->next)
510 if (am->cid == caller->cid)
511 break;
512 if (NULL == caller)
513 return;
514 switch (caller->state)
298 { 515 {
299 case PS_REGISTER: 516 case CS_RESOLVE:
300 GNUNET_assert (0); 517 GNUNET_break_op (0);
301 break; 518 break;
302 case PS_WAITING: 519 case CS_RINGING:
303 GNUNET_break (0); 520 GNUNET_break_op (0);
304 reconnect_phone (phone);
305 break; 521 break;
306 case PS_RINGING: 522 case CS_ACTIVE:
307 GNUNET_break (0); 523 caller->speaker->play (caller->speaker->cls,
308 reconnect_phone (phone); 524 ntohs (msg->size) - sizeof (struct ClientAudioMessage),
525 &am[1]);
309 break; 526 break;
310 case PS_ACTIVE: 527 case CS_CALLEE_SUSPENDED:
311 phone->speaker->play (phone->speaker->cls, 528 case CS_CALLER_SUSPENDED:
312 ntohs (msg->size) - sizeof (struct ClientAudioMessage), 529 case CS_BOTH_SUSPENDED:
313 &am[1]);
314 break; 530 break;
315 } 531 }
316} 532}
@@ -329,9 +545,9 @@ phone_error_handler (void *cls,
329 struct GNUNET_CONVERSATION_Phone *phone = cls; 545 struct GNUNET_CONVERSATION_Phone *phone = cls;
330 546
331 GNUNET_break (0); 547 GNUNET_break (0);
332 FPRINTF (stderr, 548 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
333 _("Internal error %d\n"), 549 _("Internal MQ error %d\n"),
334 error); 550 error);
335 reconnect_phone (phone); 551 reconnect_phone (phone);
336} 552}
337 553
@@ -351,7 +567,13 @@ reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone)
351 sizeof (struct ClientPhoneRingMessage) }, 567 sizeof (struct ClientPhoneRingMessage) },
352 { &handle_phone_hangup, 568 { &handle_phone_hangup,
353 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP, 569 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
354 0 }, 570 sizeof (struct ClientPhoneHangupMessage) },
571 { &handle_phone_suspend,
572 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND,
573 sizeof (struct ClientPhoneSuspendMessage) },
574 { &handle_phone_resume,
575 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME,
576 sizeof (struct ClientPhoneResumeMessage) },
355 { &handle_phone_audio_message, 577 { &handle_phone_audio_message,
356 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO, 578 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
357 0 }, 579 0 },
@@ -359,11 +581,15 @@ reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone)
359 }; 581 };
360 struct GNUNET_MQ_Envelope *e; 582 struct GNUNET_MQ_Envelope *e;
361 struct ClientPhoneRegisterMessage *reg; 583 struct ClientPhoneRegisterMessage *reg;
584 struct GNUNET_CONVERSATION_Caller *caller;
362 585
363 if (PS_ACTIVE == phone->state) 586 while (NULL != (caller = phone->caller_head))
364 { 587 {
365 phone->speaker->disable_speaker (phone->speaker->cls); 588 phone->event_handler (phone->event_handler_cls,
366 phone->mic->disable_microphone (phone->mic->cls); 589 GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
590 caller,
591 caller->caller_id_str);
592 GNUNET_CONVERSATION_caller_hang_up (caller);
367 } 593 }
368 if (NULL != phone->mq) 594 if (NULL != phone->mq)
369 { 595 {
@@ -386,7 +612,7 @@ reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone)
386 e = GNUNET_MQ_msg (reg, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER); 612 e = GNUNET_MQ_msg (reg, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER);
387 reg->line = phone->my_record.line; 613 reg->line = phone->my_record.line;
388 GNUNET_MQ_send (phone->mq, e); 614 GNUNET_MQ_send (phone->mq, e);
389 phone->state = PS_WAITING; 615 phone->state = PS_READY;
390} 616}
391 617
392 618
@@ -402,7 +628,7 @@ reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone)
402struct GNUNET_CONVERSATION_Phone * 628struct GNUNET_CONVERSATION_Phone *
403GNUNET_CONVERSATION_phone_create (const struct GNUNET_CONFIGURATION_Handle *cfg, 629GNUNET_CONVERSATION_phone_create (const struct GNUNET_CONFIGURATION_Handle *cfg,
404 const struct GNUNET_IDENTITY_Ego *ego, 630 const struct GNUNET_IDENTITY_Ego *ego,
405 GNUNET_CONVERSATION_EventHandler event_handler, 631 GNUNET_CONVERSATION_PhoneEventHandler event_handler,
406 void *event_handler_cls) 632 void *event_handler_cls)
407{ 633{
408 struct GNUNET_CONVERSATION_Phone *phone; 634 struct GNUNET_CONVERSATION_Phone *phone;
@@ -463,61 +689,39 @@ GNUNET_CONVERSATION_phone_get_record (struct GNUNET_CONVERSATION_Phone *phone,
463 689
464 690
465/** 691/**
466 * Process recorded audio data.
467 *
468 * @param cls closure with the `struct GNUNET_CONVERSATION_Phone`
469 * @param data_size number of bytes in @a data
470 * @param data audio data to play
471 */
472static void
473transmit_phone_audio (void *cls,
474 size_t data_size,
475 const void *data)
476{
477 struct GNUNET_CONVERSATION_Phone *phone = cls;
478 struct GNUNET_MQ_Envelope *e;
479 struct ClientAudioMessage *am;
480
481 GNUNET_assert (PS_ACTIVE == phone->state);
482 e = GNUNET_MQ_msg_extra (am,
483 data_size,
484 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
485 memcpy (&am[1], data, data_size);
486 GNUNET_MQ_send (phone->mq, e);
487}
488
489
490/**
491 * Picks up a (ringing) phone. This will connect the speaker 692 * Picks up a (ringing) phone. This will connect the speaker
492 * to the microphone of the other party, and vice versa. 693 * to the microphone of the other party, and vice versa.
493 * 694 *
494 * @param phone phone to pick up 695 * @param caller handle that identifies which caller should be answered
495 * @param metadata meta data to give to the other user about the pick up event 696 * @param event_handler how to notify about events by the caller
697 * @param event_handler_cls closure for @a event_handler
496 * @param speaker speaker to use 698 * @param speaker speaker to use
497 * @param mic microphone to use 699 * @param mic microphone to use
498 */ 700 */
499void 701void
500GNUNET_CONVERSATION_phone_pick_up (struct GNUNET_CONVERSATION_Phone *phone, 702GNUNET_CONVERSATION_caller_pick_up (struct GNUNET_CONVERSATION_Caller *caller,
501 const char *metadata, 703 GNUNET_CONVERSATION_CallerEventHandler event_handler,
502 struct GNUNET_SPEAKER_Handle *speaker, 704 void *event_handler_cls,
503 struct GNUNET_MICROPHONE_Handle *mic) 705 struct GNUNET_SPEAKER_Handle *speaker,
706 struct GNUNET_MICROPHONE_Handle *mic)
504{ 707{
708 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
505 struct GNUNET_MQ_Envelope *e; 709 struct GNUNET_MQ_Envelope *e;
506 struct ClientPhonePickupMessage *pick; 710 struct ClientPhonePickupMessage *pick;
507 size_t slen; 711
508 712 GNUNET_assert (CS_RINGING == caller->state);
509 GNUNET_assert (PS_RINGING == phone->state); 713 caller->speaker = speaker;
510 phone->speaker = speaker; 714 caller->mic = mic;
511 phone->mic = mic; 715 e = GNUNET_MQ_msg (pick, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP);
512 slen = strlen (metadata) + 1; 716 pick->cid = caller->cid;
513 e = GNUNET_MQ_msg_extra (pick, slen, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP);
514 memcpy (&pick[1], metadata, slen);
515 GNUNET_MQ_send (phone->mq, e); 717 GNUNET_MQ_send (phone->mq, e);
516 phone->state = PS_ACTIVE; 718 caller->state = CS_ACTIVE;
517 phone->speaker->enable_speaker (phone->speaker->cls); 719 caller->event_handler = event_handler;
518 phone->mic->enable_microphone (phone->mic->cls, 720 caller->event_handler_cls = event_handler_cls;
519 &transmit_phone_audio, 721 caller->speaker->enable_speaker (caller->speaker->cls);
520 phone); 722 caller->mic->enable_microphone (caller->mic->cls,
723 &transmit_phone_audio,
724 caller);
521} 725}
522 726
523 727
@@ -525,28 +729,35 @@ GNUNET_CONVERSATION_phone_pick_up (struct GNUNET_CONVERSATION_Phone *phone,
525 * Hang up up a (possibly ringing) phone. This will notify the other 729 * Hang up up a (possibly ringing) phone. This will notify the other
526 * party that we are no longer interested in talking with them. 730 * party that we are no longer interested in talking with them.
527 * 731 *
528 * @param phone phone to pick up 732 * @param caller conversation to hang up on
529 * @param reason text we give to the other party about why we terminated the conversation
530 */ 733 */
531void 734void
532GNUNET_CONVERSATION_phone_hang_up (struct GNUNET_CONVERSATION_Phone *phone, 735GNUNET_CONVERSATION_caller_hang_up (struct GNUNET_CONVERSATION_Caller *caller)
533 const char *reason)
534{ 736{
737 struct GNUNET_CONVERSATION_Phone *phone = caller->phone;
535 struct GNUNET_MQ_Envelope *e; 738 struct GNUNET_MQ_Envelope *e;
536 struct ClientPhoneHangupMessage *hang; 739 struct ClientPhoneHangupMessage *hang;
537 size_t slen; 740
538 741 switch (caller->state)
539 GNUNET_assert ( (PS_RINGING == phone->state) || 742 {
540 (PS_ACTIVE == phone->state) ); 743 case CS_RESOLVE:
541 phone->speaker->disable_speaker (phone->speaker->cls); 744 GNUNET_NAMESTORE_cancel (caller->qe);
542 phone->mic->disable_microphone (phone->mic->cls); 745 caller->qe = NULL;
543 phone->speaker = NULL; 746 break;
544 phone->mic = NULL; 747 case CS_ACTIVE:
545 slen = strlen (reason) + 1; 748 caller->speaker->disable_speaker (caller->speaker->cls);
546 e = GNUNET_MQ_msg_extra (hang, slen, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP); 749 caller->mic->disable_microphone (caller->mic->cls);
547 memcpy (&hang[1], reason, slen); 750 break;
751 default:
752 break;
753 }
754 GNUNET_CONTAINER_DLL_remove (phone->caller_head,
755 phone->caller_tail,
756 caller);
757 GNUNET_free_non_null (caller->caller_id_str);
758 GNUNET_free (caller);
759 e = GNUNET_MQ_msg (hang, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
548 GNUNET_MQ_send (phone->mq, e); 760 GNUNET_MQ_send (phone->mq, e);
549 phone->state = PS_WAITING;
550} 761}
551 762
552 763
@@ -558,20 +769,15 @@ GNUNET_CONVERSATION_phone_hang_up (struct GNUNET_CONVERSATION_Phone *phone,
558void 769void
559GNUNET_CONVERSATION_phone_destroy (struct GNUNET_CONVERSATION_Phone *phone) 770GNUNET_CONVERSATION_phone_destroy (struct GNUNET_CONVERSATION_Phone *phone)
560{ 771{
561 if (NULL != phone->speaker) 772 struct GNUNET_CONVERSATION_Caller *caller;
562 { 773
563 phone->speaker->disable_speaker (phone->speaker->cls); 774 while (NULL != (caller = phone->caller_head))
564 phone->speaker = NULL;
565 }
566 if (NULL != phone->mic)
567 {
568 phone->mic->disable_microphone (phone->mic->cls);
569 phone->mic = NULL;
570 }
571 if (NULL != phone->qe)
572 { 775 {
573 GNUNET_NAMESTORE_cancel (phone->qe); 776 phone->event_handler (phone->event_handler_cls,
574 phone->qe = NULL; 777 GNUNET_CONVERSATION_EC_PHONE_HUNG_UP,
778 caller,
779 caller->caller_id_str);
780 GNUNET_CONVERSATION_caller_hang_up (caller);
575 } 781 }
576 if (NULL != phone->ns) 782 if (NULL != phone->ns)
577 { 783 {
@@ -592,554 +798,33 @@ GNUNET_CONVERSATION_phone_destroy (struct GNUNET_CONVERSATION_Phone *phone)
592} 798}
593 799
594 800
595/* ******************************* Call API *************************** */
596
597/**
598 * Possible states of the phone.
599 */
600enum CallState
601{
602 /**
603 * We still need to lookup the callee.
604 */
605 CS_LOOKUP = 0,
606
607 /**
608 * The call is ringing.
609 */
610 CS_RINGING,
611
612 /**
613 * The call is in an active conversation.
614 */
615 CS_ACTIVE,
616
617 /**
618 * The call is in termination.
619 */
620 CS_SHUTDOWN
621};
622
623
624/**
625 * Handle for an outgoing call.
626 */
627struct GNUNET_CONVERSATION_Call
628{
629
630 /**
631 * Our configuration.
632 */
633 const struct GNUNET_CONFIGURATION_Handle *cfg;
634
635 /**
636 * Handle to talk with CONVERSATION service.
637 */
638 struct GNUNET_CLIENT_Connection *client;
639
640 /**
641 * Our caller identity.
642 */
643 struct GNUNET_IDENTITY_Ego *caller_id;
644
645 /**
646 * Target callee as a GNS address/name.
647 */
648 char *callee;
649
650 /**
651 * Our speaker.
652 */
653 struct GNUNET_SPEAKER_Handle *speaker;
654
655 /**
656 * Our microphone.
657 */
658 struct GNUNET_MICROPHONE_Handle *mic;
659
660 /**
661 * Function to call with events.
662 */
663 GNUNET_CONVERSATION_EventHandler event_handler;
664
665 /**
666 * Closure for @e event_handler
667 */
668 void *event_handler_cls;
669
670 /**
671 * Handle for transmitting to the CONVERSATION service.
672 */
673 struct GNUNET_MQ_Handle *mq;
674
675 /**
676 * Connection to GNS (can be NULL).
677 */
678 struct GNUNET_GNS_Handle *gns;
679
680 /**
681 * Active GNS lookup (or NULL).
682 */
683 struct GNUNET_GNS_LookupRequest *gns_lookup;
684
685 /**
686 * Target phone record, only valid after the lookup is done.
687 */
688 struct GNUNET_CONVERSATION_PhoneRecord phone_record;
689
690 /**
691 * State machine for the call.
692 */
693 enum CallState state;
694
695};
696
697
698/**
699 * The call got disconnected, reconnect to the service.
700 *
701 * @param call call to reconnect
702 */
703static void
704reconnect_call (struct GNUNET_CONVERSATION_Call *call);
705
706
707/**
708 * We received a `struct ClientPhoneBusyMessage`
709 *
710 * @param cls the `struct GNUNET_CONVERSATION_Call`
711 * @param msg the message
712 */
713static void
714handle_call_busy (void *cls,
715 const struct GNUNET_MessageHeader *msg)
716{
717 struct GNUNET_CONVERSATION_Call *call = cls;
718
719 switch (call->state)
720 {
721 case CS_LOOKUP:
722 GNUNET_break (0);
723 reconnect_call (call);
724 break;
725 case CS_RINGING:
726 call->event_handler (call->event_handler_cls,
727 GNUNET_CONVERSATION_EC_BUSY);
728 GNUNET_CONVERSATION_call_stop (call, NULL);
729 break;
730 case CS_ACTIVE:
731 GNUNET_break (0);
732 reconnect_call (call);
733 break;
734 case CS_SHUTDOWN:
735 GNUNET_CONVERSATION_call_stop (call, NULL);
736 break;
737 }
738}
739
740
741/**
742 * Process recorded audio data.
743 *
744 * @param cls closure with the `struct GNUNET_CONVERSATION_Call`
745 * @param data_size number of bytes in @a data
746 * @param data audio data to play
747 */
748static void
749transmit_call_audio (void *cls,
750 size_t data_size,
751 const void *data)
752{
753 struct GNUNET_CONVERSATION_Call *call = cls;
754 struct GNUNET_MQ_Envelope *e;
755 struct ClientAudioMessage *am;
756
757 GNUNET_assert (CS_ACTIVE == call->state);
758 e = GNUNET_MQ_msg_extra (am,
759 data_size,
760 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
761 memcpy (&am[1], data, data_size);
762 GNUNET_MQ_send (call->mq, e);
763}
764
765
766/**
767 * We received a `struct ClientPhonePickedupMessage`
768 *
769 * @param cls the `struct GNUNET_CONVERSATION_Call`
770 * @param msg the message
771 */
772static void
773handle_call_picked_up (void *cls,
774 const struct GNUNET_MessageHeader *msg)
775{
776 struct GNUNET_CONVERSATION_Call *call = cls;
777 const struct ClientPhonePickedupMessage *am;
778 const char *metadata;
779 size_t size;
780
781 am = (const struct ClientPhonePickedupMessage *) msg;
782 size = ntohs (am->header.size) - sizeof (struct ClientPhonePickedupMessage);
783 metadata = (const char *) &am[1];
784 if ( (0 == size) ||
785 ('\0' != metadata[size - 1]) )
786 metadata = NULL;
787 switch (call->state)
788 {
789 case CS_LOOKUP:
790 GNUNET_break (0);
791 reconnect_call (call);
792 break;
793 case CS_RINGING:
794 call->state = CS_ACTIVE;
795 call->event_handler (call->event_handler_cls,
796 GNUNET_CONVERSATION_EC_READY,
797 metadata);
798 call->speaker->enable_speaker (call->speaker->cls);
799 call->mic->enable_microphone (call->mic->cls,
800 &transmit_call_audio,
801 call);
802 break;
803 case CS_ACTIVE:
804 GNUNET_break (0);
805 reconnect_call (call);
806 break;
807 case CS_SHUTDOWN:
808 GNUNET_CONVERSATION_call_stop (call, NULL);
809 break;
810 }
811}
812
813
814/**
815 * We received a `struct ClientPhoneHangupMessage`
816 *
817 * @param cls the `struct GNUNET_CONVERSATION_Call`
818 * @param msg the message
819 */
820static void
821handle_call_hangup (void *cls,
822 const struct GNUNET_MessageHeader *msg)
823{
824 struct GNUNET_CONVERSATION_Call *call = cls;
825 const struct ClientPhoneHangupMessage *am;
826 const char *reason;
827 size_t size;
828
829 am = (const struct ClientPhoneHangupMessage *) msg;
830 size = ntohs (am->header.size) - sizeof (struct ClientPhoneHangupMessage);
831 reason = (const char *) &am[1];
832 if ( (0 == size) ||
833 ('\0' != reason[size - 1]) )
834 reason = NULL;
835 switch (call->state)
836 {
837 case CS_LOOKUP:
838 GNUNET_break (0);
839 reconnect_call (call);
840 break;
841 case CS_RINGING:
842 call->event_handler (call->event_handler_cls,
843 GNUNET_CONVERSATION_EC_TERMINATED,
844 reason);
845 GNUNET_CONVERSATION_call_stop (call, NULL);
846 return;
847 case CS_ACTIVE:
848 call->event_handler (call->event_handler_cls,
849 GNUNET_CONVERSATION_EC_TERMINATED,
850 reason);
851 GNUNET_CONVERSATION_call_stop (call, NULL);
852 return;
853 case CS_SHUTDOWN:
854 GNUNET_CONVERSATION_call_stop (call, NULL);
855 break;
856 }
857}
858
859
860/**
861 * We received a `struct ClientAudioMessage`
862 *
863 * @param cls the `struct GNUNET_CONVERSATION_Call`
864 * @param msg the message
865 */
866static void
867handle_call_audio_message (void *cls,
868 const struct GNUNET_MessageHeader *msg)
869{
870 struct GNUNET_CONVERSATION_Call *call = cls;
871 const struct ClientAudioMessage *am;
872
873 am = (const struct ClientAudioMessage *) msg;
874 switch (call->state)
875 {
876 case CS_LOOKUP:
877 GNUNET_break (0);
878 reconnect_call (call);
879 break;
880 case CS_RINGING:
881 GNUNET_break (0);
882 reconnect_call (call);
883 break;
884 case CS_ACTIVE:
885 call->speaker->play (call->speaker->cls,
886 ntohs (msg->size) - sizeof (struct ClientAudioMessage),
887 &am[1]);
888 break;
889 case CS_SHUTDOWN:
890 GNUNET_CONVERSATION_call_stop (call, NULL);
891 break;
892
893 }
894}
895
896
897/**
898 * Iterator called on obtained result for a GNS lookup.
899 *
900 * @param cls closure with the `struct GNUNET_CONVERSATION_Call`
901 * @param rd_count number of records in @a rd
902 * @param rd the records in reply
903 */
904static void
905handle_gns_response (void *cls,
906 uint32_t rd_count,
907 const struct GNUNET_GNSRECORD_Data *rd)
908{
909 struct GNUNET_CONVERSATION_Call *call = cls;
910 uint32_t i;
911 struct GNUNET_MQ_Envelope *e;
912 struct ClientCallMessage *ccm;
913
914 call->gns_lookup = NULL;
915 for (i=0;i<rd_count;i++)
916 {
917 if (GNUNET_GNSRECORD_TYPE_PHONE == rd[i].record_type)
918 {
919 if (rd[i].data_size != sizeof (struct GNUNET_CONVERSATION_PhoneRecord))
920 {
921 GNUNET_break_op (0);
922 continue;
923 }
924 memcpy (&call->phone_record,
925 rd[i].data,
926 rd[i].data_size);
927 e = GNUNET_MQ_msg (ccm, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL);
928 ccm->line = call->phone_record.line;
929 ccm->target = call->phone_record.peer;
930 ccm->caller_id = *GNUNET_IDENTITY_ego_get_private_key (call->caller_id);
931 GNUNET_MQ_send (call->mq, e);
932 call->state = CS_RINGING;
933 call->event_handler (call->event_handler_cls,
934 GNUNET_CONVERSATION_EC_RINGING);
935 return;
936 }
937 }
938 /* not found */
939 call->event_handler (call->event_handler_cls,
940 GNUNET_CONVERSATION_EC_GNS_FAIL);
941 GNUNET_CONVERSATION_call_stop (call, NULL);
942}
943
944
945/** 801/**
946 * We encountered an error talking with the conversation service. 802 * Pause conversation of an active call. This will disconnect the speaker
803 * and the microphone. The call can later be resumed with
804 * #GNUNET_CONVERSATION_caller_resume.
947 * 805 *
948 * @param cls the `struct GNUNET_CONVERSATION_Call` 806 * @param phone phone to pause
949 * @param error details about the error
950 */ 807 */
951static void 808void
952call_error_handler (void *cls, 809GNUNET_CONVERSATION_caller_suspend (struct GNUNET_CONVERSATION_Caller *caller)
953 enum GNUNET_MQ_Error error)
954{ 810{
955 struct GNUNET_CONVERSATION_Call *call = cls;
956
957 GNUNET_break (0); 811 GNUNET_break (0);
958 FPRINTF (stderr,
959 _("Internal error %d\n"),
960 error);
961 reconnect_call (call);
962} 812}
963 813
964 814
965/** 815/**
966 * The call got disconnected, reconnect to the service. 816 * Resume suspended conversation of a phone.
967 * 817 *
968 * @param call call to reconnect 818 * @param phone phone to resume
969 */ 819 * @param speaker speaker to use
970static void 820 * @param mic microphone to use
971reconnect_call (struct GNUNET_CONVERSATION_Call *call)
972{
973 static struct GNUNET_MQ_MessageHandler handlers[] =
974 {
975 { &handle_call_busy,
976 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_BUSY,
977 sizeof (struct ClientPhoneBusyMessage) },
978 { &handle_call_picked_up,
979 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP,
980 0 },
981 { &handle_call_hangup,
982 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
983 0 },
984 { &handle_call_audio_message,
985 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
986 0 },
987 { NULL, 0, 0 }
988 };
989 struct GNUNET_CRYPTO_EcdsaPublicKey my_zone;
990
991 if (CS_ACTIVE == call->state)
992 {
993 call->speaker->disable_speaker (call->speaker->cls);
994 call->mic->disable_microphone (call->mic->cls);
995 }
996 if (NULL != call->mq)
997 {
998 GNUNET_MQ_destroy (call->mq);
999 call->mq = NULL;
1000 }
1001 if (NULL != call->client)
1002 {
1003 GNUNET_CLIENT_disconnect (call->client);
1004 call->client = NULL;
1005 }
1006 call->state = CS_SHUTDOWN;
1007 call->client = GNUNET_CLIENT_connect ("conversation", call->cfg);
1008 if (NULL == call->client)
1009 return;
1010 call->mq = GNUNET_MQ_queue_for_connection_client (call->client,
1011 handlers,
1012 &call_error_handler,
1013 call);
1014 call->state = CS_LOOKUP;
1015 GNUNET_IDENTITY_ego_get_public_key (call->caller_id,
1016 &my_zone);
1017 call->gns_lookup = GNUNET_GNS_lookup (call->gns,
1018 call->callee,
1019 &my_zone,
1020 GNUNET_GNSRECORD_TYPE_PHONE,
1021 GNUNET_NO,
1022 NULL /* FIXME: add shortening support */,
1023 &handle_gns_response, call);
1024 GNUNET_assert (NULL != call->gns_lookup);
1025}
1026
1027
1028/**
1029 * Call the phone of another user.
1030 *
1031 * @param cfg configuration to use, specifies our phone service
1032 * @param caller_id identity of the caller
1033 * @param callee GNS name of the callee (used to locate the callee's record)
1034 * @param speaker speaker to use (will be used automatically immediately once the
1035 * #GNUNET_CONVERSATION_EC_READY event is generated); we will NOT generate
1036 * a ring tone on the speaker
1037 * @param mic microphone to use (will be used automatically immediately once the
1038 * #GNUNET_CONVERSATION_EC_READY event is generated)
1039 * @param event_handler how to notify the owner of the phone about events
1040 * @param event_handler_cls closure for @a event_handler
1041 */
1042struct GNUNET_CONVERSATION_Call *
1043GNUNET_CONVERSATION_call_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
1044 struct GNUNET_IDENTITY_Ego *caller_id,
1045 const char *callee,
1046 struct GNUNET_SPEAKER_Handle *speaker,
1047 struct GNUNET_MICROPHONE_Handle *mic,
1048 GNUNET_CONVERSATION_EventHandler event_handler,
1049 void *event_handler_cls)
1050{
1051 struct GNUNET_CONVERSATION_Call *call;
1052
1053 call = GNUNET_new (struct GNUNET_CONVERSATION_Call);
1054 call->cfg = cfg;
1055 call->caller_id = caller_id;
1056 call->callee = GNUNET_strdup (callee);
1057 call->speaker = speaker;
1058 call->mic = mic;
1059 call->event_handler = event_handler;
1060 call->event_handler_cls = event_handler_cls;
1061 call->gns = GNUNET_GNS_connect (cfg);
1062 reconnect_call (call);
1063
1064 if ( (NULL == call->client) ||
1065 (NULL == call->gns) )
1066 {
1067 GNUNET_CONVERSATION_call_stop (call, NULL);
1068 return NULL;
1069 }
1070 return call;
1071}
1072
1073
1074/**
1075 * We've sent the hang up message, now finish terminating the call.
1076 *
1077 * @param cls the `struct GNUNET_CONVERSATION_Call` to terminate
1078 */
1079static void
1080finish_stop (void *cls)
1081{
1082 struct GNUNET_CONVERSATION_Call *call = cls;
1083
1084 GNUNET_assert (CS_SHUTDOWN == call->state);
1085 GNUNET_CONVERSATION_call_stop (call, NULL);
1086}
1087
1088
1089/**
1090 * Terminate a call. The call may be ringing or ready at this time.
1091 *
1092 * @param call call to terminate
1093 * @param reason if the call was active (ringing or ready) this will be the
1094 * reason given to the other user for why we hung up
1095 */ 821 */
1096void 822void
1097GNUNET_CONVERSATION_call_stop (struct GNUNET_CONVERSATION_Call *call, 823GNUNET_CONVERSATION_caller_resume (struct GNUNET_CONVERSATION_Caller *caller,
1098 const char *reason) 824 struct GNUNET_SPEAKER_Handle *speaker,
825 struct GNUNET_MICROPHONE_Handle *mic)
1099{ 826{
1100 struct GNUNET_MQ_Envelope *e; 827 GNUNET_break (0);
1101 struct ClientPhoneHangupMessage *hang;
1102 size_t slen;
1103
1104 if ( (NULL != call->speaker) &&
1105 (CS_ACTIVE == call->state) )
1106 call->speaker->disable_speaker (call->speaker->cls);
1107 if ( (NULL != call->mic) &&
1108 (CS_ACTIVE == call->state) )
1109 call->mic->disable_microphone (call->mic->cls);
1110 if (NULL != reason)
1111 {
1112 slen = strlen (reason) + 1;
1113 e = GNUNET_MQ_msg_extra (hang, slen, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
1114 memcpy (&hang[1], reason, slen);
1115 GNUNET_MQ_notify_sent (e, &finish_stop, call);
1116 GNUNET_MQ_send (call->mq, e);
1117 call->state = CS_SHUTDOWN;
1118 return;
1119 }
1120 if (NULL != call->mq)
1121 {
1122 GNUNET_MQ_destroy (call->mq);
1123 call->mq = NULL;
1124 }
1125 if (NULL != call->client)
1126 {
1127 GNUNET_CLIENT_disconnect (call->client);
1128 call->client = NULL;
1129 }
1130 if (NULL != call->gns_lookup)
1131 {
1132 GNUNET_GNS_lookup_cancel (call->gns_lookup);
1133 call->gns_lookup = NULL;
1134 }
1135 if (NULL != call->gns)
1136 {
1137 GNUNET_GNS_disconnect (call->gns);
1138 call->gns = NULL;
1139 }
1140 GNUNET_free (call->callee);
1141 GNUNET_free (call);
1142} 828}
1143 829
1144
1145/* end of conversation_api.c */ 830/* end of conversation_api.c */
diff --git a/src/conversation/conversation_api_call.c b/src/conversation/conversation_api_call.c
new file mode 100644
index 000000000..a57bf1ddb
--- /dev/null
+++ b/src/conversation/conversation_api_call.c
@@ -0,0 +1,678 @@
1/*
2 This file is part of GNUnet
3 (C) 2013 Christian Grothoff (and other contributing authors)
4
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
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
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
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21/**
22 * @file conversation/conversation_api_call.c
23 * @brief call API to the conversation service
24 * @author Simon Dieterle
25 * @author Andreas Fuchs
26 * @author Christian Grothoff
27 */
28#include "platform.h"
29#include "gnunet_conversation_service.h"
30#include "gnunet_gnsrecord_lib.h"
31#include "gnunet_gns_service.h"
32#include "conversation.h"
33
34
35/**
36 * Possible states of the phone.
37 */
38enum CallState
39{
40 /**
41 * We still need to lookup the callee.
42 */
43 CS_LOOKUP = 0,
44
45 /**
46 * The call is ringing.
47 */
48 CS_RINGING,
49
50 /**
51 * The call is in an active conversation.
52 */
53 CS_ACTIVE,
54
55 /**
56 * The call is in termination.
57 */
58 CS_SHUTDOWN,
59
60 /**
61 * The call was suspended by the caller.
62 */
63 CS_SUSPENDED_CALLER,
64
65 /**
66 * The call was suspended by the callee.
67 */
68 CS_SUSPENDED_CALLEE,
69
70 /**
71 * The call was suspended by both caller and callee.
72 */
73 CS_SUSPENDED_BOTH
74};
75
76
77/**
78 * Handle for an outgoing call.
79 */
80struct GNUNET_CONVERSATION_Call
81{
82
83 /**
84 * Our configuration.
85 */
86 const struct GNUNET_CONFIGURATION_Handle *cfg;
87
88 /**
89 * Handle to talk with CONVERSATION service.
90 */
91 struct GNUNET_CLIENT_Connection *client;
92
93 /**
94 * Our caller identity.
95 */
96 struct GNUNET_IDENTITY_Ego *caller_id;
97
98 /**
99 * Target callee as a GNS address/name.
100 */
101 char *callee;
102
103 /**
104 * Our speaker.
105 */
106 struct GNUNET_SPEAKER_Handle *speaker;
107
108 /**
109 * Our microphone.
110 */
111 struct GNUNET_MICROPHONE_Handle *mic;
112
113 /**
114 * Function to call with events.
115 */
116 GNUNET_CONVERSATION_CallEventHandler event_handler;
117
118 /**
119 * Closure for @e event_handler
120 */
121 void *event_handler_cls;
122
123 /**
124 * Handle for transmitting to the CONVERSATION service.
125 */
126 struct GNUNET_MQ_Handle *mq;
127
128 /**
129 * Connection to GNS (can be NULL).
130 */
131 struct GNUNET_GNS_Handle *gns;
132
133 /**
134 * Active GNS lookup (or NULL).
135 */
136 struct GNUNET_GNS_LookupRequest *gns_lookup;
137
138 /**
139 * Target phone record, only valid after the lookup is done.
140 */
141 struct GNUNET_CONVERSATION_PhoneRecord phone_record;
142
143 /**
144 * State machine for the call.
145 */
146 enum CallState state;
147
148};
149
150
151/**
152 * The call got disconnected, reconnect to the service.
153 *
154 * @param call call to reconnect
155 */
156static void
157reconnect_call (struct GNUNET_CONVERSATION_Call *call);
158
159
160/**
161 * Process recorded audio data.
162 *
163 * @param cls closure with the `struct GNUNET_CONVERSATION_Call`
164 * @param data_size number of bytes in @a data
165 * @param data audio data to play
166 */
167static void
168transmit_call_audio (void *cls,
169 size_t data_size,
170 const void *data)
171{
172 struct GNUNET_CONVERSATION_Call *call = cls;
173 struct GNUNET_MQ_Envelope *e;
174 struct ClientAudioMessage *am;
175
176 GNUNET_assert (CS_ACTIVE == call->state);
177 e = GNUNET_MQ_msg_extra (am,
178 data_size,
179 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
180 memcpy (&am[1], data, data_size);
181 GNUNET_MQ_send (call->mq, e);
182}
183
184
185/**
186 * We received a `struct ClientPhoneSuspendMessage`
187 *
188 * @param cls the `struct GNUNET_CONVERSATION_Call`
189 * @param msg the message
190 */
191static void
192handle_call_suspend (void *cls,
193 const struct GNUNET_MessageHeader *msg)
194{
195 struct GNUNET_CONVERSATION_Call *call = cls;
196
197 switch (call->state)
198 {
199 case CS_LOOKUP:
200 GNUNET_break (0);
201 reconnect_call (call);
202 break;
203 case CS_RINGING:
204 GNUNET_break_op (0);
205 reconnect_call (call);
206 break;
207 case CS_SUSPENDED_CALLER:
208 call->state = CS_SUSPENDED_BOTH;
209 call->event_handler (call->event_handler_cls,
210 GNUNET_CONVERSATION_EC_CALL_SUSPENDED);
211 break;
212 case CS_SUSPENDED_CALLEE:
213 case CS_SUSPENDED_BOTH:
214 GNUNET_break_op (0);
215 break;
216 case CS_ACTIVE:
217 call->state = CS_SUSPENDED_CALLEE;
218 call->event_handler (call->event_handler_cls,
219 GNUNET_CONVERSATION_EC_CALL_SUSPENDED);
220 call->speaker->disable_speaker (call->speaker->cls);
221 call->mic->disable_microphone (call->mic->cls);
222 break;
223 case CS_SHUTDOWN:
224 GNUNET_CONVERSATION_call_stop (call);
225 break;
226 }
227}
228
229
230/**
231 * We received a `struct ClientPhoneResumeMessage`
232 *
233 * @param cls the `struct GNUNET_CONVERSATION_Call`
234 * @param msg the message
235 */
236static void
237handle_call_resume (void *cls,
238 const struct GNUNET_MessageHeader *msg)
239{
240 struct GNUNET_CONVERSATION_Call *call = cls;
241
242 switch (call->state)
243 {
244 case CS_LOOKUP:
245 GNUNET_break (0);
246 reconnect_call (call);
247 break;
248 case CS_RINGING:
249 GNUNET_break_op (0);
250 reconnect_call (call);
251 break;
252 case CS_SUSPENDED_CALLER:
253 GNUNET_break_op (0);
254 break;
255 case CS_SUSPENDED_CALLEE:
256 call->state = CS_ACTIVE;
257 call->event_handler (call->event_handler_cls,
258 GNUNET_CONVERSATION_EC_CALL_RESUMED);
259 call->speaker->enable_speaker (call->speaker->cls);
260 call->mic->enable_microphone (call->mic->cls,
261 &transmit_call_audio,
262 call);
263 break;
264 case CS_SUSPENDED_BOTH:
265 call->state = CS_SUSPENDED_CALLER;
266 call->event_handler (call->event_handler_cls,
267 GNUNET_CONVERSATION_EC_CALL_RESUMED);
268 break;
269 case CS_ACTIVE:
270 GNUNET_break_op (0);
271 break;
272 case CS_SHUTDOWN:
273 GNUNET_CONVERSATION_call_stop (call);
274 break;
275 }
276}
277
278
279/**
280 * We received a `struct ClientPhonePickedupMessage`
281 *
282 * @param cls the `struct GNUNET_CONVERSATION_Call`
283 * @param msg the message
284 */
285static void
286handle_call_picked_up (void *cls,
287 const struct GNUNET_MessageHeader *msg)
288{
289 struct GNUNET_CONVERSATION_Call *call = cls;
290
291 switch (call->state)
292 {
293 case CS_LOOKUP:
294 GNUNET_break (0);
295 reconnect_call (call);
296 break;
297 case CS_RINGING:
298 call->state = CS_ACTIVE;
299 call->event_handler (call->event_handler_cls,
300 GNUNET_CONVERSATION_EC_CALL_PICKED_UP);
301 call->speaker->enable_speaker (call->speaker->cls);
302 call->mic->enable_microphone (call->mic->cls,
303 &transmit_call_audio,
304 call);
305 break;
306 case CS_SUSPENDED_CALLER:
307 case CS_SUSPENDED_CALLEE:
308 case CS_SUSPENDED_BOTH:
309 case CS_ACTIVE:
310 GNUNET_break (0);
311 reconnect_call (call);
312 break;
313 case CS_SHUTDOWN:
314 GNUNET_CONVERSATION_call_stop (call);
315 break;
316 }
317}
318
319
320/**
321 * We received a `struct ClientPhoneHangupMessage`
322 *
323 * @param cls the `struct GNUNET_CONVERSATION_Call`
324 * @param msg the message
325 */
326static void
327handle_call_hangup (void *cls,
328 const struct GNUNET_MessageHeader *msg)
329{
330 struct GNUNET_CONVERSATION_Call *call = cls;
331
332 switch (call->state)
333 {
334 case CS_LOOKUP:
335 GNUNET_break (0);
336 reconnect_call (call);
337 break;
338 case CS_RINGING:
339 case CS_SUSPENDED_CALLER:
340 case CS_SUSPENDED_CALLEE:
341 case CS_SUSPENDED_BOTH:
342 case CS_ACTIVE:
343 call->event_handler (call->event_handler_cls,
344 GNUNET_CONVERSATION_EC_CALL_HUNG_UP);
345 GNUNET_CONVERSATION_call_stop (call);
346 return;
347 case CS_SHUTDOWN:
348 GNUNET_CONVERSATION_call_stop (call);
349 break;
350 }
351}
352
353
354/**
355 * We received a `struct ClientAudioMessage`
356 *
357 * @param cls the `struct GNUNET_CONVERSATION_Call`
358 * @param msg the message
359 */
360static void
361handle_call_audio_message (void *cls,
362 const struct GNUNET_MessageHeader *msg)
363{
364 struct GNUNET_CONVERSATION_Call *call = cls;
365 const struct ClientAudioMessage *am;
366
367 am = (const struct ClientAudioMessage *) msg;
368 switch (call->state)
369 {
370 case CS_LOOKUP:
371 GNUNET_break (0);
372 reconnect_call (call);
373 break;
374 case CS_RINGING:
375 GNUNET_break (0);
376 reconnect_call (call);
377 break;
378 case CS_SUSPENDED_CALLER:
379 /* can happen: we suspended, other peer did not yet
380 learn about this. */
381 break;
382 case CS_SUSPENDED_CALLEE:
383 case CS_SUSPENDED_BOTH:
384 /* can (rarely) also happen: other peer suspended, but mesh might
385 have had delayed data on the unreliable channel */
386 break;
387 case CS_ACTIVE:
388 call->speaker->play (call->speaker->cls,
389 ntohs (msg->size) - sizeof (struct ClientAudioMessage),
390 &am[1]);
391 break;
392 case CS_SHUTDOWN:
393 GNUNET_CONVERSATION_call_stop (call);
394 break;
395 }
396}
397
398
399/**
400 * Iterator called on obtained result for a GNS lookup.
401 *
402 * @param cls closure with the `struct GNUNET_CONVERSATION_Call`
403 * @param rd_count number of records in @a rd
404 * @param rd the records in reply
405 */
406static void
407handle_gns_response (void *cls,
408 uint32_t rd_count,
409 const struct GNUNET_GNSRECORD_Data *rd)
410{
411 struct GNUNET_CONVERSATION_Call *call = cls;
412 uint32_t i;
413 struct GNUNET_MQ_Envelope *e;
414 struct ClientCallMessage *ccm;
415
416 call->gns_lookup = NULL;
417 for (i=0;i<rd_count;i++)
418 {
419 if (GNUNET_GNSRECORD_TYPE_PHONE == rd[i].record_type)
420 {
421 if (rd[i].data_size != sizeof (struct GNUNET_CONVERSATION_PhoneRecord))
422 {
423 GNUNET_break_op (0);
424 continue;
425 }
426 memcpy (&call->phone_record,
427 rd[i].data,
428 rd[i].data_size);
429 e = GNUNET_MQ_msg (ccm, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL);
430 ccm->line = call->phone_record.line;
431 ccm->target = call->phone_record.peer;
432 ccm->caller_id = *GNUNET_IDENTITY_ego_get_private_key (call->caller_id);
433 GNUNET_MQ_send (call->mq, e);
434 call->state = CS_RINGING;
435 call->event_handler (call->event_handler_cls,
436 GNUNET_CONVERSATION_EC_CALL_RINGING);
437 return;
438 }
439 }
440 /* not found */
441 call->event_handler (call->event_handler_cls,
442 GNUNET_CONVERSATION_EC_CALL_GNS_FAIL);
443 GNUNET_CONVERSATION_call_stop (call);
444}
445
446
447/**
448 * We encountered an error talking with the conversation service.
449 *
450 * @param cls the `struct GNUNET_CONVERSATION_Call`
451 * @param error details about the error
452 */
453static void
454call_error_handler (void *cls,
455 enum GNUNET_MQ_Error error)
456{
457 struct GNUNET_CONVERSATION_Call *call = cls;
458
459 GNUNET_break (0);
460 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
461 _("Internal MQ error %d\n"),
462 error);
463 reconnect_call (call);
464}
465
466
467/**
468 * The call got disconnected, reconnect to the service.
469 *
470 * @param call call to reconnect
471 */
472static void
473reconnect_call (struct GNUNET_CONVERSATION_Call *call)
474{
475 static struct GNUNET_MQ_MessageHandler handlers[] =
476 {
477 { &handle_call_suspend,
478 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND,
479 sizeof (struct ClientPhoneSuspendMessage) },
480 { &handle_call_resume,
481 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME,
482 sizeof (struct ClientPhoneResumeMessage) },
483 { &handle_call_picked_up,
484 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP,
485 sizeof (struct ClientPhonePickedupMessage) },
486 { &handle_call_hangup,
487 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
488 sizeof (struct ClientPhoneHangupMessage) },
489 { &handle_call_audio_message,
490 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
491 0 },
492 { NULL, 0, 0 }
493 };
494 struct GNUNET_CRYPTO_EcdsaPublicKey my_zone;
495
496 if (CS_ACTIVE == call->state)
497 {
498 call->speaker->disable_speaker (call->speaker->cls);
499 call->mic->disable_microphone (call->mic->cls);
500 }
501 if (NULL != call->mq)
502 {
503 GNUNET_MQ_destroy (call->mq);
504 call->mq = NULL;
505 }
506 if (NULL != call->client)
507 {
508 GNUNET_CLIENT_disconnect (call->client);
509 call->client = NULL;
510 }
511 call->state = CS_SHUTDOWN;
512 call->client = GNUNET_CLIENT_connect ("conversation", call->cfg);
513 if (NULL == call->client)
514 return;
515 call->mq = GNUNET_MQ_queue_for_connection_client (call->client,
516 handlers,
517 &call_error_handler,
518 call);
519 call->state = CS_LOOKUP;
520 GNUNET_IDENTITY_ego_get_public_key (call->caller_id,
521 &my_zone);
522 call->gns_lookup = GNUNET_GNS_lookup (call->gns,
523 call->callee,
524 &my_zone,
525 GNUNET_GNSRECORD_TYPE_PHONE,
526 GNUNET_NO,
527 NULL /* FIXME: add shortening support */,
528 &handle_gns_response, call);
529 GNUNET_assert (NULL != call->gns_lookup);
530}
531
532
533/**
534 * Call the phone of another user.
535 *
536 * @param cfg configuration to use, specifies our phone service
537 * @param caller_id identity of the caller
538 * @param callee GNS name of the callee (used to locate the callee's record)
539 * @param speaker speaker to use (will be used automatically immediately once the
540 * #GNUNET_CONVERSATION_EC_READY event is generated); we will NOT generate
541 * a ring tone on the speaker
542 * @param mic microphone to use (will be used automatically immediately once the
543 * #GNUNET_CONVERSATION_EC_READY event is generated)
544 * @param event_handler how to notify the owner of the phone about events
545 * @param event_handler_cls closure for @a event_handler
546 */
547struct GNUNET_CONVERSATION_Call *
548GNUNET_CONVERSATION_call_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
549 struct GNUNET_IDENTITY_Ego *caller_id,
550 const char *callee,
551 struct GNUNET_SPEAKER_Handle *speaker,
552 struct GNUNET_MICROPHONE_Handle *mic,
553 GNUNET_CONVERSATION_CallEventHandler event_handler,
554 void *event_handler_cls)
555{
556 struct GNUNET_CONVERSATION_Call *call;
557
558 call = GNUNET_new (struct GNUNET_CONVERSATION_Call);
559 call->cfg = cfg;
560 call->caller_id = caller_id;
561 call->callee = GNUNET_strdup (callee);
562 call->speaker = speaker;
563 call->mic = mic;
564 call->event_handler = event_handler;
565 call->event_handler_cls = event_handler_cls;
566 call->gns = GNUNET_GNS_connect (cfg);
567 reconnect_call (call);
568
569 if ( (NULL == call->client) ||
570 (NULL == call->gns) )
571 {
572 GNUNET_CONVERSATION_call_stop (call);
573 return NULL;
574 }
575 return call;
576}
577
578
579/**
580 * We've sent the hang up message, now finish terminating the call.
581 *
582 * @param cls the `struct GNUNET_CONVERSATION_Call` to terminate
583 */
584static void
585finish_stop (void *cls)
586{
587 struct GNUNET_CONVERSATION_Call *call = cls;
588
589 GNUNET_assert (CS_SHUTDOWN == call->state);
590 GNUNET_CONVERSATION_call_stop (call);
591}
592
593
594/**
595 * Terminate a call. The call may be ringing or ready at this time.
596 *
597 * @param call call to terminate
598 * @param reason if the call was active (ringing or ready) this will be the
599 * reason given to the other user for why we hung up
600 */
601void
602GNUNET_CONVERSATION_call_stop (struct GNUNET_CONVERSATION_Call *call)
603{
604 struct GNUNET_MQ_Envelope *e;
605 struct ClientPhoneHangupMessage *hang;
606
607 if ( (NULL != call->speaker) &&
608 (CS_ACTIVE == call->state) )
609 call->speaker->disable_speaker (call->speaker->cls);
610 if ( (NULL != call->mic) &&
611 (CS_ACTIVE == call->state) )
612 call->mic->disable_microphone (call->mic->cls);
613 if (CS_SHUTDOWN != call->state)
614 {
615 e = GNUNET_MQ_msg (hang, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
616 GNUNET_MQ_notify_sent (e, &finish_stop, call);
617 GNUNET_MQ_send (call->mq, e);
618 call->state = CS_SHUTDOWN;
619 return;
620 }
621 if (NULL != call->mq)
622 {
623 GNUNET_MQ_destroy (call->mq);
624 call->mq = NULL;
625 }
626 if (NULL != call->client)
627 {
628 GNUNET_CLIENT_disconnect (call->client);
629 call->client = NULL;
630 }
631 if (NULL != call->gns_lookup)
632 {
633 GNUNET_GNS_lookup_cancel (call->gns_lookup);
634 call->gns_lookup = NULL;
635 }
636 if (NULL != call->gns)
637 {
638 GNUNET_GNS_disconnect (call->gns);
639 call->gns = NULL;
640 }
641 GNUNET_free (call->callee);
642 GNUNET_free (call);
643}
644
645
646/**
647 * Pause a call. Temporarily suspends the use of speaker and
648 * microphone.
649 *
650 * @param call call to pause
651 */
652void
653GNUNET_CONVERSATION_call_suspend (struct GNUNET_CONVERSATION_Call *call)
654{
655 GNUNET_break (0);
656}
657
658
659/**
660 * Resumes a call after #GNUNET_CONVERSATION_call_pause.
661 *
662 * @param call call to resume
663 * @param speaker speaker to use (will be used automatically immediately once the
664 * #GNUNET_CONVERSATION_EC_READY event is generated); we will NOT generate
665 * a ring tone on the speaker
666 * @param mic microphone to use (will be used automatically immediately once the
667 * #GNUNET_CONVERSATION_EC_READY event is generated)
668 */
669void
670GNUNET_CONVERSATION_call_resume (struct GNUNET_CONVERSATION_Call *call,
671 struct GNUNET_SPEAKER_Handle *speaker,
672 struct GNUNET_MICROPHONE_Handle *mic)
673{
674 GNUNET_break (0);
675}
676
677
678/* end of conversation_api_call.c */
diff --git a/src/conversation/gnunet-conversation.c b/src/conversation/gnunet-conversation.c
index ea2ba9ac8..0287114f2 100644
--- a/src/conversation/gnunet-conversation.c
+++ b/src/conversation/gnunet-conversation.c
@@ -86,16 +86,64 @@ enum ConversationState
86 86
87 87
88/** 88/**
89 * List of incoming calls
90 */
91struct CallList
92{
93
94 /**
95 * A DLL.
96 */
97 struct CallList *prev;
98
99 /**
100 * A DLL.
101 */
102 struct CallList *next;
103
104 /**
105 * Handle to hang up or activate.
106 */
107 struct GNUNET_CONVERSATION_Caller *caller;
108
109 /**
110 * String identifying the caller.
111 */
112 const char *caller_id;
113
114 /**
115 * Unique number of the call.
116 */
117 unsigned int caller_num;
118};
119
120
121/**
89 * Phone handle 122 * Phone handle
90 */ 123 */
91static struct GNUNET_CONVERSATION_Phone *phone; 124static struct GNUNET_CONVERSATION_Phone *phone;
92 125
93/** 126/**
94 * Call handle 127 * Call handle (for active outgoing call).
95 */ 128 */
96static struct GNUNET_CONVERSATION_Call *call; 129static struct GNUNET_CONVERSATION_Call *call;
97 130
98/** 131/**
132 * Caller handle (for active incoming call).
133 */
134static struct CallList *cl_active;
135
136/**
137 * Head of calls waiting to be accepted.
138 */
139static struct CallList *cl_head;
140
141/**
142 * Tail of calls waiting to be accepted.
143 */
144static struct CallList *cl_tail;
145
146/**
99 * Desired phone line. 147 * Desired phone line.
100 */ 148 */
101static unsigned int line; 149static unsigned int line;
@@ -151,6 +199,11 @@ static struct GNUNET_DISK_FileHandle *stdin_fh;
151static enum ConversationState state; 199static enum ConversationState state;
152 200
153/** 201/**
202 * Counts the number of incoming calls we have had so far.
203 */
204static unsigned int caller_num_gen;
205
206/**
154 * GNS address for this phone. 207 * GNS address for this phone.
155 */ 208 */
156static char *address; 209static char *address;
@@ -165,50 +218,55 @@ static int verbose;
165 * Function called with an event emitted by a phone. 218 * Function called with an event emitted by a phone.
166 * 219 *
167 * @param cls closure 220 * @param cls closure
168 * @param code type of the event on the phone 221 * @param code type of the event
169 * @param ... additional information, depends on @a code 222 * @param caller handle for the caller
223 * @param caller_id name of the caller in GNS
170 */ 224 */
171static void 225static void
172phone_event_handler (void *cls, 226phone_event_handler (void *cls,
173 enum GNUNET_CONVERSATION_EventCode code, 227 enum GNUNET_CONVERSATION_PhoneEventCode code,
174 ...) 228 struct GNUNET_CONVERSATION_Caller *caller,
229 const char *caller_id)
175{ 230{
176 va_list va;
177
178 va_start (va, code);
179 switch (code) 231 switch (code)
180 { 232 {
181 case GNUNET_CONVERSATION_EC_RING: 233 case GNUNET_CONVERSATION_EC_PHONE_RING:
182 GNUNET_break (CS_LISTEN == state); 234 // FIXME: update state!
183 GNUNET_free_non_null (peer_name);
184 peer_name = GNUNET_strdup (va_arg (va, const char *));
185 FPRINTF (stdout, 235 FPRINTF (stdout,
186 _("Incoming call from `%s'.\nPlease /accept or /cancel the call.\n"), 236 _("Incoming call from `%s'.\nPlease /accept #%u or /cancel %u the call.\n"),
187 peer_name); 237 caller_id,
188 state = CS_RING; 238 caller_num_gen,
189 break; 239 caller_num_gen);
190 case GNUNET_CONVERSATION_EC_RINGING:
191 GNUNET_break (0);
192 break;
193 case GNUNET_CONVERSATION_EC_READY:
194 GNUNET_break (0);
195 break;
196 case GNUNET_CONVERSATION_EC_GNS_FAIL:
197 GNUNET_break (0);
198 break;
199 case GNUNET_CONVERSATION_EC_BUSY:
200 GNUNET_break (0);
201 break; 240 break;
202 case GNUNET_CONVERSATION_EC_TERMINATED: 241 case GNUNET_CONVERSATION_EC_PHONE_HUNG_UP:
203 GNUNET_break ( (CS_RING == state) || 242 // FIXME: update state!
204 (CS_ACCEPTED == state) );
205 FPRINTF (stdout, 243 FPRINTF (stdout,
206 _("Call terminated: %s\n"), 244 _("Call terminated: %s\n"),
207 va_arg (va, const char *)); 245 caller_id);
208 state = CS_LISTEN; 246 break;
247 }
248}
249
250
251/**
252 * Function called with an event emitted by a caller.
253 *
254 * @param cls closure with the `struct CallList` of the caller
255 * @param code type of the event issued by the caller
256 */
257static void
258caller_event_handler (void *cls,
259 enum GNUNET_CONVERSATION_CallerEventCode code)
260{
261 switch (code)
262 {
263 case GNUNET_CONVERSATION_EC_CALLER_SUSPEND:
264 // FIXME: notify user!
265 break;
266 case GNUNET_CONVERSATION_EC_CALLER_RESUME:
267 // FIXME: notify user!
209 break; 268 break;
210 } 269 }
211 va_end (va);
212} 270}
213 271
214 272
@@ -257,26 +315,18 @@ start_phone ()
257 315
258 316
259/** 317/**
260 * Function called with an event emitted by a phone. 318 * Function called with an event emitted by a call.
261 * 319 *
262 * @param cls closure 320 * @param cls closure
263 * @param code type of the event on the phone 321 * @param code type of the event on the call
264 * @param ... additional information, depends on @a code
265 */ 322 */
266static void 323static void
267call_event_handler (void *cls, 324call_event_handler (void *cls,
268 enum GNUNET_CONVERSATION_EventCode code, 325 enum GNUNET_CONVERSATION_CallEventCode code)
269 ...)
270{ 326{
271 va_list va;
272
273 va_start (va, code);
274 switch (code) 327 switch (code)
275 { 328 {
276 case GNUNET_CONVERSATION_EC_RING: 329 case GNUNET_CONVERSATION_EC_CALL_RINGING:
277 GNUNET_break (0);
278 break;
279 case GNUNET_CONVERSATION_EC_RINGING:
280 GNUNET_break (CS_RESOLVING == state); 330 GNUNET_break (CS_RESOLVING == state);
281 if (verbose) 331 if (verbose)
282 FPRINTF (stdout, 332 FPRINTF (stdout,
@@ -284,41 +334,37 @@ call_event_handler (void *cls,
284 _("Resolved address. Now ringing other party.\n")); 334 _("Resolved address. Now ringing other party.\n"));
285 state = CS_RINGING; 335 state = CS_RINGING;
286 break; 336 break;
287 case GNUNET_CONVERSATION_EC_READY: 337 case GNUNET_CONVERSATION_EC_CALL_PICKED_UP:
288 GNUNET_break (CS_RINGING == state); 338 GNUNET_break (CS_RINGING == state);
289 FPRINTF (stdout, 339 FPRINTF (stdout,
290 _("Connection established to `%s': %s\n"), 340 _("Connection established to `%s'\n"),
291 peer_name, 341 peer_name);
292 va_arg (va, const char *));
293 state = CS_CONNECTED; 342 state = CS_CONNECTED;
294 break; 343 break;
295 case GNUNET_CONVERSATION_EC_GNS_FAIL: 344 case GNUNET_CONVERSATION_EC_CALL_GNS_FAIL:
296 GNUNET_break (CS_RESOLVING == state); 345 GNUNET_break (CS_RESOLVING == state);
297 FPRINTF (stdout, 346 FPRINTF (stdout,
298 _("Failed to resolve `%s'\n"), 347 _("Failed to resolve `%s'\n"),
299 ego_name); 348 ego_name);
300 call = NULL; 349 call = NULL;
301 start_phone (); 350 state = CS_LISTEN;
302 break; 351 break;
303 case GNUNET_CONVERSATION_EC_BUSY: 352 case GNUNET_CONVERSATION_EC_CALL_HUNG_UP:
304 GNUNET_break (CS_RINGING == state);
305 FPRINTF (stdout, 353 FPRINTF (stdout,
306 "%s", 354 "%s",
307 _("Line busy\n")); 355 _("Call terminated\n"));
308 call = NULL; 356 call = NULL;
309 start_phone (); 357 state = CS_LISTEN;
310 break; 358 break;
311 case GNUNET_CONVERSATION_EC_TERMINATED: 359 case GNUNET_CONVERSATION_EC_CALL_SUSPENDED:
312 GNUNET_break ( (CS_RINGING == state) || 360 // FIXME: notify user
313 (CS_CONNECTED == state) ); 361 // state = CS_SUSPENDED_CALL;
314 FPRINTF (stdout, 362 break;
315 _("Call terminated: %s\n"), 363 case GNUNET_CONVERSATION_EC_CALL_RESUMED:
316 va_arg (va, const char *)); 364 // FIXME: notify user
317 call = NULL; 365 // state = CS_CONNECTED;
318 start_phone ();
319 break; 366 break;
320 } 367 }
321 va_end (va);
322} 368}
323 369
324 370
@@ -417,7 +463,7 @@ do_call (const char *arg)
417 _("Hanging up on incoming phone call from `%s' to call `%s'.\n"), 463 _("Hanging up on incoming phone call from `%s' to call `%s'.\n"),
418 peer_name, 464 peer_name,
419 arg); 465 arg);
420 GNUNET_CONVERSATION_phone_hang_up (phone, NULL); 466 // GNUNET_CONVERSATION_caller_hang_up (caller);
421 break; 467 break;
422 case CS_ACCEPTED: 468 case CS_ACCEPTED:
423 FPRINTF (stderr, 469 FPRINTF (stderr,
@@ -430,7 +476,8 @@ do_call (const char *arg)
430 FPRINTF (stderr, 476 FPRINTF (stderr,
431 _("Aborting call to `%s'\n"), 477 _("Aborting call to `%s'\n"),
432 peer_name); 478 peer_name);
433 GNUNET_CONVERSATION_call_stop (call, NULL); 479 // GNUNET_CONVERSATION_caller_hang_up (caller);
480 // GNUNET_CONVERSATION_call_stop (call);
434 call = NULL; 481 call = NULL;
435 break; 482 break;
436 case CS_CONNECTED: 483 case CS_CONNECTED:
@@ -497,11 +544,12 @@ do_accept (const char *args)
497 peer_name); 544 peer_name);
498 return; 545 return;
499 } 546 }
500 GNUNET_assert (NULL != phone); 547 GNUNET_assert (NULL != cl_active);
501 GNUNET_CONVERSATION_phone_pick_up (phone, 548 GNUNET_CONVERSATION_caller_pick_up (cl_active->caller,
502 args, 549 &caller_event_handler,
503 speaker, 550 cl_active,
504 mic); 551 speaker,
552 mic);
505 state = CS_ACCEPTED; 553 state = CS_ACCEPTED;
506} 554}
507 555
@@ -604,14 +652,15 @@ do_reject (const char *args)
604 } 652 }
605 if (NULL == call) 653 if (NULL == call)
606 { 654 {
607 GNUNET_assert (NULL != phone); 655#if 0
608 GNUNET_CONVERSATION_phone_hang_up (phone, 656 GNUNET_assert (NULL != caller);
609 args); 657 GNUNET_CONVERSATION_caller_hang_up (caller);
658#endif
610 state = CS_LISTEN; 659 state = CS_LISTEN;
611 } 660 }
612 else 661 else
613 { 662 {
614 GNUNET_CONVERSATION_call_stop (call, args); 663 GNUNET_CONVERSATION_call_stop (call);
615 call = NULL; 664 call = NULL;
616 start_phone (); 665 start_phone ();
617 } 666 }
@@ -699,7 +748,7 @@ do_stop_task (void *cls,
699{ 748{
700 if (NULL != call) 749 if (NULL != call)
701 { 750 {
702 GNUNET_CONVERSATION_call_stop (call, NULL); 751 GNUNET_CONVERSATION_call_stop (call);
703 call = NULL; 752 call = NULL;
704 } 753 }
705 if (NULL != phone) 754 if (NULL != phone)