diff options
author | Christian Grothoff <christian@grothoff.org> | 2013-10-05 13:10:53 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2013-10-05 13:10:53 +0000 |
commit | ac3a1c494fd48fc1d01fcc4eafa0b3f4878f5441 (patch) | |
tree | a21b54452171f582444a199e70426b4c4266152b /src/conversation | |
parent | 45c9ba99503eaf130b3d298062c612e281893d6a (diff) | |
download | gnunet-ac3a1c494fd48fc1d01fcc4eafa0b3f4878f5441.tar.gz gnunet-ac3a1c494fd48fc1d01fcc4eafa0b3f4878f5441.zip |
-rename fest: new to default
Diffstat (limited to 'src/conversation')
-rw-r--r-- | src/conversation/Makefile.am | 41 | ||||
-rw-r--r-- | src/conversation/conversation_api.c | 1498 | ||||
-rw-r--r-- | src/conversation/conversation_api2.c | 1132 | ||||
-rw-r--r-- | src/conversation/gnunet-conversation-new.c | 855 | ||||
-rw-r--r-- | src/conversation/gnunet-conversation.c | 818 | ||||
-rw-r--r-- | src/conversation/gnunet-service-conversation-new.c | 1227 | ||||
-rw-r--r-- | src/conversation/gnunet-service-conversation.c | 2485 |
7 files changed, 2475 insertions, 5581 deletions
diff --git a/src/conversation/Makefile.am b/src/conversation/Makefile.am index 46d36053e..43a5a1a06 100644 --- a/src/conversation/Makefile.am +++ b/src/conversation/Makefile.am | |||
@@ -7,13 +7,6 @@ INCLUDES = \ | |||
7 | AM_CPPFLAGS = \ | 7 | AM_CPPFLAGS = \ |
8 | $(GNUNET_CPPFLAGS) | 8 | $(GNUNET_CPPFLAGS) |
9 | 9 | ||
10 | # Set this variable if you are using GNUNET libraries for all programs and | ||
11 | # libraries. You don't then need to target-specific _LDFLAGS with GNUNET_LDFLAGS | ||
12 | # AM_LDFLAGS = \ | ||
13 | # $(GNUNET_LDFLAGS) \ | ||
14 | # $(WINFLAGS) \ | ||
15 | # -export-dynamic | ||
16 | |||
17 | lib_LTLIBRARIES = \ | 10 | lib_LTLIBRARIES = \ |
18 | libgnunetmicrophone.la \ | 11 | libgnunetmicrophone.la \ |
19 | libgnunetspeaker.la \ | 12 | libgnunetspeaker.la \ |
@@ -41,8 +34,7 @@ libgnunetspeaker_la_LDFLAGS = \ | |||
41 | 34 | ||
42 | 35 | ||
43 | libgnunetconversation_la_SOURCES = \ | 36 | libgnunetconversation_la_SOURCES = \ |
44 | conversation_api.c \ | 37 | conversation_api.c |
45 | conversation_api2.c | ||
46 | libgnunetconversation_la_LIBADD = \ | 38 | libgnunetconversation_la_LIBADD = \ |
47 | $(top_builddir)/src/gns/libgnunetgns.la \ | 39 | $(top_builddir)/src/gns/libgnunetgns.la \ |
48 | $(top_builddir)/src/namestore/libgnunetnamestore.la \ | 40 | $(top_builddir)/src/namestore/libgnunetnamestore.la \ |
@@ -56,12 +48,10 @@ libgnunetconversation_la_LDFLAGS = \ | |||
56 | 48 | ||
57 | bin_PROGRAMS = \ | 49 | bin_PROGRAMS = \ |
58 | gnunet-conversation-test \ | 50 | gnunet-conversation-test \ |
59 | gnunet-conversation \ | 51 | gnunet-conversation |
60 | gnunet-conversation-new | ||
61 | 52 | ||
62 | libexec_PROGRAMS = \ | 53 | libexec_PROGRAMS = \ |
63 | gnunet-service-conversation \ | 54 | gnunet-service-conversation |
64 | gnunet-service-conversation-new | ||
65 | 55 | ||
66 | if HAVE_PULSE | 56 | if HAVE_PULSE |
67 | if HAVE_OPUS | 57 | if HAVE_OPUS |
@@ -100,39 +90,18 @@ gnunet_helper_audio_playback_LDFLAGS = \ | |||
100 | gnunet_service_conversation_SOURCES = \ | 90 | gnunet_service_conversation_SOURCES = \ |
101 | gnunet-service-conversation.c | 91 | gnunet-service-conversation.c |
102 | gnunet_service_conversation_LDADD = \ | 92 | gnunet_service_conversation_LDADD = \ |
103 | $(top_builddir)/src/gns/libgnunetgns.la \ | ||
104 | $(top_builddir)/src/mesh/libgnunetmesh.la \ | ||
105 | $(top_builddir)/src/namestore/libgnunetnamestore.la \ | ||
106 | $(top_builddir)/src/util/libgnunetutil.la \ | ||
107 | $(INTLLIBS) | ||
108 | gnunet_service_conversation_LDFLAGS = \ | ||
109 | $(GNUNET_LDFLAGS) $(WINFLAGS) | ||
110 | |||
111 | gnunet_service_conversation_new_SOURCES = \ | ||
112 | gnunet-service-conversation-new.c | ||
113 | gnunet_service_conversation_new_LDADD = \ | ||
114 | libgnunetconversation.la \ | 93 | libgnunetconversation.la \ |
115 | libgnunetspeaker.la \ | 94 | libgnunetspeaker.la \ |
116 | libgnunetmicrophone.la \ | 95 | libgnunetmicrophone.la \ |
117 | $(top_builddir)/src/mesh/libgnunetmesh.la \ | 96 | $(top_builddir)/src/mesh/libgnunetmesh.la \ |
118 | $(top_builddir)/src/util/libgnunetutil.la \ | 97 | $(top_builddir)/src/util/libgnunetutil.la \ |
119 | $(INTLLIBS) | 98 | $(INTLLIBS) |
120 | 99 | gnunet_service_conversation_LDFLAGS = \ | |
121 | gnunet_service_conversation_new_LDFLAGS = \ | ||
122 | $(GNUNET_LDFLAGS) $(WINFLAGS) | 100 | $(GNUNET_LDFLAGS) $(WINFLAGS) |
123 | 101 | ||
124 | gnunet_conversation_SOURCES = \ | 102 | gnunet_conversation_SOURCES = \ |
125 | gnunet-conversation.c | 103 | gnunet-conversation.c |
126 | gnunet_conversation_LDADD = \ | 104 | gnunet_conversation_LDADD = \ |
127 | libgnunetconversation.la \ | ||
128 | $(top_builddir)/src/util/libgnunetutil.la \ | ||
129 | $(INTLLIBS) | ||
130 | gnunet_conversation_LDFLAGS = \ | ||
131 | $(GNUNET_LDFLAGS) $(WINFLAGS) | ||
132 | |||
133 | gnunet_conversation_new_SOURCES = \ | ||
134 | gnunet-conversation-new.c | ||
135 | gnunet_conversation_new_LDADD = \ | ||
136 | libgnunetmicrophone.la \ | 105 | libgnunetmicrophone.la \ |
137 | libgnunetspeaker.la \ | 106 | libgnunetspeaker.la \ |
138 | libgnunetconversation.la \ | 107 | libgnunetconversation.la \ |
@@ -141,7 +110,7 @@ gnunet_conversation_new_LDADD = \ | |||
141 | $(top_builddir)/src/identity/libgnunetidentity.la \ | 110 | $(top_builddir)/src/identity/libgnunetidentity.la \ |
142 | $(top_builddir)/src/util/libgnunetutil.la \ | 111 | $(top_builddir)/src/util/libgnunetutil.la \ |
143 | $(INTLLIBS) | 112 | $(INTLLIBS) |
144 | gnunet_conversation_new_LDFLAGS = \ | 113 | gnunet_conversation_LDFLAGS = \ |
145 | $(GNUNET_LDFLAGS) $(WINFLAGS) | 114 | $(GNUNET_LDFLAGS) $(WINFLAGS) |
146 | 115 | ||
147 | gnunet_conversation_test_SOURCES = \ | 116 | gnunet_conversation_test_SOURCES = \ |
diff --git a/src/conversation/conversation_api.c b/src/conversation/conversation_api.c index 5bd88c01f..6ce198e64 100644 --- a/src/conversation/conversation_api.c +++ b/src/conversation/conversation_api.c | |||
@@ -1,770 +1,1132 @@ | |||
1 | /* | 1 | /* |
2 | This file is part of GNUnet. | 2 | This file is part of GNUnet |
3 | (C 2013 Christian Grothoff (and other contributing authors) | 3 | (C) 2013 Christian Grothoff (and other contributing authors) |
4 | 4 | ||
5 | GNUnet is free software; you can redistribute it and/or modify | 5 | GNUnet is free software; you can redistribute it and/or modify |
6 | it under the terms of the GNU General Public License as published | 6 | it under the terms of the GNU General Public License as published |
7 | by the Free Software Foundation; either version 3, or (at your | 7 | by the Free Software Foundation; either version 3, or (at your |
8 | option) any later version. | 8 | option) any later version. |
9 | 9 | ||
10 | GNUnet is distributed in the hope that it will be useful, but | 10 | GNUnet is distributed in the hope that it will be useful, but |
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | 11 | WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | General Public License for more details. | 13 | General Public License for more details. |
14 | 14 | ||
15 | You should have received a copy of the GNU General Public License | 15 | You should have received a copy of the GNU General Public License |
16 | along with GNUnet; see the file COPYING. If not, write to the | 16 | along with GNUnet; see the file COPYING. If not, write to the |
17 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, | 17 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
18 | Boston, MA 02111-1307, USA. | 18 | Boston, MA 02111-1307, USA. |
19 | */ | 19 | */ |
20 | 20 | ||
21 | /** | 21 | /** |
22 | * @file conversation/conversation_api.c | 22 | * @file conversation/conversation_api2.c |
23 | * @brief API for conversation | 23 | * @brief API to the conversation service |
24 | * @author Simon Dieterle | 24 | * @author Simon Dieterle |
25 | * @author Andreas Fuchs | 25 | * @author Andreas Fuchs |
26 | * STRUCTURE: | 26 | * @author Christian Grothoff |
27 | * - DATA STRUCTURES | ||
28 | * - DECLARATIONS | ||
29 | * - AUXILIARY FUNCTIONS | ||
30 | * - RECEIVE HANDLERS | ||
31 | * - SEND FUNCTIONS | ||
32 | * - API CALL DEFINITIONS | ||
33 | * | ||
34 | */ | 27 | */ |
35 | |||
36 | #include "platform.h" | 28 | #include "platform.h" |
37 | #include "gnunet_util_lib.h" | 29 | #include "gnunet_conversation_service.h" |
38 | #include "gnunet_dnsparser_lib.h" | ||
39 | #include "gnunet_gns_service.h" | 30 | #include "gnunet_gns_service.h" |
40 | #include "gnunet_protocols.h" | ||
41 | #include "conversation.h" | 31 | #include "conversation.h" |
42 | #include "gnunet_conversation_service.h" | ||
43 | |||
44 | #define MAX_TRANSMIT_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60) | ||
45 | 32 | ||
46 | /******************************************************************************/ | ||
47 | /************************ DATA STRUCTURES ****************************/ | ||
48 | /******************************************************************************/ | ||
49 | 33 | ||
50 | enum GNUNET_CONVERSATION_CallType | 34 | /** |
35 | * A phone record specifies which peer is hosting a given user and | ||
36 | * may also specify the phone line that is used (typically zero). | ||
37 | * The version is also right now always zero. | ||
38 | */ | ||
39 | struct PhoneRecord | ||
51 | { | 40 | { |
52 | CALLER = 0, | 41 | |
53 | CALLEE | 42 | /** |
43 | * Version of the phone record, for now always zero. We may | ||
44 | * use other versions for anonymously hosted phone lines in | ||
45 | * the future. | ||
46 | */ | ||
47 | uint32_t version GNUNET_PACKED; | ||
48 | |||
49 | /** | ||
50 | * Phone line to use at the peer. | ||
51 | */ | ||
52 | uint32_t line GNUNET_PACKED; | ||
53 | |||
54 | /** | ||
55 | * Identity of the peer hosting the phone service. | ||
56 | */ | ||
57 | struct GNUNET_PeerIdentity peer; | ||
58 | |||
54 | }; | 59 | }; |
55 | 60 | ||
61 | |||
56 | /** | 62 | /** |
57 | * Information about a call | 63 | * Possible states of the phone. |
58 | */ | 64 | */ |
59 | struct GNUNET_CONVERSATION_CallInformation | 65 | enum PhoneState |
60 | { | 66 | { |
67 | /** | ||
68 | * We still need to register the phone. | ||
69 | */ | ||
70 | PS_REGISTER = 0, | ||
61 | 71 | ||
62 | /** | 72 | /** |
63 | * Peer interacting with | 73 | * We are waiting for a call. |
64 | */ | 74 | */ |
65 | struct GNUNET_PeerIdentity peer; | 75 | PS_WAITING, |
66 | 76 | ||
67 | /** | 77 | /** |
68 | * Type of call (incoming or outgoing) | 78 | * The phone is ringing. |
69 | */ | 79 | */ |
70 | int type; | 80 | PS_RINGING, |
71 | 81 | ||
72 | /** | 82 | /** |
73 | * Shows if the call ist fully established | 83 | * The phone is in an active conversation. |
74 | */ | 84 | */ |
75 | int established; | 85 | PS_ACTIVE |
76 | }; | 86 | }; |
77 | 87 | ||
88 | |||
78 | /** | 89 | /** |
79 | * Opaque handle to the service. | 90 | * A phone is a device that can ring to signal an incoming call and |
91 | * that you can pick up to answer the call and hang up to terminate | ||
92 | * the call. You can also hang up a ringing phone immediately | ||
93 | * (without picking it up) to stop it from ringing. Phones have | ||
94 | * caller ID. You can ask the phone for its record and make that | ||
95 | * record available (via GNS) to enable others to call you. | ||
96 | * Multiple phones maybe connected to the same line (the line is | ||
97 | * something rather internal to a phone and not obvious from it). | ||
98 | * You can only have one conversation per phone at any time. | ||
80 | */ | 99 | */ |
81 | struct GNUNET_CONVERSATION_Handle | 100 | struct GNUNET_CONVERSATION_Phone |
82 | { | 101 | { |
83 | |||
84 | /** | 102 | /** |
85 | * Our configuration. | 103 | * Our configuration. |
86 | */ | 104 | */ |
87 | const struct GNUNET_CONFIGURATION_Handle *cfg; | 105 | const struct GNUNET_CONFIGURATION_Handle *cfg; |
88 | 106 | ||
89 | /** | 107 | /** |
90 | * Handle to the server connection, to send messages later | 108 | * Handle to talk with CONVERSATION service. |
91 | */ | 109 | */ |
92 | struct GNUNET_CLIENT_Connection *client; | 110 | struct GNUNET_CLIENT_Connection *client; |
93 | 111 | ||
94 | /** | 112 | /** |
95 | * GNS handle | 113 | * Function to call for phone events. |
96 | */ | 114 | */ |
97 | struct GNUNET_GNS_Handle *gns; | 115 | GNUNET_CONVERSATION_EventHandler event_handler; |
98 | 116 | ||
99 | /** | 117 | /** |
100 | * Namestore handle | 118 | * Closure for @e event_handler |
101 | */ | 119 | */ |
102 | struct GNUNET_NAMESTORE_Handle *namestore; | 120 | void *event_handler_cls; |
103 | 121 | ||
104 | /** | 122 | /** |
105 | * TXT record for gns | 123 | * Speaker, or NULL if none is attached. |
106 | */ | 124 | */ |
107 | int txt_record_set; | 125 | struct GNUNET_SPEAKER_Handle *speaker; |
108 | 126 | ||
109 | /** | 127 | /** |
110 | * Callback for incoming calls | 128 | * Microphone, or NULL if none is attached. |
111 | */ | 129 | */ |
112 | GNUNET_CONVERSATION_CallHandler *call_handler; | 130 | struct GNUNET_MICROPHONE_Handle *mic; |
131 | |||
132 | /** | ||
133 | * Connection to NAMESTORE (for reverse lookup). | ||
134 | */ | ||
135 | struct GNUNET_NAMESTORE_Handle *ns; | ||
113 | 136 | ||
114 | /** | 137 | /** |
115 | * Callback for rejected calls | 138 | * Active NAMESTORE lookup (or NULL). |
116 | */ | 139 | */ |
117 | GNUNET_CONVERSATION_RejectHandler *reject_handler; | 140 | struct GNUNET_NAMESTORE_QueueEntry *qe; |
118 | 141 | ||
142 | /** | ||
143 | * Handle for transmitting to the CONVERSATION service. | ||
144 | */ | ||
145 | struct GNUNET_MQ_Handle *mq; | ||
146 | |||
147 | /** | ||
148 | * This phone's record. | ||
149 | */ | ||
150 | struct PhoneRecord my_record; | ||
151 | |||
119 | /** | 152 | /** |
120 | * Callback for notifications | 153 | * My GNS zone. |
121 | */ | 154 | */ |
122 | GNUNET_CONVERSATION_NotificationHandler *notification_handler; | 155 | struct GNUNET_CRYPTO_EccPrivateKey my_zone; |
123 | 156 | ||
124 | /** | 157 | /** |
125 | * Callback for missed calls | 158 | * Identity of the person calling us (valid while in state #PS_RINGING). |
126 | */ | 159 | */ |
127 | GNUNET_CONVERSATION_MissedCallHandler *missed_call_handler; | 160 | struct GNUNET_CRYPTO_EccPublicSignKey caller_id; |
128 | 161 | ||
129 | /** | 162 | /** |
130 | * The pointer to the call | 163 | * State machine for the phone. |
131 | */ | 164 | */ |
132 | struct GNUNET_CONVERSATION_CallInformation *call; | 165 | enum PhoneState state; |
166 | |||
133 | }; | 167 | }; |
134 | 168 | ||
135 | /******************************************************************************/ | ||
136 | /*********************** AUXILIARY FUNCTIONS *************************/ | ||
137 | /******************************************************************************/ | ||
138 | 169 | ||
139 | /** | 170 | /** |
140 | * Initialize the conversation txt record in GNS | 171 | * The phone got disconnected, reconnect to the service. |
141 | */ | 172 | * |
173 | * @param phone phone to reconnect | ||
174 | */ | ||
142 | static void | 175 | static void |
143 | setup_gns_txt (struct GNUNET_CONVERSATION_Handle *handle) | 176 | reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone); |
144 | { | ||
145 | struct GNUNET_CRYPTO_EccPublicSignKey zone_pkey; | ||
146 | struct GNUNET_CRYPTO_EccPrivateKey *zone_key; | ||
147 | struct GNUNET_CRYPTO_EccPrivateKey *peer_key; | ||
148 | struct GNUNET_NAMESTORE_RecordData rd; | ||
149 | struct GNUNET_PeerIdentity peer; | ||
150 | 177 | ||
151 | char *zone_keyfile; | ||
152 | char *peer_keyfile; | ||
153 | 178 | ||
154 | rd.expiration_time = UINT64_MAX; | 179 | /** |
180 | * We have resolved the caller ID using our name service. | ||
181 | * | ||
182 | * @param cls the `struct GNUNET_CONVERSATION_Phone` | ||
183 | * @param zone our zone used for resolution | ||
184 | * @param label name of the caller | ||
185 | * @param rd_count number of records we have in @a rd | ||
186 | * @param rd records we have for the caller's label | ||
187 | */ | ||
188 | static void | ||
189 | handle_caller_name (void *cls, | ||
190 | const struct GNUNET_CRYPTO_EccPrivateKey *zone, | ||
191 | const char *label, | ||
192 | unsigned int rd_count, | ||
193 | const struct GNUNET_NAMESTORE_RecordData *rd) | ||
194 | { | ||
195 | struct GNUNET_CONVERSATION_Phone *phone = cls; | ||
196 | char *name; | ||
155 | 197 | ||
156 | if (GNUNET_OK != | 198 | phone->qe = NULL; |
157 | GNUNET_CONFIGURATION_get_value_filename (handle->cfg, "gns", "ZONEKEY", | 199 | if (NULL == label) |
158 | &zone_keyfile)) | 200 | name = GNUNET_strdup (GNUNET_NAMESTORE_pkey_to_zkey (&phone->caller_id)); |
159 | { | 201 | else |
160 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to get key from cfg\n"); | 202 | GNUNET_asprintf (&name, "%.gnu", label); |
161 | return; | 203 | phone->event_handler (phone->event_handler_cls, |
162 | } | 204 | GNUNET_CONVERSATION_EC_RING, |
205 | name); | ||
206 | GNUNET_free (name); | ||
207 | } | ||
163 | 208 | ||
164 | if (GNUNET_OK != | ||
165 | GNUNET_CONFIGURATION_get_value_filename (handle->cfg, "PEER", | ||
166 | "PRIVATE_KEY", &peer_keyfile)) | ||
167 | { | ||
168 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to get key from cfg\n"); | ||
169 | return; | ||
170 | } | ||
171 | 209 | ||
172 | zone_key = GNUNET_CRYPTO_ecc_key_create_from_file (zone_keyfile); | 210 | /** |
173 | GNUNET_CRYPTO_ecc_key_get_public_for_signature (zone_key, &zone_pkey); | 211 | * We received a `struct ClientPhoneRingMessage` |
174 | peer_key = GNUNET_CRYPTO_ecc_key_create_from_file (peer_keyfile); | 212 | * |
175 | GNUNET_CRYPTO_ecc_key_get_public_for_signature (peer_key, | 213 | * @param cls the `struct GNUNET_CONVERSATION_Phone` |
176 | &peer.public_key); | 214 | * @param msg the message |
177 | const char *h = GNUNET_i2s_full (&peer); | 215 | */ |
178 | 216 | static void | |
179 | rd.data_size = strlen (h) + 1; | 217 | handle_phone_ring (void *cls, |
180 | rd.data = h; | 218 | const struct GNUNET_MessageHeader *msg) |
181 | rd.record_type = GNUNET_DNSPARSER_TYPE_TXT; | 219 | { |
182 | rd.flags = GNUNET_NAMESTORE_RF_NONE; | 220 | struct GNUNET_CONVERSATION_Phone *phone = cls; |
183 | 221 | const struct ClientPhoneRingMessage *ring; | |
184 | /* FIXME: continuation? return value? */ | 222 | |
185 | GNUNET_NAMESTORE_records_store (handle->namestore, | 223 | ring = (const struct ClientPhoneRingMessage *) msg; |
186 | zone_key, | 224 | switch (phone->state) |
187 | "conversation", | 225 | { |
188 | 1, &rd, | 226 | case PS_REGISTER: |
189 | NULL, NULL); | 227 | GNUNET_assert (0); |
228 | break; | ||
229 | case PS_WAITING: | ||
230 | phone->state = PS_RINGING; | ||
231 | phone->caller_id = ring->caller_id; | ||
232 | phone->qe = GNUNET_NAMESTORE_zone_to_name (phone->ns, | ||
233 | &phone->my_zone, | ||
234 | &ring->caller_id, | ||
235 | &handle_caller_name, | ||
236 | phone); | ||
237 | break; | ||
238 | case PS_RINGING: | ||
239 | GNUNET_break (0); | ||
240 | reconnect_phone (phone); | ||
241 | break; | ||
242 | case PS_ACTIVE: | ||
243 | GNUNET_break (0); | ||
244 | reconnect_phone (phone); | ||
245 | break; | ||
246 | } | ||
190 | } | 247 | } |
191 | 248 | ||
249 | |||
192 | /** | 250 | /** |
193 | * Callback for checking the conversation txt gns record | 251 | * We received a `struct ClientPhoneHangupMessage`. |
194 | * | 252 | * |
195 | * @param cls closure | 253 | * @param cls the `struct GNUNET_CONVERSATION_Phone` |
196 | * @param rd_count | 254 | * @param msg the message |
197 | * @param rd | 255 | */ |
198 | */ | ||
199 | static void | 256 | static void |
200 | check_gns_cb (void *cls, uint32_t rd_count, | 257 | handle_phone_hangup (void *cls, |
201 | const struct GNUNET_NAMESTORE_RecordData *rd) | 258 | const struct GNUNET_MessageHeader *msg) |
202 | { | 259 | { |
203 | struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls; | 260 | struct GNUNET_CONVERSATION_Phone *phone = cls; |
204 | 261 | const struct ClientPhoneHangupMessage *hang; | |
205 | if (0 == rd_count) | 262 | size_t len; |
206 | { | 263 | const char *reason; |
207 | setup_gns_txt (h); | 264 | |
208 | } | 265 | hang = (const struct ClientPhoneHangupMessage *) msg; |
209 | else | 266 | reason = (const char *) &hang[1]; |
267 | len = htons (hang->header.size) - sizeof (struct ClientPhoneHangupMessage); | ||
268 | if ( (0 == len) || | ||
269 | ('\0' != reason[len-1]) ) | ||
270 | { | ||
271 | GNUNET_break (0); | ||
272 | reconnect_phone (phone); | ||
273 | return; | ||
274 | } | ||
275 | switch (phone->state) | ||
276 | { | ||
277 | case PS_REGISTER: | ||
278 | GNUNET_assert (0); | ||
279 | break; | ||
280 | case PS_WAITING: | ||
281 | GNUNET_break (0); | ||
282 | reconnect_phone (phone); | ||
283 | break; | ||
284 | case PS_RINGING: | ||
285 | if (NULL != phone->qe) | ||
210 | { | 286 | { |
211 | h->txt_record_set = GNUNET_YES; | 287 | GNUNET_NAMESTORE_cancel (phone->qe); |
288 | phone->qe = NULL; | ||
289 | phone->state = PS_WAITING; | ||
290 | break; | ||
212 | } | 291 | } |
292 | phone->state = PS_WAITING; | ||
293 | phone->event_handler (phone->event_handler_cls, | ||
294 | GNUNET_CONVERSATION_EC_TERMINATED, | ||
295 | reason); | ||
296 | break; | ||
297 | case PS_ACTIVE: | ||
298 | GNUNET_break (NULL == phone->qe); | ||
299 | phone->state = PS_WAITING; | ||
300 | phone->event_handler (phone->event_handler_cls, | ||
301 | GNUNET_CONVERSATION_EC_TERMINATED, | ||
302 | reason); | ||
303 | break; | ||
304 | } | ||
213 | } | 305 | } |
214 | 306 | ||
215 | 307 | ||
216 | /** | 308 | /** |
217 | * Check if the gns txt record for conversation exits | 309 | * We received a `struct ClientAudioMessage` |
310 | * | ||
311 | * @param cls the `struct GNUNET_CONVERSATION_Phone` | ||
312 | * @param msg the message | ||
218 | */ | 313 | */ |
219 | static void | 314 | static void |
220 | check_gns (struct GNUNET_CONVERSATION_Handle *h) | 315 | handle_phone_audio_message (void *cls, |
316 | const struct GNUNET_MessageHeader *msg) | ||
221 | { | 317 | { |
222 | GNUNET_GNS_lookup (h->gns, "conversation.gns", | 318 | struct GNUNET_CONVERSATION_Phone *phone = cls; |
223 | NULL /* FIXME_ZONE */, | 319 | const struct ClientAudioMessage *am; |
224 | GNUNET_DNSPARSER_TYPE_TXT, | ||
225 | GNUNET_NO, | ||
226 | NULL, | ||
227 | &check_gns_cb, h); | ||
228 | } | ||
229 | 320 | ||
321 | am = (const struct ClientAudioMessage *) msg; | ||
322 | switch (phone->state) | ||
323 | { | ||
324 | case PS_REGISTER: | ||
325 | GNUNET_assert (0); | ||
326 | break; | ||
327 | case PS_WAITING: | ||
328 | GNUNET_break (0); | ||
329 | reconnect_phone (phone); | ||
330 | break; | ||
331 | case PS_RINGING: | ||
332 | GNUNET_break (0); | ||
333 | reconnect_phone (phone); | ||
334 | break; | ||
335 | case PS_ACTIVE: | ||
336 | phone->speaker->play (phone->speaker->cls, | ||
337 | ntohs (msg->size) - sizeof (struct ClientAudioMessage), | ||
338 | &am[1]); | ||
339 | break; | ||
340 | } | ||
341 | } | ||
230 | 342 | ||
231 | /******************************************************************************/ | ||
232 | /*********************** RECEIVE HANDLERS ****************************/ | ||
233 | /******************************************************************************/ | ||
234 | 343 | ||
235 | /** | 344 | /** |
236 | * Function to process all messages received from the service | 345 | * We encountered an error talking with the conversation service. |
237 | * | 346 | * |
238 | * @param cls closure | 347 | * @param cls the `struct GNUNET_CONVERSATION_Phone` |
239 | * @param msg message received, NULL on timeout or fatal error | 348 | * @param error details about the error |
240 | */ | 349 | */ |
241 | static void | 350 | static void |
242 | receive_message_cb (void *cls, | 351 | phone_error_handler (void *cls, |
243 | const struct GNUNET_MessageHeader *msg) | 352 | enum GNUNET_MQ_Error error) |
244 | { | 353 | { |
245 | struct GNUNET_CONVERSATION_Handle *h = cls; | 354 | struct GNUNET_CONVERSATION_Phone *phone = cls; |
246 | struct ServerClientSessionInitiateMessage *imsg; | ||
247 | struct ServerClientSessionRejectMessage *rmsg; | ||
248 | struct GNUNET_CONVERSATION_MissedCallNotification *missed_calls; | ||
249 | |||
250 | if (NULL != msg) | ||
251 | { | ||
252 | switch (ntohs (msg->type)) | ||
253 | { | ||
254 | case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_ACCEPT: | ||
255 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
256 | _("%s has accepted your call.\n"), | ||
257 | GNUNET_i2s_full (&(h->call->peer))); | ||
258 | |||
259 | h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_CALL_ACCEPTED, | ||
260 | &(h->call->peer)); | ||
261 | h->call->type = CALLEE; | ||
262 | |||
263 | break; | ||
264 | |||
265 | case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_REJECT: | ||
266 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
267 | _("%s has rejected your call.\n"), | ||
268 | GNUNET_i2s_full (&(h->call->peer))); | ||
269 | |||
270 | rmsg = (struct ServerClientSessionRejectMessage *) msg; | ||
271 | h->reject_handler (NULL, h, ntohs (rmsg->reason), &(h->call->peer)); | ||
272 | GNUNET_free (h->call); | ||
273 | h->call = NULL; | ||
274 | |||
275 | break; | ||
276 | |||
277 | case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_TERMINATE: | ||
278 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
279 | _("%s has terminated the call.\n"), | ||
280 | GNUNET_i2s_full (&(h->call->peer))); | ||
281 | |||
282 | h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_CALL_TERMINATED, | ||
283 | &(h->call->peer)); | ||
284 | GNUNET_free (h->call); | ||
285 | h->call = NULL; | ||
286 | |||
287 | break; | ||
288 | |||
289 | case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_INITIATE: | ||
290 | imsg = (struct ServerClientSessionInitiateMessage *) msg; | ||
291 | |||
292 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("%s wants to call you.\n"), | ||
293 | GNUNET_i2s_full (&(imsg->peer))); | ||
294 | |||
295 | h->call = | ||
296 | (struct GNUNET_CONVERSATION_CallInformation *) | ||
297 | GNUNET_malloc (sizeof (struct GNUNET_CONVERSATION_CallInformation)); | ||
298 | memcpy (&(h->call->peer), &(imsg->peer), | ||
299 | sizeof (struct GNUNET_PeerIdentity)); | ||
300 | h->call_handler (NULL, h, &(h->call->peer)); | ||
301 | h->call->type = CALLEE; | ||
302 | |||
303 | break; | ||
304 | |||
305 | case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_MISSED_CALL: | ||
306 | missed_calls = | ||
307 | (struct GNUNET_CONVERSATION_MissedCallNotification *) (msg + | ||
308 | (sizeof | ||
309 | (struct | ||
310 | GNUNET_MessageHeader))); | ||
311 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
312 | _("You &d have missed a calls.\n"), | ||
313 | missed_calls->number); | ||
314 | h->missed_call_handler (NULL, h, missed_calls); | ||
315 | break; | ||
316 | |||
317 | case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SERVICE_BLOCKED: | ||
318 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("The service is blocked.\n")); | ||
319 | h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_SERVICE_BLOCKED, | ||
320 | NULL); | ||
321 | break; | ||
322 | |||
323 | case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_PEER_NOT_CONNECTED: | ||
324 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
325 | _("The peer you are calling is not connected.\n")); | ||
326 | h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_NO_PEER, NULL); | ||
327 | break; | ||
328 | |||
329 | case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_NO_ANSWER: | ||
330 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
331 | _("The peer you are calling does not answer.\n")); | ||
332 | h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_NO_ANSWER, | ||
333 | &(h->call->peer)); | ||
334 | break; | ||
335 | |||
336 | case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_ERROR: | ||
337 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Generic error occured.\n")); | ||
338 | break; | ||
339 | |||
340 | default: | ||
341 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
342 | _("Got unknown message type.\n")); | ||
343 | break; | ||
344 | } | ||
345 | 355 | ||
346 | } | 356 | GNUNET_break (0); |
347 | 357 | reconnect_phone (phone); | |
348 | GNUNET_CLIENT_receive (h->client, &receive_message_cb, h, | ||
349 | GNUNET_TIME_UNIT_FOREVER_REL); | ||
350 | } | 358 | } |
351 | 359 | ||
352 | /******************************************************************************/ | ||
353 | /************************ SEND FUNCTIONS ****************************/ | ||
354 | /******************************************************************************/ | ||
355 | 360 | ||
356 | /** | 361 | /** |
357 | * Function called to send a session initiate message to the service. | 362 | * The phone got disconnected, reconnect to the service. |
358 | * "buf" will be NULL and "size" zero if the socket was closed for writing in | ||
359 | * the meantime. | ||
360 | * | 363 | * |
361 | * @param cls closure, NULL | 364 | * @param phone phone to reconnect |
362 | * @param size number of bytes available in buf | ||
363 | * @param buf where the callee should write the initiate message | ||
364 | * @return number of bytes written to buf | ||
365 | */ | 365 | */ |
366 | static size_t | 366 | static void |
367 | transmit_session_initiate_message (void *cls, size_t size, void *buf) | 367 | reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone) |
368 | { | 368 | { |
369 | size_t msg_size; | 369 | static struct GNUNET_MQ_MessageHandler handlers[] = |
370 | struct ClientServerSessionInitiateMessage *msg; | 370 | { |
371 | struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls; | 371 | { &handle_phone_ring, |
372 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING, | ||
373 | sizeof (struct ClientPhoneRingMessage) }, | ||
374 | { &handle_phone_hangup, | ||
375 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP, | ||
376 | 0 }, | ||
377 | { &handle_phone_audio_message, | ||
378 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO, | ||
379 | 0 }, | ||
380 | { NULL, 0, 0 } | ||
381 | }; | ||
382 | struct GNUNET_MQ_Envelope *e; | ||
383 | struct ClientPhoneRegisterMessage *reg; | ||
384 | |||
385 | if (NULL != phone->mq) | ||
386 | { | ||
387 | GNUNET_MQ_destroy (phone->mq); | ||
388 | phone->mq = NULL; | ||
389 | } | ||
390 | if (NULL != phone->client) | ||
391 | { | ||
392 | GNUNET_CLIENT_disconnect (phone->client); | ||
393 | phone->client = NULL; | ||
394 | } | ||
395 | phone->state = PS_REGISTER; | ||
396 | phone->client = GNUNET_CLIENT_connect ("conversation", phone->cfg); | ||
397 | if (NULL == phone->client) | ||
398 | return; | ||
399 | phone->mq = GNUNET_MQ_queue_for_connection_client (phone->client, | ||
400 | handlers, | ||
401 | &phone_error_handler, | ||
402 | phone); | ||
403 | e = GNUNET_MQ_msg (reg, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER); | ||
404 | reg->line = phone->my_record.line; | ||
405 | GNUNET_MQ_send (phone->mq, e); | ||
406 | phone->state = PS_WAITING; | ||
407 | } | ||
372 | 408 | ||
373 | msg_size = sizeof (struct ClientServerSessionInitiateMessage); | ||
374 | 409 | ||
375 | GNUNET_assert (size >= msg_size); | 410 | /** |
376 | msg = buf; | 411 | * Create a new phone. |
377 | msg->header.size = htons (msg_size); | 412 | * |
378 | msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_INITIATE); | 413 | * @param cfg configuration for the phone; specifies the phone service and |
379 | memcpy (&msg->peer, &(h->call->peer), sizeof (struct GNUNET_PeerIdentity)); | 414 | * which line the phone is to be connected to |
415 | * @param ego ego to use for name resolution (when determining caller ID) | ||
416 | * @param event_handler how to notify the owner of the phone about events | ||
417 | * @param event_handler_cls closure for @a event_handler | ||
418 | */ | ||
419 | struct GNUNET_CONVERSATION_Phone * | ||
420 | GNUNET_CONVERSATION_phone_create (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
421 | const struct GNUNET_IDENTITY_Ego *ego, | ||
422 | GNUNET_CONVERSATION_EventHandler event_handler, | ||
423 | void *event_handler_cls) | ||
424 | { | ||
425 | struct GNUNET_CONVERSATION_Phone *phone; | ||
426 | unsigned long long line; | ||
380 | 427 | ||
381 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | 428 | if (GNUNET_OK != |
382 | _ | 429 | GNUNET_CONFIGURATION_get_value_number (cfg, |
383 | ("Sending ClientServerSessionInitiateMessage to the service for peer: %s\n"), | 430 | "CONVERSATION", |
384 | GNUNET_i2s_full (&(h->call->peer))); | 431 | "LINE", |
432 | &line)) | ||
433 | return NULL; | ||
434 | phone = GNUNET_new (struct GNUNET_CONVERSATION_Phone); | ||
435 | if (GNUNET_OK != | ||
436 | GNUNET_CRYPTO_get_host_identity (cfg, | ||
437 | &phone->my_record.peer)) | ||
438 | { | ||
439 | GNUNET_break (0); | ||
440 | GNUNET_free (phone); | ||
441 | return NULL; | ||
442 | } | ||
443 | phone->cfg = cfg; | ||
444 | phone->my_zone = *GNUNET_IDENTITY_ego_get_private_key (ego); | ||
445 | phone->event_handler = event_handler; | ||
446 | phone->event_handler_cls = event_handler_cls; | ||
447 | phone->ns = GNUNET_NAMESTORE_connect (cfg); | ||
448 | phone->my_record.line = htonl ((uint32_t) line); | ||
449 | phone->my_record.version = htonl (0); | ||
450 | reconnect_phone (phone); | ||
451 | if ( (NULL == phone->client) || | ||
452 | (NULL == phone->ns) ) | ||
453 | { | ||
454 | GNUNET_break (0); | ||
455 | GNUNET_CONVERSATION_phone_destroy (phone); | ||
456 | return NULL; | ||
457 | } | ||
458 | return phone; | ||
459 | } | ||
385 | 460 | ||
386 | h->call->type = CALLER; | ||
387 | 461 | ||
388 | return msg_size; | 462 | /** |
463 | * Fill in a namestore record with the contact information | ||
464 | * for this phone. Note that the filled in "data" value | ||
465 | * is only valid until the phone is destroyed. | ||
466 | * | ||
467 | * @param phone phone to create a record for | ||
468 | * @param rd namestore record to fill in | ||
469 | */ | ||
470 | void | ||
471 | GNUNET_CONVERSATION_phone_get_record (struct GNUNET_CONVERSATION_Phone *phone, | ||
472 | struct GNUNET_NAMESTORE_RecordData *rd) | ||
473 | { | ||
474 | rd->data = &phone->my_record; | ||
475 | rd->expiration_time = 0; | ||
476 | rd->data_size = sizeof (struct PhoneRecord); | ||
477 | rd->record_type = GNUNET_NAMESTORE_TYPE_PHONE; | ||
478 | rd->flags = GNUNET_NAMESTORE_RF_NONE; | ||
389 | } | 479 | } |
390 | 480 | ||
481 | |||
391 | /** | 482 | /** |
392 | * Function called to send a session accept message to the service. | 483 | * Process recorded audio data. |
393 | * "buf" will be NULL and "size" zero if the socket was closed for writing in | ||
394 | * the meantime. | ||
395 | * | 484 | * |
396 | * @param cls closure, NULL | 485 | * @param cls closure with the `struct GNUNET_CONVERSATION_Phone` |
397 | * @param size number of bytes available in buf | 486 | * @param data_size number of bytes in @a data |
398 | * @param buf where the callee should write the accept message | 487 | * @param data audio data to play |
399 | * @return number of bytes written to buf | ||
400 | */ | 488 | */ |
401 | static size_t | 489 | static void |
402 | transmit_session_accept_message (void *cls, size_t size, void *buf) | 490 | transmit_phone_audio (void *cls, |
491 | size_t data_size, | ||
492 | const void *data) | ||
403 | { | 493 | { |
404 | size_t msg_size; | 494 | struct GNUNET_CONVERSATION_Phone *phone = cls; |
405 | struct ClientServerSessionAcceptMessage *msg; | 495 | struct GNUNET_MQ_Envelope *e; |
406 | struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls; | 496 | struct ClientAudioMessage *am; |
407 | 497 | ||
408 | msg_size = sizeof (struct ClientServerSessionAcceptMessage); | 498 | GNUNET_assert (PS_ACTIVE == phone->state); |
499 | e = GNUNET_MQ_msg_extra (am, | ||
500 | data_size, | ||
501 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO); | ||
502 | memcpy (&am[1], data, data_size); | ||
503 | GNUNET_MQ_send (phone->mq, e); | ||
504 | } | ||
409 | 505 | ||
410 | GNUNET_assert (size >= msg_size); | ||
411 | msg = buf; | ||
412 | msg->header.size = htons (msg_size); | ||
413 | msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_ACCEPT); | ||
414 | 506 | ||
415 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | 507 | /** |
416 | _ | 508 | * Picks up a (ringing) phone. This will connect the speaker |
417 | ("Sending ClienServerSessionAcceptMessage to the service for peer: %s\n"), | 509 | * to the microphone of the other party, and vice versa. |
418 | GNUNET_i2s_full (&(h->call->peer))); | 510 | * |
511 | * @param phone phone to pick up | ||
512 | * @param metadata meta data to give to the other user about the pick up event | ||
513 | * @param speaker speaker to use | ||
514 | * @param mic microphone to use | ||
515 | */ | ||
516 | void | ||
517 | GNUNET_CONVERSATION_phone_pick_up (struct GNUNET_CONVERSATION_Phone *phone, | ||
518 | const char *metadata, | ||
519 | struct GNUNET_SPEAKER_Handle *speaker, | ||
520 | struct GNUNET_MICROPHONE_Handle *mic) | ||
521 | { | ||
522 | struct GNUNET_MQ_Envelope *e; | ||
523 | struct ClientPhonePickupMessage *pick; | ||
524 | size_t slen; | ||
525 | |||
526 | GNUNET_assert (PS_RINGING == phone->state); | ||
527 | phone->speaker = speaker; | ||
528 | phone->mic = mic; | ||
529 | slen = strlen (metadata) + 1; | ||
530 | e = GNUNET_MQ_msg_extra (pick, slen, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP); | ||
531 | memcpy (&pick[1], metadata, slen); | ||
532 | GNUNET_MQ_send (phone->mq, e); | ||
533 | phone->state = PS_ACTIVE; | ||
534 | phone->speaker->enable_speaker (phone->speaker->cls); | ||
535 | phone->mic->enable_microphone (phone->mic->cls, | ||
536 | &transmit_phone_audio, | ||
537 | phone); | ||
538 | } | ||
419 | 539 | ||
420 | h->call->established = GNUNET_YES; | ||
421 | 540 | ||
422 | return msg_size; | 541 | /** |
542 | * Hang up up a (possibly ringing) phone. This will notify the other | ||
543 | * party that we are no longer interested in talking with them. | ||
544 | * | ||
545 | * @param phone phone to pick up | ||
546 | * @param reason text we give to the other party about why we terminated the conversation | ||
547 | */ | ||
548 | void | ||
549 | GNUNET_CONVERSATION_phone_hang_up (struct GNUNET_CONVERSATION_Phone *phone, | ||
550 | const char *reason) | ||
551 | { | ||
552 | struct GNUNET_MQ_Envelope *e; | ||
553 | struct ClientPhoneHangupMessage *hang; | ||
554 | size_t slen; | ||
555 | |||
556 | GNUNET_assert ( (PS_RINGING == phone->state) || | ||
557 | (PS_ACTIVE == phone->state) ); | ||
558 | phone->speaker->disable_speaker (phone->speaker->cls); | ||
559 | phone->mic->disable_microphone (phone->mic->cls); | ||
560 | phone->speaker = NULL; | ||
561 | phone->mic = NULL; | ||
562 | slen = strlen (reason) + 1; | ||
563 | e = GNUNET_MQ_msg_extra (hang, slen, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP); | ||
564 | memcpy (&hang[1], reason, slen); | ||
565 | GNUNET_MQ_send (phone->mq, e); | ||
566 | phone->state = PS_WAITING; | ||
423 | } | 567 | } |
424 | 568 | ||
569 | |||
425 | /** | 570 | /** |
426 | * Function called to send a session reject message to the service. | 571 | * Destroys a phone. |
427 | * "buf" will be NULL and "size" zero if the socket was closed for writing in | ||
428 | * the meantime. | ||
429 | * | 572 | * |
430 | * @param cls closure, NULL | 573 | * @param phone phone to destroy |
431 | * @param size number of bytes available in buf | ||
432 | * @param buf where the callee should write the reject message | ||
433 | * @return number of bytes written to buf | ||
434 | */ | 574 | */ |
435 | static size_t | 575 | void |
436 | transmit_session_reject_message (void *cls, size_t size, void *buf) | 576 | GNUNET_CONVERSATION_phone_destroy (struct GNUNET_CONVERSATION_Phone *phone) |
437 | { | 577 | { |
438 | size_t msg_size; | 578 | if (NULL != phone->speaker) |
439 | struct ClientServerSessionRejectMessage *msg; | 579 | { |
440 | struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls; | 580 | phone->speaker->disable_speaker (phone->speaker->cls); |
581 | phone->speaker = NULL; | ||
582 | } | ||
583 | if (NULL != phone->mic) | ||
584 | { | ||
585 | phone->mic->disable_microphone (phone->mic->cls); | ||
586 | phone->mic = NULL; | ||
587 | } | ||
588 | if (NULL != phone->qe) | ||
589 | { | ||
590 | GNUNET_NAMESTORE_cancel (phone->qe); | ||
591 | phone->qe = NULL; | ||
592 | } | ||
593 | if (NULL != phone->ns) | ||
594 | { | ||
595 | GNUNET_NAMESTORE_disconnect (phone->ns); | ||
596 | phone->ns = NULL; | ||
597 | } | ||
598 | if (NULL != phone->mq) | ||
599 | { | ||
600 | GNUNET_MQ_destroy (phone->mq); | ||
601 | phone->mq = NULL; | ||
602 | } | ||
603 | if (NULL != phone->client) | ||
604 | { | ||
605 | GNUNET_CLIENT_disconnect (phone->client); | ||
606 | phone->client = NULL; | ||
607 | } | ||
608 | GNUNET_free (phone); | ||
609 | } | ||
610 | |||
611 | |||
612 | /* ******************************* Call API *************************** */ | ||
441 | 613 | ||
442 | msg_size = sizeof (struct ClientServerSessionRejectMessage); | 614 | /** |
615 | * Possible states of the phone. | ||
616 | */ | ||
617 | enum CallState | ||
618 | { | ||
619 | /** | ||
620 | * We still need to lookup the callee. | ||
621 | */ | ||
622 | CS_LOOKUP = 0, | ||
443 | 623 | ||
444 | GNUNET_assert (size >= msg_size); | 624 | /** |
445 | msg = buf; | 625 | * The call is ringing. |
446 | msg->header.size = htons (msg_size); | 626 | */ |
447 | msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_REJECT); | 627 | CS_RINGING, |
448 | msg->reason = htons (GNUNET_CONVERSATION_REJECT_REASON_NOT_WANTED); | ||
449 | 628 | ||
450 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | 629 | /** |
451 | _ | 630 | * The call is in an active conversation. |
452 | ("Sending ClientServerSessionRejectMessage to the service for peer: %s\n"), | 631 | */ |
453 | GNUNET_i2s_full (&(h->call->peer))); | 632 | CS_ACTIVE, |
454 | 633 | ||
455 | GNUNET_free (h->call); | 634 | /** |
456 | h->call = NULL; | 635 | * The call is in termination. |
636 | */ | ||
637 | CS_SHUTDOWN | ||
638 | }; | ||
457 | 639 | ||
458 | return msg_size; | ||
459 | } | ||
460 | 640 | ||
461 | /** | 641 | /** |
462 | * Function called to send a session terminate message to the service. | 642 | * Handle for an outgoing call. |
463 | * "buf" will be NULL and "size" zero if the socket was closed for writing in | ||
464 | * the meantime. | ||
465 | * | ||
466 | * @param cls closure, NULL | ||
467 | * @param size number of bytes available in buf | ||
468 | * @param buf where the callee should write the terminate message | ||
469 | * @return number of bytes written to buf | ||
470 | */ | 643 | */ |
471 | static size_t | 644 | struct GNUNET_CONVERSATION_Call |
472 | transmit_session_terminate_message (void *cls, size_t size, void *buf) | ||
473 | { | 645 | { |
474 | size_t msg_size; | ||
475 | struct ClientServerSessionTerminateMessage *msg; | ||
476 | struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls; | ||
477 | 646 | ||
478 | msg_size = sizeof (struct ClientServerSessionTerminateMessage); | 647 | /** |
648 | * Our configuration. | ||
649 | */ | ||
650 | const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
651 | |||
652 | /** | ||
653 | * Handle to talk with CONVERSATION service. | ||
654 | */ | ||
655 | struct GNUNET_CLIENT_Connection *client; | ||
656 | |||
657 | /** | ||
658 | * Our caller identity. | ||
659 | */ | ||
660 | struct GNUNET_IDENTITY_Ego *caller_id; | ||
661 | |||
662 | /** | ||
663 | * Target callee as a GNS address/name. | ||
664 | */ | ||
665 | char *callee; | ||
479 | 666 | ||
480 | GNUNET_assert (size >= msg_size); | 667 | /** |
481 | msg = buf; | 668 | * Our speaker. |
482 | msg->header.size = htons (msg_size); | 669 | */ |
483 | msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_TERMINATE); | 670 | struct GNUNET_SPEAKER_Handle *speaker; |
484 | 671 | ||
485 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | 672 | /** |
486 | _ | 673 | * Our microphone. |
487 | ("Sending ClientServerSessionTerminateMessage to the service for peer: %s\n"), | 674 | */ |
488 | GNUNET_i2s_full (&(h->call->peer))); | 675 | struct GNUNET_MICROPHONE_Handle *mic; |
676 | |||
677 | /** | ||
678 | * Function to call with events. | ||
679 | */ | ||
680 | GNUNET_CONVERSATION_EventHandler event_handler; | ||
489 | 681 | ||
490 | GNUNET_free (h->call); | 682 | /** |
491 | h->call = NULL; | 683 | * Closure for @e event_handler |
684 | */ | ||
685 | void *event_handler_cls; | ||
686 | |||
687 | /** | ||
688 | * Handle for transmitting to the CONVERSATION service. | ||
689 | */ | ||
690 | struct GNUNET_MQ_Handle *mq; | ||
691 | |||
692 | /** | ||
693 | * Connection to GNS (can be NULL). | ||
694 | */ | ||
695 | struct GNUNET_GNS_Handle *gns; | ||
696 | |||
697 | /** | ||
698 | * Active GNS lookup (or NULL). | ||
699 | */ | ||
700 | struct GNUNET_GNS_LookupRequest *gns_lookup; | ||
701 | |||
702 | /** | ||
703 | * Target phone record, only valid after the lookup is done. | ||
704 | */ | ||
705 | struct PhoneRecord phone_record; | ||
706 | |||
707 | /** | ||
708 | * State machine for the call. | ||
709 | */ | ||
710 | enum CallState state; | ||
711 | |||
712 | }; | ||
492 | 713 | ||
493 | return msg_size; | ||
494 | } | ||
495 | 714 | ||
496 | /** | 715 | /** |
497 | * Auxiliary function to call a peer. | 716 | * The call got disconnected, reconnect to the service. |
498 | * | 717 | * |
499 | * @param h conversation handle | 718 | * @param call call to reconnect |
500 | * @return | ||
501 | */ | 719 | */ |
502 | static void | 720 | static void |
503 | initiate_call (struct GNUNET_CONVERSATION_Handle *h, struct GNUNET_PeerIdentity peer) | 721 | reconnect_call (struct GNUNET_CONVERSATION_Call *call); |
504 | { | 722 | |
505 | h->call = | ||
506 | (struct GNUNET_CONVERSATION_CallInformation *) | ||
507 | GNUNET_malloc (sizeof (struct GNUNET_CONVERSATION_CallInformation)); | ||
508 | memcpy (&(h->call->peer), &peer, sizeof (struct GNUNET_PeerIdentity)); | ||
509 | |||
510 | GNUNET_CLIENT_notify_transmit_ready (h->client, | ||
511 | sizeof (struct | ||
512 | ClientServerSessionInitiateMessage), | ||
513 | MAX_TRANSMIT_DELAY, GNUNET_YES, | ||
514 | &transmit_session_initiate_message, h); | ||
515 | |||
516 | return; | ||
517 | } | ||
518 | 723 | ||
519 | /** | 724 | /** |
520 | * Auxiliary function to accept a call. | 725 | * We received a `struct ClientPhoneBusyMessage` |
521 | * | 726 | * |
522 | * @param h conversation handle | 727 | * @param cls the `struct GNUNET_CONVERSATION_Call` |
728 | * @param msg the message | ||
523 | */ | 729 | */ |
524 | static void | 730 | static void |
525 | accept_call (struct GNUNET_CONVERSATION_Handle *h) | 731 | handle_call_busy (void *cls, |
732 | const struct GNUNET_MessageHeader *msg) | ||
526 | { | 733 | { |
527 | GNUNET_CLIENT_notify_transmit_ready (h->client, | 734 | struct GNUNET_CONVERSATION_Call *call = cls; |
528 | sizeof (struct | 735 | |
529 | ClientServerSessionAcceptMessage), | 736 | switch (call->state) |
530 | MAX_TRANSMIT_DELAY, GNUNET_YES, | 737 | { |
531 | &transmit_session_accept_message, h); | 738 | case CS_LOOKUP: |
739 | GNUNET_break (0); | ||
740 | reconnect_call (call); | ||
741 | break; | ||
742 | case CS_RINGING: | ||
743 | call->event_handler (call->event_handler_cls, | ||
744 | GNUNET_CONVERSATION_EC_BUSY); | ||
745 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
746 | break; | ||
747 | case CS_ACTIVE: | ||
748 | GNUNET_break (0); | ||
749 | reconnect_call (call); | ||
750 | break; | ||
751 | case CS_SHUTDOWN: | ||
752 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
753 | break; | ||
754 | } | ||
532 | } | 755 | } |
533 | 756 | ||
534 | 757 | ||
535 | /** | 758 | /** |
536 | * Auxiliary function to reject a call. | 759 | * Process recorded audio data. |
537 | * | 760 | * |
538 | * @param h conversation handle | 761 | * @param cls closure with the `struct GNUNET_CONVERSATION_Call` |
762 | * @param data_size number of bytes in @a data | ||
763 | * @param data audio data to play | ||
539 | */ | 764 | */ |
540 | static void | 765 | static void |
541 | reject_call (struct GNUNET_CONVERSATION_Handle *h) | 766 | transmit_call_audio (void *cls, |
767 | size_t data_size, | ||
768 | const void *data) | ||
542 | { | 769 | { |
543 | GNUNET_CLIENT_notify_transmit_ready (h->client, | 770 | struct GNUNET_CONVERSATION_Call *call = cls; |
544 | sizeof (struct | 771 | struct GNUNET_MQ_Envelope *e; |
545 | ClientServerSessionRejectMessage), | 772 | struct ClientAudioMessage *am; |
546 | MAX_TRANSMIT_DELAY, GNUNET_YES, | 773 | |
547 | &transmit_session_reject_message, h); | 774 | GNUNET_assert (CS_ACTIVE == call->state); |
775 | e = GNUNET_MQ_msg_extra (am, | ||
776 | data_size, | ||
777 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO); | ||
778 | memcpy (&am[1], data, data_size); | ||
779 | GNUNET_MQ_send (call->mq, e); | ||
548 | } | 780 | } |
549 | 781 | ||
550 | 782 | ||
551 | /** | 783 | /** |
552 | * Auxiliary function to terminate a call. | 784 | * We received a `struct ClientPhonePickedupMessage` |
553 | * | 785 | * |
554 | * @param h conversation handle | 786 | * @param cls the `struct GNUNET_CONVERSATION_Call` |
787 | * @param msg the message | ||
555 | */ | 788 | */ |
556 | static void | 789 | static void |
557 | terminate_call (struct GNUNET_CONVERSATION_Handle *h) | 790 | handle_call_picked_up (void *cls, |
791 | const struct GNUNET_MessageHeader *msg) | ||
558 | { | 792 | { |
559 | GNUNET_CLIENT_notify_transmit_ready (h->client, | 793 | struct GNUNET_CONVERSATION_Call *call = cls; |
560 | sizeof (struct | 794 | const struct ClientPhonePickedupMessage *am; |
561 | ClientServerSessionTerminateMessage), | 795 | const char *metadata; |
562 | MAX_TRANSMIT_DELAY, GNUNET_YES, | 796 | size_t size; |
563 | &transmit_session_terminate_message, | 797 | |
564 | h); | 798 | am = (const struct ClientPhonePickedupMessage *) msg; |
799 | size = ntohs (am->header.size) - sizeof (struct ClientPhonePickedupMessage); | ||
800 | metadata = (const char *) &am[1]; | ||
801 | if ( (0 == size) || | ||
802 | ('\0' != metadata[size - 1]) ) | ||
803 | metadata = NULL; | ||
804 | switch (call->state) | ||
805 | { | ||
806 | case CS_LOOKUP: | ||
807 | GNUNET_break (0); | ||
808 | reconnect_call (call); | ||
809 | break; | ||
810 | case CS_RINGING: | ||
811 | call->state = CS_ACTIVE; | ||
812 | call->event_handler (call->event_handler_cls, | ||
813 | GNUNET_CONVERSATION_EC_READY, | ||
814 | metadata); | ||
815 | call->speaker->enable_speaker (call->speaker->cls); | ||
816 | call->mic->enable_microphone (call->mic->cls, | ||
817 | &transmit_call_audio, | ||
818 | call); | ||
819 | break; | ||
820 | case CS_ACTIVE: | ||
821 | GNUNET_break (0); | ||
822 | reconnect_call (call); | ||
823 | break; | ||
824 | case CS_SHUTDOWN: | ||
825 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
826 | break; | ||
827 | } | ||
565 | } | 828 | } |
566 | 829 | ||
567 | 830 | ||
568 | /** | 831 | /** |
832 | * We received a `struct ClientPhoneHangupMessage` | ||
569 | * | 833 | * |
834 | * @param cls the `struct GNUNET_CONVERSATION_Call` | ||
835 | * @param msg the message | ||
570 | */ | 836 | */ |
571 | static void | 837 | static void |
572 | gns_call_cb (void *cls, uint32_t rd_count, | 838 | handle_call_hangup (void *cls, |
573 | const struct GNUNET_NAMESTORE_RecordData *rd) | 839 | const struct GNUNET_MessageHeader *msg) |
574 | { | 840 | { |
575 | struct GNUNET_CONVERSATION_Handle *handle = cls; | 841 | struct GNUNET_CONVERSATION_Call *call = cls; |
576 | struct GNUNET_PeerIdentity peer; | 842 | const struct ClientPhoneHangupMessage *am; |
577 | unsigned int i; | 843 | const char *reason; |
578 | 844 | size_t size; | |
579 | for (i=0;i<rd_count;i++) | 845 | |
846 | am = (const struct ClientPhoneHangupMessage *) msg; | ||
847 | size = ntohs (am->header.size) - sizeof (struct ClientPhoneHangupMessage); | ||
848 | reason = (const char *) &am[1]; | ||
849 | if ( (0 == size) || | ||
850 | ('\0' != reason[size - 1]) ) | ||
851 | reason = NULL; | ||
852 | switch (call->state) | ||
580 | { | 853 | { |
581 | switch (rd[i].record_type) | 854 | case CS_LOOKUP: |
582 | { | 855 | GNUNET_break (0); |
583 | case GNUNET_DNSPARSER_TYPE_TXT: /* FIXME: use fresh record type for voide... */ | 856 | reconnect_call (call); |
584 | if (GNUNET_OK != | 857 | break; |
585 | GNUNET_CRYPTO_ecc_public_sign_key_from_string (rd[i].data, | 858 | case CS_RINGING: |
586 | rd[i].data_size, | 859 | call->event_handler (call->event_handler_cls, |
587 | &peer.public_key)) | 860 | GNUNET_CONVERSATION_EC_TERMINATED, |
588 | { | 861 | reason); |
589 | GNUNET_break_op (0); | 862 | GNUNET_CONVERSATION_call_stop (call, NULL); |
590 | continue; | 863 | return; |
591 | } | 864 | case CS_ACTIVE: |
592 | initiate_call (handle, peer); | 865 | call->event_handler (call->event_handler_cls, |
593 | return; | 866 | GNUNET_CONVERSATION_EC_TERMINATED, |
594 | default: | 867 | reason); |
595 | break; | 868 | GNUNET_CONVERSATION_call_stop (call, NULL); |
596 | } | 869 | return; |
870 | case CS_SHUTDOWN: | ||
871 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
872 | break; | ||
597 | } | 873 | } |
598 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
599 | "Lookup failed\n"); | ||
600 | handle->notification_handler (NULL, handle, | ||
601 | GNUNET_CONVERSATION_NT_NO_PEER, | ||
602 | NULL); | ||
603 | } | 874 | } |
604 | 875 | ||
605 | 876 | ||
606 | /** | 877 | /** |
607 | * GNS lookup | 878 | * We received a `struct ClientAudioMessage` |
608 | */ | 879 | * |
880 | * @param cls the `struct GNUNET_CONVERSATION_Call` | ||
881 | * @param msg the message | ||
882 | */ | ||
609 | static void | 883 | static void |
610 | gns_lookup_and_call (struct GNUNET_CONVERSATION_Handle *h, const char *callee) | 884 | handle_call_audio_message (void *cls, |
611 | { | 885 | const struct GNUNET_MessageHeader *msg) |
612 | char domain[GNUNET_DNSPARSER_MAX_NAME_LENGTH]; | ||
613 | char *pos; | ||
614 | |||
615 | pos = domain; | ||
616 | strcpy (pos, "conversation"); | ||
617 | pos += strlen ("conversation"); | ||
618 | strcpy (pos, "."); | ||
619 | pos++; | ||
620 | strcpy (pos, callee); | ||
621 | pos += strlen (callee); | ||
622 | |||
623 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Lookup for %s\n", domain); | ||
624 | |||
625 | GNUNET_GNS_lookup (h->gns, | ||
626 | domain, | ||
627 | NULL /* FIXME: ZONE! */, | ||
628 | GNUNET_DNSPARSER_TYPE_TXT, | ||
629 | GNUNET_NO, | ||
630 | NULL, | ||
631 | &gns_call_cb, h); | ||
632 | } | ||
633 | |||
634 | |||
635 | /******************************************************************************/ | ||
636 | /********************** API CALL DEFINITIONS *************************/ | ||
637 | /******************************************************************************/ | ||
638 | |||
639 | struct GNUNET_CONVERSATION_Handle * | ||
640 | GNUNET_CONVERSATION_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
641 | void *cls, | ||
642 | GNUNET_CONVERSATION_CallHandler call_handler, | ||
643 | GNUNET_CONVERSATION_RejectHandler reject_handler, | ||
644 | GNUNET_CONVERSATION_NotificationHandler notification_handler, | ||
645 | GNUNET_CONVERSATION_MissedCallHandler missed_call_handler) | ||
646 | { | 886 | { |
647 | struct GNUNET_CONVERSATION_Handle *h; | 887 | struct GNUNET_CONVERSATION_Call *call = cls; |
648 | 888 | const struct ClientAudioMessage *am; | |
649 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
650 | "GNUNET_CONVERSATION_connect()\n"); | ||
651 | h = GNUNET_malloc (sizeof (struct GNUNET_CONVERSATION_Handle)); | ||
652 | |||
653 | h->cfg = cfg; | ||
654 | h->call_handler = call_handler; | ||
655 | h->reject_handler = reject_handler; | ||
656 | h->notification_handler = notification_handler; | ||
657 | h->missed_call_handler = missed_call_handler; | ||
658 | 889 | ||
659 | if (NULL == (h->client = GNUNET_CLIENT_connect ("conversation", cfg))) | 890 | am = (const struct ClientAudioMessage *) msg; |
660 | { | 891 | switch (call->state) |
661 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not access CONVERSATION service\n"); | 892 | { |
662 | GNUNET_break (0); | 893 | case CS_LOOKUP: |
663 | GNUNET_free (h); | 894 | GNUNET_break (0); |
895 | reconnect_call (call); | ||
896 | break; | ||
897 | case CS_RINGING: | ||
898 | GNUNET_break (0); | ||
899 | reconnect_call (call); | ||
900 | break; | ||
901 | case CS_ACTIVE: | ||
902 | call->speaker->play (call->speaker->cls, | ||
903 | ntohs (msg->size) - sizeof (struct ClientAudioMessage), | ||
904 | &am[1]); | ||
905 | break; | ||
906 | case CS_SHUTDOWN: | ||
907 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
908 | break; | ||
664 | 909 | ||
665 | return NULL; | 910 | } |
666 | } | 911 | } |
667 | 912 | ||
668 | if (NULL == (h->gns = GNUNET_GNS_connect (cfg))) | ||
669 | { | ||
670 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not access GNS service\n"); | ||
671 | GNUNET_break (0); | ||
672 | GNUNET_CLIENT_disconnect (h->client); | ||
673 | GNUNET_free (h); | ||
674 | 913 | ||
675 | return NULL; | 914 | /** |
676 | } | 915 | * Iterator called on obtained result for a GNS lookup. |
916 | * | ||
917 | * @param cls closure with the `struct GNUNET_CONVERSATION_Call` | ||
918 | * @param rd_count number of records in @a rd | ||
919 | * @param rd the records in reply | ||
920 | */ | ||
921 | static void | ||
922 | handle_gns_response (void *cls, | ||
923 | uint32_t rd_count, | ||
924 | const struct GNUNET_NAMESTORE_RecordData *rd) | ||
925 | { | ||
926 | struct GNUNET_CONVERSATION_Call *call = cls; | ||
927 | uint32_t i; | ||
928 | struct GNUNET_MQ_Envelope *e; | ||
929 | struct ClientCallMessage *ccm; | ||
677 | 930 | ||
678 | if (NULL == (h->namestore = GNUNET_NAMESTORE_connect (cfg))) | 931 | for (i=0;i<rd_count;i++) |
932 | { | ||
933 | if (GNUNET_NAMESTORE_TYPE_PHONE == rd[i].record_type) | ||
679 | { | 934 | { |
680 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | 935 | if (rd[i].data_size != sizeof (struct PhoneRecord)) |
681 | "Could not access NAMESTORE service\n"); | 936 | { |
682 | GNUNET_break (0); | 937 | GNUNET_break_op (0); |
683 | GNUNET_CLIENT_disconnect (h->client); | 938 | continue; |
684 | GNUNET_GNS_disconnect (h->gns); | 939 | } |
685 | GNUNET_free (h); | 940 | memcpy (&call->phone_record, |
686 | 941 | rd[i].data, | |
687 | return NULL; | 942 | rd[i].data_size); |
943 | e = GNUNET_MQ_msg (ccm, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL); | ||
944 | ccm->line = call->phone_record.line; | ||
945 | ccm->target = call->phone_record.peer; | ||
946 | ccm->caller_id = *GNUNET_IDENTITY_ego_get_private_key (call->caller_id); | ||
947 | GNUNET_MQ_send (call->mq, e); | ||
948 | call->state = CS_RINGING; | ||
949 | call->event_handler (call->event_handler_cls, | ||
950 | GNUNET_CONVERSATION_EC_RINGING); | ||
951 | return; | ||
688 | } | 952 | } |
689 | 953 | } | |
690 | check_gns (h); | 954 | /* not found */ |
691 | GNUNET_CLIENT_receive (h->client, &receive_message_cb, h, | 955 | call->event_handler (call->event_handler_cls, |
692 | GNUNET_TIME_UNIT_FOREVER_REL); | 956 | GNUNET_CONVERSATION_EC_GNS_FAIL); |
693 | 957 | GNUNET_CONVERSATION_call_stop (call, NULL); | |
694 | return h; | ||
695 | } | 958 | } |
696 | 959 | ||
697 | 960 | ||
698 | void | 961 | /** |
699 | GNUNET_CONVERSATION_disconnect (struct GNUNET_CONVERSATION_Handle *handle) | 962 | * We encountered an error talking with the conversation service. |
963 | * | ||
964 | * @param cls the `struct GNUNET_CONVERSATION_Call` | ||
965 | * @param error details about the error | ||
966 | */ | ||
967 | static void | ||
968 | call_error_handler (void *cls, | ||
969 | enum GNUNET_MQ_Error error) | ||
700 | { | 970 | { |
701 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CONVERSATION DISCONNECT\n"); | 971 | struct GNUNET_CONVERSATION_Call *call = cls; |
702 | |||
703 | GNUNET_CLIENT_disconnect (handle->client); | ||
704 | GNUNET_GNS_disconnect (handle->gns); | ||
705 | 972 | ||
706 | GNUNET_free (handle); | 973 | GNUNET_break (0); |
707 | handle = NULL; | 974 | reconnect_call (call); |
708 | } | 975 | } |
709 | 976 | ||
710 | 977 | ||
711 | void | 978 | /** |
712 | GNUNET_CONVERSATION_call (struct GNUNET_CONVERSATION_Handle *h, | 979 | * The call got disconnected, reconnect to the service. |
713 | const char *callee, | 980 | * |
714 | int doGnsLookup) | 981 | * @param call call to reconnect |
982 | */ | ||
983 | static void | ||
984 | reconnect_call (struct GNUNET_CONVERSATION_Call *call) | ||
715 | { | 985 | { |
716 | struct GNUNET_PeerIdentity peer; | 986 | static struct GNUNET_MQ_MessageHandler handlers[] = |
717 | |||
718 | if (NULL == h || NULL == h->client) | ||
719 | return; | ||
720 | |||
721 | if (GNUNET_YES == doGnsLookup) | ||
722 | { | 987 | { |
723 | gns_lookup_and_call (h, callee); | 988 | { &handle_call_busy, |
724 | return; | 989 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_BUSY, |
990 | sizeof (struct ClientPhoneBusyMessage) }, | ||
991 | { &handle_call_picked_up, | ||
992 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP, | ||
993 | 0 }, | ||
994 | { &handle_call_hangup, | ||
995 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP, | ||
996 | 0 }, | ||
997 | { &handle_call_audio_message, | ||
998 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO, | ||
999 | 0 }, | ||
1000 | { NULL, 0, 0 } | ||
1001 | }; | ||
1002 | if (NULL != call->mq) | ||
1003 | { | ||
1004 | GNUNET_MQ_destroy (call->mq); | ||
1005 | call->mq = NULL; | ||
725 | } | 1006 | } |
726 | if (GNUNET_OK != | 1007 | if (NULL != call->client) |
727 | GNUNET_CRYPTO_ecc_public_sign_key_from_string (callee, | ||
728 | strlen (callee), | ||
729 | &peer.public_key)) | ||
730 | { | 1008 | { |
731 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | 1009 | GNUNET_CLIENT_disconnect (call->client); |
732 | _("`%s' is not a valid public key\n"), | 1010 | call->client = NULL; |
733 | callee); | 1011 | } |
734 | h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_NO_PEER, NULL); | 1012 | call->client = GNUNET_CLIENT_connect ("conversation", call->cfg); |
1013 | if (NULL == call->client) | ||
735 | return; | 1014 | return; |
736 | } | 1015 | call->mq = GNUNET_MQ_queue_for_connection_client (call->client, |
737 | initiate_call (h, peer); | 1016 | handlers, |
1017 | &call_error_handler, | ||
1018 | call); | ||
738 | } | 1019 | } |
739 | 1020 | ||
740 | 1021 | ||
741 | void | 1022 | /** |
742 | GNUNET_CONVERSATION_hangup (struct GNUNET_CONVERSATION_Handle *h) | 1023 | * Call the phone of another user. |
1024 | * | ||
1025 | * @param cfg configuration to use, specifies our phone service | ||
1026 | * @param caller_id identity of the caller | ||
1027 | * @param callee GNS name of the callee (used to locate the callee's record) | ||
1028 | * @param speaker speaker to use (will be used automatically immediately once the | ||
1029 | * #GNUNET_CONVERSATION_EC_READY event is generated); we will NOT generate | ||
1030 | * a ring tone on the speaker | ||
1031 | * @param mic microphone to use (will be used automatically immediately once the | ||
1032 | * #GNUNET_CONVERSATION_EC_READY event is generated) | ||
1033 | * @param event_handler how to notify the owner of the phone about events | ||
1034 | * @param event_handler_cls closure for @a event_handler | ||
1035 | */ | ||
1036 | struct GNUNET_CONVERSATION_Call * | ||
1037 | GNUNET_CONVERSATION_call_start (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
1038 | struct GNUNET_IDENTITY_Ego *caller_id, | ||
1039 | const char *callee, | ||
1040 | struct GNUNET_SPEAKER_Handle *speaker, | ||
1041 | struct GNUNET_MICROPHONE_Handle *mic, | ||
1042 | GNUNET_CONVERSATION_EventHandler event_handler, | ||
1043 | void *event_handler_cls) | ||
743 | { | 1044 | { |
744 | if (NULL == h || NULL == h->client) | 1045 | struct GNUNET_CONVERSATION_Call *call; |
745 | return; | 1046 | struct GNUNET_CRYPTO_EccPublicSignKey my_zone; |
746 | 1047 | ||
747 | terminate_call (h); | 1048 | GNUNET_IDENTITY_ego_get_public_key (caller_id, |
1049 | &my_zone); | ||
1050 | call = GNUNET_new (struct GNUNET_CONVERSATION_Call); | ||
1051 | call->cfg = cfg; | ||
1052 | call->caller_id = caller_id; | ||
1053 | call->callee = GNUNET_strdup (callee); | ||
1054 | call->speaker = speaker; | ||
1055 | call->mic = mic; | ||
1056 | call->event_handler = event_handler; | ||
1057 | call->event_handler_cls = event_handler_cls; | ||
1058 | call->gns = GNUNET_GNS_connect (cfg); | ||
1059 | reconnect_call (call); | ||
1060 | |||
1061 | if ( (NULL == call->client) || | ||
1062 | (NULL == call->gns) ) | ||
1063 | { | ||
1064 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
1065 | return NULL; | ||
1066 | } | ||
1067 | call->gns_lookup = GNUNET_GNS_lookup (call->gns, callee, | ||
1068 | &my_zone, | ||
1069 | GNUNET_NAMESTORE_TYPE_PHONE, | ||
1070 | GNUNET_NO, | ||
1071 | NULL /* FIXME: add shortening support */, | ||
1072 | &handle_gns_response, call); | ||
1073 | GNUNET_assert (NULL != call->gns_lookup); | ||
1074 | return call; | ||
748 | } | 1075 | } |
749 | 1076 | ||
750 | 1077 | ||
1078 | /** | ||
1079 | * Terminate a call. The call may be ringing or ready at this time. | ||
1080 | * | ||
1081 | * @param call call to terminate | ||
1082 | * @param reason if the call was active (ringing or ready) this will be the | ||
1083 | * reason given to the other user for why we hung up | ||
1084 | */ | ||
751 | void | 1085 | void |
752 | GNUNET_CONVERSATION_accept (struct GNUNET_CONVERSATION_Handle *h) | 1086 | GNUNET_CONVERSATION_call_stop (struct GNUNET_CONVERSATION_Call *call, |
1087 | const char *reason) | ||
753 | { | 1088 | { |
754 | if (NULL == h || NULL == h->client) | 1089 | if (NULL != reason) |
755 | return; | 1090 | { |
1091 | // FIXME: transmit reason to service... (not implemented!) | ||
1092 | GNUNET_break (0); | ||
1093 | // return; | ||
1094 | } | ||
1095 | if (NULL != call->speaker) | ||
1096 | { | ||
1097 | if (CS_ACTIVE == call->state) | ||
1098 | call->speaker->disable_speaker (call->speaker->cls); | ||
1099 | call->speaker = NULL; | ||
1100 | } | ||
1101 | if (NULL != call->mic) | ||
1102 | { | ||
1103 | if (CS_ACTIVE == call->state) | ||
1104 | call->mic->disable_microphone (call->mic->cls); | ||
1105 | call->mic =NULL; | ||
1106 | } | ||
1107 | if (NULL != call->mq) | ||
1108 | { | ||
1109 | GNUNET_MQ_destroy (call->mq); | ||
1110 | call->mq = NULL; | ||
1111 | } | ||
1112 | if (NULL != call->client) | ||
1113 | { | ||
1114 | GNUNET_CLIENT_disconnect (call->client); | ||
1115 | call->client = NULL; | ||
1116 | } | ||
1117 | if (NULL != call->gns_lookup) | ||
1118 | { | ||
1119 | GNUNET_GNS_lookup_cancel (call->gns_lookup); | ||
1120 | call->gns_lookup = NULL; | ||
1121 | } | ||
1122 | if (NULL != call->gns) | ||
1123 | { | ||
1124 | GNUNET_GNS_disconnect (call->gns); | ||
1125 | call->gns = NULL; | ||
1126 | } | ||
756 | 1127 | ||
757 | accept_call (h); | 1128 | GNUNET_free (call); |
758 | } | 1129 | } |
759 | 1130 | ||
760 | 1131 | ||
761 | void | ||
762 | GNUNET_CONVERSATION_reject (struct GNUNET_CONVERSATION_Handle *h) | ||
763 | { | ||
764 | if (NULL == h || NULL == h->client) | ||
765 | return; | ||
766 | |||
767 | reject_call (h); | ||
768 | } | ||
769 | |||
770 | /* end of conversation_api.c */ | 1132 | /* end of conversation_api.c */ |
diff --git a/src/conversation/conversation_api2.c b/src/conversation/conversation_api2.c deleted file mode 100644 index 6ce198e64..000000000 --- a/src/conversation/conversation_api2.c +++ /dev/null | |||
@@ -1,1132 +0,0 @@ | |||
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_api2.c | ||
23 | * @brief 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_gns_service.h" | ||
31 | #include "conversation.h" | ||
32 | |||
33 | |||
34 | /** | ||
35 | * A phone record specifies which peer is hosting a given user and | ||
36 | * may also specify the phone line that is used (typically zero). | ||
37 | * The version is also right now always zero. | ||
38 | */ | ||
39 | struct PhoneRecord | ||
40 | { | ||
41 | |||
42 | /** | ||
43 | * Version of the phone record, for now always zero. We may | ||
44 | * use other versions for anonymously hosted phone lines in | ||
45 | * the future. | ||
46 | */ | ||
47 | uint32_t version GNUNET_PACKED; | ||
48 | |||
49 | /** | ||
50 | * Phone line to use at the peer. | ||
51 | */ | ||
52 | uint32_t line GNUNET_PACKED; | ||
53 | |||
54 | /** | ||
55 | * Identity of the peer hosting the phone service. | ||
56 | */ | ||
57 | struct GNUNET_PeerIdentity peer; | ||
58 | |||
59 | }; | ||
60 | |||
61 | |||
62 | /** | ||
63 | * Possible states of the phone. | ||
64 | */ | ||
65 | enum PhoneState | ||
66 | { | ||
67 | /** | ||
68 | * We still need to register the phone. | ||
69 | */ | ||
70 | PS_REGISTER = 0, | ||
71 | |||
72 | /** | ||
73 | * We are waiting for a call. | ||
74 | */ | ||
75 | PS_WAITING, | ||
76 | |||
77 | /** | ||
78 | * The phone is ringing. | ||
79 | */ | ||
80 | PS_RINGING, | ||
81 | |||
82 | /** | ||
83 | * The phone is in an active conversation. | ||
84 | */ | ||
85 | PS_ACTIVE | ||
86 | }; | ||
87 | |||
88 | |||
89 | /** | ||
90 | * A phone is a device that can ring to signal an incoming call and | ||
91 | * that you can pick up to answer the call and hang up to terminate | ||
92 | * the call. You can also hang up a ringing phone immediately | ||
93 | * (without picking it up) to stop it from ringing. Phones have | ||
94 | * caller ID. You can ask the phone for its record and make that | ||
95 | * record available (via GNS) to enable others to call you. | ||
96 | * Multiple phones maybe connected to the same line (the line is | ||
97 | * something rather internal to a phone and not obvious from it). | ||
98 | * You can only have one conversation per phone at any time. | ||
99 | */ | ||
100 | struct GNUNET_CONVERSATION_Phone | ||
101 | { | ||
102 | /** | ||
103 | * Our configuration. | ||
104 | */ | ||
105 | const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
106 | |||
107 | /** | ||
108 | * Handle to talk with CONVERSATION service. | ||
109 | */ | ||
110 | struct GNUNET_CLIENT_Connection *client; | ||
111 | |||
112 | /** | ||
113 | * Function to call for phone events. | ||
114 | */ | ||
115 | GNUNET_CONVERSATION_EventHandler event_handler; | ||
116 | |||
117 | /** | ||
118 | * Closure for @e event_handler | ||
119 | */ | ||
120 | void *event_handler_cls; | ||
121 | |||
122 | /** | ||
123 | * Speaker, or NULL if none is attached. | ||
124 | */ | ||
125 | struct GNUNET_SPEAKER_Handle *speaker; | ||
126 | |||
127 | /** | ||
128 | * Microphone, or NULL if none is attached. | ||
129 | */ | ||
130 | struct GNUNET_MICROPHONE_Handle *mic; | ||
131 | |||
132 | /** | ||
133 | * Connection to NAMESTORE (for reverse lookup). | ||
134 | */ | ||
135 | struct GNUNET_NAMESTORE_Handle *ns; | ||
136 | |||
137 | /** | ||
138 | * Active NAMESTORE lookup (or NULL). | ||
139 | */ | ||
140 | struct GNUNET_NAMESTORE_QueueEntry *qe; | ||
141 | |||
142 | /** | ||
143 | * Handle for transmitting to the CONVERSATION service. | ||
144 | */ | ||
145 | struct GNUNET_MQ_Handle *mq; | ||
146 | |||
147 | /** | ||
148 | * This phone's record. | ||
149 | */ | ||
150 | struct PhoneRecord my_record; | ||
151 | |||
152 | /** | ||
153 | * My GNS zone. | ||
154 | */ | ||
155 | struct GNUNET_CRYPTO_EccPrivateKey my_zone; | ||
156 | |||
157 | /** | ||
158 | * Identity of the person calling us (valid while in state #PS_RINGING). | ||
159 | */ | ||
160 | struct GNUNET_CRYPTO_EccPublicSignKey caller_id; | ||
161 | |||
162 | /** | ||
163 | * State machine for the phone. | ||
164 | */ | ||
165 | enum PhoneState state; | ||
166 | |||
167 | }; | ||
168 | |||
169 | |||
170 | /** | ||
171 | * The phone got disconnected, reconnect to the service. | ||
172 | * | ||
173 | * @param phone phone to reconnect | ||
174 | */ | ||
175 | static void | ||
176 | reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone); | ||
177 | |||
178 | |||
179 | /** | ||
180 | * We have resolved the caller ID using our name service. | ||
181 | * | ||
182 | * @param cls the `struct GNUNET_CONVERSATION_Phone` | ||
183 | * @param zone our zone used for resolution | ||
184 | * @param label name of the caller | ||
185 | * @param rd_count number of records we have in @a rd | ||
186 | * @param rd records we have for the caller's label | ||
187 | */ | ||
188 | static void | ||
189 | handle_caller_name (void *cls, | ||
190 | const struct GNUNET_CRYPTO_EccPrivateKey *zone, | ||
191 | const char *label, | ||
192 | unsigned int rd_count, | ||
193 | const struct GNUNET_NAMESTORE_RecordData *rd) | ||
194 | { | ||
195 | struct GNUNET_CONVERSATION_Phone *phone = cls; | ||
196 | char *name; | ||
197 | |||
198 | phone->qe = NULL; | ||
199 | if (NULL == label) | ||
200 | name = GNUNET_strdup (GNUNET_NAMESTORE_pkey_to_zkey (&phone->caller_id)); | ||
201 | else | ||
202 | GNUNET_asprintf (&name, "%.gnu", label); | ||
203 | phone->event_handler (phone->event_handler_cls, | ||
204 | GNUNET_CONVERSATION_EC_RING, | ||
205 | name); | ||
206 | GNUNET_free (name); | ||
207 | } | ||
208 | |||
209 | |||
210 | /** | ||
211 | * We received a `struct ClientPhoneRingMessage` | ||
212 | * | ||
213 | * @param cls the `struct GNUNET_CONVERSATION_Phone` | ||
214 | * @param msg the message | ||
215 | */ | ||
216 | static void | ||
217 | handle_phone_ring (void *cls, | ||
218 | const struct GNUNET_MessageHeader *msg) | ||
219 | { | ||
220 | struct GNUNET_CONVERSATION_Phone *phone = cls; | ||
221 | const struct ClientPhoneRingMessage *ring; | ||
222 | |||
223 | ring = (const struct ClientPhoneRingMessage *) msg; | ||
224 | switch (phone->state) | ||
225 | { | ||
226 | case PS_REGISTER: | ||
227 | GNUNET_assert (0); | ||
228 | break; | ||
229 | case PS_WAITING: | ||
230 | phone->state = PS_RINGING; | ||
231 | phone->caller_id = ring->caller_id; | ||
232 | phone->qe = GNUNET_NAMESTORE_zone_to_name (phone->ns, | ||
233 | &phone->my_zone, | ||
234 | &ring->caller_id, | ||
235 | &handle_caller_name, | ||
236 | phone); | ||
237 | break; | ||
238 | case PS_RINGING: | ||
239 | GNUNET_break (0); | ||
240 | reconnect_phone (phone); | ||
241 | break; | ||
242 | case PS_ACTIVE: | ||
243 | GNUNET_break (0); | ||
244 | reconnect_phone (phone); | ||
245 | break; | ||
246 | } | ||
247 | } | ||
248 | |||
249 | |||
250 | /** | ||
251 | * We received a `struct ClientPhoneHangupMessage`. | ||
252 | * | ||
253 | * @param cls the `struct GNUNET_CONVERSATION_Phone` | ||
254 | * @param msg the message | ||
255 | */ | ||
256 | static void | ||
257 | handle_phone_hangup (void *cls, | ||
258 | const struct GNUNET_MessageHeader *msg) | ||
259 | { | ||
260 | struct GNUNET_CONVERSATION_Phone *phone = cls; | ||
261 | const struct ClientPhoneHangupMessage *hang; | ||
262 | size_t len; | ||
263 | const char *reason; | ||
264 | |||
265 | hang = (const struct ClientPhoneHangupMessage *) msg; | ||
266 | reason = (const char *) &hang[1]; | ||
267 | len = htons (hang->header.size) - sizeof (struct ClientPhoneHangupMessage); | ||
268 | if ( (0 == len) || | ||
269 | ('\0' != reason[len-1]) ) | ||
270 | { | ||
271 | GNUNET_break (0); | ||
272 | reconnect_phone (phone); | ||
273 | return; | ||
274 | } | ||
275 | switch (phone->state) | ||
276 | { | ||
277 | case PS_REGISTER: | ||
278 | GNUNET_assert (0); | ||
279 | break; | ||
280 | case PS_WAITING: | ||
281 | GNUNET_break (0); | ||
282 | reconnect_phone (phone); | ||
283 | break; | ||
284 | case PS_RINGING: | ||
285 | if (NULL != phone->qe) | ||
286 | { | ||
287 | GNUNET_NAMESTORE_cancel (phone->qe); | ||
288 | phone->qe = NULL; | ||
289 | phone->state = PS_WAITING; | ||
290 | break; | ||
291 | } | ||
292 | phone->state = PS_WAITING; | ||
293 | phone->event_handler (phone->event_handler_cls, | ||
294 | GNUNET_CONVERSATION_EC_TERMINATED, | ||
295 | reason); | ||
296 | break; | ||
297 | case PS_ACTIVE: | ||
298 | GNUNET_break (NULL == phone->qe); | ||
299 | phone->state = PS_WAITING; | ||
300 | phone->event_handler (phone->event_handler_cls, | ||
301 | GNUNET_CONVERSATION_EC_TERMINATED, | ||
302 | reason); | ||
303 | break; | ||
304 | } | ||
305 | } | ||
306 | |||
307 | |||
308 | /** | ||
309 | * We received a `struct ClientAudioMessage` | ||
310 | * | ||
311 | * @param cls the `struct GNUNET_CONVERSATION_Phone` | ||
312 | * @param msg the message | ||
313 | */ | ||
314 | static void | ||
315 | handle_phone_audio_message (void *cls, | ||
316 | const struct GNUNET_MessageHeader *msg) | ||
317 | { | ||
318 | struct GNUNET_CONVERSATION_Phone *phone = cls; | ||
319 | const struct ClientAudioMessage *am; | ||
320 | |||
321 | am = (const struct ClientAudioMessage *) msg; | ||
322 | switch (phone->state) | ||
323 | { | ||
324 | case PS_REGISTER: | ||
325 | GNUNET_assert (0); | ||
326 | break; | ||
327 | case PS_WAITING: | ||
328 | GNUNET_break (0); | ||
329 | reconnect_phone (phone); | ||
330 | break; | ||
331 | case PS_RINGING: | ||
332 | GNUNET_break (0); | ||
333 | reconnect_phone (phone); | ||
334 | break; | ||
335 | case PS_ACTIVE: | ||
336 | phone->speaker->play (phone->speaker->cls, | ||
337 | ntohs (msg->size) - sizeof (struct ClientAudioMessage), | ||
338 | &am[1]); | ||
339 | break; | ||
340 | } | ||
341 | } | ||
342 | |||
343 | |||
344 | /** | ||
345 | * We encountered an error talking with the conversation service. | ||
346 | * | ||
347 | * @param cls the `struct GNUNET_CONVERSATION_Phone` | ||
348 | * @param error details about the error | ||
349 | */ | ||
350 | static void | ||
351 | phone_error_handler (void *cls, | ||
352 | enum GNUNET_MQ_Error error) | ||
353 | { | ||
354 | struct GNUNET_CONVERSATION_Phone *phone = cls; | ||
355 | |||
356 | GNUNET_break (0); | ||
357 | reconnect_phone (phone); | ||
358 | } | ||
359 | |||
360 | |||
361 | /** | ||
362 | * The phone got disconnected, reconnect to the service. | ||
363 | * | ||
364 | * @param phone phone to reconnect | ||
365 | */ | ||
366 | static void | ||
367 | reconnect_phone (struct GNUNET_CONVERSATION_Phone *phone) | ||
368 | { | ||
369 | static struct GNUNET_MQ_MessageHandler handlers[] = | ||
370 | { | ||
371 | { &handle_phone_ring, | ||
372 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING, | ||
373 | sizeof (struct ClientPhoneRingMessage) }, | ||
374 | { &handle_phone_hangup, | ||
375 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP, | ||
376 | 0 }, | ||
377 | { &handle_phone_audio_message, | ||
378 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO, | ||
379 | 0 }, | ||
380 | { NULL, 0, 0 } | ||
381 | }; | ||
382 | struct GNUNET_MQ_Envelope *e; | ||
383 | struct ClientPhoneRegisterMessage *reg; | ||
384 | |||
385 | if (NULL != phone->mq) | ||
386 | { | ||
387 | GNUNET_MQ_destroy (phone->mq); | ||
388 | phone->mq = NULL; | ||
389 | } | ||
390 | if (NULL != phone->client) | ||
391 | { | ||
392 | GNUNET_CLIENT_disconnect (phone->client); | ||
393 | phone->client = NULL; | ||
394 | } | ||
395 | phone->state = PS_REGISTER; | ||
396 | phone->client = GNUNET_CLIENT_connect ("conversation", phone->cfg); | ||
397 | if (NULL == phone->client) | ||
398 | return; | ||
399 | phone->mq = GNUNET_MQ_queue_for_connection_client (phone->client, | ||
400 | handlers, | ||
401 | &phone_error_handler, | ||
402 | phone); | ||
403 | e = GNUNET_MQ_msg (reg, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER); | ||
404 | reg->line = phone->my_record.line; | ||
405 | GNUNET_MQ_send (phone->mq, e); | ||
406 | phone->state = PS_WAITING; | ||
407 | } | ||
408 | |||
409 | |||
410 | /** | ||
411 | * Create a new phone. | ||
412 | * | ||
413 | * @param cfg configuration for the phone; specifies the phone service and | ||
414 | * which line the phone is to be connected to | ||
415 | * @param ego ego to use for name resolution (when determining caller ID) | ||
416 | * @param event_handler how to notify the owner of the phone about events | ||
417 | * @param event_handler_cls closure for @a event_handler | ||
418 | */ | ||
419 | struct GNUNET_CONVERSATION_Phone * | ||
420 | GNUNET_CONVERSATION_phone_create (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
421 | const struct GNUNET_IDENTITY_Ego *ego, | ||
422 | GNUNET_CONVERSATION_EventHandler event_handler, | ||
423 | void *event_handler_cls) | ||
424 | { | ||
425 | struct GNUNET_CONVERSATION_Phone *phone; | ||
426 | unsigned long long line; | ||
427 | |||
428 | if (GNUNET_OK != | ||
429 | GNUNET_CONFIGURATION_get_value_number (cfg, | ||
430 | "CONVERSATION", | ||
431 | "LINE", | ||
432 | &line)) | ||
433 | return NULL; | ||
434 | phone = GNUNET_new (struct GNUNET_CONVERSATION_Phone); | ||
435 | if (GNUNET_OK != | ||
436 | GNUNET_CRYPTO_get_host_identity (cfg, | ||
437 | &phone->my_record.peer)) | ||
438 | { | ||
439 | GNUNET_break (0); | ||
440 | GNUNET_free (phone); | ||
441 | return NULL; | ||
442 | } | ||
443 | phone->cfg = cfg; | ||
444 | phone->my_zone = *GNUNET_IDENTITY_ego_get_private_key (ego); | ||
445 | phone->event_handler = event_handler; | ||
446 | phone->event_handler_cls = event_handler_cls; | ||
447 | phone->ns = GNUNET_NAMESTORE_connect (cfg); | ||
448 | phone->my_record.line = htonl ((uint32_t) line); | ||
449 | phone->my_record.version = htonl (0); | ||
450 | reconnect_phone (phone); | ||
451 | if ( (NULL == phone->client) || | ||
452 | (NULL == phone->ns) ) | ||
453 | { | ||
454 | GNUNET_break (0); | ||
455 | GNUNET_CONVERSATION_phone_destroy (phone); | ||
456 | return NULL; | ||
457 | } | ||
458 | return phone; | ||
459 | } | ||
460 | |||
461 | |||
462 | /** | ||
463 | * Fill in a namestore record with the contact information | ||
464 | * for this phone. Note that the filled in "data" value | ||
465 | * is only valid until the phone is destroyed. | ||
466 | * | ||
467 | * @param phone phone to create a record for | ||
468 | * @param rd namestore record to fill in | ||
469 | */ | ||
470 | void | ||
471 | GNUNET_CONVERSATION_phone_get_record (struct GNUNET_CONVERSATION_Phone *phone, | ||
472 | struct GNUNET_NAMESTORE_RecordData *rd) | ||
473 | { | ||
474 | rd->data = &phone->my_record; | ||
475 | rd->expiration_time = 0; | ||
476 | rd->data_size = sizeof (struct PhoneRecord); | ||
477 | rd->record_type = GNUNET_NAMESTORE_TYPE_PHONE; | ||
478 | rd->flags = GNUNET_NAMESTORE_RF_NONE; | ||
479 | } | ||
480 | |||
481 | |||
482 | /** | ||
483 | * Process recorded audio data. | ||
484 | * | ||
485 | * @param cls closure with the `struct GNUNET_CONVERSATION_Phone` | ||
486 | * @param data_size number of bytes in @a data | ||
487 | * @param data audio data to play | ||
488 | */ | ||
489 | static void | ||
490 | transmit_phone_audio (void *cls, | ||
491 | size_t data_size, | ||
492 | const void *data) | ||
493 | { | ||
494 | struct GNUNET_CONVERSATION_Phone *phone = cls; | ||
495 | struct GNUNET_MQ_Envelope *e; | ||
496 | struct ClientAudioMessage *am; | ||
497 | |||
498 | GNUNET_assert (PS_ACTIVE == phone->state); | ||
499 | e = GNUNET_MQ_msg_extra (am, | ||
500 | data_size, | ||
501 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO); | ||
502 | memcpy (&am[1], data, data_size); | ||
503 | GNUNET_MQ_send (phone->mq, e); | ||
504 | } | ||
505 | |||
506 | |||
507 | /** | ||
508 | * Picks up a (ringing) phone. This will connect the speaker | ||
509 | * to the microphone of the other party, and vice versa. | ||
510 | * | ||
511 | * @param phone phone to pick up | ||
512 | * @param metadata meta data to give to the other user about the pick up event | ||
513 | * @param speaker speaker to use | ||
514 | * @param mic microphone to use | ||
515 | */ | ||
516 | void | ||
517 | GNUNET_CONVERSATION_phone_pick_up (struct GNUNET_CONVERSATION_Phone *phone, | ||
518 | const char *metadata, | ||
519 | struct GNUNET_SPEAKER_Handle *speaker, | ||
520 | struct GNUNET_MICROPHONE_Handle *mic) | ||
521 | { | ||
522 | struct GNUNET_MQ_Envelope *e; | ||
523 | struct ClientPhonePickupMessage *pick; | ||
524 | size_t slen; | ||
525 | |||
526 | GNUNET_assert (PS_RINGING == phone->state); | ||
527 | phone->speaker = speaker; | ||
528 | phone->mic = mic; | ||
529 | slen = strlen (metadata) + 1; | ||
530 | e = GNUNET_MQ_msg_extra (pick, slen, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP); | ||
531 | memcpy (&pick[1], metadata, slen); | ||
532 | GNUNET_MQ_send (phone->mq, e); | ||
533 | phone->state = PS_ACTIVE; | ||
534 | phone->speaker->enable_speaker (phone->speaker->cls); | ||
535 | phone->mic->enable_microphone (phone->mic->cls, | ||
536 | &transmit_phone_audio, | ||
537 | phone); | ||
538 | } | ||
539 | |||
540 | |||
541 | /** | ||
542 | * Hang up up a (possibly ringing) phone. This will notify the other | ||
543 | * party that we are no longer interested in talking with them. | ||
544 | * | ||
545 | * @param phone phone to pick up | ||
546 | * @param reason text we give to the other party about why we terminated the conversation | ||
547 | */ | ||
548 | void | ||
549 | GNUNET_CONVERSATION_phone_hang_up (struct GNUNET_CONVERSATION_Phone *phone, | ||
550 | const char *reason) | ||
551 | { | ||
552 | struct GNUNET_MQ_Envelope *e; | ||
553 | struct ClientPhoneHangupMessage *hang; | ||
554 | size_t slen; | ||
555 | |||
556 | GNUNET_assert ( (PS_RINGING == phone->state) || | ||
557 | (PS_ACTIVE == phone->state) ); | ||
558 | phone->speaker->disable_speaker (phone->speaker->cls); | ||
559 | phone->mic->disable_microphone (phone->mic->cls); | ||
560 | phone->speaker = NULL; | ||
561 | phone->mic = NULL; | ||
562 | slen = strlen (reason) + 1; | ||
563 | e = GNUNET_MQ_msg_extra (hang, slen, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP); | ||
564 | memcpy (&hang[1], reason, slen); | ||
565 | GNUNET_MQ_send (phone->mq, e); | ||
566 | phone->state = PS_WAITING; | ||
567 | } | ||
568 | |||
569 | |||
570 | /** | ||
571 | * Destroys a phone. | ||
572 | * | ||
573 | * @param phone phone to destroy | ||
574 | */ | ||
575 | void | ||
576 | GNUNET_CONVERSATION_phone_destroy (struct GNUNET_CONVERSATION_Phone *phone) | ||
577 | { | ||
578 | if (NULL != phone->speaker) | ||
579 | { | ||
580 | phone->speaker->disable_speaker (phone->speaker->cls); | ||
581 | phone->speaker = NULL; | ||
582 | } | ||
583 | if (NULL != phone->mic) | ||
584 | { | ||
585 | phone->mic->disable_microphone (phone->mic->cls); | ||
586 | phone->mic = NULL; | ||
587 | } | ||
588 | if (NULL != phone->qe) | ||
589 | { | ||
590 | GNUNET_NAMESTORE_cancel (phone->qe); | ||
591 | phone->qe = NULL; | ||
592 | } | ||
593 | if (NULL != phone->ns) | ||
594 | { | ||
595 | GNUNET_NAMESTORE_disconnect (phone->ns); | ||
596 | phone->ns = NULL; | ||
597 | } | ||
598 | if (NULL != phone->mq) | ||
599 | { | ||
600 | GNUNET_MQ_destroy (phone->mq); | ||
601 | phone->mq = NULL; | ||
602 | } | ||
603 | if (NULL != phone->client) | ||
604 | { | ||
605 | GNUNET_CLIENT_disconnect (phone->client); | ||
606 | phone->client = NULL; | ||
607 | } | ||
608 | GNUNET_free (phone); | ||
609 | } | ||
610 | |||
611 | |||
612 | /* ******************************* Call API *************************** */ | ||
613 | |||
614 | /** | ||
615 | * Possible states of the phone. | ||
616 | */ | ||
617 | enum CallState | ||
618 | { | ||
619 | /** | ||
620 | * We still need to lookup the callee. | ||
621 | */ | ||
622 | CS_LOOKUP = 0, | ||
623 | |||
624 | /** | ||
625 | * The call is ringing. | ||
626 | */ | ||
627 | CS_RINGING, | ||
628 | |||
629 | /** | ||
630 | * The call is in an active conversation. | ||
631 | */ | ||
632 | CS_ACTIVE, | ||
633 | |||
634 | /** | ||
635 | * The call is in termination. | ||
636 | */ | ||
637 | CS_SHUTDOWN | ||
638 | }; | ||
639 | |||
640 | |||
641 | /** | ||
642 | * Handle for an outgoing call. | ||
643 | */ | ||
644 | struct GNUNET_CONVERSATION_Call | ||
645 | { | ||
646 | |||
647 | /** | ||
648 | * Our configuration. | ||
649 | */ | ||
650 | const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
651 | |||
652 | /** | ||
653 | * Handle to talk with CONVERSATION service. | ||
654 | */ | ||
655 | struct GNUNET_CLIENT_Connection *client; | ||
656 | |||
657 | /** | ||
658 | * Our caller identity. | ||
659 | */ | ||
660 | struct GNUNET_IDENTITY_Ego *caller_id; | ||
661 | |||
662 | /** | ||
663 | * Target callee as a GNS address/name. | ||
664 | */ | ||
665 | char *callee; | ||
666 | |||
667 | /** | ||
668 | * Our speaker. | ||
669 | */ | ||
670 | struct GNUNET_SPEAKER_Handle *speaker; | ||
671 | |||
672 | /** | ||
673 | * Our microphone. | ||
674 | */ | ||
675 | struct GNUNET_MICROPHONE_Handle *mic; | ||
676 | |||
677 | /** | ||
678 | * Function to call with events. | ||
679 | */ | ||
680 | GNUNET_CONVERSATION_EventHandler event_handler; | ||
681 | |||
682 | /** | ||
683 | * Closure for @e event_handler | ||
684 | */ | ||
685 | void *event_handler_cls; | ||
686 | |||
687 | /** | ||
688 | * Handle for transmitting to the CONVERSATION service. | ||
689 | */ | ||
690 | struct GNUNET_MQ_Handle *mq; | ||
691 | |||
692 | /** | ||
693 | * Connection to GNS (can be NULL). | ||
694 | */ | ||
695 | struct GNUNET_GNS_Handle *gns; | ||
696 | |||
697 | /** | ||
698 | * Active GNS lookup (or NULL). | ||
699 | */ | ||
700 | struct GNUNET_GNS_LookupRequest *gns_lookup; | ||
701 | |||
702 | /** | ||
703 | * Target phone record, only valid after the lookup is done. | ||
704 | */ | ||
705 | struct PhoneRecord phone_record; | ||
706 | |||
707 | /** | ||
708 | * State machine for the call. | ||
709 | */ | ||
710 | enum CallState state; | ||
711 | |||
712 | }; | ||
713 | |||
714 | |||
715 | /** | ||
716 | * The call got disconnected, reconnect to the service. | ||
717 | * | ||
718 | * @param call call to reconnect | ||
719 | */ | ||
720 | static void | ||
721 | reconnect_call (struct GNUNET_CONVERSATION_Call *call); | ||
722 | |||
723 | |||
724 | /** | ||
725 | * We received a `struct ClientPhoneBusyMessage` | ||
726 | * | ||
727 | * @param cls the `struct GNUNET_CONVERSATION_Call` | ||
728 | * @param msg the message | ||
729 | */ | ||
730 | static void | ||
731 | handle_call_busy (void *cls, | ||
732 | const struct GNUNET_MessageHeader *msg) | ||
733 | { | ||
734 | struct GNUNET_CONVERSATION_Call *call = cls; | ||
735 | |||
736 | switch (call->state) | ||
737 | { | ||
738 | case CS_LOOKUP: | ||
739 | GNUNET_break (0); | ||
740 | reconnect_call (call); | ||
741 | break; | ||
742 | case CS_RINGING: | ||
743 | call->event_handler (call->event_handler_cls, | ||
744 | GNUNET_CONVERSATION_EC_BUSY); | ||
745 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
746 | break; | ||
747 | case CS_ACTIVE: | ||
748 | GNUNET_break (0); | ||
749 | reconnect_call (call); | ||
750 | break; | ||
751 | case CS_SHUTDOWN: | ||
752 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
753 | break; | ||
754 | } | ||
755 | } | ||
756 | |||
757 | |||
758 | /** | ||
759 | * Process recorded audio data. | ||
760 | * | ||
761 | * @param cls closure with the `struct GNUNET_CONVERSATION_Call` | ||
762 | * @param data_size number of bytes in @a data | ||
763 | * @param data audio data to play | ||
764 | */ | ||
765 | static void | ||
766 | transmit_call_audio (void *cls, | ||
767 | size_t data_size, | ||
768 | const void *data) | ||
769 | { | ||
770 | struct GNUNET_CONVERSATION_Call *call = cls; | ||
771 | struct GNUNET_MQ_Envelope *e; | ||
772 | struct ClientAudioMessage *am; | ||
773 | |||
774 | GNUNET_assert (CS_ACTIVE == call->state); | ||
775 | e = GNUNET_MQ_msg_extra (am, | ||
776 | data_size, | ||
777 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO); | ||
778 | memcpy (&am[1], data, data_size); | ||
779 | GNUNET_MQ_send (call->mq, e); | ||
780 | } | ||
781 | |||
782 | |||
783 | /** | ||
784 | * We received a `struct ClientPhonePickedupMessage` | ||
785 | * | ||
786 | * @param cls the `struct GNUNET_CONVERSATION_Call` | ||
787 | * @param msg the message | ||
788 | */ | ||
789 | static void | ||
790 | handle_call_picked_up (void *cls, | ||
791 | const struct GNUNET_MessageHeader *msg) | ||
792 | { | ||
793 | struct GNUNET_CONVERSATION_Call *call = cls; | ||
794 | const struct ClientPhonePickedupMessage *am; | ||
795 | const char *metadata; | ||
796 | size_t size; | ||
797 | |||
798 | am = (const struct ClientPhonePickedupMessage *) msg; | ||
799 | size = ntohs (am->header.size) - sizeof (struct ClientPhonePickedupMessage); | ||
800 | metadata = (const char *) &am[1]; | ||
801 | if ( (0 == size) || | ||
802 | ('\0' != metadata[size - 1]) ) | ||
803 | metadata = NULL; | ||
804 | switch (call->state) | ||
805 | { | ||
806 | case CS_LOOKUP: | ||
807 | GNUNET_break (0); | ||
808 | reconnect_call (call); | ||
809 | break; | ||
810 | case CS_RINGING: | ||
811 | call->state = CS_ACTIVE; | ||
812 | call->event_handler (call->event_handler_cls, | ||
813 | GNUNET_CONVERSATION_EC_READY, | ||
814 | metadata); | ||
815 | call->speaker->enable_speaker (call->speaker->cls); | ||
816 | call->mic->enable_microphone (call->mic->cls, | ||
817 | &transmit_call_audio, | ||
818 | call); | ||
819 | break; | ||
820 | case CS_ACTIVE: | ||
821 | GNUNET_break (0); | ||
822 | reconnect_call (call); | ||
823 | break; | ||
824 | case CS_SHUTDOWN: | ||
825 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
826 | break; | ||
827 | } | ||
828 | } | ||
829 | |||
830 | |||
831 | /** | ||
832 | * We received a `struct ClientPhoneHangupMessage` | ||
833 | * | ||
834 | * @param cls the `struct GNUNET_CONVERSATION_Call` | ||
835 | * @param msg the message | ||
836 | */ | ||
837 | static void | ||
838 | handle_call_hangup (void *cls, | ||
839 | const struct GNUNET_MessageHeader *msg) | ||
840 | { | ||
841 | struct GNUNET_CONVERSATION_Call *call = cls; | ||
842 | const struct ClientPhoneHangupMessage *am; | ||
843 | const char *reason; | ||
844 | size_t size; | ||
845 | |||
846 | am = (const struct ClientPhoneHangupMessage *) msg; | ||
847 | size = ntohs (am->header.size) - sizeof (struct ClientPhoneHangupMessage); | ||
848 | reason = (const char *) &am[1]; | ||
849 | if ( (0 == size) || | ||
850 | ('\0' != reason[size - 1]) ) | ||
851 | reason = NULL; | ||
852 | switch (call->state) | ||
853 | { | ||
854 | case CS_LOOKUP: | ||
855 | GNUNET_break (0); | ||
856 | reconnect_call (call); | ||
857 | break; | ||
858 | case CS_RINGING: | ||
859 | call->event_handler (call->event_handler_cls, | ||
860 | GNUNET_CONVERSATION_EC_TERMINATED, | ||
861 | reason); | ||
862 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
863 | return; | ||
864 | case CS_ACTIVE: | ||
865 | call->event_handler (call->event_handler_cls, | ||
866 | GNUNET_CONVERSATION_EC_TERMINATED, | ||
867 | reason); | ||
868 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
869 | return; | ||
870 | case CS_SHUTDOWN: | ||
871 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
872 | break; | ||
873 | } | ||
874 | } | ||
875 | |||
876 | |||
877 | /** | ||
878 | * We received a `struct ClientAudioMessage` | ||
879 | * | ||
880 | * @param cls the `struct GNUNET_CONVERSATION_Call` | ||
881 | * @param msg the message | ||
882 | */ | ||
883 | static void | ||
884 | handle_call_audio_message (void *cls, | ||
885 | const struct GNUNET_MessageHeader *msg) | ||
886 | { | ||
887 | struct GNUNET_CONVERSATION_Call *call = cls; | ||
888 | const struct ClientAudioMessage *am; | ||
889 | |||
890 | am = (const struct ClientAudioMessage *) msg; | ||
891 | switch (call->state) | ||
892 | { | ||
893 | case CS_LOOKUP: | ||
894 | GNUNET_break (0); | ||
895 | reconnect_call (call); | ||
896 | break; | ||
897 | case CS_RINGING: | ||
898 | GNUNET_break (0); | ||
899 | reconnect_call (call); | ||
900 | break; | ||
901 | case CS_ACTIVE: | ||
902 | call->speaker->play (call->speaker->cls, | ||
903 | ntohs (msg->size) - sizeof (struct ClientAudioMessage), | ||
904 | &am[1]); | ||
905 | break; | ||
906 | case CS_SHUTDOWN: | ||
907 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
908 | break; | ||
909 | |||
910 | } | ||
911 | } | ||
912 | |||
913 | |||
914 | /** | ||
915 | * Iterator called on obtained result for a GNS lookup. | ||
916 | * | ||
917 | * @param cls closure with the `struct GNUNET_CONVERSATION_Call` | ||
918 | * @param rd_count number of records in @a rd | ||
919 | * @param rd the records in reply | ||
920 | */ | ||
921 | static void | ||
922 | handle_gns_response (void *cls, | ||
923 | uint32_t rd_count, | ||
924 | const struct GNUNET_NAMESTORE_RecordData *rd) | ||
925 | { | ||
926 | struct GNUNET_CONVERSATION_Call *call = cls; | ||
927 | uint32_t i; | ||
928 | struct GNUNET_MQ_Envelope *e; | ||
929 | struct ClientCallMessage *ccm; | ||
930 | |||
931 | for (i=0;i<rd_count;i++) | ||
932 | { | ||
933 | if (GNUNET_NAMESTORE_TYPE_PHONE == rd[i].record_type) | ||
934 | { | ||
935 | if (rd[i].data_size != sizeof (struct PhoneRecord)) | ||
936 | { | ||
937 | GNUNET_break_op (0); | ||
938 | continue; | ||
939 | } | ||
940 | memcpy (&call->phone_record, | ||
941 | rd[i].data, | ||
942 | rd[i].data_size); | ||
943 | e = GNUNET_MQ_msg (ccm, GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL); | ||
944 | ccm->line = call->phone_record.line; | ||
945 | ccm->target = call->phone_record.peer; | ||
946 | ccm->caller_id = *GNUNET_IDENTITY_ego_get_private_key (call->caller_id); | ||
947 | GNUNET_MQ_send (call->mq, e); | ||
948 | call->state = CS_RINGING; | ||
949 | call->event_handler (call->event_handler_cls, | ||
950 | GNUNET_CONVERSATION_EC_RINGING); | ||
951 | return; | ||
952 | } | ||
953 | } | ||
954 | /* not found */ | ||
955 | call->event_handler (call->event_handler_cls, | ||
956 | GNUNET_CONVERSATION_EC_GNS_FAIL); | ||
957 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
958 | } | ||
959 | |||
960 | |||
961 | /** | ||
962 | * We encountered an error talking with the conversation service. | ||
963 | * | ||
964 | * @param cls the `struct GNUNET_CONVERSATION_Call` | ||
965 | * @param error details about the error | ||
966 | */ | ||
967 | static void | ||
968 | call_error_handler (void *cls, | ||
969 | enum GNUNET_MQ_Error error) | ||
970 | { | ||
971 | struct GNUNET_CONVERSATION_Call *call = cls; | ||
972 | |||
973 | GNUNET_break (0); | ||
974 | reconnect_call (call); | ||
975 | } | ||
976 | |||
977 | |||
978 | /** | ||
979 | * The call got disconnected, reconnect to the service. | ||
980 | * | ||
981 | * @param call call to reconnect | ||
982 | */ | ||
983 | static void | ||
984 | reconnect_call (struct GNUNET_CONVERSATION_Call *call) | ||
985 | { | ||
986 | static struct GNUNET_MQ_MessageHandler handlers[] = | ||
987 | { | ||
988 | { &handle_call_busy, | ||
989 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_BUSY, | ||
990 | sizeof (struct ClientPhoneBusyMessage) }, | ||
991 | { &handle_call_picked_up, | ||
992 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP, | ||
993 | 0 }, | ||
994 | { &handle_call_hangup, | ||
995 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP, | ||
996 | 0 }, | ||
997 | { &handle_call_audio_message, | ||
998 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO, | ||
999 | 0 }, | ||
1000 | { NULL, 0, 0 } | ||
1001 | }; | ||
1002 | if (NULL != call->mq) | ||
1003 | { | ||
1004 | GNUNET_MQ_destroy (call->mq); | ||
1005 | call->mq = NULL; | ||
1006 | } | ||
1007 | if (NULL != call->client) | ||
1008 | { | ||
1009 | GNUNET_CLIENT_disconnect (call->client); | ||
1010 | call->client = NULL; | ||
1011 | } | ||
1012 | call->client = GNUNET_CLIENT_connect ("conversation", call->cfg); | ||
1013 | if (NULL == call->client) | ||
1014 | return; | ||
1015 | call->mq = GNUNET_MQ_queue_for_connection_client (call->client, | ||
1016 | handlers, | ||
1017 | &call_error_handler, | ||
1018 | call); | ||
1019 | } | ||
1020 | |||
1021 | |||
1022 | /** | ||
1023 | * Call the phone of another user. | ||
1024 | * | ||
1025 | * @param cfg configuration to use, specifies our phone service | ||
1026 | * @param caller_id identity of the caller | ||
1027 | * @param callee GNS name of the callee (used to locate the callee's record) | ||
1028 | * @param speaker speaker to use (will be used automatically immediately once the | ||
1029 | * #GNUNET_CONVERSATION_EC_READY event is generated); we will NOT generate | ||
1030 | * a ring tone on the speaker | ||
1031 | * @param mic microphone to use (will be used automatically immediately once the | ||
1032 | * #GNUNET_CONVERSATION_EC_READY event is generated) | ||
1033 | * @param event_handler how to notify the owner of the phone about events | ||
1034 | * @param event_handler_cls closure for @a event_handler | ||
1035 | */ | ||
1036 | struct GNUNET_CONVERSATION_Call * | ||
1037 | GNUNET_CONVERSATION_call_start (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
1038 | struct GNUNET_IDENTITY_Ego *caller_id, | ||
1039 | const char *callee, | ||
1040 | struct GNUNET_SPEAKER_Handle *speaker, | ||
1041 | struct GNUNET_MICROPHONE_Handle *mic, | ||
1042 | GNUNET_CONVERSATION_EventHandler event_handler, | ||
1043 | void *event_handler_cls) | ||
1044 | { | ||
1045 | struct GNUNET_CONVERSATION_Call *call; | ||
1046 | struct GNUNET_CRYPTO_EccPublicSignKey my_zone; | ||
1047 | |||
1048 | GNUNET_IDENTITY_ego_get_public_key (caller_id, | ||
1049 | &my_zone); | ||
1050 | call = GNUNET_new (struct GNUNET_CONVERSATION_Call); | ||
1051 | call->cfg = cfg; | ||
1052 | call->caller_id = caller_id; | ||
1053 | call->callee = GNUNET_strdup (callee); | ||
1054 | call->speaker = speaker; | ||
1055 | call->mic = mic; | ||
1056 | call->event_handler = event_handler; | ||
1057 | call->event_handler_cls = event_handler_cls; | ||
1058 | call->gns = GNUNET_GNS_connect (cfg); | ||
1059 | reconnect_call (call); | ||
1060 | |||
1061 | if ( (NULL == call->client) || | ||
1062 | (NULL == call->gns) ) | ||
1063 | { | ||
1064 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
1065 | return NULL; | ||
1066 | } | ||
1067 | call->gns_lookup = GNUNET_GNS_lookup (call->gns, callee, | ||
1068 | &my_zone, | ||
1069 | GNUNET_NAMESTORE_TYPE_PHONE, | ||
1070 | GNUNET_NO, | ||
1071 | NULL /* FIXME: add shortening support */, | ||
1072 | &handle_gns_response, call); | ||
1073 | GNUNET_assert (NULL != call->gns_lookup); | ||
1074 | return call; | ||
1075 | } | ||
1076 | |||
1077 | |||
1078 | /** | ||
1079 | * Terminate a call. The call may be ringing or ready at this time. | ||
1080 | * | ||
1081 | * @param call call to terminate | ||
1082 | * @param reason if the call was active (ringing or ready) this will be the | ||
1083 | * reason given to the other user for why we hung up | ||
1084 | */ | ||
1085 | void | ||
1086 | GNUNET_CONVERSATION_call_stop (struct GNUNET_CONVERSATION_Call *call, | ||
1087 | const char *reason) | ||
1088 | { | ||
1089 | if (NULL != reason) | ||
1090 | { | ||
1091 | // FIXME: transmit reason to service... (not implemented!) | ||
1092 | GNUNET_break (0); | ||
1093 | // return; | ||
1094 | } | ||
1095 | if (NULL != call->speaker) | ||
1096 | { | ||
1097 | if (CS_ACTIVE == call->state) | ||
1098 | call->speaker->disable_speaker (call->speaker->cls); | ||
1099 | call->speaker = NULL; | ||
1100 | } | ||
1101 | if (NULL != call->mic) | ||
1102 | { | ||
1103 | if (CS_ACTIVE == call->state) | ||
1104 | call->mic->disable_microphone (call->mic->cls); | ||
1105 | call->mic =NULL; | ||
1106 | } | ||
1107 | if (NULL != call->mq) | ||
1108 | { | ||
1109 | GNUNET_MQ_destroy (call->mq); | ||
1110 | call->mq = NULL; | ||
1111 | } | ||
1112 | if (NULL != call->client) | ||
1113 | { | ||
1114 | GNUNET_CLIENT_disconnect (call->client); | ||
1115 | call->client = NULL; | ||
1116 | } | ||
1117 | if (NULL != call->gns_lookup) | ||
1118 | { | ||
1119 | GNUNET_GNS_lookup_cancel (call->gns_lookup); | ||
1120 | call->gns_lookup = NULL; | ||
1121 | } | ||
1122 | if (NULL != call->gns) | ||
1123 | { | ||
1124 | GNUNET_GNS_disconnect (call->gns); | ||
1125 | call->gns = NULL; | ||
1126 | } | ||
1127 | |||
1128 | GNUNET_free (call); | ||
1129 | } | ||
1130 | |||
1131 | |||
1132 | /* end of conversation_api.c */ | ||
diff --git a/src/conversation/gnunet-conversation-new.c b/src/conversation/gnunet-conversation-new.c deleted file mode 100644 index fdf6155fd..000000000 --- a/src/conversation/gnunet-conversation-new.c +++ /dev/null | |||
@@ -1,855 +0,0 @@ | |||
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 | * @file conversation/gnunet-conversation.c | ||
22 | * @brief conversation implementation | ||
23 | * @author Simon Dieterle | ||
24 | * @author Andreas Fuchs | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_util_lib.h" | ||
28 | #include "gnunet_constants.h" | ||
29 | #include "gnunet_conversation_service.h" | ||
30 | |||
31 | |||
32 | /** | ||
33 | * Maximum length allowed for the command line input. | ||
34 | */ | ||
35 | #define MAX_MESSAGE_LENGTH 1024 | ||
36 | |||
37 | |||
38 | /** | ||
39 | * Possible states of the program. | ||
40 | */ | ||
41 | enum ConversationState | ||
42 | { | ||
43 | /** | ||
44 | * We're waiting for our own idenitty. | ||
45 | */ | ||
46 | CS_LOOKUP_EGO, | ||
47 | |||
48 | /** | ||
49 | * We're listening for calls | ||
50 | */ | ||
51 | CS_LISTEN, | ||
52 | |||
53 | /** | ||
54 | * Our phone is ringing. | ||
55 | */ | ||
56 | CS_RING, | ||
57 | |||
58 | /** | ||
59 | * We accepted an incoming phone call. | ||
60 | */ | ||
61 | CS_ACCEPTED, | ||
62 | |||
63 | /** | ||
64 | * We are looking up some other participant. | ||
65 | */ | ||
66 | CS_RESOLVING, | ||
67 | |||
68 | /** | ||
69 | * We are now ringing the other participant. | ||
70 | */ | ||
71 | CS_RINGING, | ||
72 | |||
73 | /** | ||
74 | * The other party accepted our call and we are now connected. | ||
75 | */ | ||
76 | CS_CONNECTED, | ||
77 | |||
78 | /** | ||
79 | * Internal error | ||
80 | */ | ||
81 | CS_ERROR | ||
82 | |||
83 | }; | ||
84 | |||
85 | |||
86 | /** | ||
87 | * Phone handle | ||
88 | */ | ||
89 | static struct GNUNET_CONVERSATION_Phone *phone; | ||
90 | |||
91 | /** | ||
92 | * Call handle | ||
93 | */ | ||
94 | static struct GNUNET_CONVERSATION_Call *call; | ||
95 | |||
96 | /** | ||
97 | * Desired phone line. | ||
98 | */ | ||
99 | static unsigned int line; | ||
100 | |||
101 | /** | ||
102 | * Task which handles the commands | ||
103 | */ | ||
104 | static GNUNET_SCHEDULER_TaskIdentifier handle_cmd_task; | ||
105 | |||
106 | /** | ||
107 | * Our speaker. | ||
108 | */ | ||
109 | static struct GNUNET_SPEAKER_Handle *speaker; | ||
110 | |||
111 | /** | ||
112 | * Our microphone. | ||
113 | */ | ||
114 | static struct GNUNET_MICROPHONE_Handle *mic; | ||
115 | |||
116 | /** | ||
117 | * Our configuration. | ||
118 | */ | ||
119 | static struct GNUNET_CONFIGURATION_Handle *cfg; | ||
120 | |||
121 | /** | ||
122 | * Our ego. | ||
123 | */ | ||
124 | static struct GNUNET_IDENTITY_Ego *caller_id; | ||
125 | |||
126 | /** | ||
127 | * Handle to identity service. | ||
128 | */ | ||
129 | static struct GNUNET_IDENTITY_Handle *id; | ||
130 | |||
131 | /** | ||
132 | * Name of our ego. | ||
133 | */ | ||
134 | static char *ego_name; | ||
135 | |||
136 | /** | ||
137 | * Name of conversation partner (if any). | ||
138 | */ | ||
139 | static char *peer_name; | ||
140 | |||
141 | /** | ||
142 | * File handle for stdin. | ||
143 | */ | ||
144 | static struct GNUNET_DISK_FileHandle *stdin_fh; | ||
145 | |||
146 | /** | ||
147 | * Our current state. | ||
148 | */ | ||
149 | static enum ConversationState state; | ||
150 | |||
151 | /** | ||
152 | * Be verbose. | ||
153 | */ | ||
154 | static int verbose; | ||
155 | |||
156 | |||
157 | /** | ||
158 | * Function called with an event emitted by a phone. | ||
159 | * | ||
160 | * @param cls closure | ||
161 | * @param code type of the event on the phone | ||
162 | * @param ... additional information, depends on @a code | ||
163 | */ | ||
164 | static void | ||
165 | phone_event_handler (void *cls, | ||
166 | enum GNUNET_CONVERSATION_EventCode code, | ||
167 | ...) | ||
168 | { | ||
169 | va_list va; | ||
170 | |||
171 | va_start (va, code); | ||
172 | switch (code) | ||
173 | { | ||
174 | case GNUNET_CONVERSATION_EC_RING: | ||
175 | GNUNET_break (CS_LISTEN == state); | ||
176 | GNUNET_free_non_null (peer_name); | ||
177 | peer_name = GNUNET_strdup (va_arg (va, const char *)); | ||
178 | FPRINTF (stdout, | ||
179 | _("Incoming call from `%s'.\nPlease /accept or /cancel the call.\n"), | ||
180 | peer_name); | ||
181 | state = CS_RING; | ||
182 | break; | ||
183 | case GNUNET_CONVERSATION_EC_RINGING: | ||
184 | GNUNET_break (0); | ||
185 | break; | ||
186 | case GNUNET_CONVERSATION_EC_READY: | ||
187 | GNUNET_break (0); | ||
188 | break; | ||
189 | case GNUNET_CONVERSATION_EC_GNS_FAIL: | ||
190 | GNUNET_break (0); | ||
191 | break; | ||
192 | case GNUNET_CONVERSATION_EC_BUSY: | ||
193 | GNUNET_break (0); | ||
194 | break; | ||
195 | case GNUNET_CONVERSATION_EC_TERMINATED: | ||
196 | GNUNET_break ( (CS_RING == state) || | ||
197 | (CS_ACCEPTED == state) ); | ||
198 | FPRINTF (stdout, | ||
199 | _("Call terminated: %s\n"), | ||
200 | va_arg (va, const char *)); | ||
201 | state = CS_LISTEN; | ||
202 | break; | ||
203 | } | ||
204 | va_end (va); | ||
205 | } | ||
206 | |||
207 | |||
208 | /** | ||
209 | * Start our phone. | ||
210 | */ | ||
211 | static void | ||
212 | start_phone () | ||
213 | { | ||
214 | if (NULL == caller_id) | ||
215 | { | ||
216 | FPRINTF (stderr, | ||
217 | _("Ego `%s' no longer available, phone is now down.\n"), | ||
218 | ego_name); | ||
219 | state = CS_LOOKUP_EGO; | ||
220 | return; | ||
221 | } | ||
222 | phone = GNUNET_CONVERSATION_phone_create (cfg, | ||
223 | caller_id, | ||
224 | &phone_event_handler, NULL); | ||
225 | /* FIXME: get record and print full GNS record info later here... */ | ||
226 | if (NULL == phone) | ||
227 | { | ||
228 | FPRINTF (stderr, | ||
229 | "%s", | ||
230 | _("Failed to setup phone (internal error)\n")); | ||
231 | state = CS_ERROR; | ||
232 | } | ||
233 | else | ||
234 | { | ||
235 | if (verbose) | ||
236 | FPRINTF (stdout, | ||
237 | _("Phone active on line %u\n"), | ||
238 | (unsigned int) line); | ||
239 | state = CS_LISTEN; | ||
240 | } | ||
241 | } | ||
242 | |||
243 | |||
244 | /** | ||
245 | * Function called with an event emitted by a phone. | ||
246 | * | ||
247 | * @param cls closure | ||
248 | * @param code type of the event on the phone | ||
249 | * @param ... additional information, depends on @a code | ||
250 | */ | ||
251 | static void | ||
252 | call_event_handler (void *cls, | ||
253 | enum GNUNET_CONVERSATION_EventCode code, | ||
254 | ...) | ||
255 | { | ||
256 | va_list va; | ||
257 | |||
258 | va_start (va, code); | ||
259 | switch (code) | ||
260 | { | ||
261 | case GNUNET_CONVERSATION_EC_RING: | ||
262 | GNUNET_break (0); | ||
263 | break; | ||
264 | case GNUNET_CONVERSATION_EC_RINGING: | ||
265 | GNUNET_break (CS_RESOLVING == state); | ||
266 | if (verbose) | ||
267 | FPRINTF (stdout, | ||
268 | "%s", | ||
269 | _("Resolved address. Now ringing other party.\n")); | ||
270 | state = CS_RINGING; | ||
271 | break; | ||
272 | case GNUNET_CONVERSATION_EC_READY: | ||
273 | GNUNET_break (CS_RINGING == state); | ||
274 | FPRINTF (stdout, | ||
275 | _("Connection established: %s\n"), | ||
276 | va_arg (va, const char *)); | ||
277 | state = CS_CONNECTED; | ||
278 | break; | ||
279 | case GNUNET_CONVERSATION_EC_GNS_FAIL: | ||
280 | GNUNET_break (CS_RESOLVING == state); | ||
281 | FPRINTF (stdout, | ||
282 | "%s", | ||
283 | _("Failed to resolve name\n")); | ||
284 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
285 | call = NULL; | ||
286 | start_phone (); | ||
287 | break; | ||
288 | case GNUNET_CONVERSATION_EC_BUSY: | ||
289 | GNUNET_break (CS_RINGING == state); | ||
290 | FPRINTF (stdout, | ||
291 | "%s", | ||
292 | _("Line busy\n")); | ||
293 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
294 | call = NULL; | ||
295 | start_phone (); | ||
296 | break; | ||
297 | case GNUNET_CONVERSATION_EC_TERMINATED: | ||
298 | GNUNET_break ( (CS_RINGING == state) || | ||
299 | (CS_CONNECTED == state) ); | ||
300 | FPRINTF (stdout, | ||
301 | _("Call terminated: %s\n"), | ||
302 | va_arg (va, const char *)); | ||
303 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
304 | call = NULL; | ||
305 | start_phone (); | ||
306 | break; | ||
307 | } | ||
308 | va_end (va); | ||
309 | } | ||
310 | |||
311 | |||
312 | /** | ||
313 | * Function declareation for executing a action | ||
314 | * | ||
315 | * @param arguments arguments given to the function | ||
316 | */ | ||
317 | typedef void (*ActionFunction) (const char *arguments); | ||
318 | |||
319 | |||
320 | /** | ||
321 | * Structure which defines a command | ||
322 | */ | ||
323 | struct VoipCommand | ||
324 | { | ||
325 | /** | ||
326 | * Command the user needs to enter. | ||
327 | */ | ||
328 | const char *command; | ||
329 | |||
330 | /** | ||
331 | * Function to call on command. | ||
332 | */ | ||
333 | ActionFunction Action; | ||
334 | |||
335 | /** | ||
336 | * Help text for the command. | ||
337 | */ | ||
338 | const char *helptext; | ||
339 | }; | ||
340 | |||
341 | |||
342 | /** | ||
343 | * Action function to print help for the command shell. | ||
344 | * | ||
345 | * @param arguments arguments given to the command | ||
346 | */ | ||
347 | static void | ||
348 | do_help (const char *args); | ||
349 | |||
350 | |||
351 | /** | ||
352 | * Terminate the client | ||
353 | * | ||
354 | * @param args arguments given to the command | ||
355 | */ | ||
356 | static void | ||
357 | do_quit (const char *args) | ||
358 | { | ||
359 | GNUNET_SCHEDULER_shutdown (); | ||
360 | } | ||
361 | |||
362 | |||
363 | /** | ||
364 | * Handler for unknown command. | ||
365 | * | ||
366 | * @param args arguments given to the command | ||
367 | */ | ||
368 | static void | ||
369 | do_unknown (const char *msg) | ||
370 | { | ||
371 | FPRINTF (stderr, | ||
372 | _("Unknown command `%s'\n"), | ||
373 | msg); | ||
374 | } | ||
375 | |||
376 | |||
377 | /** | ||
378 | * Initiating a new call | ||
379 | * | ||
380 | * @param args arguments given to the command | ||
381 | */ | ||
382 | static void | ||
383 | do_call (const char *arg) | ||
384 | { | ||
385 | if (NULL == caller_id) | ||
386 | { | ||
387 | FPRINTF (stderr, | ||
388 | _("Ego `%s' not available\n"), | ||
389 | ego_name); | ||
390 | return; | ||
391 | } | ||
392 | switch (state) | ||
393 | { | ||
394 | case CS_LOOKUP_EGO: | ||
395 | FPRINTF (stderr, | ||
396 | _("Ego `%s' not available\n"), | ||
397 | ego_name); | ||
398 | return; | ||
399 | case CS_LISTEN: | ||
400 | /* ok to call! */ | ||
401 | break; | ||
402 | case CS_RING: | ||
403 | FPRINTF (stdout, | ||
404 | _("Hanging up on incoming phone call from `%s' to call `%s'.\n"), | ||
405 | peer_name, | ||
406 | arg); | ||
407 | GNUNET_CONVERSATION_phone_hang_up (phone, NULL); | ||
408 | break; | ||
409 | case CS_ACCEPTED: | ||
410 | FPRINTF (stderr, | ||
411 | _("You are already in a conversation with `%s', refusing to call `%s'.\n"), | ||
412 | peer_name, | ||
413 | arg); | ||
414 | return; | ||
415 | case CS_RESOLVING: | ||
416 | case CS_RINGING: | ||
417 | FPRINTF (stderr, | ||
418 | _("Aborting call to `%s'\n"), | ||
419 | peer_name); | ||
420 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
421 | call = NULL; | ||
422 | break; | ||
423 | case CS_CONNECTED: | ||
424 | FPRINTF (stderr, | ||
425 | _("You are already in a conversation with `%s', refusing to call `%s'.\n"), | ||
426 | peer_name, | ||
427 | arg); | ||
428 | return; | ||
429 | case CS_ERROR: | ||
430 | /* ok to call */ | ||
431 | break; | ||
432 | } | ||
433 | GNUNET_assert (NULL == call); | ||
434 | if (NULL != phone) | ||
435 | { | ||
436 | GNUNET_CONVERSATION_phone_destroy (phone); | ||
437 | phone = NULL; | ||
438 | } | ||
439 | GNUNET_free_non_null (peer_name); | ||
440 | peer_name = GNUNET_strdup (arg); | ||
441 | call = GNUNET_CONVERSATION_call_start (cfg, | ||
442 | caller_id, | ||
443 | arg, | ||
444 | speaker, | ||
445 | mic, | ||
446 | &call_event_handler, NULL); | ||
447 | state = CS_RESOLVING; | ||
448 | } | ||
449 | |||
450 | |||
451 | /** | ||
452 | * Accepting an incoming call | ||
453 | * | ||
454 | * @param args arguments given to the command | ||
455 | */ | ||
456 | static void | ||
457 | do_accept (const char *args) | ||
458 | { | ||
459 | switch (state) | ||
460 | { | ||
461 | case CS_LOOKUP_EGO: | ||
462 | case CS_LISTEN: | ||
463 | case CS_ERROR: | ||
464 | FPRINTF (stderr, | ||
465 | _("There is no incoming call to be accepted!\n")); | ||
466 | return; | ||
467 | case CS_RING: | ||
468 | /* this is the expected state */ | ||
469 | break; | ||
470 | case CS_ACCEPTED: | ||
471 | FPRINTF (stderr, | ||
472 | _("You are already in a conversation with `%s'.\n"), | ||
473 | peer_name); | ||
474 | return; | ||
475 | case CS_RESOLVING: | ||
476 | case CS_RINGING: | ||
477 | FPRINTF (stderr, | ||
478 | _("You are trying to call `%s', cannot accept incoming calls right now.\n"), | ||
479 | peer_name); | ||
480 | return; | ||
481 | case CS_CONNECTED: | ||
482 | FPRINTF (stderr, | ||
483 | _("You are already in a conversation with `%s'.\n"), | ||
484 | peer_name); | ||
485 | return; | ||
486 | } | ||
487 | GNUNET_assert (NULL != phone); | ||
488 | GNUNET_CONVERSATION_phone_pick_up (phone, | ||
489 | args, | ||
490 | speaker, | ||
491 | mic); | ||
492 | state = CS_ACCEPTED; | ||
493 | } | ||
494 | |||
495 | |||
496 | /** | ||
497 | * Accepting an incoming call | ||
498 | * | ||
499 | * @param args arguments given to the command | ||
500 | */ | ||
501 | static void | ||
502 | do_status (const char *args) | ||
503 | { | ||
504 | switch (state) | ||
505 | { | ||
506 | case CS_LOOKUP_EGO: | ||
507 | FPRINTF (stdout, | ||
508 | _("We are currently trying to locate the private key for the ego `%s'.\n"), | ||
509 | ego_name); | ||
510 | break; | ||
511 | case CS_LISTEN: | ||
512 | FPRINTF (stdout, | ||
513 | _("We are listening for incoming calls for ego `%s' on line %u.\n"), | ||
514 | ego_name, | ||
515 | line); | ||
516 | break; | ||
517 | case CS_RING: | ||
518 | FPRINTF (stdout, | ||
519 | _("The phone is rining. `%s' is trying to call us.\n"), | ||
520 | peer_name); | ||
521 | break; | ||
522 | case CS_ACCEPTED: | ||
523 | case CS_CONNECTED: | ||
524 | FPRINTF (stdout, | ||
525 | _("You are having a conversation with `%s'.\n"), | ||
526 | peer_name); | ||
527 | break; | ||
528 | case CS_RESOLVING: | ||
529 | FPRINTF (stdout, | ||
530 | _("We are trying to find the network address to call `%s'.\n"), | ||
531 | peer_name); | ||
532 | break; | ||
533 | case CS_RINGING: | ||
534 | FPRINTF (stdout, | ||
535 | _("We are calling `%s', his phone should be ringing.\n"), | ||
536 | peer_name); | ||
537 | break; | ||
538 | case CS_ERROR: | ||
539 | FPRINTF (stdout, | ||
540 | _("We had an internal error setting up our phone line. You can still make calls.\n")); | ||
541 | break; | ||
542 | } | ||
543 | } | ||
544 | |||
545 | |||
546 | /** | ||
547 | * Rejecting a call | ||
548 | * | ||
549 | * @param args arguments given to the command | ||
550 | */ | ||
551 | static void | ||
552 | do_reject (const char *args) | ||
553 | { | ||
554 | switch (state) | ||
555 | { | ||
556 | case CS_LOOKUP_EGO: | ||
557 | case CS_LISTEN: | ||
558 | case CS_ERROR: | ||
559 | FPRINTF (stderr, | ||
560 | "%s", | ||
561 | _("There is no call that could be cancelled right now.\n")); | ||
562 | return; | ||
563 | case CS_RING: | ||
564 | case CS_ACCEPTED: | ||
565 | case CS_RESOLVING: | ||
566 | case CS_RINGING: | ||
567 | case CS_CONNECTED: | ||
568 | /* expected state, do rejection logic */ | ||
569 | break; | ||
570 | } | ||
571 | if (NULL == call) | ||
572 | { | ||
573 | GNUNET_assert (NULL != phone); | ||
574 | GNUNET_CONVERSATION_phone_hang_up (phone, | ||
575 | args); | ||
576 | state = CS_LISTEN; | ||
577 | } | ||
578 | else | ||
579 | { | ||
580 | GNUNET_CONVERSATION_call_stop (call, args); | ||
581 | call = NULL; | ||
582 | start_phone (); | ||
583 | } | ||
584 | } | ||
585 | |||
586 | |||
587 | /** | ||
588 | * List of supported commands. | ||
589 | */ | ||
590 | static struct VoipCommand commands[] = { | ||
591 | {"/call", &do_call, | ||
592 | gettext_noop ("Use `/call USER.gnu'")}, | ||
593 | {"/accept", &do_accept, | ||
594 | gettext_noop ("Use `/accept MESSAGE' to accept an incoming call")}, | ||
595 | {"/cancel", &do_reject, | ||
596 | gettext_noop ("Use `/cancel MESSAGE' to reject or terminate a call")}, | ||
597 | {"/status", &do_status, | ||
598 | gettext_noop ("Use `/status to print status information")}, | ||
599 | {"/quit", &do_quit, | ||
600 | gettext_noop ("Use `/quit' to terminate gnunet-conversation")}, | ||
601 | {"/help", &do_help, | ||
602 | gettext_noop ("Use `/help command' to get help for a specific command")}, | ||
603 | {"", &do_unknown, | ||
604 | NULL}, | ||
605 | {NULL, NULL, NULL}, | ||
606 | }; | ||
607 | |||
608 | |||
609 | /** | ||
610 | * Action function to print help for the command shell. | ||
611 | * | ||
612 | * @param arguments arguments given to the command | ||
613 | */ | ||
614 | static void | ||
615 | do_help (const char *args) | ||
616 | { | ||
617 | unsigned int i; | ||
618 | |||
619 | i = 0; | ||
620 | while ( (NULL != args) && | ||
621 | (0 != strlen (args)) && | ||
622 | (commands[i].Action != &do_help)) | ||
623 | { | ||
624 | if (0 == | ||
625 | strncasecmp (&args[1], &commands[i].command[1], strlen (args) - 1)) | ||
626 | { | ||
627 | FPRINTF (stdout, | ||
628 | "%s\n", | ||
629 | gettext (commands[i].helptext)); | ||
630 | return; | ||
631 | } | ||
632 | i++; | ||
633 | } | ||
634 | i = 0; | ||
635 | FPRINTF (stdout, | ||
636 | "%s", | ||
637 | "Available commands:\n"); | ||
638 | while (commands[i].Action != &do_help) | ||
639 | { | ||
640 | FPRINTF (stdout, | ||
641 | "%s\n", | ||
642 | gettext (commands[i].command)); | ||
643 | i++; | ||
644 | } | ||
645 | FPRINTF (stdout, | ||
646 | "%s", | ||
647 | "\n"); | ||
648 | FPRINTF (stdout, | ||
649 | "%s\n", | ||
650 | gettext (commands[i].helptext)); | ||
651 | } | ||
652 | |||
653 | |||
654 | /** | ||
655 | * Task run during shutdown. | ||
656 | * | ||
657 | * @param cls NULL | ||
658 | * @param tc unused | ||
659 | */ | ||
660 | static void | ||
661 | do_stop_task (void *cls, | ||
662 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
663 | { | ||
664 | if (NULL != call) | ||
665 | { | ||
666 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
667 | call = NULL; | ||
668 | } | ||
669 | if (NULL != phone) | ||
670 | { | ||
671 | GNUNET_CONVERSATION_phone_destroy (phone); | ||
672 | phone = NULL; | ||
673 | } | ||
674 | if (GNUNET_SCHEDULER_NO_TASK != handle_cmd_task) | ||
675 | { | ||
676 | GNUNET_SCHEDULER_cancel (handle_cmd_task); | ||
677 | handle_cmd_task = GNUNET_SCHEDULER_NO_TASK; | ||
678 | } | ||
679 | if (NULL != id) | ||
680 | { | ||
681 | GNUNET_IDENTITY_disconnect (id); | ||
682 | id = NULL; | ||
683 | } | ||
684 | GNUNET_SPEAKER_destroy (speaker); | ||
685 | speaker = NULL; | ||
686 | GNUNET_MICROPHONE_destroy (mic); | ||
687 | mic = NULL; | ||
688 | GNUNET_free (ego_name); | ||
689 | ego_name = NULL; | ||
690 | GNUNET_CONFIGURATION_destroy (cfg); | ||
691 | cfg = NULL; | ||
692 | GNUNET_free_non_null (peer_name); | ||
693 | state = CS_ERROR; | ||
694 | } | ||
695 | |||
696 | |||
697 | /** | ||
698 | * Task to handle commands from the terminal. | ||
699 | * | ||
700 | * @param cls NULL | ||
701 | * @param tc scheduler context | ||
702 | */ | ||
703 | static void | ||
704 | handle_command (void *cls, | ||
705 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
706 | { | ||
707 | char message[MAX_MESSAGE_LENGTH + 1]; | ||
708 | const char *ptr; | ||
709 | size_t i; | ||
710 | |||
711 | handle_cmd_task = | ||
712 | GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, | ||
713 | stdin_fh, | ||
714 | &handle_command, NULL); | ||
715 | /* read message from command line and handle it */ | ||
716 | memset (message, 0, MAX_MESSAGE_LENGTH + 1); | ||
717 | if (NULL == fgets (message, MAX_MESSAGE_LENGTH, stdin)) | ||
718 | return; | ||
719 | if (0 == strlen (message)) | ||
720 | return; | ||
721 | if (message[strlen (message) - 1] == '\n') | ||
722 | message[strlen (message) - 1] = '\0'; | ||
723 | if (0 == strlen (message)) | ||
724 | return; | ||
725 | i = 0; | ||
726 | while ((NULL != commands[i].command) && | ||
727 | (0 != strncasecmp (commands[i].command, message, | ||
728 | strlen (commands[i].command)))) | ||
729 | i++; | ||
730 | ptr = &message[strlen (commands[i].command)]; | ||
731 | while (isspace ((int) *ptr)) | ||
732 | ptr++; | ||
733 | commands[i].Action (ptr); | ||
734 | } | ||
735 | |||
736 | |||
737 | /** | ||
738 | * Function called by identity service with information about egos. | ||
739 | * | ||
740 | * @param cls NULL | ||
741 | * @param ego ego handle | ||
742 | * @param ctx unused | ||
743 | * @param name name of the ego | ||
744 | */ | ||
745 | static void | ||
746 | identity_cb (void *cls, | ||
747 | struct GNUNET_IDENTITY_Ego *ego, | ||
748 | void **ctx, | ||
749 | const char *name) | ||
750 | { | ||
751 | if (NULL == name) | ||
752 | return; | ||
753 | if (ego == caller_id) | ||
754 | { | ||
755 | if (verbose) | ||
756 | FPRINTF (stdout, | ||
757 | _("Name of our ego changed to `%s'\n"), | ||
758 | name); | ||
759 | GNUNET_free (ego_name); | ||
760 | ego_name = GNUNET_strdup (name); | ||
761 | return; | ||
762 | } | ||
763 | if (0 != strcmp (name, | ||
764 | ego_name)) | ||
765 | return; | ||
766 | if (NULL == ego) | ||
767 | { | ||
768 | if (verbose) | ||
769 | FPRINTF (stdout, | ||
770 | _("Our ego `%s' was deleted!\n"), | ||
771 | ego_name); | ||
772 | caller_id = NULL; | ||
773 | return; | ||
774 | } | ||
775 | caller_id = ego; | ||
776 | GNUNET_CONFIGURATION_set_value_number (cfg, | ||
777 | "CONVERSATION", | ||
778 | "LINE", | ||
779 | line); | ||
780 | start_phone (); | ||
781 | } | ||
782 | |||
783 | |||
784 | /** | ||
785 | * Main function that will be run by the scheduler. | ||
786 | * | ||
787 | * @param cls closure | ||
788 | * @param args remaining command-line arguments | ||
789 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
790 | * @param c configuration | ||
791 | */ | ||
792 | static void | ||
793 | run (void *cls, | ||
794 | char *const *args, | ||
795 | const char *cfgfile, | ||
796 | const struct GNUNET_CONFIGURATION_Handle *c) | ||
797 | { | ||
798 | cfg = GNUNET_CONFIGURATION_dup (c); | ||
799 | speaker = GNUNET_SPEAKER_create_from_hardware (cfg); | ||
800 | mic = GNUNET_MICROPHONE_create_from_hardware (cfg); | ||
801 | if (NULL == ego_name) | ||
802 | { | ||
803 | FPRINTF (stderr, | ||
804 | "%s", | ||
805 | _("You must specify the NAME of an ego to use\n")); | ||
806 | return; | ||
807 | } | ||
808 | id = GNUNET_IDENTITY_connect (cfg, | ||
809 | &identity_cb, | ||
810 | NULL); | ||
811 | handle_cmd_task = | ||
812 | GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_UI, | ||
813 | &handle_command, NULL); | ||
814 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_stop_task, | ||
815 | NULL); | ||
816 | } | ||
817 | |||
818 | |||
819 | /** | ||
820 | * The main function to conversation. | ||
821 | * | ||
822 | * @param argc number of arguments from the command line | ||
823 | * @param argv command line arguments | ||
824 | * @return 0 ok, 1 on error | ||
825 | */ | ||
826 | int | ||
827 | main (int argc, char *const *argv) | ||
828 | { | ||
829 | static const struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
830 | {'e', "ego", "NAME", | ||
831 | gettext_noop ("sets the NAME of the ego to use for the phone (and name resolution)"), | ||
832 | 1, &GNUNET_GETOPT_set_string, &ego_name}, | ||
833 | {'p', "phone", "LINE", | ||
834 | gettext_noop ("sets the LINE to use for the phone"), | ||
835 | 1, &GNUNET_GETOPT_set_uint, &line}, | ||
836 | GNUNET_GETOPT_OPTION_END | ||
837 | }; | ||
838 | int flags; | ||
839 | int ret; | ||
840 | |||
841 | flags = fcntl (0, F_GETFL, 0); | ||
842 | flags |= O_NONBLOCK; | ||
843 | fcntl (0, F_SETFL, flags); | ||
844 | stdin_fh = GNUNET_DISK_get_handle_from_int_fd (0); | ||
845 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
846 | return 2; | ||
847 | ret = GNUNET_PROGRAM_run (argc, argv, | ||
848 | "gnunet-conversation", | ||
849 | gettext_noop ("Enables having a conversation with other GNUnet users."), | ||
850 | options, &run, NULL); | ||
851 | GNUNET_free ((void *) argv); | ||
852 | return (GNUNET_OK == ret) ? 0 : 1; | ||
853 | } | ||
854 | |||
855 | /* end of gnunet-conversation.c */ | ||
diff --git a/src/conversation/gnunet-conversation.c b/src/conversation/gnunet-conversation.c index a4088984b..fdf6155fd 100644 --- a/src/conversation/gnunet-conversation.c +++ b/src/conversation/gnunet-conversation.c | |||
@@ -14,7 +14,7 @@ | |||
14 | 14 | ||
15 | You should have received a copy of the GNU General Public License | 15 | You should have received a copy of the GNU General Public License |
16 | along with GNUnet; see the file COPYING. If not, write to the | 16 | along with GNUnet; see the file COPYING. If not, write to the |
17 | Free Software Foundation, InGNUNET_SERVERc., 59 Temple Place - Suite 330, | 17 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
18 | Boston, MA 02111-1307, USA. | 18 | Boston, MA 02111-1307, USA. |
19 | */ | 19 | */ |
20 | /** | 20 | /** |
@@ -27,14 +27,76 @@ | |||
27 | #include "gnunet_util_lib.h" | 27 | #include "gnunet_util_lib.h" |
28 | #include "gnunet_constants.h" | 28 | #include "gnunet_constants.h" |
29 | #include "gnunet_conversation_service.h" | 29 | #include "gnunet_conversation_service.h" |
30 | #include <fcntl.h> | ||
31 | 30 | ||
32 | #define MAX_MESSAGE_LENGTH (32 * 1024) | ||
33 | 31 | ||
34 | /** | 32 | /** |
35 | * CONVERSATION handle | 33 | * Maximum length allowed for the command line input. |
36 | */ | 34 | */ |
37 | static struct GNUNET_CONVERSATION_Handle *conversation; | 35 | #define MAX_MESSAGE_LENGTH 1024 |
36 | |||
37 | |||
38 | /** | ||
39 | * Possible states of the program. | ||
40 | */ | ||
41 | enum ConversationState | ||
42 | { | ||
43 | /** | ||
44 | * We're waiting for our own idenitty. | ||
45 | */ | ||
46 | CS_LOOKUP_EGO, | ||
47 | |||
48 | /** | ||
49 | * We're listening for calls | ||
50 | */ | ||
51 | CS_LISTEN, | ||
52 | |||
53 | /** | ||
54 | * Our phone is ringing. | ||
55 | */ | ||
56 | CS_RING, | ||
57 | |||
58 | /** | ||
59 | * We accepted an incoming phone call. | ||
60 | */ | ||
61 | CS_ACCEPTED, | ||
62 | |||
63 | /** | ||
64 | * We are looking up some other participant. | ||
65 | */ | ||
66 | CS_RESOLVING, | ||
67 | |||
68 | /** | ||
69 | * We are now ringing the other participant. | ||
70 | */ | ||
71 | CS_RINGING, | ||
72 | |||
73 | /** | ||
74 | * The other party accepted our call and we are now connected. | ||
75 | */ | ||
76 | CS_CONNECTED, | ||
77 | |||
78 | /** | ||
79 | * Internal error | ||
80 | */ | ||
81 | CS_ERROR | ||
82 | |||
83 | }; | ||
84 | |||
85 | |||
86 | /** | ||
87 | * Phone handle | ||
88 | */ | ||
89 | static struct GNUNET_CONVERSATION_Phone *phone; | ||
90 | |||
91 | /** | ||
92 | * Call handle | ||
93 | */ | ||
94 | static struct GNUNET_CONVERSATION_Call *call; | ||
95 | |||
96 | /** | ||
97 | * Desired phone line. | ||
98 | */ | ||
99 | static unsigned int line; | ||
38 | 100 | ||
39 | /** | 101 | /** |
40 | * Task which handles the commands | 102 | * Task which handles the commands |
@@ -42,231 +104,483 @@ static struct GNUNET_CONVERSATION_Handle *conversation; | |||
42 | static GNUNET_SCHEDULER_TaskIdentifier handle_cmd_task; | 104 | static GNUNET_SCHEDULER_TaskIdentifier handle_cmd_task; |
43 | 105 | ||
44 | /** | 106 | /** |
45 | * Function declareation for executing a action | 107 | * Our speaker. |
46 | */ | 108 | */ |
47 | typedef int (*ActionFunction) (const char *argumetns, | 109 | static struct GNUNET_SPEAKER_Handle *speaker; |
48 | const void *xtra); | ||
49 | 110 | ||
50 | /** | 111 | /** |
51 | * Structure which defines a command | 112 | * Our microphone. |
52 | */ | 113 | */ |
53 | struct VoipCommand | 114 | static struct GNUNET_MICROPHONE_Handle *mic; |
54 | { | 115 | |
55 | const char *command; | 116 | /** |
56 | ActionFunction Action; | 117 | * Our configuration. |
57 | const char *helptext; | 118 | */ |
58 | }; | 119 | static struct GNUNET_CONFIGURATION_Handle *cfg; |
59 | 120 | ||
121 | /** | ||
122 | * Our ego. | ||
123 | */ | ||
124 | static struct GNUNET_IDENTITY_Ego *caller_id; | ||
125 | |||
126 | /** | ||
127 | * Handle to identity service. | ||
128 | */ | ||
129 | static struct GNUNET_IDENTITY_Handle *id; | ||
60 | 130 | ||
61 | static int | 131 | /** |
62 | do_help (const char *args, | 132 | * Name of our ego. |
63 | const void *xtra); | 133 | */ |
134 | static char *ego_name; | ||
64 | 135 | ||
136 | /** | ||
137 | * Name of conversation partner (if any). | ||
138 | */ | ||
139 | static char *peer_name; | ||
140 | |||
141 | /** | ||
142 | * File handle for stdin. | ||
143 | */ | ||
144 | static struct GNUNET_DISK_FileHandle *stdin_fh; | ||
145 | |||
146 | /** | ||
147 | * Our current state. | ||
148 | */ | ||
149 | static enum ConversationState state; | ||
65 | 150 | ||
66 | /** | 151 | /** |
67 | * Method called whenever a call is incoming | 152 | * Be verbose. |
153 | */ | ||
154 | static int verbose; | ||
155 | |||
156 | |||
157 | /** | ||
158 | * Function called with an event emitted by a phone. | ||
68 | * | 159 | * |
69 | * @param cls closure | 160 | * @param cls closure |
70 | * @param handle to the conversation session | 161 | * @param code type of the event on the phone |
71 | * @param caller peer that calls you | 162 | * @param ... additional information, depends on @a code |
72 | */ | 163 | */ |
73 | static void | 164 | static void |
74 | call_handler (void *cls, | 165 | phone_event_handler (void *cls, |
75 | struct GNUNET_CONVERSATION_Handle *handle, | 166 | enum GNUNET_CONVERSATION_EventCode code, |
76 | const struct GNUNET_PeerIdentity *caller) | 167 | ...) |
77 | { | 168 | { |
78 | FPRINTF (stdout, | 169 | va_list va; |
79 | _("Incoming call from peer: %s\n"), | 170 | |
80 | GNUNET_i2s_full (caller)); | 171 | va_start (va, code); |
172 | switch (code) | ||
173 | { | ||
174 | case GNUNET_CONVERSATION_EC_RING: | ||
175 | GNUNET_break (CS_LISTEN == state); | ||
176 | GNUNET_free_non_null (peer_name); | ||
177 | peer_name = GNUNET_strdup (va_arg (va, const char *)); | ||
178 | FPRINTF (stdout, | ||
179 | _("Incoming call from `%s'.\nPlease /accept or /cancel the call.\n"), | ||
180 | peer_name); | ||
181 | state = CS_RING; | ||
182 | break; | ||
183 | case GNUNET_CONVERSATION_EC_RINGING: | ||
184 | GNUNET_break (0); | ||
185 | break; | ||
186 | case GNUNET_CONVERSATION_EC_READY: | ||
187 | GNUNET_break (0); | ||
188 | break; | ||
189 | case GNUNET_CONVERSATION_EC_GNS_FAIL: | ||
190 | GNUNET_break (0); | ||
191 | break; | ||
192 | case GNUNET_CONVERSATION_EC_BUSY: | ||
193 | GNUNET_break (0); | ||
194 | break; | ||
195 | case GNUNET_CONVERSATION_EC_TERMINATED: | ||
196 | GNUNET_break ( (CS_RING == state) || | ||
197 | (CS_ACCEPTED == state) ); | ||
198 | FPRINTF (stdout, | ||
199 | _("Call terminated: %s\n"), | ||
200 | va_arg (va, const char *)); | ||
201 | state = CS_LISTEN; | ||
202 | break; | ||
203 | } | ||
204 | va_end (va); | ||
81 | } | 205 | } |
82 | 206 | ||
83 | 207 | ||
84 | /** | 208 | /** |
85 | * Method called whenever a call is rejected | 209 | * Start our phone. |
86 | * | ||
87 | * @param cls closure | ||
88 | * @param handle to the conversation session | ||
89 | * @param reason given reason why the call was rejected | ||
90 | * @param peer peer that rejected your call | ||
91 | */ | 210 | */ |
92 | static void | 211 | static void |
93 | reject_handler (void *cls, | 212 | start_phone () |
94 | struct GNUNET_CONVERSATION_Handle *handle, | ||
95 | enum GNUNET_CONVERSATION_RejectReason reason, | ||
96 | const struct GNUNET_PeerIdentity *peer) | ||
97 | { | 213 | { |
98 | FPRINTF (stdout, | 214 | if (NULL == caller_id) |
99 | _("Peer %s rejected your call. Reason: %d\n"), | 215 | { |
100 | GNUNET_i2s_full (peer), reason); | 216 | FPRINTF (stderr, |
217 | _("Ego `%s' no longer available, phone is now down.\n"), | ||
218 | ego_name); | ||
219 | state = CS_LOOKUP_EGO; | ||
220 | return; | ||
221 | } | ||
222 | phone = GNUNET_CONVERSATION_phone_create (cfg, | ||
223 | caller_id, | ||
224 | &phone_event_handler, NULL); | ||
225 | /* FIXME: get record and print full GNS record info later here... */ | ||
226 | if (NULL == phone) | ||
227 | { | ||
228 | FPRINTF (stderr, | ||
229 | "%s", | ||
230 | _("Failed to setup phone (internal error)\n")); | ||
231 | state = CS_ERROR; | ||
232 | } | ||
233 | else | ||
234 | { | ||
235 | if (verbose) | ||
236 | FPRINTF (stdout, | ||
237 | _("Phone active on line %u\n"), | ||
238 | (unsigned int) line); | ||
239 | state = CS_LISTEN; | ||
240 | } | ||
101 | } | 241 | } |
102 | 242 | ||
103 | 243 | ||
104 | /** | 244 | /** |
105 | * Method called whenever a notification is there | 245 | * Function called with an event emitted by a phone. |
106 | * | 246 | * |
107 | * @param cls closure | 247 | * @param cls closure |
108 | * @param handle to the conversation session | 248 | * @param code type of the event on the phone |
109 | * @param type the type of the notification | 249 | * @param ... additional information, depends on @a code |
110 | * @param peer peer that the notification is about | ||
111 | */ | 250 | */ |
112 | static void | 251 | static void |
113 | notification_handler (void *cls, | 252 | call_event_handler (void *cls, |
114 | struct GNUNET_CONVERSATION_Handle *handle, | 253 | enum GNUNET_CONVERSATION_EventCode code, |
115 | enum GNUNET_CONVERSATION_NotificationType type, | 254 | ...) |
116 | const struct GNUNET_PeerIdentity *peer) | ||
117 | { | 255 | { |
118 | switch (type) | 256 | va_list va; |
257 | |||
258 | va_start (va, code); | ||
259 | switch (code) | ||
119 | { | 260 | { |
120 | case GNUNET_CONVERSATION_NT_SERVICE_BLOCKED: | 261 | case GNUNET_CONVERSATION_EC_RING: |
262 | GNUNET_break (0); | ||
263 | break; | ||
264 | case GNUNET_CONVERSATION_EC_RINGING: | ||
265 | GNUNET_break (CS_RESOLVING == state); | ||
266 | if (verbose) | ||
267 | FPRINTF (stdout, | ||
268 | "%s", | ||
269 | _("Resolved address. Now ringing other party.\n")); | ||
270 | state = CS_RINGING; | ||
271 | break; | ||
272 | case GNUNET_CONVERSATION_EC_READY: | ||
273 | GNUNET_break (CS_RINGING == state); | ||
121 | FPRINTF (stdout, | 274 | FPRINTF (stdout, |
122 | _("The service is already in use. Try again later.")); | 275 | _("Connection established: %s\n"), |
123 | break; | 276 | va_arg (va, const char *)); |
124 | case GNUNET_CONVERSATION_NT_NO_PEER: | 277 | state = CS_CONNECTED; |
125 | FPRINTF (stdout, | 278 | break; |
126 | _("The Peer you were calling is no correct peer.\n")); | 279 | case GNUNET_CONVERSATION_EC_GNS_FAIL: |
127 | break; | 280 | GNUNET_break (CS_RESOLVING == state); |
128 | case GNUNET_CONVERSATION_NT_NO_ANSWER: | ||
129 | FPRINTF (stdout, | ||
130 | _("Peer %s did not answer your call.\n"), | ||
131 | GNUNET_i2s_full (peer)); | ||
132 | break; | ||
133 | case GNUNET_CONVERSATION_NT_AVAILABLE_AGAIN: | ||
134 | FPRINTF (stdout, | 281 | FPRINTF (stdout, |
135 | _("Peer %s is now available.\n"), | 282 | "%s", |
136 | GNUNET_i2s_full (peer)); | 283 | _("Failed to resolve name\n")); |
137 | break; | 284 | GNUNET_CONVERSATION_call_stop (call, NULL); |
138 | case GNUNET_CONVERSATION_NT_CALL_ACCEPTED: | 285 | call = NULL; |
139 | FPRINTF (stdout, | 286 | start_phone (); |
140 | _("Peer %s has accepted your call.\n"), | 287 | break; |
141 | GNUNET_i2s_full (peer)); | 288 | case GNUNET_CONVERSATION_EC_BUSY: |
142 | break; | 289 | GNUNET_break (CS_RINGING == state); |
143 | case GNUNET_CONVERSATION_NT_CALL_TERMINATED: | ||
144 | FPRINTF (stdout, | 290 | FPRINTF (stdout, |
145 | _("Peer %s has terminated the call.\n"), | 291 | "%s", |
146 | GNUNET_i2s_full (peer)); | 292 | _("Line busy\n")); |
293 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
294 | call = NULL; | ||
295 | start_phone (); | ||
147 | break; | 296 | break; |
148 | default: | 297 | case GNUNET_CONVERSATION_EC_TERMINATED: |
149 | GNUNET_break (0); | 298 | GNUNET_break ( (CS_RINGING == state) || |
150 | } | 299 | (CS_CONNECTED == state) ); |
300 | FPRINTF (stdout, | ||
301 | _("Call terminated: %s\n"), | ||
302 | va_arg (va, const char *)); | ||
303 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
304 | call = NULL; | ||
305 | start_phone (); | ||
306 | break; | ||
307 | } | ||
308 | va_end (va); | ||
151 | } | 309 | } |
152 | 310 | ||
153 | 311 | ||
154 | /** | 312 | /** |
155 | * Method called whenever a notification for missed calls is there | 313 | * Function declareation for executing a action |
156 | * | 314 | * |
157 | * @param cls closure | 315 | * @param arguments arguments given to the function |
158 | * @param handle to the conversation session | ||
159 | * @param missed_calls a list of missed calls | ||
160 | */ | 316 | */ |
161 | static void | 317 | typedef void (*ActionFunction) (const char *arguments); |
162 | missed_call_handler (void *cls, | ||
163 | struct GNUNET_CONVERSATION_Handle *handle, | ||
164 | struct GNUNET_CONVERSATION_MissedCallNotification *missed_calls) | ||
165 | { | ||
166 | FPRINTF (stdout, | ||
167 | _("You have missed calls.\n")); | ||
168 | } | ||
169 | 318 | ||
170 | 319 | ||
171 | /** | 320 | /** |
172 | * Terminating the client | 321 | * Structure which defines a command |
173 | */ | 322 | */ |
174 | static int | 323 | struct VoipCommand |
175 | do_quit (const char *args, | ||
176 | const void *xtra) | ||
177 | { | 324 | { |
178 | return GNUNET_SYSERR; | 325 | /** |
179 | } | 326 | * Command the user needs to enter. |
327 | */ | ||
328 | const char *command; | ||
329 | |||
330 | /** | ||
331 | * Function to call on command. | ||
332 | */ | ||
333 | ActionFunction Action; | ||
334 | |||
335 | /** | ||
336 | * Help text for the command. | ||
337 | */ | ||
338 | const char *helptext; | ||
339 | }; | ||
340 | |||
341 | |||
342 | /** | ||
343 | * Action function to print help for the command shell. | ||
344 | * | ||
345 | * @param arguments arguments given to the command | ||
346 | */ | ||
347 | static void | ||
348 | do_help (const char *args); | ||
180 | 349 | ||
181 | 350 | ||
182 | /** | 351 | /** |
352 | * Terminate the client | ||
183 | * | 353 | * |
354 | * @param args arguments given to the command | ||
184 | */ | 355 | */ |
185 | static int | 356 | static void |
186 | do_unknown (const char *msg, | 357 | do_quit (const char *args) |
187 | const void *xtra) | ||
188 | { | 358 | { |
189 | FPRINTF (stderr, | 359 | GNUNET_SCHEDULER_shutdown (); |
190 | _("Unknown command `%s'\n"), | ||
191 | msg); | ||
192 | return GNUNET_OK; | ||
193 | } | 360 | } |
194 | 361 | ||
195 | 362 | ||
196 | /** | 363 | /** |
197 | * Initiating a new call | 364 | * Handler for unknown command. |
365 | * | ||
366 | * @param args arguments given to the command | ||
198 | */ | 367 | */ |
199 | static int | 368 | static void |
200 | do_call (const char *arg, | 369 | do_unknown (const char *msg) |
201 | const void *xtra) | ||
202 | { | 370 | { |
203 | FPRINTF (stdout, | 371 | FPRINTF (stderr, |
204 | _("Initiating call to: %s\n"), | 372 | _("Unknown command `%s'\n"), |
205 | arg); | 373 | msg); |
206 | GNUNET_CONVERSATION_call (conversation, | ||
207 | arg, | ||
208 | GNUNET_YES); | ||
209 | return GNUNET_OK; | ||
210 | } | 374 | } |
211 | 375 | ||
212 | 376 | ||
213 | /** | 377 | /** |
214 | * Initiating a new call | 378 | * Initiating a new call |
379 | * | ||
380 | * @param args arguments given to the command | ||
215 | */ | 381 | */ |
216 | static int | 382 | static void |
217 | do_call_peer (const char *arg, | 383 | do_call (const char *arg) |
218 | const void *xtra) | ||
219 | { | 384 | { |
220 | FPRINTF (stdout, | 385 | if (NULL == caller_id) |
221 | _("Initiating call to: %s\n"), | 386 | { |
222 | arg); | 387 | FPRINTF (stderr, |
223 | GNUNET_CONVERSATION_call (conversation, | 388 | _("Ego `%s' not available\n"), |
224 | arg, | 389 | ego_name); |
225 | GNUNET_NO); | 390 | return; |
226 | return GNUNET_OK; | 391 | } |
392 | switch (state) | ||
393 | { | ||
394 | case CS_LOOKUP_EGO: | ||
395 | FPRINTF (stderr, | ||
396 | _("Ego `%s' not available\n"), | ||
397 | ego_name); | ||
398 | return; | ||
399 | case CS_LISTEN: | ||
400 | /* ok to call! */ | ||
401 | break; | ||
402 | case CS_RING: | ||
403 | FPRINTF (stdout, | ||
404 | _("Hanging up on incoming phone call from `%s' to call `%s'.\n"), | ||
405 | peer_name, | ||
406 | arg); | ||
407 | GNUNET_CONVERSATION_phone_hang_up (phone, NULL); | ||
408 | break; | ||
409 | case CS_ACCEPTED: | ||
410 | FPRINTF (stderr, | ||
411 | _("You are already in a conversation with `%s', refusing to call `%s'.\n"), | ||
412 | peer_name, | ||
413 | arg); | ||
414 | return; | ||
415 | case CS_RESOLVING: | ||
416 | case CS_RINGING: | ||
417 | FPRINTF (stderr, | ||
418 | _("Aborting call to `%s'\n"), | ||
419 | peer_name); | ||
420 | GNUNET_CONVERSATION_call_stop (call, NULL); | ||
421 | call = NULL; | ||
422 | break; | ||
423 | case CS_CONNECTED: | ||
424 | FPRINTF (stderr, | ||
425 | _("You are already in a conversation with `%s', refusing to call `%s'.\n"), | ||
426 | peer_name, | ||
427 | arg); | ||
428 | return; | ||
429 | case CS_ERROR: | ||
430 | /* ok to call */ | ||
431 | break; | ||
432 | } | ||
433 | GNUNET_assert (NULL == call); | ||
434 | if (NULL != phone) | ||
435 | { | ||
436 | GNUNET_CONVERSATION_phone_destroy (phone); | ||
437 | phone = NULL; | ||
438 | } | ||
439 | GNUNET_free_non_null (peer_name); | ||
440 | peer_name = GNUNET_strdup (arg); | ||
441 | call = GNUNET_CONVERSATION_call_start (cfg, | ||
442 | caller_id, | ||
443 | arg, | ||
444 | speaker, | ||
445 | mic, | ||
446 | &call_event_handler, NULL); | ||
447 | state = CS_RESOLVING; | ||
227 | } | 448 | } |
228 | 449 | ||
229 | 450 | ||
230 | /** | 451 | /** |
231 | * Accepting an incoming call | 452 | * Accepting an incoming call |
453 | * | ||
454 | * @param args arguments given to the command | ||
232 | */ | 455 | */ |
233 | static int | 456 | static void |
234 | do_accept (const char *args, | 457 | do_accept (const char *args) |
235 | const void *xtra) | ||
236 | { | 458 | { |
237 | FPRINTF (stdout, | 459 | switch (state) |
238 | _("Accepting the call\n")); | 460 | { |
239 | GNUNET_CONVERSATION_accept (conversation); | 461 | case CS_LOOKUP_EGO: |
240 | 462 | case CS_LISTEN: | |
241 | return GNUNET_OK; | 463 | case CS_ERROR: |
464 | FPRINTF (stderr, | ||
465 | _("There is no incoming call to be accepted!\n")); | ||
466 | return; | ||
467 | case CS_RING: | ||
468 | /* this is the expected state */ | ||
469 | break; | ||
470 | case CS_ACCEPTED: | ||
471 | FPRINTF (stderr, | ||
472 | _("You are already in a conversation with `%s'.\n"), | ||
473 | peer_name); | ||
474 | return; | ||
475 | case CS_RESOLVING: | ||
476 | case CS_RINGING: | ||
477 | FPRINTF (stderr, | ||
478 | _("You are trying to call `%s', cannot accept incoming calls right now.\n"), | ||
479 | peer_name); | ||
480 | return; | ||
481 | case CS_CONNECTED: | ||
482 | FPRINTF (stderr, | ||
483 | _("You are already in a conversation with `%s'.\n"), | ||
484 | peer_name); | ||
485 | return; | ||
486 | } | ||
487 | GNUNET_assert (NULL != phone); | ||
488 | GNUNET_CONVERSATION_phone_pick_up (phone, | ||
489 | args, | ||
490 | speaker, | ||
491 | mic); | ||
492 | state = CS_ACCEPTED; | ||
242 | } | 493 | } |
243 | 494 | ||
244 | 495 | ||
245 | /** | 496 | /** |
246 | * Rejecting a call | 497 | * Accepting an incoming call |
498 | * | ||
499 | * @param args arguments given to the command | ||
247 | */ | 500 | */ |
248 | static int | 501 | static void |
249 | do_reject (const char *args, | 502 | do_status (const char *args) |
250 | const void *xtra) | ||
251 | { | 503 | { |
252 | FPRINTF (stdout, | 504 | switch (state) |
253 | _("Rejecting the call\n")); | 505 | { |
254 | GNUNET_CONVERSATION_reject (conversation); | 506 | case CS_LOOKUP_EGO: |
255 | return GNUNET_OK; | 507 | FPRINTF (stdout, |
508 | _("We are currently trying to locate the private key for the ego `%s'.\n"), | ||
509 | ego_name); | ||
510 | break; | ||
511 | case CS_LISTEN: | ||
512 | FPRINTF (stdout, | ||
513 | _("We are listening for incoming calls for ego `%s' on line %u.\n"), | ||
514 | ego_name, | ||
515 | line); | ||
516 | break; | ||
517 | case CS_RING: | ||
518 | FPRINTF (stdout, | ||
519 | _("The phone is rining. `%s' is trying to call us.\n"), | ||
520 | peer_name); | ||
521 | break; | ||
522 | case CS_ACCEPTED: | ||
523 | case CS_CONNECTED: | ||
524 | FPRINTF (stdout, | ||
525 | _("You are having a conversation with `%s'.\n"), | ||
526 | peer_name); | ||
527 | break; | ||
528 | case CS_RESOLVING: | ||
529 | FPRINTF (stdout, | ||
530 | _("We are trying to find the network address to call `%s'.\n"), | ||
531 | peer_name); | ||
532 | break; | ||
533 | case CS_RINGING: | ||
534 | FPRINTF (stdout, | ||
535 | _("We are calling `%s', his phone should be ringing.\n"), | ||
536 | peer_name); | ||
537 | break; | ||
538 | case CS_ERROR: | ||
539 | FPRINTF (stdout, | ||
540 | _("We had an internal error setting up our phone line. You can still make calls.\n")); | ||
541 | break; | ||
542 | } | ||
256 | } | 543 | } |
257 | 544 | ||
258 | 545 | ||
259 | /** | 546 | /** |
260 | * Terminating a call | 547 | * Rejecting a call |
548 | * | ||
549 | * @param args arguments given to the command | ||
261 | */ | 550 | */ |
262 | static int | 551 | static void |
263 | do_hang_up (const char *args, | 552 | do_reject (const char *args) |
264 | const void *xtra) | ||
265 | { | 553 | { |
266 | FPRINTF (stdout, | 554 | switch (state) |
267 | _("Terminating the call\n")); | 555 | { |
268 | GNUNET_CONVERSATION_hangup (conversation); | 556 | case CS_LOOKUP_EGO: |
269 | return GNUNET_OK; | 557 | case CS_LISTEN: |
558 | case CS_ERROR: | ||
559 | FPRINTF (stderr, | ||
560 | "%s", | ||
561 | _("There is no call that could be cancelled right now.\n")); | ||
562 | return; | ||
563 | case CS_RING: | ||
564 | case CS_ACCEPTED: | ||
565 | case CS_RESOLVING: | ||
566 | case CS_RINGING: | ||
567 | case CS_CONNECTED: | ||
568 | /* expected state, do rejection logic */ | ||
569 | break; | ||
570 | } | ||
571 | if (NULL == call) | ||
572 | { | ||
573 | GNUNET_assert (NULL != phone); | ||
574 | GNUNET_CONVERSATION_phone_hang_up (phone, | ||
575 | args); | ||
576 | state = CS_LISTEN; | ||
577 | } | ||
578 | else | ||
579 | { | ||
580 | GNUNET_CONVERSATION_call_stop (call, args); | ||
581 | call = NULL; | ||
582 | start_phone (); | ||
583 | } | ||
270 | } | 584 | } |
271 | 585 | ||
272 | 586 | ||
@@ -274,36 +588,38 @@ do_hang_up (const char *args, | |||
274 | * List of supported commands. | 588 | * List of supported commands. |
275 | */ | 589 | */ |
276 | static struct VoipCommand commands[] = { | 590 | static struct VoipCommand commands[] = { |
277 | {"/call ", &do_call, gettext_noop ("Use `/call gns_name'")}, | 591 | {"/call", &do_call, |
278 | {"/callpeer ", &do_call_peer, | 592 | gettext_noop ("Use `/call USER.gnu'")}, |
279 | gettext_noop ("Use `/call private_key' to call a person")}, | ||
280 | {"/accept", &do_accept, | 593 | {"/accept", &do_accept, |
281 | gettext_noop ("Use `/accept' to accept an incoming call")}, | 594 | gettext_noop ("Use `/accept MESSAGE' to accept an incoming call")}, |
282 | {"/terminate", &do_hang_up, | 595 | {"/cancel", &do_reject, |
283 | gettext_noop ("Use `/terminate' to end a call")}, | 596 | gettext_noop ("Use `/cancel MESSAGE' to reject or terminate a call")}, |
284 | {"/reject", &do_reject, | 597 | {"/status", &do_status, |
285 | gettext_noop ("Use `/rejet' to reject an incoming call")}, | 598 | gettext_noop ("Use `/status to print status information")}, |
286 | {"/quit", &do_quit, gettext_noop ("Use `/quit' to terminate gnunet-conversation")}, | 599 | {"/quit", &do_quit, |
600 | gettext_noop ("Use `/quit' to terminate gnunet-conversation")}, | ||
287 | {"/help", &do_help, | 601 | {"/help", &do_help, |
288 | gettext_noop ("Use `/help command' to get help for a specific command")}, | 602 | gettext_noop ("Use `/help command' to get help for a specific command")}, |
289 | {"/", &do_unknown, NULL}, | 603 | {"", &do_unknown, |
290 | {"", &do_unknown, NULL}, | 604 | NULL}, |
291 | {NULL, NULL, NULL}, | 605 | {NULL, NULL, NULL}, |
292 | }; | 606 | }; |
293 | 607 | ||
294 | 608 | ||
295 | /** | 609 | /** |
610 | * Action function to print help for the command shell. | ||
296 | * | 611 | * |
612 | * @param arguments arguments given to the command | ||
297 | */ | 613 | */ |
298 | static int | 614 | static void |
299 | do_help (const char *args, | 615 | do_help (const char *args) |
300 | const void *xtra) | ||
301 | { | 616 | { |
302 | int i; | 617 | unsigned int i; |
303 | 618 | ||
304 | i = 0; | 619 | i = 0; |
305 | while ((NULL != args) && (0 != strlen (args)) && | 620 | while ( (NULL != args) && |
306 | (commands[i].Action != &do_help)) | 621 | (0 != strlen (args)) && |
622 | (commands[i].Action != &do_help)) | ||
307 | { | 623 | { |
308 | if (0 == | 624 | if (0 == |
309 | strncasecmp (&args[1], &commands[i].command[1], strlen (args) - 1)) | 625 | strncasecmp (&args[1], &commands[i].command[1], strlen (args) - 1)) |
@@ -311,18 +627,18 @@ do_help (const char *args, | |||
311 | FPRINTF (stdout, | 627 | FPRINTF (stdout, |
312 | "%s\n", | 628 | "%s\n", |
313 | gettext (commands[i].helptext)); | 629 | gettext (commands[i].helptext)); |
314 | return GNUNET_OK; | 630 | return; |
315 | } | 631 | } |
316 | i++; | 632 | i++; |
317 | } | 633 | } |
318 | i = 0; | 634 | i = 0; |
319 | FPRINTF (stdout, | 635 | FPRINTF (stdout, |
320 | "%s", | 636 | "%s", |
321 | "Available commands:"); | 637 | "Available commands:\n"); |
322 | while (commands[i].Action != &do_help) | 638 | while (commands[i].Action != &do_help) |
323 | { | 639 | { |
324 | FPRINTF (stdout, | 640 | FPRINTF (stdout, |
325 | " %s", | 641 | "%s\n", |
326 | gettext (commands[i].command)); | 642 | gettext (commands[i].command)); |
327 | i++; | 643 | i++; |
328 | } | 644 | } |
@@ -332,73 +648,136 @@ do_help (const char *args, | |||
332 | FPRINTF (stdout, | 648 | FPRINTF (stdout, |
333 | "%s\n", | 649 | "%s\n", |
334 | gettext (commands[i].helptext)); | 650 | gettext (commands[i].helptext)); |
335 | return GNUNET_OK; | ||
336 | } | 651 | } |
337 | 652 | ||
338 | 653 | ||
339 | /** | 654 | /** |
655 | * Task run during shutdown. | ||
340 | * | 656 | * |
657 | * @param cls NULL | ||
658 | * @param tc unused | ||
341 | */ | 659 | */ |
342 | static void | 660 | static void |
343 | do_stop_task (void *cls, | 661 | do_stop_task (void *cls, |
344 | const struct GNUNET_SCHEDULER_TaskContext *tc) | 662 | const struct GNUNET_SCHEDULER_TaskContext *tc) |
345 | { | 663 | { |
346 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | 664 | if (NULL != call) |
347 | "Running shutdown task\n"); | 665 | { |
348 | GNUNET_CONVERSATION_disconnect (conversation); | 666 | GNUNET_CONVERSATION_call_stop (call, NULL); |
349 | 667 | call = NULL; | |
350 | if (handle_cmd_task != GNUNET_SCHEDULER_NO_TASK) | 668 | } |
669 | if (NULL != phone) | ||
670 | { | ||
671 | GNUNET_CONVERSATION_phone_destroy (phone); | ||
672 | phone = NULL; | ||
673 | } | ||
674 | if (GNUNET_SCHEDULER_NO_TASK != handle_cmd_task) | ||
351 | { | 675 | { |
352 | GNUNET_SCHEDULER_cancel (handle_cmd_task); | 676 | GNUNET_SCHEDULER_cancel (handle_cmd_task); |
353 | handle_cmd_task = GNUNET_SCHEDULER_NO_TASK; | 677 | handle_cmd_task = GNUNET_SCHEDULER_NO_TASK; |
354 | } | 678 | } |
355 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | 679 | if (NULL != id) |
356 | "Running shutdown task finished\n"); | 680 | { |
681 | GNUNET_IDENTITY_disconnect (id); | ||
682 | id = NULL; | ||
683 | } | ||
684 | GNUNET_SPEAKER_destroy (speaker); | ||
685 | speaker = NULL; | ||
686 | GNUNET_MICROPHONE_destroy (mic); | ||
687 | mic = NULL; | ||
688 | GNUNET_free (ego_name); | ||
689 | ego_name = NULL; | ||
690 | GNUNET_CONFIGURATION_destroy (cfg); | ||
691 | cfg = NULL; | ||
692 | GNUNET_free_non_null (peer_name); | ||
693 | state = CS_ERROR; | ||
357 | } | 694 | } |
358 | 695 | ||
359 | 696 | ||
360 | /** | 697 | /** |
698 | * Task to handle commands from the terminal. | ||
361 | * | 699 | * |
700 | * @param cls NULL | ||
701 | * @param tc scheduler context | ||
362 | */ | 702 | */ |
363 | static void | 703 | static void |
364 | handle_command (void *cls, | 704 | handle_command (void *cls, |
365 | const struct GNUNET_SCHEDULER_TaskContext *tc) | 705 | const struct GNUNET_SCHEDULER_TaskContext *tc) |
366 | { | 706 | { |
367 | char message[MAX_MESSAGE_LENGTH + 1]; | 707 | char message[MAX_MESSAGE_LENGTH + 1]; |
368 | int i; | 708 | const char *ptr; |
709 | size_t i; | ||
369 | 710 | ||
711 | handle_cmd_task = | ||
712 | GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, | ||
713 | stdin_fh, | ||
714 | &handle_command, NULL); | ||
370 | /* read message from command line and handle it */ | 715 | /* read message from command line and handle it */ |
371 | memset (message, 0, MAX_MESSAGE_LENGTH + 1); | 716 | memset (message, 0, MAX_MESSAGE_LENGTH + 1); |
372 | if (NULL == fgets (message, MAX_MESSAGE_LENGTH, stdin)) | 717 | if (NULL == fgets (message, MAX_MESSAGE_LENGTH, stdin)) |
373 | goto next; | 718 | return; |
374 | if (strlen (message) == 0) | 719 | if (0 == strlen (message)) |
375 | goto next; | 720 | return; |
376 | if (message[strlen (message) - 1] == '\n') | 721 | if (message[strlen (message) - 1] == '\n') |
377 | message[strlen (message) - 1] = '\0'; | 722 | message[strlen (message) - 1] = '\0'; |
378 | if (strlen (message) == 0) | 723 | if (0 == strlen (message)) |
379 | goto next; | 724 | return; |
380 | i = 0; | 725 | i = 0; |
381 | while ((NULL != commands[i].command) && | 726 | while ((NULL != commands[i].command) && |
382 | (0 != | 727 | (0 != strncasecmp (commands[i].command, message, |
383 | strncasecmp (commands[i].command, message, | 728 | strlen (commands[i].command)))) |
384 | strlen (commands[i].command)))) | ||
385 | i++; | 729 | i++; |
386 | if (GNUNET_OK != | 730 | ptr = &message[strlen (commands[i].command)]; |
387 | commands[i].Action (&message[strlen (commands[i].command)], NULL)) | 731 | while (isspace ((int) *ptr)) |
388 | goto out; | 732 | ptr++; |
733 | commands[i].Action (ptr); | ||
734 | } | ||
389 | 735 | ||
390 | next: | 736 | |
391 | handle_cmd_task = | 737 | /** |
392 | GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_relative_multiply | 738 | * Function called by identity service with information about egos. |
393 | (GNUNET_TIME_UNIT_MILLISECONDS, | 739 | * |
394 | 100), | 740 | * @param cls NULL |
395 | GNUNET_SCHEDULER_PRIORITY_UI, | 741 | * @param ego ego handle |
396 | &handle_command, NULL); | 742 | * @param ctx unused |
397 | return; | 743 | * @param name name of the ego |
398 | 744 | */ | |
399 | out: | 745 | static void |
400 | handle_cmd_task = GNUNET_SCHEDULER_NO_TASK; | 746 | identity_cb (void *cls, |
401 | GNUNET_SCHEDULER_shutdown (); | 747 | struct GNUNET_IDENTITY_Ego *ego, |
748 | void **ctx, | ||
749 | const char *name) | ||
750 | { | ||
751 | if (NULL == name) | ||
752 | return; | ||
753 | if (ego == caller_id) | ||
754 | { | ||
755 | if (verbose) | ||
756 | FPRINTF (stdout, | ||
757 | _("Name of our ego changed to `%s'\n"), | ||
758 | name); | ||
759 | GNUNET_free (ego_name); | ||
760 | ego_name = GNUNET_strdup (name); | ||
761 | return; | ||
762 | } | ||
763 | if (0 != strcmp (name, | ||
764 | ego_name)) | ||
765 | return; | ||
766 | if (NULL == ego) | ||
767 | { | ||
768 | if (verbose) | ||
769 | FPRINTF (stdout, | ||
770 | _("Our ego `%s' was deleted!\n"), | ||
771 | ego_name); | ||
772 | caller_id = NULL; | ||
773 | return; | ||
774 | } | ||
775 | caller_id = ego; | ||
776 | GNUNET_CONFIGURATION_set_value_number (cfg, | ||
777 | "CONVERSATION", | ||
778 | "LINE", | ||
779 | line); | ||
780 | start_phone (); | ||
402 | } | 781 | } |
403 | 782 | ||
404 | 783 | ||
@@ -416,20 +795,19 @@ run (void *cls, | |||
416 | const char *cfgfile, | 795 | const char *cfgfile, |
417 | const struct GNUNET_CONFIGURATION_Handle *c) | 796 | const struct GNUNET_CONFIGURATION_Handle *c) |
418 | { | 797 | { |
419 | if (NULL == | 798 | cfg = GNUNET_CONFIGURATION_dup (c); |
420 | (conversation = | 799 | speaker = GNUNET_SPEAKER_create_from_hardware (cfg); |
421 | GNUNET_CONVERSATION_connect (c, NULL, | 800 | mic = GNUNET_MICROPHONE_create_from_hardware (cfg); |
422 | &call_handler, | 801 | if (NULL == ego_name) |
423 | &reject_handler, | ||
424 | ¬ification_handler, | ||
425 | &missed_call_handler))) | ||
426 | { | 802 | { |
427 | FPRINTF (stderr, | 803 | FPRINTF (stderr, |
428 | "%s", | 804 | "%s", |
429 | _("Could not access CONVERSATION service. Exiting.\n")); | 805 | _("You must specify the NAME of an ego to use\n")); |
430 | return; | 806 | return; |
431 | } | 807 | } |
432 | 808 | id = GNUNET_IDENTITY_connect (cfg, | |
809 | &identity_cb, | ||
810 | NULL); | ||
433 | handle_cmd_task = | 811 | handle_cmd_task = |
434 | GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_UI, | 812 | GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_UI, |
435 | &handle_command, NULL); | 813 | &handle_command, NULL); |
@@ -449,25 +827,29 @@ int | |||
449 | main (int argc, char *const *argv) | 827 | main (int argc, char *const *argv) |
450 | { | 828 | { |
451 | static const struct GNUNET_GETOPT_CommandLineOption options[] = { | 829 | static const struct GNUNET_GETOPT_CommandLineOption options[] = { |
830 | {'e', "ego", "NAME", | ||
831 | gettext_noop ("sets the NAME of the ego to use for the phone (and name resolution)"), | ||
832 | 1, &GNUNET_GETOPT_set_string, &ego_name}, | ||
833 | {'p', "phone", "LINE", | ||
834 | gettext_noop ("sets the LINE to use for the phone"), | ||
835 | 1, &GNUNET_GETOPT_set_uint, &line}, | ||
452 | GNUNET_GETOPT_OPTION_END | 836 | GNUNET_GETOPT_OPTION_END |
453 | }; | 837 | }; |
454 | |||
455 | int flags; | 838 | int flags; |
456 | int ret; | 839 | int ret; |
457 | 840 | ||
458 | flags = fcntl (0, F_GETFL, 0); | 841 | flags = fcntl (0, F_GETFL, 0); |
459 | flags |= O_NONBLOCK; | 842 | flags |= O_NONBLOCK; |
460 | fcntl (0, F_SETFL, flags); | 843 | fcntl (0, F_SETFL, flags); |
461 | 844 | stdin_fh = GNUNET_DISK_get_handle_from_int_fd (0); | |
462 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | 845 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) |
463 | return 2; | 846 | return 2; |
464 | 847 | ret = GNUNET_PROGRAM_run (argc, argv, | |
465 | ret = GNUNET_PROGRAM_run (argc, argv, "gnunet-conversation", | 848 | "gnunet-conversation", |
466 | gettext_noop ("Print information about conversation."), | 849 | gettext_noop ("Enables having a conversation with other GNUnet users."), |
467 | options, &run, NULL); | 850 | options, &run, NULL); |
468 | GNUNET_free ((void *) argv); | 851 | GNUNET_free ((void *) argv); |
469 | 852 | return (GNUNET_OK == ret) ? 0 : 1; | |
470 | return ret; | ||
471 | } | 853 | } |
472 | 854 | ||
473 | /* end of gnunet-conversation.c */ | 855 | /* end of gnunet-conversation.c */ |
diff --git a/src/conversation/gnunet-service-conversation-new.c b/src/conversation/gnunet-service-conversation-new.c deleted file mode 100644 index d379931f7..000000000 --- a/src/conversation/gnunet-service-conversation-new.c +++ /dev/null | |||
@@ -1,1227 +0,0 @@ | |||
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 | * @file conversation/gnunet-service-conversation.c | ||
22 | * @brief conversation service implementation | ||
23 | * @author Simon Dieterle | ||
24 | * @author Andreas Fuchs | ||
25 | * @author Christian Grothoff | ||
26 | */ | ||
27 | #include "platform.h" | ||
28 | #include "gnunet_util_lib.h" | ||
29 | #include "gnunet_protocols.h" | ||
30 | #include "gnunet_applications.h" | ||
31 | #include "gnunet_constants.h" | ||
32 | #include "gnunet_signatures.h" | ||
33 | #include "gnunet_mesh_service.h" | ||
34 | #include "gnunet_conversation_service.h" | ||
35 | #include "conversation.h" | ||
36 | |||
37 | |||
38 | /** | ||
39 | * How long is our signature on a call valid? Needs to be long enough for time zone | ||
40 | * differences and network latency to not matter. No strong need for it to be short, | ||
41 | * but we simply like all signatures to eventually expire. | ||
42 | */ | ||
43 | #define RING_TIMEOUT GNUNET_TIME_UNIT_DAYS | ||
44 | |||
45 | |||
46 | /** | ||
47 | * The possible connection status | ||
48 | */ | ||
49 | enum LineStatus | ||
50 | { | ||
51 | /** | ||
52 | * We are waiting for incoming calls. | ||
53 | */ | ||
54 | LS_CALLEE_LISTEN, | ||
55 | |||
56 | /** | ||
57 | * Our phone is ringing, waiting for the client to pick up. | ||
58 | */ | ||
59 | LS_CALLEE_RINGING, | ||
60 | |||
61 | /** | ||
62 | * We are talking! | ||
63 | */ | ||
64 | LS_CALLEE_CONNECTED, | ||
65 | |||
66 | /** | ||
67 | * We're in shutdown, sending hangup messages before cleaning up. | ||
68 | */ | ||
69 | LS_CALLEE_SHUTDOWN, | ||
70 | |||
71 | /** | ||
72 | * We are waiting for the phone to be picked up. | ||
73 | */ | ||
74 | LS_CALLER_CALLING, | ||
75 | |||
76 | /** | ||
77 | * We are talking! | ||
78 | */ | ||
79 | LS_CALLER_CONNECTED, | ||
80 | |||
81 | /** | ||
82 | * We're in shutdown, sending hangup messages before cleaning up. | ||
83 | */ | ||
84 | LS_CALLER_SHUTDOWN | ||
85 | }; | ||
86 | |||
87 | |||
88 | /** | ||
89 | * A line connects a local client with a mesh tunnel (or, if it is an | ||
90 | * open line, is waiting for a mesh tunnel). | ||
91 | */ | ||
92 | struct Line | ||
93 | { | ||
94 | /** | ||
95 | * Kept in a DLL. | ||
96 | */ | ||
97 | struct Line *next; | ||
98 | |||
99 | /** | ||
100 | * Kept in a DLL. | ||
101 | */ | ||
102 | struct Line *prev; | ||
103 | |||
104 | /** | ||
105 | * Handle for the reliable tunnel (contol data) | ||
106 | */ | ||
107 | struct GNUNET_MESH_Tunnel *tunnel_reliable; | ||
108 | |||
109 | /** | ||
110 | * Handle for unreliable tunnel (audio data) | ||
111 | */ | ||
112 | struct GNUNET_MESH_Tunnel *tunnel_unreliable; | ||
113 | |||
114 | /** | ||
115 | * Transmit handle for pending audio messages | ||
116 | */ | ||
117 | struct GNUNET_MESH_TransmitHandle *unreliable_mth; | ||
118 | |||
119 | /** | ||
120 | * Message queue for control messages | ||
121 | */ | ||
122 | struct GNUNET_MQ_Handle *reliable_mq; | ||
123 | |||
124 | /** | ||
125 | * Handle to the line client. | ||
126 | */ | ||
127 | struct GNUNET_SERVER_Client *client; | ||
128 | |||
129 | /** | ||
130 | * Target of the line, if we are the caller. | ||
131 | */ | ||
132 | struct GNUNET_PeerIdentity target; | ||
133 | |||
134 | /** | ||
135 | * Temporary buffer for audio data. | ||
136 | */ | ||
137 | void *audio_data; | ||
138 | |||
139 | /** | ||
140 | * Number of bytes in @e audio_data. | ||
141 | */ | ||
142 | size_t audio_size; | ||
143 | |||
144 | /** | ||
145 | * Our line number. | ||
146 | */ | ||
147 | uint32_t local_line; | ||
148 | |||
149 | /** | ||
150 | * Remote line number. | ||
151 | */ | ||
152 | uint32_t remote_line; | ||
153 | |||
154 | /** | ||
155 | * Current status of this line. | ||
156 | */ | ||
157 | enum LineStatus status; | ||
158 | |||
159 | }; | ||
160 | |||
161 | |||
162 | /** | ||
163 | * Our configuration. | ||
164 | */ | ||
165 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
166 | |||
167 | /** | ||
168 | * Notification context containing all connected clients. | ||
169 | */ | ||
170 | static struct GNUNET_SERVER_NotificationContext *nc; | ||
171 | |||
172 | /** | ||
173 | * Handle for mesh | ||
174 | */ | ||
175 | static struct GNUNET_MESH_Handle *mesh; | ||
176 | |||
177 | /** | ||
178 | * Identity of this peer. | ||
179 | */ | ||
180 | static struct GNUNET_PeerIdentity my_identity; | ||
181 | |||
182 | /** | ||
183 | * Head of DLL of active lines. | ||
184 | */ | ||
185 | static struct Line *lines_head; | ||
186 | |||
187 | /** | ||
188 | * Tail of DLL of active lines. | ||
189 | */ | ||
190 | static struct Line *lines_tail; | ||
191 | |||
192 | /** | ||
193 | * Counter for generating local line numbers. | ||
194 | * FIXME: randomize generation in the future | ||
195 | * to eliminate information leakage. | ||
196 | */ | ||
197 | static uint32_t local_line_cnt; | ||
198 | |||
199 | |||
200 | /** | ||
201 | * Function to register a phone. | ||
202 | * | ||
203 | * @param cls closure, NULL | ||
204 | * @param client the client from which the message is | ||
205 | * @param message the message from the client | ||
206 | */ | ||
207 | static void | ||
208 | handle_client_register_message (void *cls, | ||
209 | struct GNUNET_SERVER_Client *client, | ||
210 | const struct GNUNET_MessageHeader *message) | ||
211 | { | ||
212 | const struct ClientPhoneRegisterMessage *msg; | ||
213 | struct Line *line; | ||
214 | |||
215 | msg = (struct ClientPhoneRegisterMessage *) message; | ||
216 | line = GNUNET_SERVER_client_get_user_context (client, struct Line); | ||
217 | if (NULL != line) | ||
218 | { | ||
219 | GNUNET_break (0); | ||
220 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | ||
221 | return; | ||
222 | } | ||
223 | line = GNUNET_new (struct Line); | ||
224 | line->client = client; | ||
225 | GNUNET_SERVER_notification_context_add (nc, client); | ||
226 | GNUNET_CONTAINER_DLL_insert (lines_head, | ||
227 | lines_tail, | ||
228 | line); | ||
229 | line->local_line = ntohl (msg->line); | ||
230 | GNUNET_SERVER_client_set_user_context (client, line); | ||
231 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
232 | } | ||
233 | |||
234 | |||
235 | /** | ||
236 | * Function to handle a pickup request message from the client | ||
237 | * | ||
238 | * @param cls closure, NULL | ||
239 | * @param client the client from which the message is | ||
240 | * @param message the message from the client | ||
241 | */ | ||
242 | static void | ||
243 | handle_client_pickup_message (void *cls, | ||
244 | struct GNUNET_SERVER_Client *client, | ||
245 | const struct GNUNET_MessageHeader *message) | ||
246 | { | ||
247 | const struct ClientPhonePickupMessage *msg; | ||
248 | struct GNUNET_MQ_Envelope *e; | ||
249 | struct MeshPhonePickupMessage *mppm; | ||
250 | const char *meta; | ||
251 | struct Line *line; | ||
252 | size_t len; | ||
253 | |||
254 | msg = (struct ClientPhonePickupMessage *) message; | ||
255 | meta = (const char *) &msg[1]; | ||
256 | len = ntohs (msg->header.size) - sizeof (struct ClientPhonePickupMessage); | ||
257 | if ( (0 == len) || | ||
258 | ('\0' != meta[len - 1]) ) | ||
259 | { | ||
260 | meta = NULL; | ||
261 | len = 0; | ||
262 | } | ||
263 | line = GNUNET_SERVER_client_get_user_context (client, struct Line); | ||
264 | if (NULL == line) | ||
265 | { | ||
266 | GNUNET_break (0); | ||
267 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | ||
268 | return; | ||
269 | } | ||
270 | switch (line->status) | ||
271 | { | ||
272 | case LS_CALLEE_LISTEN: | ||
273 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
274 | "Ignoring client's PICKUP message, caller has HUNG UP already\n"); | ||
275 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
276 | break; | ||
277 | case LS_CALLEE_RINGING: | ||
278 | line->status = LS_CALLEE_CONNECTED; | ||
279 | break; | ||
280 | case LS_CALLEE_CONNECTED: | ||
281 | GNUNET_break (0); | ||
282 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | ||
283 | return; | ||
284 | case LS_CALLEE_SHUTDOWN: | ||
285 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
286 | "Ignoring client's PICKUP message, line is in SHUTDOWN\n"); | ||
287 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
288 | break; | ||
289 | case LS_CALLER_CALLING: | ||
290 | case LS_CALLER_CONNECTED: | ||
291 | case LS_CALLER_SHUTDOWN: | ||
292 | GNUNET_break (0); | ||
293 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | ||
294 | return; | ||
295 | } | ||
296 | line->status = LS_CALLEE_CONNECTED; | ||
297 | e = GNUNET_MQ_msg_extra (mppm, | ||
298 | len, | ||
299 | GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_PICK_UP); | ||
300 | memcpy (&mppm[1], meta, len); | ||
301 | GNUNET_MQ_send (line->reliable_mq, e); | ||
302 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
303 | } | ||
304 | |||
305 | |||
306 | /** | ||
307 | * Destroy the mesh tunnels of a line. | ||
308 | * | ||
309 | * @param line line to shutdown tunnels of | ||
310 | */ | ||
311 | static void | ||
312 | destroy_line_mesh_tunnels (struct Line *line) | ||
313 | { | ||
314 | if (NULL != line->reliable_mq) | ||
315 | { | ||
316 | GNUNET_MQ_destroy (line->reliable_mq); | ||
317 | line->reliable_mq = NULL; | ||
318 | } | ||
319 | if (NULL != line->unreliable_mth) | ||
320 | { | ||
321 | GNUNET_MESH_notify_transmit_ready_cancel (line->unreliable_mth); | ||
322 | line->unreliable_mth = NULL; | ||
323 | } | ||
324 | if (NULL != line->tunnel_unreliable) | ||
325 | { | ||
326 | GNUNET_MESH_tunnel_destroy (line->tunnel_unreliable); | ||
327 | line->tunnel_unreliable = NULL; | ||
328 | } | ||
329 | if (NULL != line->tunnel_reliable) | ||
330 | { | ||
331 | GNUNET_MESH_tunnel_destroy (line->tunnel_reliable); | ||
332 | line->tunnel_reliable = NULL; | ||
333 | } | ||
334 | } | ||
335 | |||
336 | |||
337 | /** | ||
338 | * We are done signalling shutdown to the other peer. Close down | ||
339 | * (or reset) the line. | ||
340 | * | ||
341 | * @param cls the `struct Line` to reset/terminate | ||
342 | */ | ||
343 | static void | ||
344 | mq_done_finish_caller_shutdown (void *cls) | ||
345 | { | ||
346 | struct Line *line = cls; | ||
347 | |||
348 | switch (line->status) | ||
349 | { | ||
350 | case LS_CALLEE_LISTEN: | ||
351 | GNUNET_break (0); | ||
352 | break; | ||
353 | case LS_CALLEE_RINGING: | ||
354 | GNUNET_break (0); | ||
355 | break; | ||
356 | case LS_CALLEE_CONNECTED: | ||
357 | GNUNET_break (0); | ||
358 | break; | ||
359 | case LS_CALLEE_SHUTDOWN: | ||
360 | line->status = LS_CALLEE_LISTEN; | ||
361 | destroy_line_mesh_tunnels (line); | ||
362 | return; | ||
363 | case LS_CALLER_CALLING: | ||
364 | line->status = LS_CALLER_SHUTDOWN; | ||
365 | break; | ||
366 | case LS_CALLER_CONNECTED: | ||
367 | line->status = LS_CALLER_SHUTDOWN; | ||
368 | break; | ||
369 | case LS_CALLER_SHUTDOWN: | ||
370 | destroy_line_mesh_tunnels (line); | ||
371 | GNUNET_CONTAINER_DLL_remove (lines_head, | ||
372 | lines_tail, | ||
373 | line); | ||
374 | GNUNET_free_non_null (line->audio_data); | ||
375 | GNUNET_free (line); | ||
376 | break; | ||
377 | } | ||
378 | } | ||
379 | |||
380 | |||
381 | /** | ||
382 | * Function to handle a hangup request message from the client | ||
383 | * | ||
384 | * @param cls closure, NULL | ||
385 | * @param client the client from which the message is | ||
386 | * @param message the message from the client | ||
387 | */ | ||
388 | static void | ||
389 | handle_client_hangup_message (void *cls, | ||
390 | struct GNUNET_SERVER_Client *client, | ||
391 | const struct GNUNET_MessageHeader *message) | ||
392 | { | ||
393 | const struct ClientPhoneHangupMessage *msg; | ||
394 | struct GNUNET_MQ_Envelope *e; | ||
395 | struct MeshPhoneHangupMessage *mhum; | ||
396 | const char *meta; | ||
397 | struct Line *line; | ||
398 | size_t len; | ||
399 | |||
400 | msg = (struct ClientPhoneHangupMessage *) message; | ||
401 | meta = (const char *) &msg[1]; | ||
402 | len = ntohs (msg->header.size) - sizeof (struct ClientPhoneHangupMessage); | ||
403 | if ( (0 == len) || | ||
404 | ('\0' != meta[len - 1]) ) | ||
405 | { | ||
406 | meta = NULL; | ||
407 | len = 0; | ||
408 | } | ||
409 | line = GNUNET_SERVER_client_get_user_context (client, struct Line); | ||
410 | if (NULL == line) | ||
411 | { | ||
412 | GNUNET_break (0); | ||
413 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | ||
414 | return; | ||
415 | } | ||
416 | switch (line->status) | ||
417 | { | ||
418 | case LS_CALLEE_LISTEN: | ||
419 | GNUNET_break (0); | ||
420 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
421 | return; | ||
422 | case LS_CALLEE_RINGING: | ||
423 | line->status = LS_CALLEE_SHUTDOWN; | ||
424 | break; | ||
425 | case LS_CALLEE_CONNECTED: | ||
426 | line->status = LS_CALLEE_SHUTDOWN; | ||
427 | break; | ||
428 | case LS_CALLEE_SHUTDOWN: | ||
429 | GNUNET_break (0); | ||
430 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
431 | return; | ||
432 | case LS_CALLER_CALLING: | ||
433 | line->status = LS_CALLER_SHUTDOWN; | ||
434 | break; | ||
435 | case LS_CALLER_CONNECTED: | ||
436 | line->status = LS_CALLER_SHUTDOWN; | ||
437 | break; | ||
438 | case LS_CALLER_SHUTDOWN: | ||
439 | GNUNET_break (0); | ||
440 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
441 | return; | ||
442 | } | ||
443 | e = GNUNET_MQ_msg_extra (mhum, | ||
444 | len, | ||
445 | GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP); | ||
446 | memcpy (&mhum[1], meta, len); | ||
447 | GNUNET_MQ_notify_sent (e, | ||
448 | &mq_done_finish_caller_shutdown, | ||
449 | line); | ||
450 | GNUNET_MQ_send (line->reliable_mq, e); | ||
451 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
452 | } | ||
453 | |||
454 | |||
455 | /** | ||
456 | * Function to handle call request the client | ||
457 | * | ||
458 | * @param cls closure, NULL | ||
459 | * @param client the client from which the message is | ||
460 | * @param message the message from the client | ||
461 | */ | ||
462 | static void | ||
463 | handle_client_call_message (void *cls, | ||
464 | struct GNUNET_SERVER_Client *client, | ||
465 | const struct GNUNET_MessageHeader *message) | ||
466 | { | ||
467 | const struct ClientCallMessage *msg; | ||
468 | struct Line *line; | ||
469 | struct GNUNET_MQ_Envelope *e; | ||
470 | struct MeshPhoneRingMessage *ring; | ||
471 | |||
472 | msg = (struct ClientCallMessage *) message; | ||
473 | line = GNUNET_SERVER_client_get_user_context (client, struct Line); | ||
474 | if (NULL != line) | ||
475 | { | ||
476 | GNUNET_break (0); | ||
477 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | ||
478 | return; | ||
479 | } | ||
480 | line = GNUNET_new (struct Line); | ||
481 | line->target = msg->target; | ||
482 | GNUNET_CONTAINER_DLL_insert (lines_head, | ||
483 | lines_tail, | ||
484 | line); | ||
485 | line->remote_line = ntohl (msg->line); | ||
486 | line->status = LS_CALLER_CALLING; | ||
487 | line->tunnel_reliable = GNUNET_MESH_tunnel_create (mesh, | ||
488 | line, | ||
489 | &msg->target, | ||
490 | GNUNET_APPLICATION_TYPE_CONVERSATION_CONTROL, | ||
491 | GNUNET_NO, | ||
492 | GNUNET_YES); | ||
493 | line->reliable_mq = GNUNET_MESH_mq_create (line->tunnel_reliable); | ||
494 | line->local_line = local_line_cnt++; | ||
495 | e = GNUNET_MQ_msg (ring, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RING); | ||
496 | ring->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING); | ||
497 | ring->purpose.size = htonl (sizeof (struct GNUNET_PeerIdentity) * 2 + | ||
498 | sizeof (struct GNUNET_TIME_AbsoluteNBO) + | ||
499 | sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) + | ||
500 | sizeof (struct GNUNET_CRYPTO_EccPublicSignKey)); | ||
501 | GNUNET_CRYPTO_ecc_key_get_public_for_signature (&msg->caller_id, | ||
502 | &ring->caller_id); | ||
503 | ring->remote_line = msg->line; | ||
504 | ring->source_line = line->local_line; | ||
505 | ring->target = msg->target; | ||
506 | ring->source = my_identity; | ||
507 | ring->expiration_time = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (RING_TIMEOUT)); | ||
508 | GNUNET_CRYPTO_ecc_sign (&msg->caller_id, | ||
509 | &ring->purpose, | ||
510 | &ring->signature); | ||
511 | GNUNET_MQ_send (line->reliable_mq, e); | ||
512 | GNUNET_SERVER_client_set_user_context (client, line); | ||
513 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
514 | } | ||
515 | |||
516 | |||
517 | /** | ||
518 | * Transmit audio data via unreliable mesh channel. | ||
519 | * | ||
520 | * @param cls the `struct Line` we are transmitting for | ||
521 | * @param size number of bytes available in @a buf | ||
522 | * @param buf where to copy the data | ||
523 | * @return number of bytes copied to @buf | ||
524 | */ | ||
525 | static size_t | ||
526 | transmit_line_audio (void *cls, | ||
527 | size_t size, | ||
528 | void *buf) | ||
529 | { | ||
530 | struct Line *line = cls; | ||
531 | struct MeshAudioMessage *mam = buf; | ||
532 | |||
533 | line->unreliable_mth = NULL; | ||
534 | if ( (NULL == buf) || | ||
535 | (size < sizeof (struct MeshAudioMessage) + line->audio_size) ) | ||
536 | { | ||
537 | /* eh, other error handling? */ | ||
538 | return 0; | ||
539 | } | ||
540 | mam->header.size = htons (sizeof (struct MeshAudioMessage) + line->audio_size); | ||
541 | mam->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_AUDIO); | ||
542 | mam->remote_line = htonl (line->remote_line); | ||
543 | memcpy (&mam[1], line->audio_data, line->audio_size); | ||
544 | GNUNET_free (line->audio_data); | ||
545 | line->audio_data = NULL; | ||
546 | return sizeof (struct MeshAudioMessage) + line->audio_size; | ||
547 | } | ||
548 | |||
549 | |||
550 | /** | ||
551 | * Function to handle audio data from the client | ||
552 | * | ||
553 | * @param cls closure, NULL | ||
554 | * @param client the client from which the message is | ||
555 | * @param message the message from the client | ||
556 | */ | ||
557 | static void | ||
558 | handle_client_audio_message (void *cls, | ||
559 | struct GNUNET_SERVER_Client *client, | ||
560 | const struct GNUNET_MessageHeader *message) | ||
561 | { | ||
562 | const struct ClientAudioMessage *msg; | ||
563 | struct Line *line; | ||
564 | size_t size; | ||
565 | |||
566 | size = ntohs (message->size) - sizeof (struct ClientAudioMessage); | ||
567 | msg = (struct ClientAudioMessage *) message; | ||
568 | line = GNUNET_SERVER_client_get_user_context (client, struct Line); | ||
569 | if (NULL == line) | ||
570 | { | ||
571 | GNUNET_break (0); | ||
572 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | ||
573 | return; | ||
574 | } | ||
575 | switch (line->status) | ||
576 | { | ||
577 | case LS_CALLEE_LISTEN: | ||
578 | case LS_CALLEE_RINGING: | ||
579 | case LS_CALLER_CALLING: | ||
580 | GNUNET_break (0); | ||
581 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | ||
582 | return; | ||
583 | case LS_CALLEE_CONNECTED: | ||
584 | case LS_CALLER_CONNECTED: | ||
585 | /* common case, handled below */ | ||
586 | break; | ||
587 | case LS_CALLEE_SHUTDOWN: | ||
588 | case LS_CALLER_SHUTDOWN: | ||
589 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
590 | "Mesh audio channel in shutdown; audio data dropped\n"); | ||
591 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
592 | return; | ||
593 | } | ||
594 | if (NULL == line->tunnel_unreliable) | ||
595 | { | ||
596 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
597 | _("Mesh audio channel not ready; audio data dropped\n")); | ||
598 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
599 | return; | ||
600 | } | ||
601 | if (NULL != line->unreliable_mth) | ||
602 | { | ||
603 | /* NOTE: we may want to not do this and instead combine the data */ | ||
604 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
605 | "Dropping previous audio data segment with %u bytes\n", | ||
606 | line->audio_size); | ||
607 | GNUNET_MESH_notify_transmit_ready_cancel (line->unreliable_mth); | ||
608 | GNUNET_free (line->audio_data); | ||
609 | } | ||
610 | line->audio_size = size; | ||
611 | line->audio_data = GNUNET_malloc (line->audio_size); | ||
612 | memcpy (line->audio_data, | ||
613 | &msg[1], | ||
614 | size); | ||
615 | line->unreliable_mth = GNUNET_MESH_notify_transmit_ready (line->tunnel_unreliable, | ||
616 | GNUNET_NO, | ||
617 | GNUNET_TIME_UNIT_FOREVER_REL, | ||
618 | sizeof (struct MeshAudioMessage) | ||
619 | + line->audio_size, | ||
620 | &transmit_line_audio, | ||
621 | line); | ||
622 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
623 | } | ||
624 | |||
625 | |||
626 | /** | ||
627 | * We are done signalling shutdown to the other peer. | ||
628 | * Destroy the tunnel. | ||
629 | * | ||
630 | * @param cls the `struct GNUNET_MESH_tunnel` to destroy | ||
631 | */ | ||
632 | static void | ||
633 | mq_done_destroy_tunnel (void *cls) | ||
634 | { | ||
635 | struct GNUNET_MESH_Tunnel *tunnel = cls; | ||
636 | |||
637 | GNUNET_MESH_tunnel_destroy (tunnel); | ||
638 | } | ||
639 | |||
640 | |||
641 | /** | ||
642 | * Function to handle a ring message incoming over mesh | ||
643 | * | ||
644 | * @param cls closure, NULL | ||
645 | * @param tunnel the tunnel over which the message arrived | ||
646 | * @param tunnel_ctx the tunnel context, can be NULL | ||
647 | * @param message the incoming message | ||
648 | * @return #GNUNET_OK | ||
649 | */ | ||
650 | static int | ||
651 | handle_mesh_ring_message (void *cls, | ||
652 | struct GNUNET_MESH_Tunnel *tunnel, | ||
653 | void **tunnel_ctx, | ||
654 | const struct GNUNET_MessageHeader *message) | ||
655 | { | ||
656 | const struct MeshPhoneRingMessage *msg; | ||
657 | struct Line *line; | ||
658 | struct GNUNET_MQ_Envelope *e; | ||
659 | struct MeshPhoneBusyMessage *busy; | ||
660 | struct ClientPhoneRingMessage cring; | ||
661 | |||
662 | msg = (const struct MeshPhoneRingMessage *) message; | ||
663 | if ( (msg->purpose.size != htonl (sizeof (struct GNUNET_PeerIdentity) * 2 + | ||
664 | sizeof (struct GNUNET_TIME_AbsoluteNBO) + | ||
665 | sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) + | ||
666 | sizeof (struct GNUNET_CRYPTO_EccPublicSignKey))) || | ||
667 | (GNUNET_OK != | ||
668 | GNUNET_CRYPTO_ecc_verify (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING, | ||
669 | &msg->purpose, | ||
670 | &msg->signature, | ||
671 | &msg->caller_id)) ) | ||
672 | { | ||
673 | GNUNET_break_op (0); | ||
674 | return GNUNET_SYSERR; | ||
675 | } | ||
676 | for (line = lines_head; NULL != line; line = line->next) | ||
677 | if ( (line->local_line == ntohl (msg->remote_line)) && | ||
678 | (LS_CALLEE_LISTEN == line->status) ) | ||
679 | break; | ||
680 | if (NULL == line) | ||
681 | { | ||
682 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
683 | _("No available phone for incoming call on line %u, sending BUSY signal\n"), | ||
684 | ntohl (msg->remote_line)); | ||
685 | e = GNUNET_MQ_msg (busy, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_BUSY); | ||
686 | GNUNET_MQ_notify_sent (e, | ||
687 | &mq_done_destroy_tunnel, | ||
688 | tunnel); | ||
689 | GNUNET_MQ_send (line->reliable_mq, e); | ||
690 | GNUNET_MESH_receive_done (tunnel); /* needed? */ | ||
691 | return GNUNET_OK; | ||
692 | } | ||
693 | line->status = LS_CALLEE_RINGING; | ||
694 | line->remote_line = ntohl (msg->source_line); | ||
695 | line->tunnel_reliable = tunnel; | ||
696 | line->reliable_mq = GNUNET_MESH_mq_create (line->tunnel_reliable); | ||
697 | *tunnel_ctx = line; | ||
698 | cring.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING); | ||
699 | cring.header.size = htons (sizeof (cring)); | ||
700 | cring.reserved = htonl (0); | ||
701 | cring.caller_id = msg->caller_id; | ||
702 | GNUNET_SERVER_notification_context_unicast (nc, | ||
703 | line->client, | ||
704 | &cring.header, | ||
705 | GNUNET_NO); | ||
706 | GNUNET_MESH_receive_done (tunnel); | ||
707 | return GNUNET_OK; | ||
708 | } | ||
709 | |||
710 | |||
711 | /** | ||
712 | * Function to handle a hangup message incoming over mesh | ||
713 | * | ||
714 | * @param cls closure, NULL | ||
715 | * @param tunnel the tunnel over which the message arrived | ||
716 | * @param tunnel_ctx the tunnel context, can be NULL | ||
717 | * @param message the incoming message | ||
718 | * @return #GNUNET_OK | ||
719 | */ | ||
720 | static int | ||
721 | handle_mesh_hangup_message (void *cls, | ||
722 | struct GNUNET_MESH_Tunnel *tunnel, | ||
723 | void **tunnel_ctx, | ||
724 | const struct GNUNET_MessageHeader *message) | ||
725 | { | ||
726 | struct Line *line = *tunnel_ctx; | ||
727 | const struct MeshPhoneHangupMessage *msg; | ||
728 | const char *reason; | ||
729 | size_t len = ntohs (message->size) - sizeof (struct MeshPhoneHangupMessage); | ||
730 | char buf[len + sizeof (struct ClientPhoneHangupMessage)]; | ||
731 | struct ClientPhoneHangupMessage *hup; | ||
732 | |||
733 | msg = (const struct MeshPhoneHangupMessage *) message; | ||
734 | len = ntohs (msg->header.size) - sizeof (struct MeshPhoneHangupMessage); | ||
735 | reason = (const char *) &msg[1]; | ||
736 | if ( (0 == len) || | ||
737 | ('\0' != reason[len - 1]) ) | ||
738 | { | ||
739 | reason = NULL; | ||
740 | len = 0; | ||
741 | } | ||
742 | if (NULL == line) | ||
743 | { | ||
744 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
745 | "HANGUP message received for non-existing line, dropping tunnel.\n"); | ||
746 | return GNUNET_SYSERR; | ||
747 | } | ||
748 | *tunnel_ctx = NULL; | ||
749 | switch (line->status) | ||
750 | { | ||
751 | case LS_CALLEE_LISTEN: | ||
752 | GNUNET_break (0); | ||
753 | return GNUNET_SYSERR; | ||
754 | case LS_CALLEE_RINGING: | ||
755 | line->status = LS_CALLEE_LISTEN; | ||
756 | destroy_line_mesh_tunnels (line); | ||
757 | break; | ||
758 | case LS_CALLEE_CONNECTED: | ||
759 | line->status = LS_CALLEE_LISTEN; | ||
760 | destroy_line_mesh_tunnels (line); | ||
761 | break; | ||
762 | case LS_CALLEE_SHUTDOWN: | ||
763 | line->status = LS_CALLEE_LISTEN; | ||
764 | destroy_line_mesh_tunnels (line); | ||
765 | return GNUNET_OK; | ||
766 | case LS_CALLER_CALLING: | ||
767 | line->status = LS_CALLER_SHUTDOWN; | ||
768 | mq_done_finish_caller_shutdown (line); | ||
769 | break; | ||
770 | case LS_CALLER_CONNECTED: | ||
771 | line->status = LS_CALLER_SHUTDOWN; | ||
772 | mq_done_finish_caller_shutdown (line); | ||
773 | break; | ||
774 | case LS_CALLER_SHUTDOWN: | ||
775 | mq_done_finish_caller_shutdown (line); | ||
776 | return GNUNET_OK; | ||
777 | } | ||
778 | hup = (struct ClientPhoneHangupMessage *) buf; | ||
779 | hup->header.size = sizeof (buf); | ||
780 | hup->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP); | ||
781 | memcpy (&hup[1], reason, len); | ||
782 | GNUNET_SERVER_notification_context_unicast (nc, | ||
783 | line->client, | ||
784 | &hup->header, | ||
785 | GNUNET_NO); | ||
786 | GNUNET_MESH_receive_done (tunnel); | ||
787 | return GNUNET_OK; | ||
788 | } | ||
789 | |||
790 | |||
791 | /** | ||
792 | * Function to handle a pickup message incoming over mesh | ||
793 | * | ||
794 | * @param cls closure, NULL | ||
795 | * @param tunnel the tunnel over which the message arrived | ||
796 | * @param tunnel_ctx the tunnel context, can be NULL | ||
797 | * @param message the incoming message | ||
798 | * @return #GNUNET_OK | ||
799 | */ | ||
800 | static int | ||
801 | handle_mesh_pickup_message (void *cls, | ||
802 | struct GNUNET_MESH_Tunnel *tunnel, | ||
803 | void **tunnel_ctx, | ||
804 | const struct GNUNET_MessageHeader *message) | ||
805 | { | ||
806 | const struct MeshPhonePickupMessage *msg; | ||
807 | struct Line *line = *tunnel_ctx; | ||
808 | const char *metadata; | ||
809 | size_t len = ntohs (message->size) - sizeof (struct MeshPhonePickupMessage); | ||
810 | char buf[len + sizeof (struct ClientPhonePickupMessage)]; | ||
811 | struct ClientPhonePickupMessage *pick; | ||
812 | |||
813 | msg = (const struct MeshPhonePickupMessage *) message; | ||
814 | len = ntohs (msg->header.size) - sizeof (struct MeshPhonePickupMessage); | ||
815 | metadata = (const char *) &msg[1]; | ||
816 | if ( (0 == len) || | ||
817 | ('\0' != metadata[len - 1]) ) | ||
818 | { | ||
819 | metadata = NULL; | ||
820 | len = 0; | ||
821 | } | ||
822 | if (NULL == line) | ||
823 | { | ||
824 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
825 | "PICKUP message received for non-existing line, dropping tunnel.\n"); | ||
826 | return GNUNET_SYSERR; | ||
827 | } | ||
828 | GNUNET_MESH_receive_done (tunnel); | ||
829 | switch (line->status) | ||
830 | { | ||
831 | case LS_CALLEE_LISTEN: | ||
832 | GNUNET_break (0); | ||
833 | return GNUNET_SYSERR; | ||
834 | case LS_CALLEE_RINGING: | ||
835 | case LS_CALLEE_CONNECTED: | ||
836 | GNUNET_break_op (0); | ||
837 | destroy_line_mesh_tunnels (line); | ||
838 | line->status = LS_CALLEE_LISTEN; | ||
839 | return GNUNET_SYSERR; | ||
840 | case LS_CALLEE_SHUTDOWN: | ||
841 | GNUNET_break_op (0); | ||
842 | line->status = LS_CALLEE_LISTEN; | ||
843 | destroy_line_mesh_tunnels (line); | ||
844 | break; | ||
845 | case LS_CALLER_CALLING: | ||
846 | line->status = LS_CALLER_CONNECTED; | ||
847 | break; | ||
848 | case LS_CALLER_CONNECTED: | ||
849 | GNUNET_break_op (0); | ||
850 | return GNUNET_OK; | ||
851 | case LS_CALLER_SHUTDOWN: | ||
852 | GNUNET_break_op (0); | ||
853 | mq_done_finish_caller_shutdown (line); | ||
854 | return GNUNET_SYSERR; | ||
855 | } | ||
856 | pick = (struct ClientPhonePickupMessage *) buf; | ||
857 | pick->header.size = sizeof (buf); | ||
858 | pick->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP); | ||
859 | memcpy (&pick[1], metadata, len); | ||
860 | GNUNET_SERVER_notification_context_unicast (nc, | ||
861 | line->client, | ||
862 | &pick->header, | ||
863 | GNUNET_NO); | ||
864 | line->tunnel_unreliable = GNUNET_MESH_tunnel_create (mesh, | ||
865 | line, | ||
866 | &line->target, | ||
867 | GNUNET_APPLICATION_TYPE_CONVERSATION_AUDIO, | ||
868 | GNUNET_YES, | ||
869 | GNUNET_NO); | ||
870 | return GNUNET_OK; | ||
871 | } | ||
872 | |||
873 | |||
874 | /** | ||
875 | * Function to handle a busy message incoming over mesh | ||
876 | * | ||
877 | * @param cls closure, NULL | ||
878 | * @param tunnel the tunnel over which the message arrived | ||
879 | * @param tunnel_ctx the tunnel context, can be NULL | ||
880 | * @param message the incoming message | ||
881 | * @return #GNUNET_OK | ||
882 | */ | ||
883 | static int | ||
884 | handle_mesh_busy_message (void *cls, | ||
885 | struct GNUNET_MESH_Tunnel *tunnel, | ||
886 | void **tunnel_ctx, | ||
887 | const struct GNUNET_MessageHeader *message) | ||
888 | { | ||
889 | struct Line *line = *tunnel_ctx; | ||
890 | struct ClientPhoneBusyMessage busy; | ||
891 | |||
892 | if (NULL == line) | ||
893 | { | ||
894 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
895 | "HANGUP message received for non-existing line, dropping tunnel.\n"); | ||
896 | return GNUNET_SYSERR; | ||
897 | } | ||
898 | busy.header.size = sizeof (busy); | ||
899 | busy.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_BUSY); | ||
900 | GNUNET_SERVER_notification_context_unicast (nc, | ||
901 | line->client, | ||
902 | &busy.header, | ||
903 | GNUNET_NO); | ||
904 | GNUNET_MESH_receive_done (tunnel); | ||
905 | *tunnel_ctx = NULL; | ||
906 | switch (line->status) | ||
907 | { | ||
908 | case LS_CALLEE_LISTEN: | ||
909 | GNUNET_break (0); | ||
910 | return GNUNET_SYSERR; | ||
911 | case LS_CALLEE_RINGING: | ||
912 | GNUNET_break_op (0); | ||
913 | break; | ||
914 | case LS_CALLEE_CONNECTED: | ||
915 | GNUNET_break_op (0); | ||
916 | break; | ||
917 | case LS_CALLEE_SHUTDOWN: | ||
918 | GNUNET_break_op (0); | ||
919 | break; | ||
920 | case LS_CALLER_CALLING: | ||
921 | line->status = LS_CALLER_SHUTDOWN; | ||
922 | mq_done_finish_caller_shutdown (line); | ||
923 | break; | ||
924 | case LS_CALLER_CONNECTED: | ||
925 | line->status = LS_CALLER_SHUTDOWN; | ||
926 | mq_done_finish_caller_shutdown (line); | ||
927 | break; | ||
928 | case LS_CALLER_SHUTDOWN: | ||
929 | mq_done_finish_caller_shutdown (line); | ||
930 | break; | ||
931 | } | ||
932 | return GNUNET_OK; | ||
933 | } | ||
934 | |||
935 | |||
936 | /** | ||
937 | * Function to handle an audio message incoming over mesh | ||
938 | * | ||
939 | * @param cls closure, NULL | ||
940 | * @param tunnel the tunnel over which the message arrived | ||
941 | * @param tunnel_ctx the tunnel context, can be NULL | ||
942 | * @param message the incoming message | ||
943 | * @return #GNUNET_OK | ||
944 | */ | ||
945 | static int | ||
946 | handle_mesh_audio_message (void *cls, | ||
947 | struct GNUNET_MESH_Tunnel *tunnel, | ||
948 | void **tunnel_ctx, | ||
949 | const struct GNUNET_MessageHeader *message) | ||
950 | { | ||
951 | const struct MeshAudioMessage *msg; | ||
952 | struct Line *line = *tunnel_ctx; | ||
953 | struct GNUNET_PeerIdentity sender; | ||
954 | size_t msize = ntohs (message->size) - sizeof (struct MeshAudioMessage); | ||
955 | char buf[msize + sizeof (struct ClientAudioMessage)]; | ||
956 | struct ClientAudioMessage *cam; | ||
957 | |||
958 | msg = (const struct MeshAudioMessage *) message; | ||
959 | if (NULL == line) | ||
960 | { | ||
961 | sender = *GNUNET_MESH_tunnel_get_info (tunnel, | ||
962 | GNUNET_MESH_OPTION_PEER)->peer; | ||
963 | for (line = lines_head; NULL != line; line = line->next) | ||
964 | if ( (line->local_line == ntohl (msg->remote_line)) && | ||
965 | (LS_CALLEE_CONNECTED == line->status) && | ||
966 | (0 == memcmp (&line->target, | ||
967 | &sender, | ||
968 | sizeof (struct GNUNET_PeerIdentity))) && | ||
969 | (NULL == line->tunnel_unreliable) ) | ||
970 | break; | ||
971 | if (NULL == line) | ||
972 | { | ||
973 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
974 | "Received AUDIO data for non-existing line %u, dropping.\n", | ||
975 | ntohl (msg->remote_line)); | ||
976 | return GNUNET_SYSERR; | ||
977 | } | ||
978 | line->tunnel_unreliable = tunnel; | ||
979 | *tunnel_ctx = line; | ||
980 | } | ||
981 | cam = (struct ClientAudioMessage *) buf; | ||
982 | cam->header.size = htons (sizeof (buf)); | ||
983 | cam->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO); | ||
984 | memcpy (&cam[1], &msg[1], msize); | ||
985 | GNUNET_SERVER_notification_context_unicast (nc, | ||
986 | line->client, | ||
987 | &cam->header, | ||
988 | GNUNET_YES); | ||
989 | GNUNET_MESH_receive_done (tunnel); | ||
990 | return GNUNET_OK; | ||
991 | } | ||
992 | |||
993 | |||
994 | /** | ||
995 | * Method called whenever another peer has added us to a tunnel | ||
996 | * the other peer initiated. | ||
997 | * | ||
998 | * @param cls closure | ||
999 | * @param tunnel new handle to the tunnel | ||
1000 | * @param initiator peer that started the tunnel | ||
1001 | * @param port port | ||
1002 | * @return initial tunnel context for the tunnel (can be NULL -- that's not an error) | ||
1003 | */ | ||
1004 | static void * | ||
1005 | inbound_tunnel (void *cls, | ||
1006 | struct GNUNET_MESH_Tunnel *tunnel, | ||
1007 | const struct GNUNET_PeerIdentity *initiator, | ||
1008 | uint32_t port) | ||
1009 | { | ||
1010 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1011 | _("Received incoming tunnel on port %d\n"), | ||
1012 | port); | ||
1013 | return NULL; | ||
1014 | } | ||
1015 | |||
1016 | |||
1017 | /** | ||
1018 | * Function called whenever an inbound tunnel is destroyed. Should clean up | ||
1019 | * any associated state. | ||
1020 | * | ||
1021 | * @param cls closure (set from #GNUNET_MESH_connect) | ||
1022 | * @param tunnel connection to the other end (henceforth invalid) | ||
1023 | * @param tunnel_ctx place where local state associated | ||
1024 | * with the tunnel is stored | ||
1025 | */ | ||
1026 | static void | ||
1027 | inbound_end (void *cls, | ||
1028 | const struct GNUNET_MESH_Tunnel *tunnel, | ||
1029 | void *tunnel_ctx) | ||
1030 | { | ||
1031 | struct Line *line = tunnel_ctx; | ||
1032 | struct ClientPhoneHangupMessage hup; | ||
1033 | |||
1034 | if (NULL == line) | ||
1035 | return; | ||
1036 | if (line->tunnel_unreliable == tunnel) | ||
1037 | { | ||
1038 | line->tunnel_unreliable = NULL; | ||
1039 | return; | ||
1040 | } | ||
1041 | hup.header.size = sizeof (hup); | ||
1042 | hup.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP); | ||
1043 | switch (line->status) | ||
1044 | { | ||
1045 | case LS_CALLEE_LISTEN: | ||
1046 | GNUNET_break (0); | ||
1047 | return; | ||
1048 | case LS_CALLEE_RINGING: | ||
1049 | case LS_CALLEE_CONNECTED: | ||
1050 | GNUNET_SERVER_notification_context_unicast (nc, | ||
1051 | line->client, | ||
1052 | &hup.header, | ||
1053 | GNUNET_NO); | ||
1054 | line->status = LS_CALLEE_LISTEN; | ||
1055 | break; | ||
1056 | case LS_CALLEE_SHUTDOWN: | ||
1057 | line->status = LS_CALLEE_LISTEN; | ||
1058 | destroy_line_mesh_tunnels (line); | ||
1059 | break; | ||
1060 | case LS_CALLER_CALLING: | ||
1061 | case LS_CALLER_CONNECTED: | ||
1062 | GNUNET_SERVER_notification_context_unicast (nc, | ||
1063 | line->client, | ||
1064 | &hup.header, | ||
1065 | GNUNET_NO); | ||
1066 | destroy_line_mesh_tunnels (line); | ||
1067 | GNUNET_CONTAINER_DLL_remove (lines_head, | ||
1068 | lines_tail, | ||
1069 | line); | ||
1070 | GNUNET_free_non_null (line->audio_data); | ||
1071 | GNUNET_free (line); | ||
1072 | break; | ||
1073 | case LS_CALLER_SHUTDOWN: | ||
1074 | destroy_line_mesh_tunnels (line); | ||
1075 | GNUNET_CONTAINER_DLL_remove (lines_head, | ||
1076 | lines_tail, | ||
1077 | line); | ||
1078 | GNUNET_free (line); | ||
1079 | break; | ||
1080 | } | ||
1081 | } | ||
1082 | |||
1083 | |||
1084 | /** | ||
1085 | * A client disconnected. Remove all of its data structure entries. | ||
1086 | * | ||
1087 | * @param cls closure, NULL | ||
1088 | * @param client identification of the client | ||
1089 | */ | ||
1090 | static void | ||
1091 | handle_client_disconnect (void *cls, | ||
1092 | struct GNUNET_SERVER_Client *client) | ||
1093 | { | ||
1094 | struct Line *line; | ||
1095 | |||
1096 | line = GNUNET_SERVER_client_get_user_context (client, struct Line); | ||
1097 | if (NULL == line) | ||
1098 | return; | ||
1099 | GNUNET_CONTAINER_DLL_remove (lines_head, | ||
1100 | lines_tail, | ||
1101 | line); | ||
1102 | GNUNET_free (line); | ||
1103 | GNUNET_SERVER_client_set_user_context (client, NULL); | ||
1104 | } | ||
1105 | |||
1106 | |||
1107 | /** | ||
1108 | * Shutdown nicely | ||
1109 | * | ||
1110 | * @param cls closure, NULL | ||
1111 | * @param tc the task context | ||
1112 | */ | ||
1113 | static void | ||
1114 | do_shutdown (void *cls, | ||
1115 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
1116 | { | ||
1117 | if (NULL != mesh) | ||
1118 | { | ||
1119 | GNUNET_MESH_disconnect (mesh); | ||
1120 | mesh = NULL; | ||
1121 | } | ||
1122 | if (NULL != nc) | ||
1123 | { | ||
1124 | GNUNET_SERVER_notification_context_destroy (nc); | ||
1125 | nc = NULL; | ||
1126 | } | ||
1127 | } | ||
1128 | |||
1129 | |||
1130 | /** | ||
1131 | * Main function that will be run by the scheduler. | ||
1132 | * | ||
1133 | * @param cls closure | ||
1134 | * @param server server handle | ||
1135 | * @param c configuration | ||
1136 | */ | ||
1137 | static void | ||
1138 | run (void *cls, | ||
1139 | struct GNUNET_SERVER_Handle *server, | ||
1140 | const struct GNUNET_CONFIGURATION_Handle *c) | ||
1141 | { | ||
1142 | static const struct GNUNET_SERVER_MessageHandler server_handlers[] = { | ||
1143 | {&handle_client_register_message, NULL, | ||
1144 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER, | ||
1145 | sizeof (struct ClientPhoneRegisterMessage)}, | ||
1146 | {&handle_client_pickup_message, NULL, | ||
1147 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP, | ||
1148 | 0}, | ||
1149 | {&handle_client_hangup_message, NULL, | ||
1150 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP, | ||
1151 | 0}, | ||
1152 | {&handle_client_call_message, NULL, | ||
1153 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL, | ||
1154 | 0}, | ||
1155 | {&handle_client_audio_message, NULL, | ||
1156 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO, | ||
1157 | 0}, | ||
1158 | {NULL, NULL, 0, 0} | ||
1159 | }; | ||
1160 | static struct GNUNET_MESH_MessageHandler mesh_handlers[] = { | ||
1161 | {&handle_mesh_ring_message, | ||
1162 | GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RING, | ||
1163 | sizeof (struct MeshPhoneRingMessage)}, | ||
1164 | {&handle_mesh_hangup_message, | ||
1165 | GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP, | ||
1166 | 0}, | ||
1167 | {&handle_mesh_pickup_message, | ||
1168 | GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_PICK_UP, | ||
1169 | 0}, | ||
1170 | {&handle_mesh_busy_message, | ||
1171 | GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_BUSY, | ||
1172 | sizeof (struct MeshPhoneBusyMessage)}, | ||
1173 | {&handle_mesh_audio_message, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_AUDIO, | ||
1174 | 0}, | ||
1175 | {NULL, 0, 0} | ||
1176 | }; | ||
1177 | static uint32_t ports[] = { | ||
1178 | GNUNET_APPLICATION_TYPE_CONVERSATION_CONTROL, | ||
1179 | GNUNET_APPLICATION_TYPE_CONVERSATION_AUDIO, | ||
1180 | 0 | ||
1181 | }; | ||
1182 | |||
1183 | cfg = c; | ||
1184 | GNUNET_assert (GNUNET_OK == | ||
1185 | GNUNET_CRYPTO_get_host_identity (cfg, | ||
1186 | &my_identity)); | ||
1187 | mesh = GNUNET_MESH_connect (cfg, | ||
1188 | NULL, | ||
1189 | &inbound_tunnel, | ||
1190 | &inbound_end, | ||
1191 | mesh_handlers, | ||
1192 | ports); | ||
1193 | |||
1194 | if (NULL == mesh) | ||
1195 | { | ||
1196 | GNUNET_break (0); | ||
1197 | GNUNET_SCHEDULER_shutdown (); | ||
1198 | return; | ||
1199 | } | ||
1200 | nc = GNUNET_SERVER_notification_context_create (server, 16); | ||
1201 | GNUNET_SERVER_add_handlers (server, server_handlers); | ||
1202 | GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL); | ||
1203 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, | ||
1204 | &do_shutdown, | ||
1205 | NULL); | ||
1206 | } | ||
1207 | |||
1208 | |||
1209 | /** | ||
1210 | * The main function for the conversation service. | ||
1211 | * | ||
1212 | * @param argc number of arguments from the command line | ||
1213 | * @param argv command line arguments | ||
1214 | * @return 0 ok, 1 on error | ||
1215 | */ | ||
1216 | int | ||
1217 | main (int argc, | ||
1218 | char *const *argv) | ||
1219 | { | ||
1220 | return (GNUNET_OK == | ||
1221 | GNUNET_SERVICE_run (argc, argv, | ||
1222 | "conversation", | ||
1223 | GNUNET_SERVICE_OPTION_NONE, | ||
1224 | &run, NULL)) ? 0 : 1; | ||
1225 | } | ||
1226 | |||
1227 | /* end of gnunet-service-conversation.c */ | ||
diff --git a/src/conversation/gnunet-service-conversation.c b/src/conversation/gnunet-service-conversation.c index 4c4bdc133..d379931f7 100644 --- a/src/conversation/gnunet-service-conversation.c +++ b/src/conversation/gnunet-service-conversation.c | |||
@@ -22,56 +22,140 @@ | |||
22 | * @brief conversation service implementation | 22 | * @brief conversation service implementation |
23 | * @author Simon Dieterle | 23 | * @author Simon Dieterle |
24 | * @author Andreas Fuchs | 24 | * @author Andreas Fuchs |
25 | * STRUCTURE: | 25 | * @author Christian Grothoff |
26 | * - Variables | ||
27 | * - AUXILIARY FUNCTIONS | ||
28 | * - SENDING FUNCTIONS CL -> SERVER | ||
29 | * - RECEIVE FUNCTIONS CL -> SERVER | ||
30 | * - SENDING FUNCTIONS MESH | ||
31 | * - RECEIVE FUNCTIONS MESH | ||
32 | * - HELPER | ||
33 | * - TUNNEL HANDLING | ||
34 | * - CLIENT HANDLING | ||
35 | */ | 26 | */ |
36 | #include "platform.h" | 27 | #include "platform.h" |
37 | #include "gnunet_util_lib.h" | 28 | #include "gnunet_util_lib.h" |
38 | #include "gnunet_protocols.h" | 29 | #include "gnunet_protocols.h" |
30 | #include "gnunet_applications.h" | ||
39 | #include "gnunet_constants.h" | 31 | #include "gnunet_constants.h" |
32 | #include "gnunet_signatures.h" | ||
40 | #include "gnunet_mesh_service.h" | 33 | #include "gnunet_mesh_service.h" |
41 | #include "gnunet_conversation_service.h" | 34 | #include "gnunet_conversation_service.h" |
42 | #include "conversation.h" | 35 | #include "conversation.h" |
43 | 36 | ||
44 | 37 | ||
38 | /** | ||
39 | * How long is our signature on a call valid? Needs to be long enough for time zone | ||
40 | * differences and network latency to not matter. No strong need for it to be short, | ||
41 | * but we simply like all signatures to eventually expire. | ||
42 | */ | ||
43 | #define RING_TIMEOUT GNUNET_TIME_UNIT_DAYS | ||
45 | 44 | ||
46 | /* | 45 | |
47 | * The possible connection status | 46 | /** |
48 | */ | 47 | * The possible connection status |
49 | enum connection_status | 48 | */ |
49 | enum LineStatus | ||
50 | { | 50 | { |
51 | LISTEN, | 51 | /** |
52 | CALLER, | 52 | * We are waiting for incoming calls. |
53 | CALLEE, | 53 | */ |
54 | CONNECTED | 54 | LS_CALLEE_LISTEN, |
55 | |||
56 | /** | ||
57 | * Our phone is ringing, waiting for the client to pick up. | ||
58 | */ | ||
59 | LS_CALLEE_RINGING, | ||
60 | |||
61 | /** | ||
62 | * We are talking! | ||
63 | */ | ||
64 | LS_CALLEE_CONNECTED, | ||
65 | |||
66 | /** | ||
67 | * We're in shutdown, sending hangup messages before cleaning up. | ||
68 | */ | ||
69 | LS_CALLEE_SHUTDOWN, | ||
70 | |||
71 | /** | ||
72 | * We are waiting for the phone to be picked up. | ||
73 | */ | ||
74 | LS_CALLER_CALLING, | ||
75 | |||
76 | /** | ||
77 | * We are talking! | ||
78 | */ | ||
79 | LS_CALLER_CONNECTED, | ||
80 | |||
81 | /** | ||
82 | * We're in shutdown, sending hangup messages before cleaning up. | ||
83 | */ | ||
84 | LS_CALLER_SHUTDOWN | ||
55 | }; | 85 | }; |
56 | 86 | ||
57 | 87 | ||
58 | /******************************************************** | 88 | /** |
59 | * Ugly hack because of not working MESH API | 89 | * A line connects a local client with a mesh tunnel (or, if it is an |
60 | */ | 90 | * open line, is waiting for a mesh tunnel). |
61 | typedef uint32_t MESH_TunnelNumber; | 91 | */ |
62 | struct GNUNET_MESH_Tunnel | 92 | struct Line |
63 | { | 93 | { |
64 | struct GNUNET_MESH_Tunnel *next; | 94 | /** |
65 | struct GNUNET_MESH_Tunnel *prev; | 95 | * Kept in a DLL. |
66 | struct GNUNET_MESH_Handle *mesh; | 96 | */ |
67 | MESH_TunnelNumber tid; | 97 | struct Line *next; |
68 | uint32_t port; | 98 | |
69 | GNUNET_PEER_Id peer; | 99 | /** |
70 | void *ctx; | 100 | * Kept in a DLL. |
71 | unsigned int packet_size; | 101 | */ |
72 | int buffering; | 102 | struct Line *prev; |
73 | int reliable; | 103 | |
74 | int allow_send; | 104 | /** |
105 | * Handle for the reliable tunnel (contol data) | ||
106 | */ | ||
107 | struct GNUNET_MESH_Tunnel *tunnel_reliable; | ||
108 | |||
109 | /** | ||
110 | * Handle for unreliable tunnel (audio data) | ||
111 | */ | ||
112 | struct GNUNET_MESH_Tunnel *tunnel_unreliable; | ||
113 | |||
114 | /** | ||
115 | * Transmit handle for pending audio messages | ||
116 | */ | ||
117 | struct GNUNET_MESH_TransmitHandle *unreliable_mth; | ||
118 | |||
119 | /** | ||
120 | * Message queue for control messages | ||
121 | */ | ||
122 | struct GNUNET_MQ_Handle *reliable_mq; | ||
123 | |||
124 | /** | ||
125 | * Handle to the line client. | ||
126 | */ | ||
127 | struct GNUNET_SERVER_Client *client; | ||
128 | |||
129 | /** | ||
130 | * Target of the line, if we are the caller. | ||
131 | */ | ||
132 | struct GNUNET_PeerIdentity target; | ||
133 | |||
134 | /** | ||
135 | * Temporary buffer for audio data. | ||
136 | */ | ||
137 | void *audio_data; | ||
138 | |||
139 | /** | ||
140 | * Number of bytes in @e audio_data. | ||
141 | */ | ||
142 | size_t audio_size; | ||
143 | |||
144 | /** | ||
145 | * Our line number. | ||
146 | */ | ||
147 | uint32_t local_line; | ||
148 | |||
149 | /** | ||
150 | * Remote line number. | ||
151 | */ | ||
152 | uint32_t remote_line; | ||
153 | |||
154 | /** | ||
155 | * Current status of this line. | ||
156 | */ | ||
157 | enum LineStatus status; | ||
158 | |||
75 | }; | 159 | }; |
76 | 160 | ||
77 | 161 | ||
@@ -81,1421 +165,831 @@ struct GNUNET_MESH_Tunnel | |||
81 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | 165 | static const struct GNUNET_CONFIGURATION_Handle *cfg; |
82 | 166 | ||
83 | /** | 167 | /** |
84 | * Head of the list of current clients. | ||
85 | */ | ||
86 | static struct GNUNET_CONTAINER_SList *clients; | ||
87 | |||
88 | /** | ||
89 | * Notification context containing all connected clients. | 168 | * Notification context containing all connected clients. |
90 | */ | 169 | */ |
91 | struct GNUNET_SERVER_NotificationContext *nc = NULL; | 170 | static struct GNUNET_SERVER_NotificationContext *nc; |
92 | |||
93 | /** | ||
94 | * The connection status | ||
95 | */ | ||
96 | static struct ConnectionStatus connection; | ||
97 | 171 | ||
98 | /** | 172 | /** |
99 | * Handle for the record helper | 173 | * Handle for mesh |
100 | */ | ||
101 | static struct GNUNET_HELPER_Handle *record_helper; | ||
102 | |||
103 | /** Handle for the playback handler | ||
104 | * | ||
105 | */ | ||
106 | static struct GNUNET_HELPER_Handle *playback_helper; | ||
107 | |||
108 | /** | ||
109 | * Handle for mesh | ||
110 | */ | ||
111 | static struct GNUNET_MESH_Handle *mesh; | ||
112 | |||
113 | /** | ||
114 | * Transmit handle for audio messages | ||
115 | */ | ||
116 | static struct GNUNET_MESH_TransmitHandle *mth = NULL; | ||
117 | |||
118 | /** | ||
119 | * Handle for the reliable tunnel (contol data) | ||
120 | */ | ||
121 | static struct GNUNET_MESH_Tunnel *tunnel_reliable; | ||
122 | |||
123 | /** | ||
124 | * Handle for unreliable tunnel (audio data) | ||
125 | */ | ||
126 | static struct GNUNET_MESH_Tunnel *tunnel_unreliable; | ||
127 | |||
128 | /** | ||
129 | * List for missed calls | ||
130 | */ | ||
131 | static struct GNUNET_CONTAINER_SList *missed_calls; | ||
132 | |||
133 | /** | ||
134 | * List for peers to notify that we are available again | ||
135 | */ | ||
136 | static struct GNUNET_CONTAINER_SList *peers_to_notify; | ||
137 | |||
138 | /** | ||
139 | * Audio buffer (outgoing) | ||
140 | */ | ||
141 | static struct GNUNET_CONTAINER_SList *audio_buffer; | ||
142 | |||
143 | /** | ||
144 | * The pointer to the task for sending audio | ||
145 | */ | ||
146 | static GNUNET_SCHEDULER_TaskIdentifier audio_task; | ||
147 | |||
148 | /** | ||
149 | * The pointer to the task for checking timeouts an calling a peer | ||
150 | */ | ||
151 | static GNUNET_SCHEDULER_TaskIdentifier timeout_task; | ||
152 | |||
153 | /** | ||
154 | * Timestamp for call statistics | ||
155 | */ | ||
156 | static struct GNUNET_TIME_Absolute start_time; | ||
157 | |||
158 | /** | ||
159 | * Number of payload packes sent | ||
160 | */ | 174 | */ |
161 | static int data_sent; | 175 | static struct GNUNET_MESH_Handle *mesh; |
162 | static int data_sent_size; | ||
163 | 176 | ||
164 | /** | 177 | /** |
165 | * Number of payload packets received | 178 | * Identity of this peer. |
166 | */ | 179 | */ |
167 | static int data_received; | 180 | static struct GNUNET_PeerIdentity my_identity; |
168 | static int data_received_size; | ||
169 | |||
170 | 181 | ||
171 | /** | 182 | /** |
172 | * Transmit a mesh message | 183 | * Head of DLL of active lines. |
173 | * @param cls closure, NULL | ||
174 | * @param size number of bytes available in buf | ||
175 | * @param buf where the callee should write the error message | ||
176 | * @return number of bytes written to buf | ||
177 | */ | 184 | */ |
178 | static size_t transmit_mesh_message (void *cls, size_t size, void *buf); | 185 | static struct Line *lines_head; |
179 | 186 | ||
180 | /** | 187 | /** |
181 | * Function called to send a peer no answer message to the client. | 188 | * Tail of DLL of active lines. |
182 | * "buf" will be NULL and "size" zero if the socket was closed for writing in | ||
183 | * the meantime. | ||
184 | * | ||
185 | * @param cls closure, NULL | ||
186 | * @param size number of bytes available in buf | ||
187 | * @param buf where the callee should write the peer no answer message | ||
188 | * @return number of bytes written to buf | ||
189 | */ | 189 | */ |
190 | static size_t | 190 | static struct Line *lines_tail; |
191 | transmit_server_no_answer_message (void *cls, size_t size, void *buf); | ||
192 | 191 | ||
193 | /** | 192 | /** |
194 | * Task to schedule a audio transmission. | 193 | * Counter for generating local line numbers. |
195 | * | 194 | * FIXME: randomize generation in the future |
196 | * @param cls Closure. | 195 | * to eliminate information leakage. |
197 | * @param tc Task Context. | ||
198 | */ | 196 | */ |
199 | static void | 197 | static uint32_t local_line_cnt; |
200 | transmit_audio_task (void *cls, | ||
201 | const struct GNUNET_SCHEDULER_TaskContext *tc); | ||
202 | 198 | ||
203 | /** | ||
204 | * Start the audio helpers | ||
205 | */ | ||
206 | int start_helpers (void); | ||
207 | 199 | ||
208 | /** | 200 | /** |
209 | * Stop the audio helpers | 201 | * Function to register a phone. |
210 | */ | ||
211 | void stop_helpers (void); | ||
212 | |||
213 | |||
214 | |||
215 | /******************************************************************************/ | ||
216 | /*********************** AUXILIARY FUNCTIONS *************************/ | ||
217 | /******************************************************************************/ | ||
218 | |||
219 | /** | ||
220 | * Function which displays some call stats | ||
221 | */ | ||
222 | static void | ||
223 | show_end_data (void) | ||
224 | { | ||
225 | static struct GNUNET_TIME_Absolute end_time; | ||
226 | static struct GNUNET_TIME_Relative total_time; | ||
227 | |||
228 | end_time = GNUNET_TIME_absolute_get (); | ||
229 | total_time = GNUNET_TIME_absolute_get_difference (start_time, end_time); | ||
230 | FPRINTF (stderr, "\nResults of send\n"); | ||
231 | FPRINTF (stderr, "Test time %s\n", | ||
232 | GNUNET_STRINGS_relative_time_to_string (total_time, | ||
233 | GNUNET_NO)); | ||
234 | FPRINTF (stderr, "Test total packets: %d\n", | ||
235 | data_sent); | ||
236 | FPRINTF (stderr, "Test bandwidth: %f kb/s\n", | ||
237 | data_sent_size * 1000.0 / (total_time.rel_value_us + 1)); // 4bytes * us | ||
238 | FPRINTF (stderr, "Test throughput: %f packets/s\n\n", | ||
239 | data_sent * 1000000.0 / (total_time.rel_value_us + 1)); // packets * us | ||
240 | |||
241 | FPRINTF (stderr, "\nResults of recv\n"); | ||
242 | FPRINTF (stderr, "Test time %s\n", | ||
243 | GNUNET_STRINGS_relative_time_to_string (total_time, | ||
244 | GNUNET_NO)); | ||
245 | FPRINTF (stderr, "Test total packets: %d\n", | ||
246 | data_received); | ||
247 | FPRINTF (stderr, "Test bandwidth: %f kb/s\n", | ||
248 | data_received_size * 1000.0 / (total_time.rel_value_us + 1)); // 4bytes * us | ||
249 | FPRINTF (stderr, "Test throughput: %f packets/s\n\n", | ||
250 | data_received * 1000000.0 / (total_time.rel_value_us + 1)); // packets * us | ||
251 | } | ||
252 | |||
253 | /** | ||
254 | * Function which sets the connection state to LISTEN | ||
255 | */ | ||
256 | static void | ||
257 | status_to_listen (void) | ||
258 | { | ||
259 | |||
260 | if (CONNECTED == connection.status) | ||
261 | { | ||
262 | show_end_data (); | ||
263 | } | ||
264 | |||
265 | if (timeout_task != GNUNET_SCHEDULER_NO_TASK) | ||
266 | { | ||
267 | GNUNET_SCHEDULER_cancel (timeout_task); | ||
268 | timeout_task = GNUNET_SCHEDULER_NO_TASK; | ||
269 | } | ||
270 | |||
271 | stop_helpers (); | ||
272 | |||
273 | connection.status = LISTEN; | ||
274 | connection.client = NULL; | ||
275 | |||
276 | data_sent = 0; | ||
277 | data_sent_size = 0; | ||
278 | data_received = 0; | ||
279 | data_received_size = 0; | ||
280 | |||
281 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Changed connection status to %s\n"), | ||
282 | "LISTEN"); | ||
283 | } | ||
284 | |||
285 | /** | ||
286 | * Function to terminate the active call | ||
287 | */ | ||
288 | static void | ||
289 | terminate_call () | ||
290 | { | ||
291 | size_t msg_size; | ||
292 | msg_size = sizeof (struct MeshSessionTerminateMessage); | ||
293 | struct MeshSessionTerminateMessage *message_mesh_terminate = | ||
294 | (struct MeshSessionTerminateMessage *) GNUNET_malloc (msg_size); | ||
295 | |||
296 | if (NULL == message_mesh_terminate) | ||
297 | { | ||
298 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
299 | _("Could not create MeshSessionTerminateMessage\n")); | ||
300 | status_to_listen (); | ||
301 | |||
302 | return; | ||
303 | } | ||
304 | |||
305 | message_mesh_terminate->header.size = htons (msg_size); | ||
306 | message_mesh_terminate->header.type = | ||
307 | htons (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_TERMINATE); | ||
308 | |||
309 | if (NULL == | ||
310 | GNUNET_MESH_notify_transmit_ready (tunnel_reliable, 0, | ||
311 | MAX_TRANSMIT_DELAY, msg_size, | ||
312 | &transmit_mesh_message, | ||
313 | (void *) message_mesh_terminate)) | ||
314 | { | ||
315 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
316 | _("Could not queue MeshSessionTerminateMessage\n")); | ||
317 | GNUNET_free (message_mesh_terminate); | ||
318 | status_to_listen (); | ||
319 | } | ||
320 | } | ||
321 | |||
322 | /** | ||
323 | * Function to reject a call | ||
324 | * | ||
325 | * @param tunnel the tunnel where to reject the incoming call | ||
326 | * @param reason te reson why the call is rejected | ||
327 | */ | ||
328 | static void | ||
329 | reject_call (struct GNUNET_MESH_Tunnel *tunnel, int reason) | ||
330 | { | ||
331 | size_t msg_size; | ||
332 | msg_size = sizeof (struct MeshSessionRejectMessage); | ||
333 | struct MeshSessionRejectMessage *message_mesh_reject = | ||
334 | (struct MeshSessionRejectMessage *) GNUNET_malloc (msg_size); | ||
335 | |||
336 | if (NULL == message_mesh_reject) | ||
337 | { | ||
338 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
339 | _("Could not create MeshSessionRejectMessage\n")); | ||
340 | status_to_listen (); | ||
341 | |||
342 | return; | ||
343 | } | ||
344 | |||
345 | message_mesh_reject->header.size = htons (msg_size); | ||
346 | message_mesh_reject->header.type = | ||
347 | htons (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_REJECT); | ||
348 | message_mesh_reject->reason = htons (reason); | ||
349 | |||
350 | if (NULL == | ||
351 | GNUNET_MESH_notify_transmit_ready (tunnel_reliable, 0, | ||
352 | MAX_TRANSMIT_DELAY, msg_size, | ||
353 | &transmit_mesh_message, | ||
354 | (void *) message_mesh_reject)) | ||
355 | { | ||
356 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
357 | _("Could not queue MeshSessionRejectMessage\n")); | ||
358 | GNUNET_free (message_mesh_reject); | ||
359 | status_to_listen (); | ||
360 | } | ||
361 | } | ||
362 | |||
363 | /** | ||
364 | * Check for timeout when calling a peer | ||
365 | * | 202 | * |
366 | * @param cls closure, NULL | 203 | * @param cls closure, NULL |
367 | * @param tc the task context (can be NULL) | 204 | * @param client the client from which the message is |
205 | * @param message the message from the client | ||
368 | */ | 206 | */ |
369 | static void | 207 | static void |
370 | check_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | 208 | handle_client_register_message (void *cls, |
371 | { | 209 | struct GNUNET_SERVER_Client *client, |
372 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Call timeout\n"); | 210 | const struct GNUNET_MessageHeader *message) |
373 | |||
374 | if (NULL == | ||
375 | GNUNET_SERVER_notify_transmit_ready (connection.client, | ||
376 | sizeof (struct | ||
377 | ServerClientNoAnswerMessage), | ||
378 | MAX_TRANSMIT_DELAY, | ||
379 | &transmit_server_no_answer_message, | ||
380 | NULL)) | ||
381 | { | ||
382 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
383 | _("Could not queue ServerClientNoAnswerMessage\n")); | ||
384 | } | ||
385 | |||
386 | terminate_call (); | ||
387 | } | ||
388 | |||
389 | /******************************************************************************/ | ||
390 | /*********************** SENDING FUNCTIONS CL -> SERVER *******************/ | ||
391 | /******************************************************************************/ | ||
392 | |||
393 | /** | ||
394 | * Function called to send a session initiate message to the client. | ||
395 | * "buf" will be NULL and "size" zero if the socket was closed for writing in | ||
396 | * the meantime. | ||
397 | * | ||
398 | * @param cls closure, NULL | ||
399 | * @param size number of bytes available in buf | ||
400 | * @param buf where the callee should write the initiate message | ||
401 | * @return number of bytes written to buf | ||
402 | */ | ||
403 | static size_t | ||
404 | transmit_server_initiate_message (void *cls, size_t size, void *buf) | ||
405 | { | ||
406 | struct ServerClientSessionInitiateMessage *msg; | ||
407 | size_t msg_size; | ||
408 | |||
409 | msg_size = sizeof (struct ServerClientSessionInitiateMessage); | ||
410 | |||
411 | GNUNET_assert (size >= msg_size); | ||
412 | |||
413 | msg = (struct ServerClientSessionInitiateMessage *) buf; | ||
414 | msg->header.size = htons (msg_size); | ||
415 | msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_INITIATE); | ||
416 | memcpy (&(msg->peer), (struct GNUNET_PeerIdentity *) cls, | ||
417 | sizeof (struct GNUNET_PeerIdentity)); | ||
418 | |||
419 | return msg_size; | ||
420 | } | ||
421 | |||
422 | /** | ||
423 | * Function called to send a session accept message to the client. | ||
424 | * "buf" will be NULL and "size" zero if the socket was closed for writing in | ||
425 | * the meantime. | ||
426 | * | ||
427 | * @param cls closure, NULL | ||
428 | * @param size number of bytes available in buf | ||
429 | * @param buf where the callee should write the accept message | ||
430 | * @return number of bytes written to buf | ||
431 | */ | ||
432 | static size_t | ||
433 | transmit_server_accept_message (void *cls, size_t size, void *buf) | ||
434 | { | 211 | { |
435 | struct ServerClientSessionAcceptMessage *msg; | 212 | const struct ClientPhoneRegisterMessage *msg; |
436 | size_t msg_size; | 213 | struct Line *line; |
437 | 214 | ||
438 | msg_size = sizeof (struct ServerClientSessionAcceptMessage); | 215 | msg = (struct ClientPhoneRegisterMessage *) message; |
439 | 216 | line = GNUNET_SERVER_client_get_user_context (client, struct Line); | |
440 | GNUNET_assert (size >= msg_size); | 217 | if (NULL != line) |
441 | 218 | { | |
442 | msg = (struct ServerClientSessionAcceptMessage *) buf; | 219 | GNUNET_break (0); |
443 | msg->header.size = htons (msg_size); | 220 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); |
444 | msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_ACCEPT); | 221 | return; |
445 | 222 | } | |
446 | return msg_size; | 223 | line = GNUNET_new (struct Line); |
224 | line->client = client; | ||
225 | GNUNET_SERVER_notification_context_add (nc, client); | ||
226 | GNUNET_CONTAINER_DLL_insert (lines_head, | ||
227 | lines_tail, | ||
228 | line); | ||
229 | line->local_line = ntohl (msg->line); | ||
230 | GNUNET_SERVER_client_set_user_context (client, line); | ||
231 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
447 | } | 232 | } |
448 | 233 | ||
449 | /** | ||
450 | * Function called to send a session reject message to the client. | ||
451 | * "buf" will be NULL and "size" zero if the socket was closed for writing in | ||
452 | * the meantime. | ||
453 | * | ||
454 | * @param cls closure, NULL | ||
455 | * @param size number of bytes available in buf | ||
456 | * @param buf where the callee should write the reject message | ||
457 | * @return number of bytes written to buf | ||
458 | */ | ||
459 | static size_t | ||
460 | transmit_server_reject_message (void *cls, size_t size, void *buf) | ||
461 | { | ||
462 | struct ServerClientSessionRejectMessage *msg; | ||
463 | size_t msg_size; | ||
464 | |||
465 | msg_size = sizeof (struct ServerClientSessionRejectMessage); | ||
466 | |||
467 | GNUNET_assert (size >= msg_size); | ||
468 | |||
469 | msg = (struct ServerClientSessionRejectMessage *) buf; | ||
470 | msg->header.size = htons (msg_size); | ||
471 | msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_REJECT); | ||
472 | |||
473 | if (NULL == cls) | ||
474 | { | ||
475 | msg->reason = htons (GNUNET_CONVERSATION_REJECT_REASON_NOT_AVAILABLE); | ||
476 | } | ||
477 | else | ||
478 | { | ||
479 | msg->reason = ((struct MeshSessionRejectMessage *) cls)->reason; | ||
480 | } | ||
481 | |||
482 | return msg_size; | ||
483 | } | ||
484 | 234 | ||
485 | /** | 235 | /** |
486 | * Function called to send a session terminate message to the client. | 236 | * Function to handle a pickup request message from the client |
487 | * "buf" will be NULL and "size" zero if the socket was closed for writing in | ||
488 | * the meantime. | ||
489 | * | 237 | * |
490 | * @param cls closure, NULL | 238 | * @param cls closure, NULL |
491 | * @param size number of bytes available in buf | 239 | * @param client the client from which the message is |
492 | * @param buf where the callee should write the terminate message | 240 | * @param message the message from the client |
493 | * @return number of bytes written to buf | ||
494 | */ | 241 | */ |
495 | static size_t | 242 | static void |
496 | transmit_server_terminate_message (void *cls, size_t size, void *buf) | 243 | handle_client_pickup_message (void *cls, |
244 | struct GNUNET_SERVER_Client *client, | ||
245 | const struct GNUNET_MessageHeader *message) | ||
497 | { | 246 | { |
498 | struct ServerClientSessionTerminateMessage *msg; | 247 | const struct ClientPhonePickupMessage *msg; |
499 | size_t msg_size; | 248 | struct GNUNET_MQ_Envelope *e; |
500 | 249 | struct MeshPhonePickupMessage *mppm; | |
501 | msg_size = sizeof (struct ServerClientSessionTerminateMessage); | 250 | const char *meta; |
502 | 251 | struct Line *line; | |
503 | GNUNET_assert (size >= msg_size); | 252 | size_t len; |
504 | 253 | ||
505 | msg = (struct ServerClientSessionTerminateMessage *) buf; | 254 | msg = (struct ClientPhonePickupMessage *) message; |
506 | msg->header.size = htons (msg_size); | 255 | meta = (const char *) &msg[1]; |
507 | msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_TERMINATE); | 256 | len = ntohs (msg->header.size) - sizeof (struct ClientPhonePickupMessage); |
508 | 257 | if ( (0 == len) || | |
509 | return msg_size; | 258 | ('\0' != meta[len - 1]) ) |
259 | { | ||
260 | meta = NULL; | ||
261 | len = 0; | ||
262 | } | ||
263 | line = GNUNET_SERVER_client_get_user_context (client, struct Line); | ||
264 | if (NULL == line) | ||
265 | { | ||
266 | GNUNET_break (0); | ||
267 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | ||
268 | return; | ||
269 | } | ||
270 | switch (line->status) | ||
271 | { | ||
272 | case LS_CALLEE_LISTEN: | ||
273 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
274 | "Ignoring client's PICKUP message, caller has HUNG UP already\n"); | ||
275 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
276 | break; | ||
277 | case LS_CALLEE_RINGING: | ||
278 | line->status = LS_CALLEE_CONNECTED; | ||
279 | break; | ||
280 | case LS_CALLEE_CONNECTED: | ||
281 | GNUNET_break (0); | ||
282 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | ||
283 | return; | ||
284 | case LS_CALLEE_SHUTDOWN: | ||
285 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
286 | "Ignoring client's PICKUP message, line is in SHUTDOWN\n"); | ||
287 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
288 | break; | ||
289 | case LS_CALLER_CALLING: | ||
290 | case LS_CALLER_CONNECTED: | ||
291 | case LS_CALLER_SHUTDOWN: | ||
292 | GNUNET_break (0); | ||
293 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | ||
294 | return; | ||
295 | } | ||
296 | line->status = LS_CALLEE_CONNECTED; | ||
297 | e = GNUNET_MQ_msg_extra (mppm, | ||
298 | len, | ||
299 | GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_PICK_UP); | ||
300 | memcpy (&mppm[1], meta, len); | ||
301 | GNUNET_MQ_send (line->reliable_mq, e); | ||
302 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
510 | } | 303 | } |
511 | 304 | ||
512 | /** | ||
513 | * Function called to send a missed call message to the client. | ||
514 | * "buf" will be NULL and "size" zero if the socket was closed for writing in | ||
515 | * the meantime. | ||
516 | * | ||
517 | * @param cls closure, NULL | ||
518 | * @param size number of bytes available in buf | ||
519 | * @param buf where the callee should write the missed call message | ||
520 | * @return number of bytes written to buf | ||
521 | */ | ||
522 | static size_t | ||
523 | transmit_server_missed_call_message (void *cls, size_t size, void *buf) | ||
524 | { | ||
525 | struct ServerClientMissedCallMessage *msg; | ||
526 | msg = (struct ServerClientMissedCallMessage *) cls; | ||
527 | |||
528 | memcpy (buf, msg, size); | ||
529 | GNUNET_free (msg); | ||
530 | |||
531 | return size; | ||
532 | } | ||
533 | 305 | ||
534 | /** | 306 | /** |
535 | * Function called to send a service blocked message to the client. | 307 | * Destroy the mesh tunnels of a line. |
536 | * "buf" will be NULL and "size" zero if the socket was closed for writing in | ||
537 | * the meantime. | ||
538 | * | 308 | * |
539 | * @param cls closure, NULL | 309 | * @param line line to shutdown tunnels of |
540 | * @param size number of bytes available in buf | ||
541 | * @param buf where the callee should write the service blocked message | ||
542 | * @return number of bytes written to buf | ||
543 | */ | 310 | */ |
544 | static size_t | 311 | static void |
545 | transmit_server_service_blocked_message (void *cls, size_t size, void *buf) | 312 | destroy_line_mesh_tunnels (struct Line *line) |
546 | { | 313 | { |
547 | struct ServerClientServiceBlockedMessage *msg; | 314 | if (NULL != line->reliable_mq) |
548 | size_t msg_size; | 315 | { |
549 | 316 | GNUNET_MQ_destroy (line->reliable_mq); | |
550 | msg_size = sizeof (struct ServerClientServiceBlockedMessage); | 317 | line->reliable_mq = NULL; |
551 | 318 | } | |
552 | GNUNET_assert (size >= msg_size); | 319 | if (NULL != line->unreliable_mth) |
553 | 320 | { | |
554 | msg = (struct ServerClientServiceBlockedMessage *) buf; | 321 | GNUNET_MESH_notify_transmit_ready_cancel (line->unreliable_mth); |
555 | msg->header.size = htons (msg_size); | 322 | line->unreliable_mth = NULL; |
556 | msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SERVICE_BLOCKED); | 323 | } |
557 | 324 | if (NULL != line->tunnel_unreliable) | |
558 | return msg_size; | 325 | { |
326 | GNUNET_MESH_tunnel_destroy (line->tunnel_unreliable); | ||
327 | line->tunnel_unreliable = NULL; | ||
328 | } | ||
329 | if (NULL != line->tunnel_reliable) | ||
330 | { | ||
331 | GNUNET_MESH_tunnel_destroy (line->tunnel_reliable); | ||
332 | line->tunnel_reliable = NULL; | ||
333 | } | ||
559 | } | 334 | } |
560 | 335 | ||
561 | /** | ||
562 | * Function called to send a peer not connected message to the client. | ||
563 | * "buf" will be NULL and "size" zero if the socket was closed for writing in | ||
564 | * the meantime. | ||
565 | * | ||
566 | * @param cls closure, NULL | ||
567 | * @param size number of bytes available in buf | ||
568 | * @param buf where the callee should write the peer not connected message | ||
569 | * @return number of bytes written to buf | ||
570 | */ | ||
571 | static size_t | ||
572 | transmit_server_peer_not_connected_message (void *cls, size_t size, void *buf) | ||
573 | { | ||
574 | struct ServerClientPeerNotConnectedMessage *msg; | ||
575 | size_t msg_size; | ||
576 | |||
577 | msg_size = sizeof (struct ServerClientPeerNotConnectedMessage); | ||
578 | |||
579 | GNUNET_assert (size >= msg_size); | ||
580 | |||
581 | msg = (struct ServerClientPeerNotConnectedMessage *) buf; | ||
582 | msg->header.size = htons (msg_size); | ||
583 | msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_PEER_NOT_CONNECTED); | ||
584 | |||
585 | return msg_size; | ||
586 | } | ||
587 | 336 | ||
588 | /** | 337 | /** |
589 | * Function called to send a peer no answer message to the client. | 338 | * We are done signalling shutdown to the other peer. Close down |
590 | * "buf" will be NULL and "size" zero if the socket was closed for writing in | 339 | * (or reset) the line. |
591 | * the meantime. | ||
592 | * | 340 | * |
593 | * @param cls closure, NULL | 341 | * @param cls the `struct Line` to reset/terminate |
594 | * @param size number of bytes available in buf | ||
595 | * @param buf where the callee should write the peer no answer message | ||
596 | * @return number of bytes written to buf | ||
597 | */ | 342 | */ |
598 | static size_t | 343 | static void |
599 | transmit_server_no_answer_message (void *cls, size_t size, void *buf) | 344 | mq_done_finish_caller_shutdown (void *cls) |
600 | { | ||
601 | struct ServerClientNoAnswerMessage *msg; | ||
602 | size_t msg_size; | ||
603 | |||
604 | msg_size = sizeof (struct ServerClientNoAnswerMessage); | ||
605 | |||
606 | GNUNET_assert (size >= msg_size); | ||
607 | |||
608 | msg = (struct ServerClientNoAnswerMessage *) buf; | ||
609 | msg->header.size = htons (msg_size); | ||
610 | msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_NO_ANSWER); | ||
611 | |||
612 | return msg_size; | ||
613 | } | ||
614 | |||
615 | /** | ||
616 | * Function called to send a error message to the client. | ||
617 | * "buf" will be NULL and "size" zero if the socket was closed for writing in | ||
618 | * the meantime. | ||
619 | * | ||
620 | * @param cls closure, NULL | ||
621 | * @param size number of bytes available in buf | ||
622 | * @param buf where the callee should write the error message | ||
623 | * @return number of bytes written to buf | ||
624 | */ | ||
625 | static size_t | ||
626 | transmit_server_error_message (void *cls, size_t size, void *buf) | ||
627 | { | 345 | { |
628 | struct ServerClientErrorMessage *msg; | 346 | struct Line *line = cls; |
629 | size_t msg_size; | 347 | |
630 | 348 | switch (line->status) | |
631 | msg_size = sizeof (struct ServerClientErrorMessage); | 349 | { |
632 | 350 | case LS_CALLEE_LISTEN: | |
633 | GNUNET_assert (size >= msg_size); | 351 | GNUNET_break (0); |
634 | 352 | break; | |
635 | msg = (struct ServerClientErrorMessage *) buf; | 353 | case LS_CALLEE_RINGING: |
636 | msg->header.size = htons (msg_size); | 354 | GNUNET_break (0); |
637 | msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_ERROR); | 355 | break; |
638 | 356 | case LS_CALLEE_CONNECTED: | |
639 | return msg_size; | 357 | GNUNET_break (0); |
358 | break; | ||
359 | case LS_CALLEE_SHUTDOWN: | ||
360 | line->status = LS_CALLEE_LISTEN; | ||
361 | destroy_line_mesh_tunnels (line); | ||
362 | return; | ||
363 | case LS_CALLER_CALLING: | ||
364 | line->status = LS_CALLER_SHUTDOWN; | ||
365 | break; | ||
366 | case LS_CALLER_CONNECTED: | ||
367 | line->status = LS_CALLER_SHUTDOWN; | ||
368 | break; | ||
369 | case LS_CALLER_SHUTDOWN: | ||
370 | destroy_line_mesh_tunnels (line); | ||
371 | GNUNET_CONTAINER_DLL_remove (lines_head, | ||
372 | lines_tail, | ||
373 | line); | ||
374 | GNUNET_free_non_null (line->audio_data); | ||
375 | GNUNET_free (line); | ||
376 | break; | ||
377 | } | ||
640 | } | 378 | } |
641 | 379 | ||
642 | /******************************************************************************/ | ||
643 | /*********************** RECEIVE FUNCTIONS CL -> SERVER ********************/ | ||
644 | /******************************************************************************/ | ||
645 | 380 | ||
646 | /** | 381 | /** |
647 | * Function to handle a session initiate message from the client | 382 | * Function to handle a hangup request message from the client |
648 | * | 383 | * |
649 | * @param cls closure, NULL | 384 | * @param cls closure, NULL |
650 | * @param client the client from which the message is | 385 | * @param client the client from which the message is |
651 | * @param message the message from the client | 386 | * @param message the message from the client |
652 | */ | 387 | */ |
653 | static void | 388 | static void |
654 | handle_session_initiate_message (void *cls, | 389 | handle_client_hangup_message (void *cls, |
655 | struct GNUNET_SERVER_Client *client, | 390 | struct GNUNET_SERVER_Client *client, |
656 | const struct GNUNET_MessageHeader *message) | 391 | const struct GNUNET_MessageHeader *message) |
657 | { | 392 | { |
658 | static uint32_t port = 50002; | 393 | const struct ClientPhoneHangupMessage *msg; |
659 | size_t msg_size; | 394 | struct GNUNET_MQ_Envelope *e; |
660 | struct ClientServerSessionInitiateMessage *msg = | 395 | struct MeshPhoneHangupMessage *mhum; |
661 | (struct ClientServerSessionInitiateMessage *) message; | 396 | const char *meta; |
662 | struct GNUNET_PeerIdentity *peer = &(msg->peer); | 397 | struct Line *line; |
663 | 398 | size_t len; | |
399 | |||
400 | msg = (struct ClientPhoneHangupMessage *) message; | ||
401 | meta = (const char *) &msg[1]; | ||
402 | len = ntohs (msg->header.size) - sizeof (struct ClientPhoneHangupMessage); | ||
403 | if ( (0 == len) || | ||
404 | ('\0' != meta[len - 1]) ) | ||
405 | { | ||
406 | meta = NULL; | ||
407 | len = 0; | ||
408 | } | ||
409 | line = GNUNET_SERVER_client_get_user_context (client, struct Line); | ||
410 | if (NULL == line) | ||
411 | { | ||
412 | GNUNET_break (0); | ||
413 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | ||
414 | return; | ||
415 | } | ||
416 | switch (line->status) | ||
417 | { | ||
418 | case LS_CALLEE_LISTEN: | ||
419 | GNUNET_break (0); | ||
420 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
421 | return; | ||
422 | case LS_CALLEE_RINGING: | ||
423 | line->status = LS_CALLEE_SHUTDOWN; | ||
424 | break; | ||
425 | case LS_CALLEE_CONNECTED: | ||
426 | line->status = LS_CALLEE_SHUTDOWN; | ||
427 | break; | ||
428 | case LS_CALLEE_SHUTDOWN: | ||
429 | GNUNET_break (0); | ||
430 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
431 | return; | ||
432 | case LS_CALLER_CALLING: | ||
433 | line->status = LS_CALLER_SHUTDOWN; | ||
434 | break; | ||
435 | case LS_CALLER_CONNECTED: | ||
436 | line->status = LS_CALLER_SHUTDOWN; | ||
437 | break; | ||
438 | case LS_CALLER_SHUTDOWN: | ||
439 | GNUNET_break (0); | ||
440 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
441 | return; | ||
442 | } | ||
443 | e = GNUNET_MQ_msg_extra (mhum, | ||
444 | len, | ||
445 | GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP); | ||
446 | memcpy (&mhum[1], meta, len); | ||
447 | GNUNET_MQ_notify_sent (e, | ||
448 | &mq_done_finish_caller_shutdown, | ||
449 | line); | ||
450 | GNUNET_MQ_send (line->reliable_mq, e); | ||
664 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | 451 | GNUNET_SERVER_receive_done (client, GNUNET_OK); |
665 | |||
666 | if (NULL != connection.client) | ||
667 | { | ||
668 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
669 | _("There is already a peer in interaction\n")); | ||
670 | GNUNET_SERVER_notify_transmit_ready (client, | ||
671 | sizeof (struct | ||
672 | ServerClientServiceBlockedMessage), | ||
673 | MAX_TRANSMIT_DELAY, | ||
674 | &transmit_server_service_blocked_message, | ||
675 | NULL); | ||
676 | |||
677 | return; | ||
678 | } | ||
679 | |||
680 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Creating tunnel to: %s\n"), | ||
681 | GNUNET_i2s_full (peer)); | ||
682 | tunnel_reliable = | ||
683 | GNUNET_MESH_tunnel_create (mesh, NULL, peer, port, GNUNET_NO, GNUNET_NO); | ||
684 | if (NULL == tunnel_reliable) | ||
685 | { | ||
686 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
687 | _("Could not create reliable tunnel\n")); | ||
688 | GNUNET_SERVER_notify_transmit_ready (client, | ||
689 | sizeof (struct | ||
690 | ServerClientPeerNotConnectedMessage), | ||
691 | MAX_TRANSMIT_DELAY, | ||
692 | &transmit_server_peer_not_connected_message, | ||
693 | NULL); | ||
694 | |||
695 | return; | ||
696 | } | ||
697 | |||
698 | msg_size = sizeof (struct MeshSessionInitiateMessage); | ||
699 | struct MeshSessionInitiateMessage *message_mesh_initiate = | ||
700 | (struct MeshSessionInitiateMessage *) GNUNET_malloc (msg_size); | ||
701 | |||
702 | if (NULL == message_mesh_initiate) | ||
703 | { | ||
704 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
705 | _("Could not create MeshSessionInitiateMessage\n")); | ||
706 | GNUNET_MESH_tunnel_destroy (tunnel_reliable); | ||
707 | tunnel_reliable = NULL; | ||
708 | GNUNET_SERVER_notify_transmit_ready (client, | ||
709 | sizeof (struct | ||
710 | ServerClientErrorMessage), | ||
711 | MAX_TRANSMIT_DELAY, | ||
712 | &transmit_server_error_message, | ||
713 | NULL); | ||
714 | |||
715 | return; | ||
716 | } | ||
717 | |||
718 | message_mesh_initiate->header.size = htons (msg_size); | ||
719 | message_mesh_initiate->header.type = | ||
720 | htons (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_INITIATE); | ||
721 | |||
722 | if (NULL == | ||
723 | GNUNET_MESH_notify_transmit_ready (tunnel_reliable, 0, | ||
724 | MAX_TRANSMIT_DELAY, msg_size, | ||
725 | &transmit_mesh_message, | ||
726 | (void *) message_mesh_initiate)) | ||
727 | { | ||
728 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
729 | _("Could not queue MeshSessionInitiateMessage\n")); | ||
730 | GNUNET_MESH_tunnel_destroy (tunnel_reliable); | ||
731 | tunnel_reliable = NULL; | ||
732 | GNUNET_free (message_mesh_initiate); | ||
733 | GNUNET_SERVER_notify_transmit_ready (client, | ||
734 | sizeof (struct | ||
735 | ServerClientErrorMessage), | ||
736 | MAX_TRANSMIT_DELAY, | ||
737 | &transmit_server_error_message, | ||
738 | NULL); | ||
739 | |||
740 | return; | ||
741 | } | ||
742 | |||
743 | connection.status = CALLER; | ||
744 | connection.client = client; | ||
745 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Changed connection status to %d\n"), | ||
746 | connection.status); | ||
747 | memcpy (&(connection.peer), peer, sizeof (struct GNUNET_PeerIdentity)); | ||
748 | |||
749 | return; | ||
750 | } | 452 | } |
751 | 453 | ||
454 | |||
752 | /** | 455 | /** |
753 | * Function to handle a session accept message from the client | 456 | * Function to handle call request the client |
754 | * | 457 | * |
755 | * @param cls closure, NULL | 458 | * @param cls closure, NULL |
756 | * @param client the client from which the message is | 459 | * @param client the client from which the message is |
757 | * @param message the message from the client | 460 | * @param message the message from the client |
758 | */ | 461 | */ |
759 | static void | 462 | static void |
760 | handle_session_accept_message (void *cls, struct GNUNET_SERVER_Client *client, | 463 | handle_client_call_message (void *cls, |
761 | const struct GNUNET_MessageHeader *message) | 464 | struct GNUNET_SERVER_Client *client, |
465 | const struct GNUNET_MessageHeader *message) | ||
762 | { | 466 | { |
763 | size_t msg_size; | 467 | const struct ClientCallMessage *msg; |
764 | 468 | struct Line *line; | |
469 | struct GNUNET_MQ_Envelope *e; | ||
470 | struct MeshPhoneRingMessage *ring; | ||
471 | |||
472 | msg = (struct ClientCallMessage *) message; | ||
473 | line = GNUNET_SERVER_client_get_user_context (client, struct Line); | ||
474 | if (NULL != line) | ||
475 | { | ||
476 | GNUNET_break (0); | ||
477 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | ||
478 | return; | ||
479 | } | ||
480 | line = GNUNET_new (struct Line); | ||
481 | line->target = msg->target; | ||
482 | GNUNET_CONTAINER_DLL_insert (lines_head, | ||
483 | lines_tail, | ||
484 | line); | ||
485 | line->remote_line = ntohl (msg->line); | ||
486 | line->status = LS_CALLER_CALLING; | ||
487 | line->tunnel_reliable = GNUNET_MESH_tunnel_create (mesh, | ||
488 | line, | ||
489 | &msg->target, | ||
490 | GNUNET_APPLICATION_TYPE_CONVERSATION_CONTROL, | ||
491 | GNUNET_NO, | ||
492 | GNUNET_YES); | ||
493 | line->reliable_mq = GNUNET_MESH_mq_create (line->tunnel_reliable); | ||
494 | line->local_line = local_line_cnt++; | ||
495 | e = GNUNET_MQ_msg (ring, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RING); | ||
496 | ring->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING); | ||
497 | ring->purpose.size = htonl (sizeof (struct GNUNET_PeerIdentity) * 2 + | ||
498 | sizeof (struct GNUNET_TIME_AbsoluteNBO) + | ||
499 | sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) + | ||
500 | sizeof (struct GNUNET_CRYPTO_EccPublicSignKey)); | ||
501 | GNUNET_CRYPTO_ecc_key_get_public_for_signature (&msg->caller_id, | ||
502 | &ring->caller_id); | ||
503 | ring->remote_line = msg->line; | ||
504 | ring->source_line = line->local_line; | ||
505 | ring->target = msg->target; | ||
506 | ring->source = my_identity; | ||
507 | ring->expiration_time = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (RING_TIMEOUT)); | ||
508 | GNUNET_CRYPTO_ecc_sign (&msg->caller_id, | ||
509 | &ring->purpose, | ||
510 | &ring->signature); | ||
511 | GNUNET_MQ_send (line->reliable_mq, e); | ||
512 | GNUNET_SERVER_client_set_user_context (client, line); | ||
765 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | 513 | GNUNET_SERVER_receive_done (client, GNUNET_OK); |
766 | |||
767 | if (connection.status != CALLEE) | ||
768 | { | ||
769 | // TODO send illegal command | ||
770 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
771 | _ | ||
772 | ("handle_session_accept_message called when not allowed\n")); | ||
773 | return; | ||
774 | } | ||
775 | |||
776 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Accepting the call of: %s\n"), | ||
777 | GNUNET_i2s_full (&(connection.peer))); | ||
778 | |||
779 | msg_size = sizeof (struct MeshSessionAcceptMessage); | ||
780 | struct MeshSessionAcceptMessage *message_mesh_accept = | ||
781 | (struct MeshSessionAcceptMessage *) GNUNET_malloc (msg_size); | ||
782 | |||
783 | if (NULL == message_mesh_accept) | ||
784 | { | ||
785 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
786 | _("Could not create MeshSessionAcceptMessage\n")); | ||
787 | return; | ||
788 | } | ||
789 | |||
790 | message_mesh_accept->header.size = htons (msg_size); | ||
791 | message_mesh_accept->header.type = | ||
792 | htons (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_ACCEPT); | ||
793 | |||
794 | if (NULL == | ||
795 | GNUNET_MESH_notify_transmit_ready (tunnel_reliable, 0, | ||
796 | MAX_TRANSMIT_DELAY, msg_size, | ||
797 | &transmit_mesh_message, | ||
798 | (void *) message_mesh_accept)) | ||
799 | { | ||
800 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
801 | _("Could not queue MeshSessionAcceptMessage\n")); | ||
802 | GNUNET_free (message_mesh_accept); | ||
803 | return; | ||
804 | } | ||
805 | |||
806 | connection.status = CONNECTED; | ||
807 | connection.client = client; | ||
808 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Changed connection status to %d\n"), | ||
809 | connection.status); | ||
810 | |||
811 | return; | ||
812 | } | 514 | } |
813 | 515 | ||
516 | |||
814 | /** | 517 | /** |
815 | * Function to handle a session reject message from the client | 518 | * Transmit audio data via unreliable mesh channel. |
816 | * | 519 | * |
817 | * @param cls closure, NULL | 520 | * @param cls the `struct Line` we are transmitting for |
818 | * @param client the client from which the message is | 521 | * @param size number of bytes available in @a buf |
819 | * @param message the message from the client | 522 | * @param buf where to copy the data |
820 | */ | 523 | * @return number of bytes copied to @buf |
821 | static void | 524 | */ |
822 | handle_session_reject_message (void *cls, struct GNUNET_SERVER_Client *client, | 525 | static size_t |
823 | const struct GNUNET_MessageHeader *message) | 526 | transmit_line_audio (void *cls, |
527 | size_t size, | ||
528 | void *buf) | ||
824 | { | 529 | { |
825 | struct ClientServerSessionRejectMessage *message_received; | 530 | struct Line *line = cls; |
826 | 531 | struct MeshAudioMessage *mam = buf; | |
827 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | 532 | |
828 | 533 | line->unreliable_mth = NULL; | |
829 | if (connection.status != CALLEE) | 534 | if ( (NULL == buf) || |
535 | (size < sizeof (struct MeshAudioMessage) + line->audio_size) ) | ||
830 | { | 536 | { |
831 | // TODO send illegal command | 537 | /* eh, other error handling? */ |
832 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | 538 | return 0; |
833 | _ | 539 | } |
834 | ("handle_session_reject_message called when not allowed\n")); | 540 | mam->header.size = htons (sizeof (struct MeshAudioMessage) + line->audio_size); |
835 | return; | 541 | mam->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_AUDIO); |
836 | } | 542 | mam->remote_line = htonl (line->remote_line); |
837 | 543 | memcpy (&mam[1], line->audio_data, line->audio_size); | |
838 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Rejecting the call of: %s\n"), | 544 | GNUNET_free (line->audio_data); |
839 | GNUNET_i2s_full (&(connection.peer))); | 545 | line->audio_data = NULL; |
840 | message_received = (struct ClientServerSessionRejectMessage *) message; | 546 | return sizeof (struct MeshAudioMessage) + line->audio_size; |
841 | reject_call (tunnel_reliable, ntohs (message_received->reason)); | ||
842 | |||
843 | return; | ||
844 | } | 547 | } |
845 | 548 | ||
549 | |||
846 | /** | 550 | /** |
847 | * Function to handle a session terminate message from the client | 551 | * Function to handle audio data from the client |
848 | * | 552 | * |
849 | * @param cls closure, NULL | 553 | * @param cls closure, NULL |
850 | * @param client the client from which the message is | 554 | * @param client the client from which the message is |
851 | * @param message the message from the client | 555 | * @param message the message from the client |
852 | */ | 556 | */ |
853 | static void | 557 | static void |
854 | handle_session_terminate_message (void *cls, | 558 | handle_client_audio_message (void *cls, |
855 | struct GNUNET_SERVER_Client *client, | 559 | struct GNUNET_SERVER_Client *client, |
856 | const struct GNUNET_MessageHeader *message) | 560 | const struct GNUNET_MessageHeader *message) |
857 | { | 561 | { |
562 | const struct ClientAudioMessage *msg; | ||
563 | struct Line *line; | ||
564 | size_t size; | ||
565 | |||
566 | size = ntohs (message->size) - sizeof (struct ClientAudioMessage); | ||
567 | msg = (struct ClientAudioMessage *) message; | ||
568 | line = GNUNET_SERVER_client_get_user_context (client, struct Line); | ||
569 | if (NULL == line) | ||
570 | { | ||
571 | GNUNET_break (0); | ||
572 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | ||
573 | return; | ||
574 | } | ||
575 | switch (line->status) | ||
576 | { | ||
577 | case LS_CALLEE_LISTEN: | ||
578 | case LS_CALLEE_RINGING: | ||
579 | case LS_CALLER_CALLING: | ||
580 | GNUNET_break (0); | ||
581 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | ||
582 | return; | ||
583 | case LS_CALLEE_CONNECTED: | ||
584 | case LS_CALLER_CONNECTED: | ||
585 | /* common case, handled below */ | ||
586 | break; | ||
587 | case LS_CALLEE_SHUTDOWN: | ||
588 | case LS_CALLER_SHUTDOWN: | ||
589 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
590 | "Mesh audio channel in shutdown; audio data dropped\n"); | ||
591 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
592 | return; | ||
593 | } | ||
594 | if (NULL == line->tunnel_unreliable) | ||
595 | { | ||
596 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
597 | _("Mesh audio channel not ready; audio data dropped\n")); | ||
598 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
599 | return; | ||
600 | } | ||
601 | if (NULL != line->unreliable_mth) | ||
602 | { | ||
603 | /* NOTE: we may want to not do this and instead combine the data */ | ||
604 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
605 | "Dropping previous audio data segment with %u bytes\n", | ||
606 | line->audio_size); | ||
607 | GNUNET_MESH_notify_transmit_ready_cancel (line->unreliable_mth); | ||
608 | GNUNET_free (line->audio_data); | ||
609 | } | ||
610 | line->audio_size = size; | ||
611 | line->audio_data = GNUNET_malloc (line->audio_size); | ||
612 | memcpy (line->audio_data, | ||
613 | &msg[1], | ||
614 | size); | ||
615 | line->unreliable_mth = GNUNET_MESH_notify_transmit_ready (line->tunnel_unreliable, | ||
616 | GNUNET_NO, | ||
617 | GNUNET_TIME_UNIT_FOREVER_REL, | ||
618 | sizeof (struct MeshAudioMessage) | ||
619 | + line->audio_size, | ||
620 | &transmit_line_audio, | ||
621 | line); | ||
858 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | 622 | GNUNET_SERVER_receive_done (client, GNUNET_OK); |
859 | |||
860 | if (connection.client == NULL || connection.status == CALLEE) | ||
861 | { | ||
862 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
863 | _ | ||
864 | ("handle_session_terminate_message called when not allowed\n")); | ||
865 | return; | ||
866 | } | ||
867 | |||
868 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Terminating the call with: %s\n"), | ||
869 | GNUNET_i2s_full (&(connection.peer))); | ||
870 | terminate_call (); | ||
871 | } | 623 | } |
872 | 624 | ||
873 | /******************************************************************************/ | ||
874 | /*********************** SENDING FUNCTIONS MESH *******************/ | ||
875 | /******************************************************************************/ | ||
876 | 625 | ||
877 | /** | 626 | /** |
878 | * Transmit a mesh message | 627 | * We are done signalling shutdown to the other peer. |
879 | * @param cls closure, NULL | 628 | * Destroy the tunnel. |
880 | * @param size number of bytes available in buf | 629 | * |
881 | * @param buf where the callee should write the message | 630 | * @param cls the `struct GNUNET_MESH_tunnel` to destroy |
882 | * @return number of bytes written to buf | ||
883 | */ | ||
884 | static size_t | ||
885 | transmit_mesh_message (void *cls, size_t size, void *buf) | ||
886 | { | ||
887 | struct VoIPMeshMessageHeader *msg_header = | ||
888 | (struct VoIPMeshMessageHeader *) cls; | ||
889 | |||
890 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Transmitting message over mesh\n")); | ||
891 | |||
892 | memcpy (buf, cls, size); | ||
893 | // Check if this is correct | ||
894 | |||
895 | |||
896 | if ((GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_TERMINATE == | ||
897 | ntohs (msg_header->header.type)) | ||
898 | || (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_REJECT == | ||
899 | ntohs (msg_header->header.type))) | ||
900 | { | ||
901 | status_to_listen (); | ||
902 | } | ||
903 | else if (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_INITIATE == | ||
904 | ntohs (msg_header->header.type)) | ||
905 | { | ||
906 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Starting timeout task.\n")); | ||
907 | timeout_task = | ||
908 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply | ||
909 | (GNUNET_TIME_UNIT_SECONDS, 30), | ||
910 | &check_timeout, NULL); | ||
911 | } | ||
912 | |||
913 | GNUNET_free (cls); | ||
914 | |||
915 | return size; | ||
916 | } | ||
917 | |||
918 | /** | ||
919 | * Transmit a audo message over mesh | ||
920 | * @param cls closure, NULL | ||
921 | * @param size number of bytes available in buf | ||
922 | * @param buf where the callee should write the message | ||
923 | * @return number of bytes written to buf | ||
924 | */ | ||
925 | static size_t | ||
926 | transmit_mesh_audio_message (void *cls, size_t size, void *buf) | ||
927 | { | ||
928 | struct AudioMessage *message = (struct AudioMessage *) cls; | ||
929 | |||
930 | if (size < sizeof (struct AudioMessage) || NULL == buf) | ||
931 | { | ||
932 | GNUNET_break (0); | ||
933 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
934 | "size %u, buf %p, data_sent %u, data_received %u\n", | ||
935 | size, buf, data_sent, data_received); | ||
936 | return 0; | ||
937 | } | ||
938 | |||
939 | memcpy (buf, message, size); | ||
940 | |||
941 | data_sent++; | ||
942 | data_sent_size += size; | ||
943 | |||
944 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, " Sent packet %d\n", data_sent); | ||
945 | |||
946 | audio_task = GNUNET_SCHEDULER_add_now (&transmit_audio_task, NULL); | ||
947 | |||
948 | return size; | ||
949 | } | ||
950 | |||
951 | /** | ||
952 | * Task to schedule a audio transmission. | ||
953 | * | ||
954 | * @param cls Closure. | ||
955 | * @param tc Task Context. | ||
956 | */ | 631 | */ |
957 | static void | 632 | static void |
958 | transmit_audio_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | 633 | mq_done_destroy_tunnel (void *cls) |
959 | { | 634 | { |
960 | struct GNUNET_CONTAINER_SList_Iterator iterator; | 635 | struct GNUNET_MESH_Tunnel *tunnel = cls; |
961 | struct AudioMessage *msg; | 636 | |
962 | int ab_length = GNUNET_CONTAINER_slist_count (audio_buffer); | 637 | GNUNET_MESH_tunnel_destroy (tunnel); |
963 | |||
964 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, "We have %d packets.\n", ab_length); | ||
965 | |||
966 | if (NULL == cls) | ||
967 | { | ||
968 | if (0 == ab_length && CONNECTED == connection.status) | ||
969 | { | ||
970 | audio_task = | ||
971 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply | ||
972 | (GNUNET_TIME_UNIT_MILLISECONDS, 10), | ||
973 | &transmit_audio_task, NULL); | ||
974 | return; | ||
975 | } | ||
976 | |||
977 | iterator = GNUNET_CONTAINER_slist_begin (audio_buffer); | ||
978 | msg = | ||
979 | (struct AudioMessage *) GNUNET_CONTAINER_slist_get (&iterator, NULL); | ||
980 | GNUNET_CONTAINER_slist_erase (&iterator); | ||
981 | GNUNET_CONTAINER_slist_iter_destroy (&iterator); | ||
982 | } | ||
983 | else | ||
984 | { | ||
985 | msg = (struct AudioMessage *) cls; | ||
986 | } | ||
987 | |||
988 | if (NULL == tunnel_unreliable) | ||
989 | { | ||
990 | GNUNET_CONTAINER_slist_clear (audio_buffer); | ||
991 | return; | ||
992 | } | ||
993 | |||
994 | mth = GNUNET_MESH_notify_transmit_ready (tunnel_unreliable, GNUNET_NO, | ||
995 | MAX_TRANSMIT_DELAY, | ||
996 | sizeof (struct AudioMessage), | ||
997 | &transmit_mesh_audio_message, | ||
998 | (void *) msg); | ||
999 | |||
1000 | if (NULL == mth) | ||
1001 | { | ||
1002 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1003 | "Need to retransmit audio packet\n"); | ||
1004 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, " in 1 ms\n"); | ||
1005 | audio_task = | ||
1006 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS, | ||
1007 | &transmit_audio_task, (void *) msg); | ||
1008 | } | ||
1009 | } | 638 | } |
1010 | 639 | ||
1011 | /******************************************************************************/ | ||
1012 | /*********************** RECEIVE FUNCTIONS MESH ********************/ | ||
1013 | /******************************************************************************/ | ||
1014 | 640 | ||
1015 | /** | 641 | /** |
1016 | * Function to handle a initiation messaage incoming over mesh | 642 | * Function to handle a ring message incoming over mesh |
643 | * | ||
1017 | * @param cls closure, NULL | 644 | * @param cls closure, NULL |
1018 | * @param tunnel the tunnel over which the message arrived | 645 | * @param tunnel the tunnel over which the message arrived |
1019 | * @param tunnel_ctx the tunnel context, can be NULL | 646 | * @param tunnel_ctx the tunnel context, can be NULL |
1020 | * @param message the incoming message | 647 | * @param message the incoming message |
1021 | * | 648 | * @return #GNUNET_OK |
1022 | * @return GNUNET_OK | 649 | */ |
1023 | */ | 650 | static int |
1024 | int | 651 | handle_mesh_ring_message (void *cls, |
1025 | handle_mesh_initiate_message (void *cls, struct GNUNET_MESH_Tunnel *tunnel, | 652 | struct GNUNET_MESH_Tunnel *tunnel, |
1026 | void **tunnel_ctx, | 653 | void **tunnel_ctx, |
1027 | const struct GNUNET_MessageHeader *message) | 654 | const struct GNUNET_MessageHeader *message) |
1028 | { | 655 | { |
1029 | int reject_reason; | 656 | const struct MeshPhoneRingMessage *msg; |
1030 | //struct GNUNET_PeerIdentity *peer = (GNUNET_MESH_tunnel_get_info(tunnel, GNUNET_MESH_OPTION_PEER))->peer; | 657 | struct Line *line; |
1031 | const struct GNUNET_PeerIdentity *peer = | 658 | struct GNUNET_MQ_Envelope *e; |
1032 | GNUNET_PEER_resolve2 (tunnel->peer); | 659 | struct MeshPhoneBusyMessage *busy; |
1033 | 660 | struct ClientPhoneRingMessage cring; | |
1034 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | 661 | |
1035 | _("Handling MeshSessionInitiateMessage from peer: %s\n"), | 662 | msg = (const struct MeshPhoneRingMessage *) message; |
1036 | GNUNET_i2s_full (peer)); | 663 | if ( (msg->purpose.size != htonl (sizeof (struct GNUNET_PeerIdentity) * 2 + |
664 | sizeof (struct GNUNET_TIME_AbsoluteNBO) + | ||
665 | sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) + | ||
666 | sizeof (struct GNUNET_CRYPTO_EccPublicSignKey))) || | ||
667 | (GNUNET_OK != | ||
668 | GNUNET_CRYPTO_ecc_verify (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING, | ||
669 | &msg->purpose, | ||
670 | &msg->signature, | ||
671 | &msg->caller_id)) ) | ||
672 | { | ||
673 | GNUNET_break_op (0); | ||
674 | return GNUNET_SYSERR; | ||
675 | } | ||
676 | for (line = lines_head; NULL != line; line = line->next) | ||
677 | if ( (line->local_line == ntohl (msg->remote_line)) && | ||
678 | (LS_CALLEE_LISTEN == line->status) ) | ||
679 | break; | ||
680 | if (NULL == line) | ||
681 | { | ||
682 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
683 | _("No available phone for incoming call on line %u, sending BUSY signal\n"), | ||
684 | ntohl (msg->remote_line)); | ||
685 | e = GNUNET_MQ_msg (busy, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_BUSY); | ||
686 | GNUNET_MQ_notify_sent (e, | ||
687 | &mq_done_destroy_tunnel, | ||
688 | tunnel); | ||
689 | GNUNET_MQ_send (line->reliable_mq, e); | ||
690 | GNUNET_MESH_receive_done (tunnel); /* needed? */ | ||
691 | return GNUNET_OK; | ||
692 | } | ||
693 | line->status = LS_CALLEE_RINGING; | ||
694 | line->remote_line = ntohl (msg->source_line); | ||
695 | line->tunnel_reliable = tunnel; | ||
696 | line->reliable_mq = GNUNET_MESH_mq_create (line->tunnel_reliable); | ||
697 | *tunnel_ctx = line; | ||
698 | cring.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING); | ||
699 | cring.header.size = htons (sizeof (cring)); | ||
700 | cring.reserved = htonl (0); | ||
701 | cring.caller_id = msg->caller_id; | ||
702 | GNUNET_SERVER_notification_context_unicast (nc, | ||
703 | line->client, | ||
704 | &cring.header, | ||
705 | GNUNET_NO); | ||
1037 | GNUNET_MESH_receive_done (tunnel); | 706 | GNUNET_MESH_receive_done (tunnel); |
1038 | |||
1039 | if (LISTEN != connection.status | ||
1040 | || 1 > GNUNET_CONTAINER_slist_count (clients)) | ||
1041 | { | ||
1042 | |||
1043 | if (CONNECTED == connection.status) | ||
1044 | { | ||
1045 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1046 | _ | ||
1047 | ("Rejected call from %s because there is an active call"), | ||
1048 | GNUNET_i2s_full (peer)); | ||
1049 | reject_reason = htons (GNUNET_CONVERSATION_REJECT_REASON_ACTIVE_CALL); | ||
1050 | |||
1051 | // Notifying client about missed call | ||
1052 | size_t msg_size = | ||
1053 | sizeof (struct ServerClientMissedCallMessage) + | ||
1054 | sizeof (struct MissedCall); | ||
1055 | struct ServerClientMissedCallMessage *message = | ||
1056 | GNUNET_malloc (msg_size); | ||
1057 | |||
1058 | message->header.size = htons (msg_size); | ||
1059 | message->header.type = | ||
1060 | htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_MISSED_CALL); | ||
1061 | message->number = 1; | ||
1062 | |||
1063 | memcpy (&(message->missed_call->peer), peer, | ||
1064 | sizeof (struct GNUNET_PeerIdentity)); | ||
1065 | message->missed_call->time = GNUNET_TIME_absolute_get (); | ||
1066 | |||
1067 | if (NULL == | ||
1068 | GNUNET_SERVER_notify_transmit_ready (connection.client, | ||
1069 | sizeof (struct | ||
1070 | ServerClientMissedCallMessage), | ||
1071 | MAX_TRANSMIT_DELAY, | ||
1072 | &transmit_server_missed_call_message, | ||
1073 | (void *) message)) | ||
1074 | { | ||
1075 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1076 | _ | ||
1077 | ("Could not queue ServerClientMissedCallMessage\n")); | ||
1078 | GNUNET_free (message); | ||
1079 | } | ||
1080 | } | ||
1081 | |||
1082 | if (1 > GNUNET_CONTAINER_slist_count (clients)) | ||
1083 | { | ||
1084 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1085 | _("Got a call from %s while no client connected.\n"), | ||
1086 | GNUNET_i2s_full (peer)); | ||
1087 | reject_reason = htons (GNUNET_CONVERSATION_REJECT_REASON_NO_CLIENT); | ||
1088 | // Store missed calls | ||
1089 | struct MissedCall call; | ||
1090 | memcpy (&(call.peer), peer, sizeof (struct GNUNET_PeerIdentity)); | ||
1091 | call.time = GNUNET_TIME_absolute_get (); | ||
1092 | GNUNET_CONTAINER_slist_add_end (missed_calls, | ||
1093 | GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT, | ||
1094 | &call, sizeof (struct MissedCall)); | ||
1095 | |||
1096 | } | ||
1097 | |||
1098 | reject_call (tunnel, reject_reason); | ||
1099 | } | ||
1100 | else | ||
1101 | { | ||
1102 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Initiated call from: %s\n"), | ||
1103 | GNUNET_i2s_full (peer)); | ||
1104 | tunnel_reliable = tunnel; | ||
1105 | connection.status = CALLEE; | ||
1106 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1107 | _("Changed connection status to %d\n"), connection.status); | ||
1108 | memcpy (&(connection.peer), peer, sizeof (struct GNUNET_PeerIdentity)); | ||
1109 | |||
1110 | struct GNUNET_CONTAINER_SList_Iterator iterator = | ||
1111 | GNUNET_CONTAINER_slist_begin (clients); | ||
1112 | do | ||
1113 | { | ||
1114 | struct VoipClient *conversation_client = | ||
1115 | (struct VoipClient *) GNUNET_CONTAINER_slist_get (&iterator, | ||
1116 | NULL); | ||
1117 | struct GNUNET_SERVER_Client *client = conversation_client->client; | ||
1118 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Client found: %p\n"), | ||
1119 | client); | ||
1120 | |||
1121 | if (NULL == | ||
1122 | GNUNET_SERVER_notify_transmit_ready (client, | ||
1123 | sizeof (struct | ||
1124 | ServerClientSessionInitiateMessage), | ||
1125 | MAX_TRANSMIT_DELAY, | ||
1126 | &transmit_server_initiate_message, | ||
1127 | (void *) peer)) | ||
1128 | { | ||
1129 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1130 | _ | ||
1131 | ("Could not queue ServerClientSessionInitiateMessage\n")); | ||
1132 | } | ||
1133 | |||
1134 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Client notified.\n")); | ||
1135 | } | ||
1136 | while (GNUNET_OK == GNUNET_CONTAINER_slist_next (&iterator)); | ||
1137 | |||
1138 | GNUNET_CONTAINER_slist_iter_destroy (&iterator); | ||
1139 | |||
1140 | } | ||
1141 | |||
1142 | return GNUNET_OK; | 707 | return GNUNET_OK; |
1143 | } | 708 | } |
1144 | 709 | ||
710 | |||
1145 | /** | 711 | /** |
1146 | * Function to handle an accept messaage incoming over mesh | 712 | * Function to handle a hangup message incoming over mesh |
713 | * | ||
1147 | * @param cls closure, NULL | 714 | * @param cls closure, NULL |
1148 | * @param tunnel the tunnel over which the message arrived | 715 | * @param tunnel the tunnel over which the message arrived |
1149 | * @param tunnel_ctx the tunnel context, can be NULL | 716 | * @param tunnel_ctx the tunnel context, can be NULL |
1150 | * @param message the incoming message | 717 | * @param message the incoming message |
1151 | * | 718 | * @return #GNUNET_OK |
1152 | * @return GNUNET_OK | 719 | */ |
1153 | */ | 720 | static int |
1154 | int | 721 | handle_mesh_hangup_message (void *cls, |
1155 | handle_mesh_accept_message (void *cls, struct GNUNET_MESH_Tunnel *tunnel, | 722 | struct GNUNET_MESH_Tunnel *tunnel, |
1156 | void **tunnel_ctx, | 723 | void **tunnel_ctx, |
1157 | const struct GNUNET_MessageHeader *message) | 724 | const struct GNUNET_MessageHeader *message) |
1158 | { | 725 | { |
1159 | static uint32_t port = 50003; | 726 | struct Line *line = *tunnel_ctx; |
1160 | //struct GNUNET_PeerIdentity *peer = (GNUNET_MESH_tunnel_get_info(tunnel, GNUNET_MESH_OPTION_PEER))->peer; | 727 | const struct MeshPhoneHangupMessage *msg; |
1161 | const struct GNUNET_PeerIdentity *peer = | 728 | const char *reason; |
1162 | GNUNET_PEER_resolve2 (tunnel->peer); | 729 | size_t len = ntohs (message->size) - sizeof (struct MeshPhoneHangupMessage); |
1163 | 730 | char buf[len + sizeof (struct ClientPhoneHangupMessage)]; | |
1164 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | 731 | struct ClientPhoneHangupMessage *hup; |
1165 | _ | 732 | |
1166 | ("Handling MeshSessionAccpetMessage from peer: %s (connection.peer: %s)\n"), | 733 | msg = (const struct MeshPhoneHangupMessage *) message; |
1167 | GNUNET_i2s_full (peer), GNUNET_i2s_full (&(connection.peer))); | 734 | len = ntohs (msg->header.size) - sizeof (struct MeshPhoneHangupMessage); |
735 | reason = (const char *) &msg[1]; | ||
736 | if ( (0 == len) || | ||
737 | ('\0' != reason[len - 1]) ) | ||
738 | { | ||
739 | reason = NULL; | ||
740 | len = 0; | ||
741 | } | ||
742 | if (NULL == line) | ||
743 | { | ||
744 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
745 | "HANGUP message received for non-existing line, dropping tunnel.\n"); | ||
746 | return GNUNET_SYSERR; | ||
747 | } | ||
748 | *tunnel_ctx = NULL; | ||
749 | switch (line->status) | ||
750 | { | ||
751 | case LS_CALLEE_LISTEN: | ||
752 | GNUNET_break (0); | ||
753 | return GNUNET_SYSERR; | ||
754 | case LS_CALLEE_RINGING: | ||
755 | line->status = LS_CALLEE_LISTEN; | ||
756 | destroy_line_mesh_tunnels (line); | ||
757 | break; | ||
758 | case LS_CALLEE_CONNECTED: | ||
759 | line->status = LS_CALLEE_LISTEN; | ||
760 | destroy_line_mesh_tunnels (line); | ||
761 | break; | ||
762 | case LS_CALLEE_SHUTDOWN: | ||
763 | line->status = LS_CALLEE_LISTEN; | ||
764 | destroy_line_mesh_tunnels (line); | ||
765 | return GNUNET_OK; | ||
766 | case LS_CALLER_CALLING: | ||
767 | line->status = LS_CALLER_SHUTDOWN; | ||
768 | mq_done_finish_caller_shutdown (line); | ||
769 | break; | ||
770 | case LS_CALLER_CONNECTED: | ||
771 | line->status = LS_CALLER_SHUTDOWN; | ||
772 | mq_done_finish_caller_shutdown (line); | ||
773 | break; | ||
774 | case LS_CALLER_SHUTDOWN: | ||
775 | mq_done_finish_caller_shutdown (line); | ||
776 | return GNUNET_OK; | ||
777 | } | ||
778 | hup = (struct ClientPhoneHangupMessage *) buf; | ||
779 | hup->header.size = sizeof (buf); | ||
780 | hup->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP); | ||
781 | memcpy (&hup[1], reason, len); | ||
782 | GNUNET_SERVER_notification_context_unicast (nc, | ||
783 | line->client, | ||
784 | &hup->header, | ||
785 | GNUNET_NO); | ||
1168 | GNUNET_MESH_receive_done (tunnel); | 786 | GNUNET_MESH_receive_done (tunnel); |
1169 | |||
1170 | if (0 == | ||
1171 | memcmp (peer, &(connection.peer), sizeof (struct GNUNET_PeerIdentity)) | ||
1172 | && connection.status == CALLER) | ||
1173 | { | ||
1174 | tunnel_unreliable = | ||
1175 | GNUNET_MESH_tunnel_create (mesh, NULL, peer, port, GNUNET_NO, | ||
1176 | GNUNET_NO); | ||
1177 | if (NULL == tunnel_unreliable) | ||
1178 | { | ||
1179 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1180 | _("Could not create unreliable tunnel\n")); | ||
1181 | |||
1182 | status_to_listen (); | ||
1183 | |||
1184 | GNUNET_SERVER_notify_transmit_ready (connection.client, | ||
1185 | sizeof (struct | ||
1186 | ServerClientSessionRejectMessage), | ||
1187 | MAX_TRANSMIT_DELAY, | ||
1188 | &transmit_server_reject_message, | ||
1189 | NULL); | ||
1190 | return GNUNET_SYSERR; | ||
1191 | } | ||
1192 | |||
1193 | if (timeout_task != GNUNET_SCHEDULER_NO_TASK) | ||
1194 | { | ||
1195 | GNUNET_SCHEDULER_cancel (timeout_task); | ||
1196 | timeout_task = GNUNET_SCHEDULER_NO_TASK; | ||
1197 | } | ||
1198 | |||
1199 | connection.status = CONNECTED; | ||
1200 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1201 | _("Changed connection status to %d\n"), connection.status); | ||
1202 | |||
1203 | if (NULL == | ||
1204 | GNUNET_SERVER_notify_transmit_ready (connection.client, | ||
1205 | sizeof (struct | ||
1206 | ServerClientSessionAcceptMessage), | ||
1207 | MAX_TRANSMIT_DELAY, | ||
1208 | &transmit_server_accept_message, | ||
1209 | (void *) message)) | ||
1210 | { | ||
1211 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1212 | _ | ||
1213 | ("Could not queue ServerClientSessionAcceptMessage\n")); | ||
1214 | return GNUNET_SYSERR; | ||
1215 | } | ||
1216 | |||
1217 | start_time = GNUNET_TIME_absolute_get (); | ||
1218 | start_helpers (); | ||
1219 | audio_task = GNUNET_SCHEDULER_add_now (&transmit_audio_task, NULL); | ||
1220 | } | ||
1221 | |||
1222 | return GNUNET_OK; | 787 | return GNUNET_OK; |
1223 | } | 788 | } |
1224 | 789 | ||
790 | |||
1225 | /** | 791 | /** |
1226 | * Function to handle a reject messaage incoming over mesh | 792 | * Function to handle a pickup message incoming over mesh |
793 | * | ||
1227 | * @param cls closure, NULL | 794 | * @param cls closure, NULL |
1228 | * @param tunnel the tunnel over which the message arrived | 795 | * @param tunnel the tunnel over which the message arrived |
1229 | * @param tunnel_ctx the tunnel context, can be NULL | 796 | * @param tunnel_ctx the tunnel context, can be NULL |
1230 | * @param message the incoming message | 797 | * @param message the incoming message |
1231 | * | 798 | * @return #GNUNET_OK |
1232 | * @return GNUNET_OK | 799 | */ |
1233 | */ | 800 | static int |
1234 | int | 801 | handle_mesh_pickup_message (void *cls, |
1235 | handle_mesh_reject_message (void *cls, struct GNUNET_MESH_Tunnel *tunnel, | 802 | struct GNUNET_MESH_Tunnel *tunnel, |
1236 | void **tunnel_ctx, | 803 | void **tunnel_ctx, |
1237 | const struct GNUNET_MessageHeader *message) | 804 | const struct GNUNET_MessageHeader *message) |
1238 | { | 805 | { |
1239 | //struct GNUNET_PeerIdentity *peer = (GNUNET_MESH_tunnel_get_info(tunnel, GNUNET_MESH_OPTION_PEER))->peer; | 806 | const struct MeshPhonePickupMessage *msg; |
1240 | const struct GNUNET_PeerIdentity *peer = | 807 | struct Line *line = *tunnel_ctx; |
1241 | GNUNET_PEER_resolve2 (tunnel->peer); | 808 | const char *metadata; |
1242 | 809 | size_t len = ntohs (message->size) - sizeof (struct MeshPhonePickupMessage); | |
1243 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | 810 | char buf[len + sizeof (struct ClientPhonePickupMessage)]; |
1244 | _ | 811 | struct ClientPhonePickupMessage *pick; |
1245 | ("Handling MeshSessionRejectMessage from peer: %s (connection.peer: %s)\n"), | 812 | |
1246 | GNUNET_i2s_full (peer), GNUNET_i2s_full (&(connection.peer))); | 813 | msg = (const struct MeshPhonePickupMessage *) message; |
814 | len = ntohs (msg->header.size) - sizeof (struct MeshPhonePickupMessage); | ||
815 | metadata = (const char *) &msg[1]; | ||
816 | if ( (0 == len) || | ||
817 | ('\0' != metadata[len - 1]) ) | ||
818 | { | ||
819 | metadata = NULL; | ||
820 | len = 0; | ||
821 | } | ||
822 | if (NULL == line) | ||
823 | { | ||
824 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
825 | "PICKUP message received for non-existing line, dropping tunnel.\n"); | ||
826 | return GNUNET_SYSERR; | ||
827 | } | ||
1247 | GNUNET_MESH_receive_done (tunnel); | 828 | GNUNET_MESH_receive_done (tunnel); |
1248 | 829 | switch (line->status) | |
1249 | if (0 == | 830 | { |
1250 | memcmp (peer, &(connection.peer), sizeof (struct GNUNET_PeerIdentity)) | 831 | case LS_CALLEE_LISTEN: |
1251 | && connection.status == CALLER) | 832 | GNUNET_break (0); |
1252 | { | 833 | return GNUNET_SYSERR; |
1253 | if (NULL == | 834 | case LS_CALLEE_RINGING: |
1254 | GNUNET_SERVER_notify_transmit_ready (connection.client, | 835 | case LS_CALLEE_CONNECTED: |
1255 | sizeof (struct | 836 | GNUNET_break_op (0); |
1256 | ServerClientSessionRejectMessage), | 837 | destroy_line_mesh_tunnels (line); |
1257 | MAX_TRANSMIT_DELAY, | 838 | line->status = LS_CALLEE_LISTEN; |
1258 | &transmit_server_reject_message, | 839 | return GNUNET_SYSERR; |
1259 | (void *) message)) | 840 | case LS_CALLEE_SHUTDOWN: |
1260 | { | 841 | GNUNET_break_op (0); |
1261 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | 842 | line->status = LS_CALLEE_LISTEN; |
1262 | _ | 843 | destroy_line_mesh_tunnels (line); |
1263 | ("Could not queue ServerClientSessionRejectMessage\n")); | 844 | break; |
1264 | } | 845 | case LS_CALLER_CALLING: |
1265 | 846 | line->status = LS_CALLER_CONNECTED; | |
1266 | status_to_listen (); | 847 | break; |
1267 | 848 | case LS_CALLER_CONNECTED: | |
1268 | if (NULL != tunnel_reliable) | 849 | GNUNET_break_op (0); |
1269 | { | 850 | return GNUNET_OK; |
1270 | GNUNET_MESH_tunnel_destroy (tunnel_reliable); | 851 | case LS_CALLER_SHUTDOWN: |
1271 | tunnel_reliable = NULL; | 852 | GNUNET_break_op (0); |
1272 | } | 853 | mq_done_finish_caller_shutdown (line); |
1273 | } | 854 | return GNUNET_SYSERR; |
1274 | 855 | } | |
856 | pick = (struct ClientPhonePickupMessage *) buf; | ||
857 | pick->header.size = sizeof (buf); | ||
858 | pick->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP); | ||
859 | memcpy (&pick[1], metadata, len); | ||
860 | GNUNET_SERVER_notification_context_unicast (nc, | ||
861 | line->client, | ||
862 | &pick->header, | ||
863 | GNUNET_NO); | ||
864 | line->tunnel_unreliable = GNUNET_MESH_tunnel_create (mesh, | ||
865 | line, | ||
866 | &line->target, | ||
867 | GNUNET_APPLICATION_TYPE_CONVERSATION_AUDIO, | ||
868 | GNUNET_YES, | ||
869 | GNUNET_NO); | ||
1275 | return GNUNET_OK; | 870 | return GNUNET_OK; |
1276 | } | 871 | } |
1277 | 872 | ||
873 | |||
1278 | /** | 874 | /** |
1279 | * Function to handle a terminate messaage incoming over mesh | 875 | * Function to handle a busy message incoming over mesh |
876 | * | ||
1280 | * @param cls closure, NULL | 877 | * @param cls closure, NULL |
1281 | * @param tunnel the tunnel over which the message arrived | 878 | * @param tunnel the tunnel over which the message arrived |
1282 | * @param tunnel_ctx the tunnel context, can be NULL | 879 | * @param tunnel_ctx the tunnel context, can be NULL |
1283 | * @param message the incoming message | 880 | * @param message the incoming message |
1284 | * | 881 | * @return #GNUNET_OK |
1285 | * @return GNUNET_OK | 882 | */ |
1286 | */ | 883 | static int |
1287 | int | 884 | handle_mesh_busy_message (void *cls, |
1288 | handle_mesh_terminate_message (void *cls, struct GNUNET_MESH_Tunnel *tunnel, | 885 | struct GNUNET_MESH_Tunnel *tunnel, |
1289 | void **tunnel_ctx, | 886 | void **tunnel_ctx, |
1290 | const struct GNUNET_MessageHeader *message) | 887 | const struct GNUNET_MessageHeader *message) |
1291 | { | 888 | { |
1292 | //struct GNUNET_PeerIdentity *peer = (GNUNET_MESH_tunnel_get_info(tunnel, GNUNET_MESH_OPTION_PEER))->peer; | 889 | struct Line *line = *tunnel_ctx; |
1293 | const struct GNUNET_PeerIdentity *peer = | 890 | struct ClientPhoneBusyMessage busy; |
1294 | GNUNET_PEER_resolve2 (tunnel->peer); | 891 | |
1295 | 892 | if (NULL == line) | |
1296 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | 893 | { |
1297 | _ | 894 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, |
1298 | ("Handling MeshSessionTerminateMessage from peer: %s (connection.peer: %s)\n"), | 895 | "HANGUP message received for non-existing line, dropping tunnel.\n"); |
1299 | GNUNET_i2s_full (peer), GNUNET_i2s_full (&(connection.peer))); | 896 | return GNUNET_SYSERR; |
897 | } | ||
898 | busy.header.size = sizeof (busy); | ||
899 | busy.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_BUSY); | ||
900 | GNUNET_SERVER_notification_context_unicast (nc, | ||
901 | line->client, | ||
902 | &busy.header, | ||
903 | GNUNET_NO); | ||
1300 | GNUNET_MESH_receive_done (tunnel); | 904 | GNUNET_MESH_receive_done (tunnel); |
1301 | 905 | *tunnel_ctx = NULL; | |
1302 | if (!memcmp (peer, &(connection.peer), sizeof (struct GNUNET_PeerIdentity)) | 906 | switch (line->status) |
1303 | && (connection.status == CONNECTED || connection.status == CALLEE)) | 907 | { |
1304 | { | 908 | case LS_CALLEE_LISTEN: |
1305 | status_to_listen (); | 909 | GNUNET_break (0); |
1306 | 910 | return GNUNET_SYSERR; | |
1307 | if (NULL != tunnel_unreliable) | 911 | case LS_CALLEE_RINGING: |
1308 | { | 912 | GNUNET_break_op (0); |
1309 | GNUNET_MESH_tunnel_destroy (tunnel_unreliable); | 913 | break; |
1310 | tunnel_unreliable = NULL; | 914 | case LS_CALLEE_CONNECTED: |
1311 | } | 915 | GNUNET_break_op (0); |
1312 | 916 | break; | |
1313 | if (NULL != tunnel_reliable) | 917 | case LS_CALLEE_SHUTDOWN: |
1314 | { | 918 | GNUNET_break_op (0); |
1315 | GNUNET_MESH_tunnel_destroy (tunnel_reliable); | 919 | break; |
1316 | tunnel_reliable = NULL; | 920 | case LS_CALLER_CALLING: |
1317 | } | 921 | line->status = LS_CALLER_SHUTDOWN; |
1318 | } | 922 | mq_done_finish_caller_shutdown (line); |
1319 | 923 | break; | |
924 | case LS_CALLER_CONNECTED: | ||
925 | line->status = LS_CALLER_SHUTDOWN; | ||
926 | mq_done_finish_caller_shutdown (line); | ||
927 | break; | ||
928 | case LS_CALLER_SHUTDOWN: | ||
929 | mq_done_finish_caller_shutdown (line); | ||
930 | break; | ||
931 | } | ||
1320 | return GNUNET_OK; | 932 | return GNUNET_OK; |
1321 | } | 933 | } |
1322 | 934 | ||
935 | |||
1323 | /** | 936 | /** |
1324 | * Function to handle a audio messaage incoming over mesh | 937 | * Function to handle an audio message incoming over mesh |
938 | * | ||
1325 | * @param cls closure, NULL | 939 | * @param cls closure, NULL |
1326 | * @param tunnel the tunnel over which the message arrived | 940 | * @param tunnel the tunnel over which the message arrived |
1327 | * @param tunnel_ctx the tunnel context, can be NULL | 941 | * @param tunnel_ctx the tunnel context, can be NULL |
1328 | * @param message the incoming message | 942 | * @param message the incoming message |
1329 | * | 943 | * @return #GNUNET_OK |
1330 | * @return GNUNET_OK | 944 | */ |
1331 | */ | ||
1332 | int | ||
1333 | handle_mesh_audio_message (void *cls, struct GNUNET_MESH_Tunnel *tunnel, | ||
1334 | void **tunnel_ctx, | ||
1335 | const struct GNUNET_MessageHeader *message) | ||
1336 | { | ||
1337 | const struct AudioMessage *audio; | ||
1338 | audio = (const struct AudioMessage *) message; | ||
1339 | |||
1340 | GNUNET_MESH_receive_done (tunnel); | ||
1341 | if (CONNECTED != connection.status) | ||
1342 | return GNUNET_OK; | ||
1343 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1344 | "[RECV] %dbytes\n", | ||
1345 | ntohs (audio->header.size)); | ||
1346 | if (NULL == playback_helper) | ||
1347 | return GNUNET_OK; | ||
1348 | (void) GNUNET_HELPER_send (playback_helper, | ||
1349 | message, GNUNET_YES, NULL, NULL); | ||
1350 | return GNUNET_OK; | ||
1351 | } | ||
1352 | |||
1353 | /******************************************************************************/ | ||
1354 | /*********************** HELPER *******************/ | ||
1355 | /******************************************************************************/ | ||
1356 | |||
1357 | /** | ||
1358 | * Function to process the audio from the record helper | ||
1359 | * @param cls closure, NULL | ||
1360 | * @param client NULL | ||
1361 | * @param msg the message from the helper | ||
1362 | * | ||
1363 | * @return GNUNET_OK | ||
1364 | */ | ||
1365 | static int | 945 | static int |
1366 | process_record_messages (void *cls GNUNET_UNUSED, void *client, | 946 | handle_mesh_audio_message (void *cls, |
1367 | const struct GNUNET_MessageHeader *msg) | 947 | struct GNUNET_MESH_Tunnel *tunnel, |
948 | void **tunnel_ctx, | ||
949 | const struct GNUNET_MessageHeader *message) | ||
1368 | { | 950 | { |
1369 | const struct AudioMessage *message = (const struct AudioMessage *) msg; | 951 | const struct MeshAudioMessage *msg; |
1370 | 952 | struct Line *line = *tunnel_ctx; | |
1371 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | 953 | struct GNUNET_PeerIdentity sender; |
1372 | " [REC] %dbyte\n", | 954 | size_t msize = ntohs (message->size) - sizeof (struct MeshAudioMessage); |
1373 | ntohs (message->header.size)); | 955 | char buf[msize + sizeof (struct ClientAudioMessage)]; |
1374 | GNUNET_CONTAINER_slist_add_end (audio_buffer, | 956 | struct ClientAudioMessage *cam; |
1375 | GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT, | 957 | |
1376 | message, ntohs (message->header.size)); | 958 | msg = (const struct MeshAudioMessage *) message; |
1377 | 959 | if (NULL == line) | |
1378 | return GNUNET_OK; | 960 | { |
1379 | } | 961 | sender = *GNUNET_MESH_tunnel_get_info (tunnel, |
1380 | 962 | GNUNET_MESH_OPTION_PEER)->peer; | |
1381 | /** | 963 | for (line = lines_head; NULL != line; line = line->next) |
1382 | * Function to to start the playback helper | 964 | if ( (line->local_line == ntohl (msg->remote_line)) && |
1383 | * | 965 | (LS_CALLEE_CONNECTED == line->status) && |
1384 | * @return 0 ok, 1 on error | 966 | (0 == memcmp (&line->target, |
1385 | */ | 967 | &sender, |
1386 | int | 968 | sizeof (struct GNUNET_PeerIdentity))) && |
1387 | start_playback_helper (void) | 969 | (NULL == line->tunnel_unreliable) ) |
1388 | { | 970 | break; |
1389 | static char *playback_helper_argv[1]; | 971 | if (NULL == line) |
1390 | int success = 1; | ||
1391 | |||
1392 | playback_helper_argv[0] = "gnunet-helper-audio-playback"; | ||
1393 | playback_helper = GNUNET_HELPER_start (GNUNET_NO, | ||
1394 | "gnunet-helper-audio-playback", | ||
1395 | playback_helper_argv, | ||
1396 | NULL, NULL, NULL); | ||
1397 | |||
1398 | if (NULL == playback_helper) | ||
1399 | { | ||
1400 | success = 0; | ||
1401 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1402 | _("Could not start playback audio helper.\n")); | ||
1403 | } | ||
1404 | |||
1405 | return success; | ||
1406 | } | ||
1407 | |||
1408 | /** | ||
1409 | * Function to to start the record helper | ||
1410 | * | ||
1411 | * @return 0 ok, 1 on error | ||
1412 | */ | ||
1413 | int | ||
1414 | start_record_helper (void) | ||
1415 | { | ||
1416 | static char *record_helper_argv[1]; | ||
1417 | int success = 1; | ||
1418 | |||
1419 | record_helper_argv[0] = "gnunet-helper-audio-record"; | ||
1420 | record_helper = GNUNET_HELPER_start (GNUNET_NO, | ||
1421 | "gnunet-helper-audio-record", | ||
1422 | record_helper_argv, | ||
1423 | &process_record_messages, NULL, NULL); | ||
1424 | |||
1425 | if (NULL == record_helper) | ||
1426 | { | ||
1427 | success = 0; | ||
1428 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1429 | _("Could not start record audio helper\n")); | ||
1430 | } | ||
1431 | |||
1432 | return success; | ||
1433 | } | ||
1434 | |||
1435 | |||
1436 | /** | ||
1437 | * Function to to start both helpers | ||
1438 | * | ||
1439 | * @return 0 ok, 1 on error | ||
1440 | */ | ||
1441 | int | ||
1442 | start_helpers (void) | ||
1443 | { | ||
1444 | |||
1445 | if (0 == start_playback_helper () || 0 == start_record_helper ()) | ||
1446 | { | ||
1447 | stop_helpers (); | ||
1448 | return 0; | ||
1449 | } | ||
1450 | |||
1451 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Started helpers\n")); | ||
1452 | |||
1453 | return 1; | ||
1454 | } | ||
1455 | |||
1456 | /** | ||
1457 | * Function to to stop the playback helper | ||
1458 | */ | ||
1459 | void | ||
1460 | stop_playback_helper (void) | ||
1461 | { | ||
1462 | if (NULL != playback_helper) | ||
1463 | { | ||
1464 | GNUNET_HELPER_stop (playback_helper, GNUNET_NO); | ||
1465 | playback_helper = NULL; | ||
1466 | |||
1467 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Stopped playback helper\n")); | ||
1468 | } | ||
1469 | } | ||
1470 | |||
1471 | /** | ||
1472 | * Function to to stop the record helper | ||
1473 | */ | ||
1474 | void | ||
1475 | stop_record_helper (void) | ||
1476 | { | ||
1477 | if (NULL != record_helper) | ||
1478 | { | 972 | { |
1479 | GNUNET_HELPER_stop (record_helper, GNUNET_NO); | 973 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, |
1480 | record_helper = NULL; | 974 | "Received AUDIO data for non-existing line %u, dropping.\n", |
1481 | 975 | ntohl (msg->remote_line)); | |
1482 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Stopped record helper\n")); | 976 | return GNUNET_SYSERR; |
1483 | } | 977 | } |
978 | line->tunnel_unreliable = tunnel; | ||
979 | *tunnel_ctx = line; | ||
980 | } | ||
981 | cam = (struct ClientAudioMessage *) buf; | ||
982 | cam->header.size = htons (sizeof (buf)); | ||
983 | cam->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO); | ||
984 | memcpy (&cam[1], &msg[1], msize); | ||
985 | GNUNET_SERVER_notification_context_unicast (nc, | ||
986 | line->client, | ||
987 | &cam->header, | ||
988 | GNUNET_YES); | ||
989 | GNUNET_MESH_receive_done (tunnel); | ||
990 | return GNUNET_OK; | ||
1484 | } | 991 | } |
1485 | 992 | ||
1486 | /** | ||
1487 | * Function to stop both audio helpers | ||
1488 | */ | ||
1489 | void | ||
1490 | stop_helpers (void) | ||
1491 | { | ||
1492 | stop_playback_helper (); | ||
1493 | stop_record_helper (); | ||
1494 | } | ||
1495 | |||
1496 | /******************************************************************************/ | ||
1497 | /*********************** TUNNEL HANDLING *******************/ | ||
1498 | /******************************************************************************/ | ||
1499 | 993 | ||
1500 | /** | 994 | /** |
1501 | * Method called whenever another peer has added us to a tunnel | 995 | * Method called whenever another peer has added us to a tunnel |
@@ -1508,21 +1002,14 @@ stop_helpers (void) | |||
1508 | * @return initial tunnel context for the tunnel (can be NULL -- that's not an error) | 1002 | * @return initial tunnel context for the tunnel (can be NULL -- that's not an error) |
1509 | */ | 1003 | */ |
1510 | static void * | 1004 | static void * |
1511 | inbound_tunnel (void *cls, struct GNUNET_MESH_Tunnel *tunnel, | 1005 | inbound_tunnel (void *cls, |
1512 | const struct GNUNET_PeerIdentity *initiator, uint32_t port) | 1006 | struct GNUNET_MESH_Tunnel *tunnel, |
1007 | const struct GNUNET_PeerIdentity *initiator, | ||
1008 | uint32_t port) | ||
1513 | { | 1009 | { |
1514 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | 1010 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, |
1515 | _("Received incoming tunnel on port %d\n"), port); | 1011 | _("Received incoming tunnel on port %d\n"), |
1516 | if (50003 == port) | 1012 | port); |
1517 | { | ||
1518 | tunnel_unreliable = tunnel; | ||
1519 | |||
1520 | start_time = GNUNET_TIME_absolute_get (); | ||
1521 | |||
1522 | start_helpers (); | ||
1523 | audio_task = GNUNET_SCHEDULER_add_now (&transmit_audio_task, NULL); | ||
1524 | } | ||
1525 | |||
1526 | return NULL; | 1013 | return NULL; |
1527 | } | 1014 | } |
1528 | 1015 | ||
@@ -1531,115 +1018,68 @@ inbound_tunnel (void *cls, struct GNUNET_MESH_Tunnel *tunnel, | |||
1531 | * Function called whenever an inbound tunnel is destroyed. Should clean up | 1018 | * Function called whenever an inbound tunnel is destroyed. Should clean up |
1532 | * any associated state. | 1019 | * any associated state. |
1533 | * | 1020 | * |
1534 | * @param cls closure (set from GNUNET_MESH_connect) | 1021 | * @param cls closure (set from #GNUNET_MESH_connect) |
1535 | * @param tunnel connection to the other end (henceforth invalid) | 1022 | * @param tunnel connection to the other end (henceforth invalid) |
1536 | * @param tunnel_ctx place where local state associated | 1023 | * @param tunnel_ctx place where local state associated |
1537 | * with the tunnel is stored | 1024 | * with the tunnel is stored |
1538 | */ | 1025 | */ |
1539 | static void | 1026 | static void |
1540 | inbound_end (void *cls, const struct GNUNET_MESH_Tunnel *tunnel, | 1027 | inbound_end (void *cls, |
1028 | const struct GNUNET_MESH_Tunnel *tunnel, | ||
1541 | void *tunnel_ctx) | 1029 | void *tunnel_ctx) |
1542 | { | 1030 | { |
1543 | if (tunnel == tunnel_unreliable) | 1031 | struct Line *line = tunnel_ctx; |
1544 | { | 1032 | struct ClientPhoneHangupMessage hup; |
1545 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Tunnel closed: audio\n"); | 1033 | |
1546 | 1034 | if (NULL == line) | |
1547 | stop_helpers (); | 1035 | return; |
1548 | tunnel_unreliable = NULL; | 1036 | if (line->tunnel_unreliable == tunnel) |
1549 | } | 1037 | { |
1550 | 1038 | line->tunnel_unreliable = NULL; | |
1551 | if (tunnel == tunnel_reliable) | 1039 | return; |
1552 | { | 1040 | } |
1553 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Tunnel closed: control\n"); | 1041 | hup.header.size = sizeof (hup); |
1554 | 1042 | hup.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP); | |
1555 | if (LISTEN != connection.status && NULL != connection.client) | 1043 | switch (line->status) |
1556 | { | 1044 | { |
1557 | if (NULL == | 1045 | case LS_CALLEE_LISTEN: |
1558 | GNUNET_SERVER_notify_transmit_ready (connection.client, | 1046 | GNUNET_break (0); |
1559 | sizeof (struct | 1047 | return; |
1560 | ServerClientSessionTerminateMessage), | 1048 | case LS_CALLEE_RINGING: |
1561 | MAX_TRANSMIT_DELAY, | 1049 | case LS_CALLEE_CONNECTED: |
1562 | &transmit_server_terminate_message, | 1050 | GNUNET_SERVER_notification_context_unicast (nc, |
1563 | NULL)) | 1051 | line->client, |
1564 | { | 1052 | &hup.header, |
1565 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | 1053 | GNUNET_NO); |
1566 | _ | 1054 | line->status = LS_CALLEE_LISTEN; |
1567 | ("Could not queue ServerClientSessionTerminateMessage\n")); | 1055 | break; |
1568 | } | 1056 | case LS_CALLEE_SHUTDOWN: |
1569 | } | 1057 | line->status = LS_CALLEE_LISTEN; |
1570 | 1058 | destroy_line_mesh_tunnels (line); | |
1571 | status_to_listen (); | 1059 | break; |
1572 | } | 1060 | case LS_CALLER_CALLING: |
1061 | case LS_CALLER_CONNECTED: | ||
1062 | GNUNET_SERVER_notification_context_unicast (nc, | ||
1063 | line->client, | ||
1064 | &hup.header, | ||
1065 | GNUNET_NO); | ||
1066 | destroy_line_mesh_tunnels (line); | ||
1067 | GNUNET_CONTAINER_DLL_remove (lines_head, | ||
1068 | lines_tail, | ||
1069 | line); | ||
1070 | GNUNET_free_non_null (line->audio_data); | ||
1071 | GNUNET_free (line); | ||
1072 | break; | ||
1073 | case LS_CALLER_SHUTDOWN: | ||
1074 | destroy_line_mesh_tunnels (line); | ||
1075 | GNUNET_CONTAINER_DLL_remove (lines_head, | ||
1076 | lines_tail, | ||
1077 | line); | ||
1078 | GNUNET_free (line); | ||
1079 | break; | ||
1080 | } | ||
1573 | } | 1081 | } |
1574 | 1082 | ||
1575 | /******************************************************************************/ | ||
1576 | /*********************** CLIENT HANDLING *******************/ | ||
1577 | /******************************************************************************/ | ||
1578 | |||
1579 | /** | ||
1580 | * A client connected. | ||
1581 | * | ||
1582 | * @param cls closure, NULL | ||
1583 | * @param client identification of the client | ||
1584 | */ | ||
1585 | |||
1586 | static void | ||
1587 | handle_client_connect (void *cls, struct GNUNET_SERVER_Client *cl) | ||
1588 | { | ||
1589 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client connected\n"); | ||
1590 | struct ServerClientMissedCallMessage *message; | ||
1591 | size_t msg_size; | ||
1592 | struct VoipClient c; | ||
1593 | c.client = cl; | ||
1594 | |||
1595 | GNUNET_CONTAINER_slist_add_end (clients, | ||
1596 | GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT, | ||
1597 | &c, sizeof (struct VoipClient)); | ||
1598 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Client added: %p\n"), cl); | ||
1599 | |||
1600 | if (0 < GNUNET_CONTAINER_slist_count (missed_calls)) | ||
1601 | { | ||
1602 | int i = 0; | ||
1603 | msg_size = | ||
1604 | sizeof (struct ServerClientMissedCallMessage) + | ||
1605 | GNUNET_CONTAINER_slist_count (missed_calls) * | ||
1606 | sizeof (struct MissedCall); | ||
1607 | message = | ||
1608 | (struct ServerClientMissedCallMessage *) GNUNET_malloc (msg_size); | ||
1609 | |||
1610 | message->header.size = htons (msg_size); | ||
1611 | message->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_SC_MISSED_CALL); | ||
1612 | message->number = GNUNET_CONTAINER_slist_count (missed_calls); | ||
1613 | |||
1614 | struct GNUNET_CONTAINER_SList_Iterator iterator = | ||
1615 | GNUNET_CONTAINER_slist_begin (missed_calls); | ||
1616 | do | ||
1617 | { | ||
1618 | memcpy (&(message->missed_call[i]), | ||
1619 | GNUNET_CONTAINER_slist_get (&iterator, NULL), | ||
1620 | sizeof (struct MissedCall)); | ||
1621 | i++; | ||
1622 | } | ||
1623 | while (GNUNET_OK == GNUNET_CONTAINER_slist_next (&iterator)); | ||
1624 | |||
1625 | GNUNET_CONTAINER_slist_iter_destroy (&iterator); | ||
1626 | GNUNET_CONTAINER_slist_clear (missed_calls); | ||
1627 | |||
1628 | |||
1629 | if (NULL == | ||
1630 | GNUNET_SERVER_notify_transmit_ready (cl, msg_size, | ||
1631 | MAX_TRANSMIT_DELAY, | ||
1632 | &transmit_server_missed_call_message, | ||
1633 | (void *) message)) | ||
1634 | { | ||
1635 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1636 | _("Could not queue ServerClientMissedCallMessage\n")); | ||
1637 | GNUNET_free (message); | ||
1638 | } | ||
1639 | } | ||
1640 | |||
1641 | return; | ||
1642 | } | ||
1643 | 1083 | ||
1644 | /** | 1084 | /** |
1645 | * A client disconnected. Remove all of its data structure entries. | 1085 | * A client disconnected. Remove all of its data structure entries. |
@@ -1648,42 +1088,21 @@ handle_client_connect (void *cls, struct GNUNET_SERVER_Client *cl) | |||
1648 | * @param client identification of the client | 1088 | * @param client identification of the client |
1649 | */ | 1089 | */ |
1650 | static void | 1090 | static void |
1651 | handle_client_disconnect (void *cls, struct GNUNET_SERVER_Client *cl) | 1091 | handle_client_disconnect (void *cls, |
1092 | struct GNUNET_SERVER_Client *client) | ||
1652 | { | 1093 | { |
1653 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client disconnected\n"); | 1094 | struct Line *line; |
1654 | 1095 | ||
1655 | if (connection.client == cl) | 1096 | line = GNUNET_SERVER_client_get_user_context (client, struct Line); |
1656 | { | 1097 | if (NULL == line) |
1657 | if (CONNECTED == connection.status) | 1098 | return; |
1658 | { | 1099 | GNUNET_CONTAINER_DLL_remove (lines_head, |
1659 | terminate_call (); | 1100 | lines_tail, |
1660 | } | 1101 | line); |
1661 | else | 1102 | GNUNET_free (line); |
1662 | { | 1103 | GNUNET_SERVER_client_set_user_context (client, NULL); |
1663 | status_to_listen (); | ||
1664 | } | ||
1665 | } | ||
1666 | |||
1667 | struct GNUNET_CONTAINER_SList_Iterator iterator = | ||
1668 | GNUNET_CONTAINER_slist_begin (clients); | ||
1669 | do | ||
1670 | { | ||
1671 | if (((struct VoipClient *) | ||
1672 | GNUNET_CONTAINER_slist_get (&iterator, NULL))->client == cl) | ||
1673 | { | ||
1674 | GNUNET_CONTAINER_slist_erase (&iterator); | ||
1675 | } | ||
1676 | } | ||
1677 | while (GNUNET_OK == GNUNET_CONTAINER_slist_next (&iterator)); | ||
1678 | |||
1679 | GNUNET_CONTAINER_slist_iter_destroy (&iterator); | ||
1680 | |||
1681 | return; | ||
1682 | } | 1104 | } |
1683 | 1105 | ||
1684 | /******************************************************************************/ | ||
1685 | /*********************** SERVICE *******************/ | ||
1686 | /******************************************************************************/ | ||
1687 | 1106 | ||
1688 | /** | 1107 | /** |
1689 | * Shutdown nicely | 1108 | * Shutdown nicely |
@@ -1692,60 +1111,23 @@ handle_client_disconnect (void *cls, struct GNUNET_SERVER_Client *cl) | |||
1692 | * @param tc the task context | 1111 | * @param tc the task context |
1693 | */ | 1112 | */ |
1694 | static void | 1113 | static void |
1695 | do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | 1114 | do_shutdown (void *cls, |
1115 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
1696 | { | 1116 | { |
1697 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Shutdown\n"); | ||
1698 | |||
1699 | stop_helpers (); | ||
1700 | |||
1701 | if (NULL != tunnel_reliable) | ||
1702 | { | ||
1703 | GNUNET_MESH_tunnel_destroy (tunnel_reliable); | ||
1704 | } | ||
1705 | |||
1706 | if (NULL != tunnel_unreliable) | ||
1707 | { | ||
1708 | GNUNET_MESH_tunnel_destroy (tunnel_unreliable); | ||
1709 | } | ||
1710 | |||
1711 | if (NULL != mesh) | 1117 | if (NULL != mesh) |
1712 | { | 1118 | { |
1713 | GNUNET_MESH_disconnect (mesh); | 1119 | GNUNET_MESH_disconnect (mesh); |
1714 | } | 1120 | mesh = NULL; |
1715 | 1121 | } | |
1716 | if (NULL != nc) | 1122 | if (NULL != nc) |
1717 | { | 1123 | { |
1718 | GNUNET_SERVER_notification_context_destroy (nc); | 1124 | GNUNET_SERVER_notification_context_destroy (nc); |
1719 | nc = NULL; | 1125 | nc = NULL; |
1720 | } | 1126 | } |
1721 | |||
1722 | GNUNET_CONTAINER_slist_destroy (audio_buffer); | ||
1723 | GNUNET_CONTAINER_slist_destroy (clients); | ||
1724 | GNUNET_CONTAINER_slist_destroy (missed_calls); | ||
1725 | GNUNET_CONTAINER_slist_destroy (peers_to_notify); | ||
1726 | } | 1127 | } |
1727 | 1128 | ||
1728 | 1129 | ||
1729 | /** | 1130 | /** |
1730 | * Handler array for traffic received | ||
1731 | */ | ||
1732 | static struct GNUNET_MESH_MessageHandler mesh_handlers[] = { | ||
1733 | {&handle_mesh_initiate_message, | ||
1734 | GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_INITIATE, | ||
1735 | sizeof (struct MeshSessionInitiateMessage)}, | ||
1736 | {&handle_mesh_accept_message, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_ACCEPT, | ||
1737 | sizeof (struct MeshSessionAcceptMessage)}, | ||
1738 | {&handle_mesh_reject_message, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_REJECT, | ||
1739 | sizeof (struct MeshSessionRejectMessage)}, | ||
1740 | {&handle_mesh_terminate_message, | ||
1741 | GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_SESSION_TERMINATE, | ||
1742 | sizeof (struct MeshSessionTerminateMessage)}, | ||
1743 | {&handle_mesh_audio_message, GNUNET_MESSAGE_TYPE_CONVERSATION_AUDIO, | ||
1744 | sizeof (struct AudioMessage)}, | ||
1745 | {NULL, 0, 0} | ||
1746 | }; | ||
1747 | |||
1748 | /** | ||
1749 | * Main function that will be run by the scheduler. | 1131 | * Main function that will be run by the scheduler. |
1750 | * | 1132 | * |
1751 | * @param cls closure | 1133 | * @param cls closure |
@@ -1753,67 +1135,77 @@ static struct GNUNET_MESH_MessageHandler mesh_handlers[] = { | |||
1753 | * @param c configuration | 1135 | * @param c configuration |
1754 | */ | 1136 | */ |
1755 | static void | 1137 | static void |
1756 | run (void *cls, struct GNUNET_SERVER_Handle *server, | 1138 | run (void *cls, |
1139 | struct GNUNET_SERVER_Handle *server, | ||
1757 | const struct GNUNET_CONFIGURATION_Handle *c) | 1140 | const struct GNUNET_CONFIGURATION_Handle *c) |
1758 | { | 1141 | { |
1142 | static const struct GNUNET_SERVER_MessageHandler server_handlers[] = { | ||
1143 | {&handle_client_register_message, NULL, | ||
1144 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER, | ||
1145 | sizeof (struct ClientPhoneRegisterMessage)}, | ||
1146 | {&handle_client_pickup_message, NULL, | ||
1147 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP, | ||
1148 | 0}, | ||
1149 | {&handle_client_hangup_message, NULL, | ||
1150 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP, | ||
1151 | 0}, | ||
1152 | {&handle_client_call_message, NULL, | ||
1153 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL, | ||
1154 | 0}, | ||
1155 | {&handle_client_audio_message, NULL, | ||
1156 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO, | ||
1157 | 0}, | ||
1158 | {NULL, NULL, 0, 0} | ||
1159 | }; | ||
1160 | static struct GNUNET_MESH_MessageHandler mesh_handlers[] = { | ||
1161 | {&handle_mesh_ring_message, | ||
1162 | GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RING, | ||
1163 | sizeof (struct MeshPhoneRingMessage)}, | ||
1164 | {&handle_mesh_hangup_message, | ||
1165 | GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP, | ||
1166 | 0}, | ||
1167 | {&handle_mesh_pickup_message, | ||
1168 | GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_PICK_UP, | ||
1169 | 0}, | ||
1170 | {&handle_mesh_busy_message, | ||
1171 | GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_BUSY, | ||
1172 | sizeof (struct MeshPhoneBusyMessage)}, | ||
1173 | {&handle_mesh_audio_message, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_AUDIO, | ||
1174 | 0}, | ||
1175 | {NULL, 0, 0} | ||
1176 | }; | ||
1177 | static uint32_t ports[] = { | ||
1178 | GNUNET_APPLICATION_TYPE_CONVERSATION_CONTROL, | ||
1179 | GNUNET_APPLICATION_TYPE_CONVERSATION_AUDIO, | ||
1180 | 0 | ||
1181 | }; | ||
1759 | 1182 | ||
1760 | static uint32_t ports[] = { 50002, 50003, 0 }; | ||
1761 | cfg = c; | 1183 | cfg = c; |
1762 | 1184 | GNUNET_assert (GNUNET_OK == | |
1185 | GNUNET_CRYPTO_get_host_identity (cfg, | ||
1186 | &my_identity)); | ||
1763 | mesh = GNUNET_MESH_connect (cfg, | 1187 | mesh = GNUNET_MESH_connect (cfg, |
1764 | NULL, | 1188 | NULL, |
1765 | &inbound_tunnel, | 1189 | &inbound_tunnel, |
1766 | &inbound_end, mesh_handlers, ports); | 1190 | &inbound_end, |
1191 | mesh_handlers, | ||
1192 | ports); | ||
1767 | 1193 | ||
1768 | if (NULL == mesh) | 1194 | if (NULL == mesh) |
1769 | { | 1195 | { |
1770 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Couldn't connect to mesh\n"); | 1196 | GNUNET_break (0); |
1771 | return; | 1197 | GNUNET_SCHEDULER_shutdown (); |
1772 | } | 1198 | return; |
1773 | else | 1199 | } |
1774 | { | ||
1775 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connected to mesh\n"); | ||
1776 | } | ||
1777 | |||
1778 | static const struct GNUNET_SERVER_MessageHandler server_handlers[] = { | ||
1779 | {&handle_session_initiate_message, NULL, | ||
1780 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_INITIATE, | ||
1781 | sizeof (struct ClientServerSessionInitiateMessage)}, | ||
1782 | {&handle_session_accept_message, NULL, | ||
1783 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_ACCEPT, | ||
1784 | sizeof (struct ClientServerSessionAcceptMessage)}, | ||
1785 | {&handle_session_reject_message, NULL, | ||
1786 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_REJECT, | ||
1787 | sizeof (struct ClientServerSessionRejectMessage)}, | ||
1788 | {&handle_session_terminate_message, NULL, | ||
1789 | GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_TERMINATE, | ||
1790 | sizeof (struct ClientServerSessionTerminateMessage)}, | ||
1791 | {NULL, NULL, 0, 0} | ||
1792 | }; | ||
1793 | |||
1794 | connection.status = LISTEN; | ||
1795 | |||
1796 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Changed connection status to %d\n"), | ||
1797 | connection.status); | ||
1798 | |||
1799 | nc = GNUNET_SERVER_notification_context_create (server, 16); | 1200 | nc = GNUNET_SERVER_notification_context_create (server, 16); |
1800 | |||
1801 | GNUNET_SERVER_add_handlers (server, server_handlers); | 1201 | GNUNET_SERVER_add_handlers (server, server_handlers); |
1802 | GNUNET_SERVER_connect_notify (server, &handle_client_connect, NULL); | ||
1803 | GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL); | 1202 | GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL); |
1804 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_shutdown, | 1203 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, |
1204 | &do_shutdown, | ||
1805 | NULL); | 1205 | NULL); |
1806 | |||
1807 | clients = GNUNET_CONTAINER_slist_create (); | ||
1808 | |||
1809 | // Missed calls | ||
1810 | missed_calls = GNUNET_CONTAINER_slist_create (); | ||
1811 | peers_to_notify = GNUNET_CONTAINER_slist_create (); | ||
1812 | audio_buffer = GNUNET_CONTAINER_slist_create (); | ||
1813 | |||
1814 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Voip service running\n")); | ||
1815 | } | 1206 | } |
1816 | 1207 | ||
1208 | |||
1817 | /** | 1209 | /** |
1818 | * The main function for the conversation service. | 1210 | * The main function for the conversation service. |
1819 | * | 1211 | * |
@@ -1822,10 +1214,13 @@ run (void *cls, struct GNUNET_SERVER_Handle *server, | |||
1822 | * @return 0 ok, 1 on error | 1214 | * @return 0 ok, 1 on error |
1823 | */ | 1215 | */ |
1824 | int | 1216 | int |
1825 | main (int argc, char *const *argv) | 1217 | main (int argc, |
1218 | char *const *argv) | ||
1826 | { | 1219 | { |
1827 | return (GNUNET_OK == | 1220 | return (GNUNET_OK == |
1828 | GNUNET_SERVICE_run (argc, argv, "conversation", GNUNET_SERVICE_OPTION_NONE, | 1221 | GNUNET_SERVICE_run (argc, argv, |
1222 | "conversation", | ||
1223 | GNUNET_SERVICE_OPTION_NONE, | ||
1829 | &run, NULL)) ? 0 : 1; | 1224 | &run, NULL)) ? 0 : 1; |
1830 | } | 1225 | } |
1831 | 1226 | ||