diff options
author | Christian Grothoff <christian@grothoff.org> | 2013-11-15 19:06:55 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2013-11-15 19:06:55 +0000 |
commit | dd594a4b0a7dbc57ea49f8a797a7a412e53baa53 (patch) | |
tree | 6a651b061395ebd9370cef01f14655ff50e81a66 /src/conversation | |
parent | 1c405f3fb76f509113526678dbe9a5736dfce394 (diff) | |
download | gnunet-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.am | 1 | ||||
-rw-r--r-- | src/conversation/conversation.h | 77 | ||||
-rw-r--r-- | src/conversation/conversation_api.c | 1115 | ||||
-rw-r--r-- | src/conversation/conversation_api_call.c | 678 | ||||
-rw-r--r-- | src/conversation/gnunet-conversation.c | 201 |
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 | ||
144 | test_conversation_api_SOURCES = \ | 144 | test_conversation_api_SOURCES = \ |
145 | test_conversation_api_call.c \ | ||
145 | test_conversation_api.c | 146 | test_conversation_api.c |
146 | test_conversation_api_LDADD = \ | 147 | test_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 | */ |
100 | struct ClientPhoneBusyMessage | 101 | struct 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 | */ | ||
120 | struct 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 | */ | ||
319 | struct 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 | */ |
283 | struct MeshPhoneBusyMessage | 332 | struct 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 | */ |
38 | enum PhoneState | 38 | enum 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 | */ | ||
76 | struct 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 | */ | ||
145 | enum 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 | */ | ||
281 | static void | ||
282 | transmit_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 | */ | ||
402 | static void | ||
403 | handle_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 | */ | ||
450 | static void | ||
451 | handle_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) | |||
402 | struct GNUNET_CONVERSATION_Phone * | 628 | struct GNUNET_CONVERSATION_Phone * |
403 | GNUNET_CONVERSATION_phone_create (const struct GNUNET_CONFIGURATION_Handle *cfg, | 629 | GNUNET_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 | */ | ||
472 | static void | ||
473 | transmit_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 | */ |
499 | void | 701 | void |
500 | GNUNET_CONVERSATION_phone_pick_up (struct GNUNET_CONVERSATION_Phone *phone, | 702 | GNUNET_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 | */ |
531 | void | 734 | void |
532 | GNUNET_CONVERSATION_phone_hang_up (struct GNUNET_CONVERSATION_Phone *phone, | 735 | GNUNET_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, | |||
558 | void | 769 | void |
559 | GNUNET_CONVERSATION_phone_destroy (struct GNUNET_CONVERSATION_Phone *phone) | 770 | GNUNET_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 | */ | ||
600 | enum 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 | */ | ||
627 | struct 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 | */ | ||
703 | static void | ||
704 | reconnect_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 | */ | ||
713 | static void | ||
714 | handle_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 | */ | ||
748 | static void | ||
749 | transmit_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 | */ | ||
772 | static void | ||
773 | handle_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 | */ | ||
820 | static void | ||
821 | handle_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 | */ | ||
866 | static void | ||
867 | handle_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 | */ | ||
904 | static void | ||
905 | handle_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 | */ |
951 | static void | 808 | void |
952 | call_error_handler (void *cls, | 809 | GNUNET_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 |
970 | static void | 820 | * @param mic microphone to use |
971 | reconnect_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 | */ | ||
1042 | struct GNUNET_CONVERSATION_Call * | ||
1043 | GNUNET_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 | */ | ||
1079 | static void | ||
1080 | finish_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 | */ |
1096 | void | 822 | void |
1097 | GNUNET_CONVERSATION_call_stop (struct GNUNET_CONVERSATION_Call *call, | 823 | GNUNET_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 | */ | ||
38 | enum 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 | */ | ||
80 | struct 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 | */ | ||
156 | static void | ||
157 | reconnect_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 | */ | ||
167 | static void | ||
168 | transmit_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 | */ | ||
191 | static void | ||
192 | handle_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 | */ | ||
236 | static void | ||
237 | handle_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 | */ | ||
285 | static void | ||
286 | handle_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 | */ | ||
326 | static void | ||
327 | handle_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 | */ | ||
360 | static void | ||
361 | handle_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 | */ | ||
406 | static void | ||
407 | handle_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 | */ | ||
453 | static void | ||
454 | call_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 | */ | ||
472 | static void | ||
473 | reconnect_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 | */ | ||
547 | struct GNUNET_CONVERSATION_Call * | ||
548 | GNUNET_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 | */ | ||
584 | static void | ||
585 | finish_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 | */ | ||
601 | void | ||
602 | GNUNET_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 | */ | ||
652 | void | ||
653 | GNUNET_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 | */ | ||
669 | void | ||
670 | GNUNET_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 | */ | ||
91 | struct 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 | */ |
91 | static struct GNUNET_CONVERSATION_Phone *phone; | 124 | static struct GNUNET_CONVERSATION_Phone *phone; |
92 | 125 | ||
93 | /** | 126 | /** |
94 | * Call handle | 127 | * Call handle (for active outgoing call). |
95 | */ | 128 | */ |
96 | static struct GNUNET_CONVERSATION_Call *call; | 129 | static struct GNUNET_CONVERSATION_Call *call; |
97 | 130 | ||
98 | /** | 131 | /** |
132 | * Caller handle (for active incoming call). | ||
133 | */ | ||
134 | static struct CallList *cl_active; | ||
135 | |||
136 | /** | ||
137 | * Head of calls waiting to be accepted. | ||
138 | */ | ||
139 | static struct CallList *cl_head; | ||
140 | |||
141 | /** | ||
142 | * Tail of calls waiting to be accepted. | ||
143 | */ | ||
144 | static struct CallList *cl_tail; | ||
145 | |||
146 | /** | ||
99 | * Desired phone line. | 147 | * Desired phone line. |
100 | */ | 148 | */ |
101 | static unsigned int line; | 149 | static unsigned int line; |
@@ -151,6 +199,11 @@ static struct GNUNET_DISK_FileHandle *stdin_fh; | |||
151 | static enum ConversationState state; | 199 | static enum ConversationState state; |
152 | 200 | ||
153 | /** | 201 | /** |
202 | * Counts the number of incoming calls we have had so far. | ||
203 | */ | ||
204 | static unsigned int caller_num_gen; | ||
205 | |||
206 | /** | ||
154 | * GNS address for this phone. | 207 | * GNS address for this phone. |
155 | */ | 208 | */ |
156 | static char *address; | 209 | static 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 | */ |
171 | static void | 225 | static void |
172 | phone_event_handler (void *cls, | 226 | phone_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 | */ | ||
257 | static void | ||
258 | caller_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 | */ |
266 | static void | 323 | static void |
267 | call_event_handler (void *cls, | 324 | call_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) |