| 1 /* |
|
| 2 * This program is free software; you can redistribute it and/or modify |
|
| 3 * it under the terms of the GNU General Public License as published by |
|
| 4 * the Free Software Foundation; either version 2 of the License, or |
|
| 5 * (at your option) any later version. |
|
| 6 * |
|
| 7 * This program is distributed in the hope that it will be useful, |
|
| 8 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 10 * GNU Library General Public License for more details. |
|
| 11 * |
|
| 12 * You should have received a copy of the GNU General Public License |
|
| 13 * along with this program; if not, write to the Free Software |
|
| 14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
|
| 15 */ |
|
| 16 |
|
| 17 #include <string.h> |
|
| 18 |
|
| 19 #include "dns_sd.h" |
|
| 20 #include "bonjour.h" |
|
| 21 #include "buddy.h" |
|
| 22 #include "debug.h" |
|
| 23 |
|
| 24 /* Private data */ |
|
| 25 |
|
| 26 typedef struct _dns_sd_packet |
|
| 27 { |
|
| 28 gchar *name; |
|
| 29 gchar *txtvers; |
|
| 30 gchar *version; |
|
| 31 gchar *first; |
|
| 32 gchar *last; |
|
| 33 gint port_p2pj; |
|
| 34 gchar *phsh; |
|
| 35 gchar *status; |
|
| 36 gchar *message; |
|
| 37 gchar *email; |
|
| 38 gchar *vc; |
|
| 39 gchar *jid; |
|
| 40 gchar *AIM; |
|
| 41 } dns_sd_packet; |
|
| 42 |
|
| 43 /* End private data */ |
|
| 44 |
|
| 45 /* Private functions */ |
|
| 46 |
|
| 47 static sw_result HOWL_API |
|
| 48 _publish_reply(sw_discovery discovery, sw_discovery_oid oid, |
|
| 49 sw_discovery_publish_status status, sw_opaque extra) |
|
| 50 { |
|
| 51 gaim_debug_warning("bonjour", "_publish_reply --> Start\n"); |
|
| 52 |
|
| 53 /* Check the answer from the mDNS daemon */ |
|
| 54 switch (status) |
|
| 55 { |
|
| 56 case SW_DISCOVERY_PUBLISH_STARTED : |
|
| 57 gaim_debug_info("bonjour", "_publish_reply --> Service started\n"); |
|
| 58 break; |
|
| 59 case SW_DISCOVERY_PUBLISH_STOPPED : |
|
| 60 gaim_debug_info("bonjour", "_publish_reply --> Service stopped\n"); |
|
| 61 break; |
|
| 62 case SW_DISCOVERY_PUBLISH_NAME_COLLISION : |
|
| 63 gaim_debug_info("bonjour", "_publish_reply --> Name collision\n"); |
|
| 64 break; |
|
| 65 case SW_DISCOVERY_PUBLISH_INVALID : |
|
| 66 gaim_debug_info("bonjour", "_publish_reply --> Service invalid\n"); |
|
| 67 break; |
|
| 68 } |
|
| 69 |
|
| 70 return SW_OKAY; |
|
| 71 } |
|
| 72 |
|
| 73 static sw_result HOWL_API |
|
| 74 _resolve_reply(sw_discovery discovery, sw_discovery_oid oid, |
|
| 75 sw_uint32 interface_index, sw_const_string name, |
|
| 76 sw_const_string type, sw_const_string domain, |
|
| 77 sw_ipv4_address address, sw_port port, |
|
| 78 sw_octets text_record, sw_ulong text_record_len, |
|
| 79 sw_opaque extra) |
|
| 80 { |
|
| 81 BonjourBuddy *buddy; |
|
| 82 GaimAccount *account = (GaimAccount*)extra; |
|
| 83 gchar *txtvers = NULL; |
|
| 84 gchar *version = NULL; |
|
| 85 gchar *first = NULL; |
|
| 86 gint port_p2pj = -1; |
|
| 87 gchar *phsh = NULL; |
|
| 88 gchar *status = NULL; |
|
| 89 gchar *email = NULL; |
|
| 90 gchar *last = NULL; |
|
| 91 gchar *jid = NULL; |
|
| 92 gchar *AIM = NULL; |
|
| 93 gchar *vc = NULL; |
|
| 94 gchar *msg = NULL; |
|
| 95 gint address_length = 16; |
|
| 96 gchar *ip = NULL; |
|
| 97 sw_text_record_iterator iterator; |
|
| 98 char key[SW_TEXT_RECORD_MAX_LEN]; |
|
| 99 char value[SW_TEXT_RECORD_MAX_LEN]; |
|
| 100 sw_uint32 value_length; |
|
| 101 |
|
| 102 sw_discovery_cancel(discovery, oid); |
|
| 103 |
|
| 104 /* Get the ip as a string */ |
|
| 105 ip = malloc(address_length); |
|
| 106 sw_ipv4_address_name(address, ip, address_length); |
|
| 107 |
|
| 108 /* Obtain the parameters from the text_record */ |
|
| 109 if ((text_record_len > 0) && (text_record) && (*text_record != '\0')) |
|
| 110 { |
|
| 111 sw_text_record_iterator_init(&iterator, text_record, text_record_len); |
|
| 112 while (sw_text_record_iterator_next(iterator, key, (sw_octet *)value, &value_length) == SW_OKAY) |
|
| 113 { |
|
| 114 /* Compare the keys with the possible ones and save them on */ |
|
| 115 /* the appropiate place of the buddy_list */ |
|
| 116 if (strcmp(key, "txtvers") == 0) { |
|
| 117 txtvers = g_strdup(value); |
|
| 118 } else if (strcmp(key, "version") == 0) { |
|
| 119 version = g_strdup(value); |
|
| 120 } else if (strcmp(key, "1st") == 0) { |
|
| 121 first = g_strdup(value); |
|
| 122 } else if (strcmp(key, "port.p2pj") == 0) { |
|
| 123 port_p2pj = atoi(value); |
|
| 124 } else if (strcmp(key, "status") == 0) { |
|
| 125 status = g_strdup(value); |
|
| 126 } else if (strcmp(key, "email") == 0) { |
|
| 127 email = g_strdup(value); |
|
| 128 } else if (strcmp(key, "last") == 0) { |
|
| 129 last = g_strdup(value); |
|
| 130 } else if (strcmp(key, "jid") == 0) { |
|
| 131 jid = g_strdup(value); |
|
| 132 } else if (strcmp(key, "AIM") == 0) { |
|
| 133 AIM = g_strdup(value); |
|
| 134 } else if (strcmp(key, "vc") == 0) { |
|
| 135 vc = g_strdup(value); |
|
| 136 } else if (strcmp(key, "phsh") == 0) { |
|
| 137 phsh = g_strdup(value); |
|
| 138 } else if (strcmp(key, "msg") == 0) { |
|
| 139 msg = g_strdup(value); |
|
| 140 } |
|
| 141 } |
|
| 142 } |
|
| 143 |
|
| 144 /* Put the parameters of the text_record in a buddy and add the buddy to */ |
|
| 145 /* the buddy list */ |
|
| 146 buddy = bonjour_buddy_new((gchar *)name, first, port_p2pj, phsh, |
|
| 147 status, email, last, jid, AIM, vc, ip, msg); |
|
| 148 |
|
| 149 if (bonjour_buddy_check(buddy) == FALSE) |
|
| 150 { |
|
| 151 return SW_DISCOVERY_E_UNKNOWN; |
|
| 152 } |
|
| 153 |
|
| 154 /* Add or update the buddy in our buddy list */ |
|
| 155 bonjour_buddy_add_to_gaim(account, buddy); |
|
| 156 |
|
| 157 /* Free all the temporal strings */ |
|
| 158 g_free(txtvers); |
|
| 159 g_free(version); |
|
| 160 g_free(first); |
|
| 161 g_free(last); |
|
| 162 g_free(status); |
|
| 163 g_free(email); |
|
| 164 g_free(jid); |
|
| 165 g_free(AIM); |
|
| 166 g_free(vc); |
|
| 167 g_free(phsh); |
|
| 168 g_free(msg); |
|
| 169 |
|
| 170 return SW_OKAY; |
|
| 171 } |
|
| 172 |
|
| 173 static sw_result HOWL_API |
|
| 174 _browser_reply(sw_discovery discovery, sw_discovery_oid oid, |
|
| 175 sw_discovery_browse_status status, |
|
| 176 sw_uint32 interface_index, sw_const_string name, |
|
| 177 sw_const_string type, sw_const_string domain, |
|
| 178 sw_opaque_t extra) |
|
| 179 { |
|
| 180 sw_discovery_resolve_id rid; |
|
| 181 GaimAccount *account = (GaimAccount*)extra; |
|
| 182 GaimBuddy *gb = NULL; |
|
| 183 |
|
| 184 switch (status) |
|
| 185 { |
|
| 186 case SW_DISCOVERY_BROWSE_INVALID: |
|
| 187 gaim_debug_warning("bonjour", "_browser_reply --> Invalid\n"); |
|
| 188 break; |
|
| 189 case SW_DISCOVERY_BROWSE_RELEASE: |
|
| 190 gaim_debug_warning("bonjour", "_browser_reply --> Release\n"); |
|
| 191 break; |
|
| 192 case SW_DISCOVERY_BROWSE_ADD_DOMAIN: |
|
| 193 gaim_debug_warning("bonjour", "_browser_reply --> Add domain\n"); |
|
| 194 break; |
|
| 195 case SW_DISCOVERY_BROWSE_ADD_DEFAULT_DOMAIN: |
|
| 196 gaim_debug_warning("bonjour", "_browser_reply --> Add default domain\n"); |
|
| 197 break; |
|
| 198 case SW_DISCOVERY_BROWSE_REMOVE_DOMAIN: |
|
| 199 gaim_debug_warning("bonjour", "_browser_reply --> Remove domain\n"); |
|
| 200 break; |
|
| 201 case SW_DISCOVERY_BROWSE_ADD_SERVICE: |
|
| 202 /* A new peer has join the network and uses iChat bonjour */ |
|
| 203 gaim_debug_info("bonjour", "_browser_reply --> Add service\n"); |
|
| 204 if (g_ascii_strcasecmp(name, account->username) != 0) |
|
| 205 { |
|
| 206 if (sw_discovery_resolve(discovery, interface_index, name, type, |
|
| 207 domain, _resolve_reply, extra, &rid) != SW_OKAY) |
|
| 208 { |
|
| 209 gaim_debug_warning("bonjour", "_browser_reply --> Cannot send resolve\n"); |
|
| 210 } |
|
| 211 } |
|
| 212 break; |
|
| 213 case SW_DISCOVERY_BROWSE_REMOVE_SERVICE: |
|
| 214 gaim_debug_info("bonjour", "_browser_reply --> Remove service\n"); |
|
| 215 gb = gaim_find_buddy((GaimAccount*)extra, name); |
|
| 216 if (gb != NULL) |
|
| 217 { |
|
| 218 bonjour_buddy_delete(gb->proto_data); |
|
| 219 gaim_blist_remove_buddy(gb); |
|
| 220 } |
|
| 221 break; |
|
| 222 case SW_DISCOVERY_BROWSE_RESOLVED: |
|
| 223 gaim_debug_info("bonjour", "_browse_reply --> Resolved\n"); |
|
| 224 break; |
|
| 225 default: |
|
| 226 break; |
|
| 227 } |
|
| 228 |
|
| 229 return SW_OKAY; |
|
| 230 } |
|
| 231 |
|
| 232 static int |
|
| 233 _dns_sd_publish(BonjourDnsSd *data, PublishType type) |
|
| 234 { |
|
| 235 sw_text_record dns_data; |
|
| 236 sw_result publish_result = SW_OKAY; |
|
| 237 |
|
| 238 /* Fill the data for the service */ |
|
| 239 if (sw_text_record_init(&dns_data) != SW_OKAY) |
|
| 240 { |
|
| 241 gaim_debug_error("bonjour", "Unable to initialize the data for the mDNS.\n"); |
|
| 242 return -1; |
|
| 243 } |
|
| 244 |
|
| 245 sw_text_record_add_key_and_string_value(dns_data, "txtvers", data->txtvers); |
|
| 246 sw_text_record_add_key_and_string_value(dns_data, "version", data->version); |
|
| 247 sw_text_record_add_key_and_string_value(dns_data, "1st", data->first); |
|
| 248 sw_text_record_add_key_and_string_value(dns_data, "last", data->last); |
|
| 249 /* sw_text_record_add_key_and_string_value(dns_data, "port.p2pj", itoa(data->port_p2pj)); */ |
|
| 250 sw_text_record_add_key_and_string_value(dns_data, "port.p2pj", BONJOUR_DEFAULT_PORT); |
|
| 251 sw_text_record_add_key_and_string_value(dns_data, "phsh", data->phsh); |
|
| 252 sw_text_record_add_key_and_string_value(dns_data, "status", data->status); |
|
| 253 sw_text_record_add_key_and_string_value(dns_data, "msg", data->msg); |
|
| 254 sw_text_record_add_key_and_string_value(dns_data, "email", data->email); |
|
| 255 sw_text_record_add_key_and_string_value(dns_data, "vc", data->vc); |
|
| 256 sw_text_record_add_key_and_string_value(dns_data, "jid", data->jid); |
|
| 257 sw_text_record_add_key_and_string_value(dns_data, "AIM", data->AIM); |
|
| 258 |
|
| 259 /* Publish the service */ |
|
| 260 switch (type) |
|
| 261 { |
|
| 262 case PUBLISH_START: |
|
| 263 publish_result = sw_discovery_publish(data->session, 0, data->name, ICHAT_SERVICE, NULL, |
|
| 264 NULL, data->port_p2pj, sw_text_record_bytes(dns_data), sw_text_record_len(dns_data), |
|
| 265 _publish_reply, NULL, &data->session_id); |
|
| 266 break; |
|
| 267 case PUBLISH_UPDATE: |
|
| 268 publish_result = sw_discovery_publish_update(data->session, data->session_id, |
|
| 269 sw_text_record_bytes(dns_data), sw_text_record_len(dns_data)); |
|
| 270 break; |
|
| 271 } |
|
| 272 if (publish_result != SW_OKAY) |
|
| 273 { |
|
| 274 gaim_debug_error("bonjour", "Unable to publish or change the status of the _presence._tcp service."); |
|
| 275 return -1; |
|
| 276 } |
|
| 277 |
|
| 278 /* Free the memory used by temp data */ |
|
| 279 sw_text_record_fina(dns_data); |
|
| 280 |
|
| 281 return 0; |
|
| 282 } |
|
| 283 |
|
| 284 static void |
|
| 285 _dns_sd_handle_packets(gpointer data, gint source, GaimInputCondition condition) |
|
| 286 { |
|
| 287 sw_discovery_read_socket((sw_discovery)data); |
|
| 288 } |
|
| 289 |
|
| 290 /* End private functions */ |
|
| 291 |
|
| 292 /** |
|
| 293 * Allocate space for the dns-sd data. |
|
| 294 */ |
|
| 295 BonjourDnsSd * |
|
| 296 bonjour_dns_sd_new() |
|
| 297 { |
|
| 298 BonjourDnsSd *data = g_new0(BonjourDnsSd, 1); |
|
| 299 |
|
| 300 return data; |
|
| 301 } |
|
| 302 |
|
| 303 /** |
|
| 304 * Deallocate the space of the dns-sd data. |
|
| 305 */ |
|
| 306 void |
|
| 307 bonjour_dns_sd_free(BonjourDnsSd *data) |
|
| 308 { |
|
| 309 g_free(data->first); |
|
| 310 g_free(data->last); |
|
| 311 g_free(data->email); |
|
| 312 g_free(data); |
|
| 313 } |
|
| 314 |
|
| 315 /** |
|
| 316 * Send a new dns-sd packet updating our status. |
|
| 317 */ |
|
| 318 void |
|
| 319 bonjour_dns_sd_send_status(BonjourDnsSd *data, const char *status, const char *status_message) |
|
| 320 { |
|
| 321 g_free(data->status); |
|
| 322 g_free(data->msg); |
|
| 323 |
|
| 324 data->status = g_strdup(status); |
|
| 325 data->msg = g_strdup(status_message); |
|
| 326 |
|
| 327 /* Update our text record with the new status */ |
|
| 328 _dns_sd_publish(data, PUBLISH_UPDATE); /* <--We must control the errors */ |
|
| 329 } |
|
| 330 |
|
| 331 /** |
|
| 332 * Advertise our presence within the dns-sd daemon and start browsing |
|
| 333 * for other bonjour peers. |
|
| 334 */ |
|
| 335 gboolean |
|
| 336 bonjour_dns_sd_start(BonjourDnsSd *data) |
|
| 337 { |
|
| 338 GaimAccount *account; |
|
| 339 GaimConnection *gc; |
|
| 340 gint dns_sd_socket; |
|
| 341 sw_discovery_oid session_id; |
|
| 342 |
|
| 343 account = data->account; |
|
| 344 gc = gaim_account_get_connection(account); |
|
| 345 |
|
| 346 /* Initialize the dns-sd data and session */ |
|
| 347 if (sw_discovery_init(&data->session) != SW_OKAY) |
|
| 348 { |
|
| 349 gaim_debug_error("bonjour", "Unable to initialize an mDNS session.\n"); |
|
| 350 |
|
| 351 /* In Avahi, sw_discovery_init frees data->session but doesn't clear it */ |
|
| 352 data->session = NULL; |
|
| 353 |
|
| 354 return FALSE; |
|
| 355 } |
|
| 356 |
|
| 357 /* Publish our bonjour IM client at the mDNS daemon */ |
|
| 358 _dns_sd_publish(data, PUBLISH_START); /* <--We must control the errors */ |
|
| 359 |
|
| 360 /* Advise the daemon that we are waiting for connections */ |
|
| 361 if (sw_discovery_browse(data->session, 0, ICHAT_SERVICE, NULL, _browser_reply, |
|
| 362 data->account, &session_id) != SW_OKAY) |
|
| 363 { |
|
| 364 gaim_debug_error("bonjour", "Unable to get service."); |
|
| 365 return FALSE; |
|
| 366 } |
|
| 367 |
|
| 368 /* Get the socket that communicates with the mDNS daemon and bind it to a */ |
|
| 369 /* callback that will handle the dns_sd packets */ |
|
| 370 dns_sd_socket = sw_discovery_socket(data->session); |
|
| 371 gc->inpa = gaim_input_add(dns_sd_socket, GAIM_INPUT_READ, |
|
| 372 _dns_sd_handle_packets, data->session); |
|
| 373 |
|
| 374 return TRUE; |
|
| 375 } |
|
| 376 |
|
| 377 /** |
|
| 378 * Unregister the "_presence._tcp" service at the mDNS daemon. |
|
| 379 */ |
|
| 380 void |
|
| 381 bonjour_dns_sd_stop(BonjourDnsSd *data) |
|
| 382 { |
|
| 383 GaimAccount *account; |
|
| 384 GaimConnection *gc; |
|
| 385 |
|
| 386 if (data->session == NULL) |
|
| 387 return; |
|
| 388 |
|
| 389 sw_discovery_cancel(data->session, data->session_id); |
|
| 390 |
|
| 391 account = data->account; |
|
| 392 gc = gaim_account_get_connection(account); |
|
| 393 gaim_input_remove(gc->inpa); |
|
| 394 |
|
| 395 g_free(data->session); |
|
| 396 data->session = NULL; |
|
| 397 } |
|