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