comparison src/protocols/rendezvous/rendezvous.c @ 8487:c3ffec7fab94

[gaim-migrate @ 9222] Apparently if you don't cvs add a directory before you cvs add files in that directory, cvs just puts the files in the napster directory. ... Seems logical enough to me. committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Tue, 23 Mar 2004 03:45:13 +0000
parents
children b06dcc915c45
comparison
equal deleted inserted replaced
8486:538f4d0faf1d 8487:c3ffec7fab94
1 /*
2 * gaim - Rendezvous Protocol Plugin
3 *
4 * Gaim is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
6 * source distribution.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22 #include <glib.h>
23 #include <glib/gprintf.h>
24
25 #include "internal.h"
26
27 #include "account.h"
28 #include "accountopt.h"
29 #include "blist.h"
30 #include "conversation.h"
31 #include "debug.h"
32 #include "prpl.h"
33
34 #include "mdns.h"
35 #include "util.h"
36
37 #define RENDEZVOUS_CONNECT_STEPS 2
38
39 typedef struct _RendezvousData {
40 int fd;
41 GHashTable *buddies;
42 } RendezvousData;
43
44 typedef struct _RendezvousBuddy {
45 gchar *firstandlast;
46 gchar *aim;
47 int p2pjport;
48 int status;
49 int idle;
50 gchar *msg;
51 } RendezvousBuddy;
52
53 #define UC_IDLE 2
54
55 /****************************/
56 /* Utility Functions */
57 /****************************/
58 static void rendezvous_buddy_free(gpointer data)
59 {
60 RendezvousBuddy *rb = data;
61
62 g_free(rb->firstandlast);
63 g_free(rb->msg);
64 g_free(rb);
65 }
66
67 /*
68 * Extract the "user@host" name from a full presence domain
69 * of the form "user@host._presence._tcp.local"
70 *
71 * @return If the domain is NOT a _presence._tcp.local domain
72 * then return NULL. Otherwise return a newly allocated
73 * null-terminated string containing the "user@host" for
74 * the given domain. This string should be g_free'd
75 * when no longer needed.
76 */
77 static gchar *rendezvous_extract_name(gchar *domain)
78 {
79 gchar *ret, *suffix;
80
81 if (!g_str_has_suffix(domain, "._presence._tcp.local"))
82 return NULL;
83
84 suffix = strstr(domain, "._presence._tcp.local");
85 ret = g_strndup(domain, suffix - domain);
86
87 return ret;
88 }
89
90 /****************************/
91 /* Buddy List Functions */
92 /****************************/
93 static void rendezvous_addtolocal(GaimConnection *gc, const char *name, const char *domain)
94 {
95 GaimAccount *account = gaim_connection_get_account(gc);
96 GaimBuddy *b;
97 GaimGroup *g;
98
99 g = gaim_find_group(domain);
100 if (g == NULL) {
101 g = gaim_group_new(domain);
102 gaim_blist_add_group(g, NULL);
103 }
104
105 b = gaim_find_buddy_in_group(account, name, g);
106 if (b != NULL)
107 return;
108
109 b = gaim_buddy_new(account, name, NULL);
110 gaim_blist_add_buddy(b, NULL, g, NULL);
111 serv_got_update(gc, b->name, 1, 0, 0, 0, 0);
112 }
113
114 static void rendezvous_removefromlocal(GaimConnection *gc, const char *name, const char *domain)
115 {
116 GaimAccount *account = gaim_connection_get_account(gc);
117 GaimBuddy *b;
118 GaimGroup *g;
119
120 g = gaim_find_group(domain);
121 if (g == NULL)
122 return;
123
124 b = gaim_find_buddy_in_group(account, name, g);
125 if (b == NULL)
126 return;
127
128 serv_got_update(gc, b->name, 0, 0, 0, 0, 0);
129 gaim_blist_remove_buddy(b);
130 }
131
132 static void rendezvous_removeallfromlocal(GaimConnection *gc)
133 {
134 GaimAccount *account = gaim_connection_get_account(gc);
135 GaimBuddyList *blist;
136 GaimBlistNode *gnode, *cnode, *bnode;
137 GaimBuddy *b;
138
139 /* Go through and remove all buddies that belong to this account */
140 if ((blist = gaim_get_blist()) != NULL) {
141 for (gnode = blist->root; gnode; gnode = gnode->next) {
142 if (!GAIM_BLIST_NODE_IS_GROUP(gnode))
143 continue;
144 for (cnode = gnode->child; cnode; cnode = cnode->next) {
145 if (!GAIM_BLIST_NODE_IS_CONTACT(cnode))
146 continue;
147 for (bnode = cnode->child; bnode; bnode = bnode->next) {
148 if (!GAIM_BLIST_NODE_IS_BUDDY(bnode))
149 continue;
150 b = (GaimBuddy *)bnode;
151 if (b->account != account)
152 continue;
153 serv_got_update(gc, b->name, 0, 0, 0, 0, 0);
154 gaim_blist_remove_buddy(b);
155 }
156 }
157 }
158 }
159 }
160
161 static void rendezvous_handle_rr_txt(GaimConnection *gc, ResourceRecord *rr, const gchar *name)
162 {
163 RendezvousData *rd = gc->proto_data;
164 RendezvousBuddy *rb;
165 GHashTable *rdata;
166 gchar *tmp1, *tmp2;
167
168 rb = g_hash_table_lookup(rd->buddies, name);
169 if (rb == NULL) {
170 rb = g_malloc0(sizeof(RendezvousBuddy));
171 g_hash_table_insert(rd->buddies, g_strdup(name), rb);
172 }
173
174 rdata = rr->rdata;
175
176 tmp1 = g_hash_table_lookup(rdata, "1st");
177 tmp2 = g_hash_table_lookup(rdata, "last");
178 g_free(rb->firstandlast);
179 rb->firstandlast = g_strdup_printf("%s%s%s",
180 (tmp1 ? tmp1 : ""),
181 (tmp1 || tmp2 ? " " : ""),
182 (tmp2 ? tmp2 : ""));
183 serv_got_alias(gc, name, rb->firstandlast);
184
185 tmp1 = g_hash_table_lookup(rdata, "aim");
186 if (tmp1 != NULL) {
187 g_free(rb->aim);
188 rb->aim = g_strdup(tmp1);
189 }
190
191 tmp1 = g_hash_table_lookup(rdata, "port.p2pj");
192 rb->p2pjport = atoi(tmp1);
193
194 tmp1 = g_hash_table_lookup(rdata, "status");
195 if (tmp1 != NULL) {
196 if (!strcmp(tmp1, "dnd")) {
197 /* Available */
198 rb->status = 0;
199 } else if (!strcmp(tmp1, "away")) {
200 /* Idle */
201 tmp2 = g_hash_table_lookup(rdata, "away");
202 rb->idle = atoi(tmp2);
203 gaim_debug_error("XXX", "User has been idle for %d\n", rb->idle);
204 rb->status = UC_IDLE;
205 } else if (!strcmp(tmp1, "avail")) {
206 /* Away */
207 rb->status = UC_UNAVAILABLE;
208 }
209 serv_got_update(gc, name, 1, 0, 0, 0, rb->status);
210 }
211
212 tmp1 = g_hash_table_lookup(rdata, "msg");
213 if (tmp1 != NULL) {
214 g_free(rb->msg);
215 rb->msg = g_strdup(tmp1);
216 }
217 }
218
219 /*
220 * Parse a resource record and do stuff if we need to.
221 */
222 static void rendezvous_handle_rr(GaimConnection *gc, ResourceRecord *rr)
223 {
224 gchar *name;
225
226 gaim_debug_error("XXX", "Parsing resource record with domain name %s\n", rr->name);
227
228 switch (rr->type) {
229 case RENDEZVOUS_RRTYPE_NULL: {
230 if ((name = rendezvous_extract_name(rr->name)) != NULL) {
231 if (rr->rdlength > 0) {
232 /* Data is a buddy icon */
233 gaim_buddy_icons_set_for_user(gaim_connection_get_account(gc), name, rr->rdata, rr->rdlength);
234 }
235
236 g_free(name);
237 }
238 } break;
239
240 case RENDEZVOUS_RRTYPE_PTR: {
241 gchar *rdata = rr->rdata;
242 if ((name = rendezvous_extract_name(rdata)) != NULL) {
243 if (rr->ttl > 0)
244 rendezvous_addtolocal(gc, name, "Rendezvous");
245 else
246 rendezvous_removefromlocal(gc, name, "Rendezvous");
247
248 g_free(name);
249 }
250 } break;
251
252 case RENDEZVOUS_RRTYPE_TXT: {
253 if ((name = rendezvous_extract_name(rr->name)) != NULL) {
254 rendezvous_handle_rr_txt(gc, rr, name);
255 g_free(name);
256 }
257 } break;
258 }
259 }
260
261 /****************************/
262 /* Icon and Emblem Funtions */
263 /****************************/
264 static const char* rendezvous_prpl_list_icon(GaimAccount *a, GaimBuddy *b)
265 {
266 return "rendezvous";
267 }
268
269 static void rendezvous_prpl_list_emblems(GaimBuddy *b, char **se, char **sw, char **nw, char **ne)
270 {
271 if (GAIM_BUDDY_IS_ONLINE(b)) {
272 if (b->uc & UC_UNAVAILABLE)
273 *se = "away";
274 } else {
275 *se = "offline";
276 }
277 }
278
279 static gchar *rendezvous_prpl_status_text(GaimBuddy *b)
280 {
281 GaimConnection *gc = b->account->gc;
282 RendezvousData *rd = gc->proto_data;
283 RendezvousBuddy *rb;
284 gchar *ret;
285
286 rb = g_hash_table_lookup(rd->buddies, b->name);
287 if ((rb == NULL) || (rb->msg == NULL))
288 return NULL;
289
290 ret = g_strdup(rb->msg);
291
292 return ret;
293 }
294
295 static gchar *rendezvous_prpl_tooltip_text(GaimBuddy *b)
296 {
297 GaimConnection *gc = b->account->gc;
298 RendezvousData *rd = gc->proto_data;
299 RendezvousBuddy *rb;
300 GString *ret;
301
302 rb = g_hash_table_lookup(rd->buddies, b->name);
303 if (rb == NULL)
304 return NULL;
305
306 ret = g_string_new("");
307
308 if (rb->aim != NULL)
309 g_string_append_printf(ret, _("<b>AIM Screen name</b>: %s\n"), rb->aim);
310
311 if (rb->msg != NULL) {
312 if (rb->status == UC_UNAVAILABLE)
313 g_string_append_printf(ret, _("<b>Away Message</b>: %s\n"), rb->msg);
314 else
315 g_string_append_printf(ret, _("<b>Available Message</b>: %s\n"), rb->msg);
316 }
317
318 /* XXX - Fix blist.c so we can prepend the \n's rather than appending them */
319
320 return g_string_free(ret, FALSE);
321 }
322
323 /****************************/
324 /* Connection Funtions */
325 /****************************/
326 static void rendezvous_callback(gpointer data, gint source, GaimInputCondition condition)
327 {
328 GaimConnection *gc = data;
329 RendezvousData *rd = gc->proto_data;
330 DNSPacket *dns;
331 int i;
332
333 gaim_debug_misc("rendezvous", "Received rendezvous datagram\n");
334
335 dns = mdns_read(rd->fd);
336 if (dns == NULL)
337 return;
338
339 /* Handle the DNS packet */
340 for (i = 0; i < dns->header.numanswers; i++)
341 rendezvous_handle_rr(gc, &dns->answers[i]);
342 for (i = 0; i < dns->header.numauthority; i++)
343 rendezvous_handle_rr(gc, &dns->authority[i]);
344 for (i = 0; i < dns->header.numadditional; i++)
345 rendezvous_handle_rr(gc, &dns->additional[i]);
346
347 mdns_free(dns);
348 }
349
350 static void rendezvous_prpl_login(GaimAccount *account)
351 {
352 GaimConnection *gc = gaim_account_get_connection(account);
353 RendezvousData *rd;
354
355 rd = g_new0(RendezvousData, 1);
356 rd->buddies = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, rendezvous_buddy_free);
357
358 gc->proto_data = rd;
359
360 gaim_connection_update_progress(gc, _("Preparing Buddy List"), 0, RENDEZVOUS_CONNECT_STEPS);
361
362 rendezvous_removeallfromlocal(gc);
363
364 gaim_connection_update_progress(gc, _("Connecting"), 1, RENDEZVOUS_CONNECT_STEPS);
365
366 rd->fd = mdns_establish_socket();
367 if (rd->fd == -1) {
368 gaim_connection_error(gc, _("Unable to login to rendezvous"));
369 return;
370 }
371
372 gc->inpa = gaim_input_add(rd->fd, GAIM_INPUT_READ, rendezvous_callback, gc);
373
374 gaim_connection_set_state(gc, GAIM_CONNECTED);
375
376 mdns_query(rd->fd, "_presence._tcp.local");
377
378 #if 0
379 text_record_add("txtvers", "1");
380 text_record_add("status", "avail");
381 text_record_add("1st", gaim_account_get_string(account, "first", "Gaim"));
382 text_record_add("AIM", "markdoliner");
383 text_record_add("version", "1");
384 text_record_add("port.p2pj", "5298");
385 text_record_add("last", gaim_account_get_string(account, "last", _("User")));
386
387 publish(account->username, "_presence._tcp", 5298);
388 #endif
389 }
390
391 static void rendezvous_prpl_close(GaimConnection *gc)
392 {
393 RendezvousData *rd = (RendezvousData *)gc->proto_data;
394
395 if (gc->inpa)
396 gaim_input_remove(gc->inpa);
397
398 rendezvous_removeallfromlocal(gc);
399
400 if (!rd)
401 return;
402
403 if (rd->fd >= 0)
404 close(rd->fd);
405
406 g_hash_table_destroy(rd->buddies);
407
408 g_free(rd);
409 }
410
411 static int rendezvous_prpl_send_im(GaimConnection *gc, const char *who, const char *message, GaimConvImFlags flags)
412 {
413 gaim_debug_info("rendezvous", "Sending IM\n");
414
415 return 1;
416 }
417
418 static void rendezvous_prpl_setaway(GaimConnection *gc, const char *state, const char *text)
419 {
420 gaim_debug_error("rendezvous", "Set away, state=%s, text=%s\n", state, text);
421 }
422
423 static GaimPlugin *my_protocol = NULL;
424
425 static GaimPluginProtocolInfo prpl_info =
426 {
427 OPT_PROTO_NO_PASSWORD,
428 NULL,
429 NULL,
430 NULL,
431 rendezvous_prpl_list_icon,
432 rendezvous_prpl_list_emblems,
433 rendezvous_prpl_status_text,
434 rendezvous_prpl_tooltip_text,
435 NULL,
436 NULL,
437 NULL,
438 NULL,
439 rendezvous_prpl_login,
440 rendezvous_prpl_close,
441 rendezvous_prpl_send_im,
442 NULL,
443 NULL,
444 NULL,
445 rendezvous_prpl_setaway,
446 NULL,
447 NULL,
448 NULL,
449 NULL,
450 NULL,
451 NULL,
452 NULL,
453 NULL,
454 NULL,
455 NULL,
456 NULL,
457 NULL,
458 NULL,
459 NULL,
460 NULL,
461 NULL,
462 NULL,
463 NULL,
464 NULL,
465 NULL,
466 NULL,
467 NULL,
468 NULL,
469 NULL,
470 NULL,
471 NULL,
472 NULL,
473 NULL,
474 NULL,
475 NULL,
476 NULL
477 };
478
479 static GaimPluginInfo info =
480 {
481 2, /**< api_version */
482 GAIM_PLUGIN_PROTOCOL, /**< type */
483 NULL, /**< ui_requirement */
484 0, /**< flags */
485 NULL, /**< dependencies */
486 GAIM_PRIORITY_DEFAULT, /**< priority */
487
488 "prpl-rendezvous", /**< id */
489 "Rendezvous", /**< name */
490 VERSION, /**< version */
491 /** summary */
492 N_("Rendezvous Protocol Plugin"),
493 /** description */
494 N_("Rendezvous Protocol Plugin"),
495 NULL, /**< author */
496 GAIM_WEBSITE, /**< homepage */
497
498 NULL, /**< load */
499 NULL, /**< unload */
500 NULL, /**< destroy */
501
502 NULL, /**< ui_info */
503 &prpl_info /**< extra_info */
504 };
505
506 static void init_plugin(GaimPlugin *plugin)
507 {
508 GaimAccountUserSplit *split;
509 GaimAccountOption *option;
510
511 /* Try to avoid making this configurable... */
512 split = gaim_account_user_split_new(_("Host Name"), "localhost", '@');
513 prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
514
515 option = gaim_account_option_string_new(_("First Name"), "first", "Gaim");
516 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
517 option);
518
519 option = gaim_account_option_string_new(_("Last Name"), "last", _("User"));
520 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
521 option);
522
523 option = gaim_account_option_bool_new(_("Share AIM screen name"), "shareaim", TRUE);
524 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
525 option);
526
527 my_protocol = plugin;
528 }
529
530 GAIM_INIT_PLUGIN(rendezvous, init_plugin, info);