1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
5 * Copyright (C) 1998-2001, Mark Spencer <markster@marko.net>
6 * Some code borrowed from GtkZephyr, by
7 * Jag/Sean Dilda <agrajag@linuxpower.org>/<smdilda@unity.ncsu.edu>
8 * http://gtkzephyr.linuxpower.org/
10 * Some code borrowed from kzephyr, by
11 * Chris Colohan <colohan+@cs.cmu.edu>
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
29 #include "libpurple/internal.h"
31 #include "accountopt.h"
45 #define ZEPHYR_FALLBACK_CHARSET "ISO-8859-1"
47 /* these are deliberately high, since most people don't send multiple "PING"s */
48 #define ZEPHYR_TYPING_SEND_TIMEOUT 15
49 #define ZEPHYR_TYPING_RECV_TIMEOUT 10
50 #define ZEPHYR_FD_READ 0
51 #define ZEPHYR_FD_WRITE 1
53 static PurpleProtocol
*my_protocol
= NULL
;
54 static GSList
*cmds
= NULL
;
56 extern Code_t
ZGetLocations(ZLocations_t
*, int *);
57 extern Code_t
ZSetLocation(char *);
58 extern Code_t
ZUnsetLocation(void);
59 extern Code_t
ZGetSubscriptions(ZSubscription_t
*, int*);
60 extern char __Zephyr_realm
[];
61 typedef struct _zframe zframe
;
62 typedef struct _zephyr_triple zephyr_triple
;
63 typedef struct _zephyr_account zephyr_account
;
64 typedef struct _parse_tree parse_tree
;
67 PURPLE_ZEPHYR_NONE
, /* Non-kerberized ZEPH0.2 */
68 PURPLE_ZEPHYR_KRB4
, /* ZEPH0.2 w/ KRB4 support */
69 PURPLE_ZEPHYR_TZC
, /* tzc executable proxy */
70 PURPLE_ZEPHYR_INTERGALACTIC_KRB4
/* Kerberized ZEPH0.3 */
71 } zephyr_connection_type
;
73 struct _zephyr_account
{
74 PurpleAccount
* account
;
78 char* galaxy
; /* not yet useful */
79 char* krbtkfile
; /* not yet useful */
82 GList
*pending_zloc_names
;
86 char ourhost
[HOST_NAME_MAX
+ 1];
87 char ourhostcanon
[HOST_NAME_MAX
+ 1];
88 zephyr_connection_type connection_type
;
96 #define MAXCHILDREN 20
100 parse_tree
*children
[MAXCHILDREN
];
104 parse_tree null_parse_tree
= {
110 #define use_none(zephyr) ((zephyr->connection_type == PURPLE_ZEPHYR_NONE)?1:0)
111 #define use_krb4(zephyr) ((zephyr->connection_type == PURPLE_ZEPHYR_KRB4)?1:0)
112 #define use_tzc(zephyr) ((zephyr->connection_type == PURPLE_ZEPHYR_TZC)?1:0)
114 #define use_zeph02(zephyr) ( (zephyr->connection_type == PURPLE_ZEPHYR_NONE)?1: ((zephyr->connection_type == PURPLE_ZEPHYR_KRB4)?1:0))
116 /* struct I need for zephyr_to_html */
118 /* true for everything but @color, since inside the parens of that one is
123 /* }=1, ]=2, )=4, >=8 */
127 /* </i>, </font>, </b>, etc. */
129 /* text including the opening html thingie. */
134 struct _zframe
*enclosing
;
137 struct _zephyr_triple
{
146 #define z_call(func) if (func != ZERR_NONE)\
148 #define z_call_r(func) if (func != ZERR_NONE)\
151 #define z_call_s(func, err) if (func != ZERR_NONE) {\
152 purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, err);\
157 extern const char *username
;
160 static Code_t
zephyr_subscribe_to(zephyr_account
* zephyr
, char* class, char *instance
, char *recipient
, char* galaxy
) {
164 if (use_tzc(zephyr
)) {
165 /* ((tzcfodder . subscribe) ("class" "instance" "recipient")) */
166 gchar
*zsubstr
= g_strdup_printf("((tzcfodder . subscribe) (\"%s\" \"%s\" \"%s\"))\n",class,instance
,recipient
);
167 size_t len
= strlen(zsubstr
);
168 result
= write(zephyr
->totzc
[ZEPHYR_FD_WRITE
],zsubstr
,len
);
170 purple_debug_error("zephyr", "Unable to write a message: %s\n", g_strerror(errno
));
177 if (use_zeph02(zephyr
)) {
179 sub
.zsub_class
= class;
180 sub
.zsub_classinst
= instance
;
181 sub
.zsub_recipient
= recipient
;
182 ret_val
= ZSubscribeTo(&sub
,1,0);
188 char *local_zephyr_normalize(zephyr_account
* zephyr
,const char *);
189 static void zephyr_chat_set_topic(PurpleConnection
* gc
, int id
, const char *topic
);
190 char* zephyr_tzc_deescape_str(const char *message
);
192 static char *zephyr_strip_local_realm(zephyr_account
* zephyr
,const char* user
){
194 Takes in a username of the form username or username@realm
196 username, if there is no realm, or the realm is the local realm
198 username@realm if there is a realm and it is foreign
200 char *tmp
= g_strdup(user
);
201 char *at
= strchr(tmp
,'@');
202 if (at
&& !g_ascii_strcasecmp(at
+1,zephyr
->realm
)) {
203 /* We're passed in a username of the form user@users-realm */
206 tmp2
= g_strdup(tmp
);
211 /* We're passed in a username of the form user or user@foreign-realm */
216 /* this is so bad, and if Zephyr weren't so fucked up to begin with I
217 * wouldn't do this. but it is so i will. */
219 /* just for debugging */
220 static void handle_unknown(ZNotice_t
*notice
)
222 purple_debug_error("zephyr","z_packet: %s\n", notice
->z_packet
);
223 purple_debug_error("zephyr","z_version: %s\n", notice
->z_version
);
224 purple_debug_error("zephyr","z_kind: %d\n", (int)(notice
->z_kind
));
225 purple_debug_error("zephyr","z_class: %s\n", notice
->z_class
);
226 purple_debug_error("zephyr","z_class_inst: %s\n", notice
->z_class_inst
);
227 purple_debug_error("zephyr","z_opcode: %s\n", notice
->z_opcode
);
228 purple_debug_error("zephyr","z_sender: %s\n", notice
->z_sender
);
229 purple_debug_error("zephyr","z_recipient: %s\n", notice
->z_recipient
);
230 purple_debug_error("zephyr","z_message: %s\n", notice
->z_message
);
231 purple_debug_error("zephyr","z_message_len: %d\n", notice
->z_message_len
);
235 static zephyr_triple
*new_triple(zephyr_account
*zephyr
,const char *c
, const char *i
, const char *r
)
239 zt
= g_new0(zephyr_triple
, 1);
240 zt
->class = g_strdup(c
);
241 zt
->instance
= g_strdup(i
);
242 zt
->recipient
= g_strdup(r
);
243 zt
->name
= g_strdup_printf("%s,%s,%s", c
, i
?i
:"", r
?r
:"");
244 zt
->id
= ++(zephyr
->last_id
);
249 static void free_triple(zephyr_triple
* zt
)
252 g_free(zt
->instance
);
253 g_free(zt
->recipient
);
258 /* returns true if zt1 is a subset of zt2. This function is used to
259 determine whether a zephyr sent to zt1 should be placed in the chat
262 zt1 is a subset of zt2
263 iff. the classnames are identical ignoring case
264 AND. the instance names are identical (ignoring case), or zt2->instance is *.
265 AND. the recipient names are identical
268 static gboolean
triple_subset(zephyr_triple
* zt1
, zephyr_triple
* zt2
)
272 purple_debug_error("zephyr","zt2 doesn't exist\n");
276 purple_debug_error("zephyr","zt1 doesn't exist\n");
280 purple_debug_error("zephyr","zt1c doesn't exist\n");
283 if (!(zt1
->instance
)) {
284 purple_debug_error("zephyr","zt1i doesn't exist\n");
287 if (!(zt1
->recipient
)) {
288 purple_debug_error("zephyr","zt1r doesn't exist\n");
292 purple_debug_error("zephyr","zt2c doesn't exist\n");
295 if (!(zt2
->recipient
)) {
296 purple_debug_error("zephyr","zt2r doesn't exist\n");
299 if (!(zt2
->instance
)) {
300 purple_debug_error("zephyr","zt2i doesn't exist\n");
304 if (g_ascii_strcasecmp(zt2
->class, zt1
->class)) {
307 if (g_ascii_strcasecmp(zt2
->instance
, zt1
->instance
) && g_ascii_strcasecmp(zt2
->instance
, "*")) {
310 if (g_ascii_strcasecmp(zt2
->recipient
, zt1
->recipient
)) {
313 purple_debug_info("zephyr","<%s,%s,%s> is in <%s,%s,%s>\n",zt1
->class,zt1
->instance
,zt1
->recipient
,zt2
->class,zt2
->instance
,zt2
->recipient
);
317 static zephyr_triple
*find_sub_by_triple(zephyr_account
*zephyr
,zephyr_triple
* zt
)
319 zephyr_triple
*curr_t
;
320 GSList
*curr
= zephyr
->subscrips
;
324 if (triple_subset(zt
, curr_t
))
331 static zephyr_triple
*find_sub_by_id(zephyr_account
*zephyr
,int id
)
334 GSList
*curr
= zephyr
->subscrips
;
346 Converts strings to utf-8 if necessary using user specified encoding
349 static gchar
*zephyr_recv_convert(PurpleConnection
*gc
, gchar
*string
)
353 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
354 if (g_utf8_validate(string
, -1, NULL
)) {
355 return g_strdup(string
);
357 utf8
= g_convert(string
, -1, "UTF-8", zephyr
->encoding
, NULL
, NULL
, &err
);
359 purple_debug_error("zephyr", "recv conversion error: %s\n", err
->message
);
360 utf8
= g_strdup(_("(There was an error converting this message. Check the 'Encoding' option in the Account Editor)"));
368 /* This parses HTML formatting (put out by one of the gtkimhtml widgets
369 And converts it to zephyr formatting.
370 It currently deals properly with <b>, <br>, <i>, <font face=...>, <font color=...>,
371 It ignores <font back=...>
373 <font size = "1 or 2" -> @small
376 <a href is dealt with by outputting "description <link>" or just "description" as appropriate
379 static char *html_to_zephyr(const char *message
)
381 zframe
*frames
, *new_f
;
384 if (*message
== '\0')
387 frames
= g_new(zframe
, 1);
388 frames
->text
= g_string_new("");
390 frames
->is_href
= FALSE
;
391 frames
->enclosing
= NULL
;
392 frames
->closing
= NULL
;
394 frames
->has_closer
= FALSE
;
395 frames
->closer_mask
= 15;
397 purple_debug_info("zephyr","html received %s\n",message
);
399 if (frames
->closing
&& !g_ascii_strncasecmp(message
, frames
->closing
, strlen(frames
->closing
))) {
401 message
+= strlen(frames
->closing
);
403 frames
= frames
->enclosing
;
404 if (popped
->is_href
) {
405 frames
->href
= popped
->text
;
407 g_string_append(frames
->text
, popped
->env
);
408 if (popped
->has_closer
) {
409 g_string_append_c(frames
->text
,
410 (popped
->closer_mask
& 1) ? '{' :
411 (popped
->closer_mask
& 2) ? '[' :
412 (popped
->closer_mask
& 4) ? '(' :
415 g_string_append(frames
->text
, popped
->text
->str
);
418 int text_len
= strlen(popped
->text
->str
), href_len
= strlen(popped
->href
->str
);
419 if (!((text_len
== href_len
&& !strncmp(popped
->href
->str
, popped
->text
->str
, text_len
)) ||
420 (7 + text_len
== href_len
&& !strncmp(popped
->href
->str
, "http://", 7) &&
421 !strncmp(popped
->href
->str
+ 7, popped
->text
->str
, text_len
)) ||
422 (7 + text_len
== href_len
&& !strncmp(popped
->href
->str
, "mailto:", 7) &&
423 !strncmp(popped
->href
->str
+ 7, popped
->text
->str
, text_len
)))) {
424 g_string_append(frames
->text
, " <");
425 g_string_append(frames
->text
, popped
->href
->str
);
426 if (popped
->closer_mask
& ~8) {
427 g_string_append_c(frames
->text
, '>');
428 popped
->closer_mask
&= ~8;
430 g_string_append(frames
->text
, "@{>}");
433 g_string_free(popped
->href
, TRUE
);
435 if (popped
->has_closer
) {
436 g_string_append_c(frames
->text
,
437 (popped
->closer_mask
& 1) ? '}' :
438 (popped
->closer_mask
& 2) ? ']' :
439 (popped
->closer_mask
& 4) ? ')' :
442 if (!popped
->has_closer
)
443 frames
->closer_mask
= popped
->closer_mask
;
444 g_string_free(popped
->text
, TRUE
);
447 } else if (*message
== '<') {
448 if (!g_ascii_strncasecmp(message
+ 1, "i>", 2)) {
449 new_f
= g_new(zframe
, 1);
450 new_f
->enclosing
= frames
;
451 new_f
->text
= g_string_new("");
453 new_f
->is_href
= FALSE
;
454 new_f
->closing
= "</i>";
456 new_f
->has_closer
= TRUE
;
457 new_f
->closer_mask
= 15;
460 } else if (!g_ascii_strncasecmp(message
+ 1, "b>", 2)) {
461 new_f
= g_new(zframe
, 1);
462 new_f
->enclosing
= frames
;
463 new_f
->text
= g_string_new("");
465 new_f
->is_href
= FALSE
;
466 new_f
->closing
= "</b>";
468 new_f
->has_closer
= TRUE
;
469 new_f
->closer_mask
= 15;
472 } else if (!g_ascii_strncasecmp(message
+ 1, "br>", 3)) {
473 g_string_append_c(frames
->text
, '\n');
475 } else if (!g_ascii_strncasecmp(message
+ 1, "a href=\"", 8)) {
477 new_f
= g_new(zframe
, 1);
478 new_f
->enclosing
= frames
;
479 new_f
->text
= g_string_new("");
481 new_f
->is_href
= FALSE
;
482 new_f
->closing
= "</a>";
484 new_f
->has_closer
= FALSE
;
485 new_f
->closer_mask
= frames
->closer_mask
;
487 new_f
= g_new(zframe
, 1);
488 new_f
->enclosing
= frames
;
489 new_f
->text
= g_string_new("");
491 new_f
->is_href
= TRUE
;
492 new_f
->closing
= "\">";
493 new_f
->has_closer
= FALSE
;
494 new_f
->closer_mask
= frames
->closer_mask
;
496 } else if (!g_ascii_strncasecmp(message
+ 1, "font", 4)) {
497 new_f
= g_new(zframe
, 1);
498 new_f
->enclosing
= frames
;
499 new_f
->text
= g_string_new("");
501 new_f
->is_href
= FALSE
;
502 new_f
->closing
= "</font>";
503 new_f
->has_closer
= TRUE
;
504 new_f
->closer_mask
= 15;
506 while (*message
== ' ')
508 if (!g_ascii_strncasecmp(message
, "color=\"", 7)) {
512 new_f
= g_new(zframe
, 1);
513 new_f
->enclosing
= frames
;
514 new_f
->env
= "@color";
515 new_f
->text
= g_string_new("");
517 new_f
->is_href
= FALSE
;
518 new_f
->closing
= "\">";
519 new_f
->has_closer
= TRUE
;
520 new_f
->closer_mask
= 15;
521 } else if (!g_ascii_strncasecmp(message
, "face=\"", 6)) {
525 new_f
= g_new(zframe
, 1);
526 new_f
->enclosing
= frames
;
527 new_f
->env
= "@font";
528 new_f
->text
= g_string_new("");
530 new_f
->is_href
= FALSE
;
531 new_f
->closing
= "\">";
532 new_f
->has_closer
= TRUE
;
533 new_f
->closer_mask
= 15;
534 } else if (!g_ascii_strncasecmp(message
, "size=\"", 6)) {
536 if ((*message
== '1') || (*message
== '2')) {
537 new_f
->env
= "@small";
538 } else if ((*message
== '3')
539 || (*message
== '4')) {
540 new_f
->env
= "@medium";
541 } else if ((*message
== '5')
543 || (*message
== '7')) {
544 new_f
->env
= "@large";
547 new_f
->has_closer
= FALSE
;
548 new_f
->closer_mask
= frames
->closer_mask
;
552 /* Drop all unrecognized/misparsed font tags */
554 new_f
->has_closer
= FALSE
;
555 new_f
->closer_mask
= frames
->closer_mask
;
556 while (g_ascii_strncasecmp(message
, "\">", 2) != 0) {
559 if (*message
!= '\0')
564 /* Catch all for all unrecognized/misparsed <foo> tage */
565 g_string_append_c(frames
->text
, *message
++);
567 } else if (*message
== '@') {
568 g_string_append(frames
->text
, "@@");
570 } else if (*message
== '}') {
571 if (frames
->closer_mask
& ~1) {
572 frames
->closer_mask
&= ~1;
573 g_string_append_c(frames
->text
, *message
++);
575 g_string_append(frames
->text
, "@[}]");
578 } else if (*message
== ']') {
579 if (frames
->closer_mask
& ~2) {
580 frames
->closer_mask
&= ~2;
581 g_string_append_c(frames
->text
, *message
++);
583 g_string_append(frames
->text
, "@{]}");
586 } else if (*message
== ')') {
587 if (frames
->closer_mask
& ~4) {
588 frames
->closer_mask
&= ~4;
589 g_string_append_c(frames
->text
, *message
++);
591 g_string_append(frames
->text
, "@{)}");
594 } else if (!g_ascii_strncasecmp(message
, ">", 4)) {
595 if (frames
->closer_mask
& ~8) {
596 frames
->closer_mask
&= ~8;
597 g_string_append_c(frames
->text
, *message
++);
599 g_string_append(frames
->text
, "@{>}");
603 g_string_append_c(frames
->text
, *message
++);
606 ret
= frames
->text
->str
;
607 g_string_free(frames
->text
, FALSE
);
609 purple_debug_info("zephyr","zephyr outputted %s\n",ret
);
613 /* this parses zephyr formatting and converts it to html. For example, if
614 * you pass in "@{@color(blue)@i(hello)}" you should get out
615 * "<font color=blue><i>hello</i></font>". */
616 static char *zephyr_to_html(const char *message
)
618 zframe
*frames
, *curr
;
621 frames
= g_new(zframe
, 1);
622 frames
->text
= g_string_new("");
623 frames
->enclosing
= NULL
;
624 frames
->closing
= "";
625 frames
->has_closer
= FALSE
;
626 frames
->closer
= NULL
;
629 if (*message
== '@' && message
[1] == '@') {
630 g_string_append(frames
->text
, "@");
632 } else if (*message
== '@') {
634 for (end
= 1; message
[end
] && (isalnum(message
[end
]) || message
[end
] == '_'); end
++);
636 (message
[end
] == '{' || message
[end
] == '[' || message
[end
] == '(' ||
637 !g_ascii_strncasecmp(message
+ end
, "<", 4))) {
640 buf
= g_new0(char, end
);
641 g_snprintf(buf
, end
, "%s", message
+ 1);
643 new_f
= g_new(zframe
, 1);
644 new_f
->enclosing
= frames
;
645 new_f
->has_closer
= TRUE
;
646 new_f
->closer
= (*message
== '{' ? "}" :
647 *message
== '[' ? "]" :
648 *message
== '(' ? ")" :
650 message
+= (*message
== '&' ? 4 : 1);
651 if (!g_ascii_strcasecmp(buf
, "italic") || !g_ascii_strcasecmp(buf
, "i")) {
652 new_f
->text
= g_string_new("<i>");
653 new_f
->closing
= "</i>";
654 } else if (!g_ascii_strcasecmp(buf
, "small")) {
655 new_f
->text
= g_string_new("<font size=\"1\">");
656 new_f
->closing
= "</font>";
657 } else if (!g_ascii_strcasecmp(buf
, "medium")) {
658 new_f
->text
= g_string_new("<font size=\"3\">");
659 new_f
->closing
= "</font>";
660 } else if (!g_ascii_strcasecmp(buf
, "large")) {
661 new_f
->text
= g_string_new("<font size=\"7\">");
662 new_f
->closing
= "</font>";
663 } else if (!g_ascii_strcasecmp(buf
, "bold")
664 || !g_ascii_strcasecmp(buf
, "b")) {
665 new_f
->text
= g_string_new("<b>");
666 new_f
->closing
= "</b>";
667 } else if (!g_ascii_strcasecmp(buf
, "font")) {
669 extra_f
= g_new(zframe
, 1);
670 extra_f
->enclosing
= frames
;
671 new_f
->enclosing
= extra_f
;
672 extra_f
->text
= g_string_new("");
673 extra_f
->has_closer
= FALSE
;
674 extra_f
->closer
= frames
->closer
;
675 extra_f
->closing
= "</font>";
676 new_f
->text
= g_string_new("<font face=\"");
677 new_f
->closing
= "\">";
678 } else if (!g_ascii_strcasecmp(buf
, "color")) {
680 extra_f
= g_new(zframe
, 1);
681 extra_f
->enclosing
= frames
;
682 new_f
->enclosing
= extra_f
;
683 extra_f
->text
= g_string_new("");
684 extra_f
->has_closer
= FALSE
;
685 extra_f
->closer
= frames
->closer
;
686 extra_f
->closing
= "</font>";
687 new_f
->text
= g_string_new("<font color=\"");
688 new_f
->closing
= "\">";
690 new_f
->text
= g_string_new("");
695 /* Not a formatting tag, add the character as normal. */
696 g_string_append_c(frames
->text
, *message
++);
698 } else if (frames
->closer
&& !g_ascii_strncasecmp(message
, frames
->closer
, strlen(frames
->closer
))) {
700 gboolean last_had_closer
;
702 message
+= strlen(frames
->closer
);
703 if (frames
->enclosing
) {
706 frames
= frames
->enclosing
;
707 g_string_append(frames
->text
, popped
->text
->str
);
708 g_string_append(frames
->text
, popped
->closing
);
709 g_string_free(popped
->text
, TRUE
);
710 last_had_closer
= popped
->has_closer
;
712 } while (frames
->enclosing
&& !last_had_closer
);
714 g_string_append_c(frames
->text
, *message
);
716 } else if (*message
== '\n') {
717 g_string_append(frames
->text
, "<br>");
720 g_string_append_c(frames
->text
, *message
++);
723 /* go through all the stuff that they didn't close */
724 while (frames
->enclosing
) {
726 g_string_append(frames
->enclosing
->text
, frames
->text
->str
);
727 g_string_append(frames
->enclosing
->text
, frames
->closing
);
728 g_string_free(frames
->text
, TRUE
);
729 frames
= frames
->enclosing
;
732 ret
= frames
->text
->str
;
733 g_string_free(frames
->text
, FALSE
);
738 static gboolean
pending_zloc(zephyr_account
*zephyr
, const char *who
)
742 for (curr
= zephyr
->pending_zloc_names
; curr
!= NULL
; curr
= curr
->next
) {
743 char* normalized_who
= local_zephyr_normalize(zephyr
,who
);
744 if (!g_ascii_strcasecmp(normalized_who
, (char *)curr
->data
)) {
745 g_free((char *)curr
->data
);
746 zephyr
->pending_zloc_names
= g_list_remove(zephyr
->pending_zloc_names
, curr
->data
);
753 /* Called when the server notifies us a message couldn't get sent */
755 static void message_failed(PurpleConnection
*gc
, ZNotice_t
*notice
, struct sockaddr_in from
)
757 if (g_ascii_strcasecmp(notice
->z_class
, "message")) {
758 gchar
* chat_failed
= g_strdup_printf(
759 _("Unable to send to chat %s,%s,%s"),
760 notice
->z_class
, notice
->z_class_inst
,
761 notice
->z_recipient
);
762 purple_notify_error(gc
,"",chat_failed
,NULL
,
763 purple_request_cpar_from_connection(gc
));
766 purple_notify_error(gc
, notice
->z_recipient
,
767 _("User is offline"), NULL
,
768 purple_request_cpar_from_connection(gc
));
772 static void handle_message(PurpleConnection
*gc
, ZNotice_t
*notice_p
)
775 zephyr_account
* zephyr
= purple_connection_get_protocol_data(gc
);
777 memcpy(¬ice
, notice_p
, sizeof(notice
)); /* TODO - use pointer? */
779 if (!g_ascii_strcasecmp(notice
.z_class
, LOGIN_CLASS
)) {
780 /* well, we'll be updating in 20 seconds anyway, might as well ignore this. */
781 } else if (!g_ascii_strcasecmp(notice
.z_class
, LOCATE_CLASS
)) {
782 if (!g_ascii_strcasecmp(notice
.z_opcode
, LOCATE_LOCATE
)) {
788 /* XXX add real error reporting */
789 if (ZParseLocations(¬ice
, NULL
, &nlocs
, &user
) != ZERR_NONE
)
792 if ((b
= purple_blist_find_buddy(purple_connection_get_account(gc
), user
)) == NULL
) {
793 char* stripped_user
= zephyr_strip_local_realm(zephyr
,user
);
794 b
= purple_blist_find_buddy(purple_connection_get_account(gc
),stripped_user
);
795 g_free(stripped_user
);
798 bname
= b
? purple_buddy_get_name(b
) : NULL
;
799 if ((b
&& pending_zloc(zephyr
,bname
)) || pending_zloc(zephyr
,user
)) {
802 PurpleNotifyUserInfo
*user_info
= purple_notify_user_info_new();
806 /* TODO: Check whether it's correct to call add_pair_html,
807 or if we should be using add_pair_plaintext */
808 purple_notify_user_info_add_pair_html(user_info
, _("User"), (b
? bname
: user
));
809 balias
= purple_buddy_get_local_alias(b
);
811 purple_notify_user_info_add_pair_plaintext(user_info
, _("Alias"), balias
);
814 purple_notify_user_info_add_pair_plaintext(user_info
, NULL
, _("Hidden or not logged-in"));
816 for (; nlocs
> 0; nlocs
--) {
817 /* XXX add real error reporting */
819 ZGetLocations(&locs
, &one
);
820 /* TODO: Need to escape locs.host and locs.time? */
821 tmp
= g_strdup_printf(_("<br>At %s since %s"), locs
.host
, locs
.time
);
822 purple_notify_user_info_add_pair_html(user_info
, _("Location"), tmp
);
825 purple_notify_userinfo(gc
, (b
? bname
: user
),
826 user_info
, NULL
, NULL
);
827 purple_notify_user_info_destroy(user_info
);
830 purple_protocol_got_user_status(purple_connection_get_account(gc
), b
? bname
: user
, "available", NULL
);
832 purple_protocol_got_user_status(purple_connection_get_account(gc
), b
? bname
: user
, "offline", NULL
);
838 char *buf
, *buf2
, *buf3
;
840 PurpleChatConversation
*gcc
;
841 char *ptr
= (char *) notice
.z_message
+ (strlen(notice
.z_message
) + 1);
843 char *stripped_sender
;
844 int signature_length
= strlen(notice
.z_message
);
845 PurpleMessageFlags flags
= 0;
848 /* Need to deal with 0 length messages to handle typing notification (OPCODE) ping messages */
849 /* One field zephyrs would have caused purple to crash */
850 if ( (notice
.z_message_len
== 0) || (signature_length
>= notice
.z_message_len
- 1)) {
852 purple_debug_info("zephyr","message_size %d %d %d\n",len
,notice
.z_message_len
,signature_length
);
856 len
= notice
.z_message_len
- ( signature_length
+1);
857 purple_debug_info("zephyr","message_size %d %d %d\n",len
,notice
.z_message_len
,signature_length
);
858 buf
= g_malloc(len
+ 1);
859 g_snprintf(buf
, len
+ 1, "%s", ptr
);
861 tmpescape
= g_markup_escape_text(buf
, -1);
863 buf2
= zephyr_to_html(tmpescape
);
864 buf3
= zephyr_recv_convert(gc
, buf2
);
869 stripped_sender
= zephyr_strip_local_realm(zephyr
,notice
.z_sender
);
871 if (!g_ascii_strcasecmp(notice
.z_class
, "MESSAGE") && !g_ascii_strcasecmp(notice
.z_class_inst
, "PERSONAL")
872 && !g_ascii_strcasecmp(notice
.z_recipient
,zephyr
->username
)) {
873 if (!g_ascii_strcasecmp(notice
.z_message
, "Automated reply:"))
874 flags
|= PURPLE_MESSAGE_AUTO_RESP
;
876 if (!g_ascii_strcasecmp(notice
.z_opcode
,"PING"))
877 purple_serv_got_typing(gc
,stripped_sender
,ZEPHYR_TYPING_RECV_TIMEOUT
, PURPLE_IM_TYPING
);
879 purple_serv_got_im(gc
, stripped_sender
, buf3
, flags
, time(NULL
));
882 zephyr_triple
*zt1
, *zt2
;
883 gchar
*send_inst_utf8
;
884 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
885 zt1
= new_triple(zephyr
,notice
.z_class
, notice
.z_class_inst
, notice
.z_recipient
);
886 zt2
= find_sub_by_triple(zephyr
,zt1
);
888 /* This is a server supplied subscription */
889 zephyr
->subscrips
= g_slist_append(zephyr
->subscrips
, new_triple(zephyr
,zt1
->class,zt1
->instance
,zt1
->recipient
));
890 zt2
= find_sub_by_triple(zephyr
,zt1
);
895 purple_serv_got_joined_chat(gc
, zt2
->id
, zt2
->name
);
896 zephyr_chat_set_topic(gc
,zt2
->id
,notice
.z_class_inst
);
899 if (!g_ascii_strcasecmp(notice
.z_class_inst
,"PERSONAL"))
900 send_inst_utf8
= g_strdup(stripped_sender
);
902 send_inst
= g_strdup_printf("[%s] %s",notice
.z_class_inst
,stripped_sender
);
903 send_inst_utf8
= zephyr_recv_convert(gc
,send_inst
);
905 if (!send_inst_utf8
) {
906 purple_debug_error("zephyr","Failed to convert instance for sender %s.\n", stripped_sender
);
907 send_inst_utf8
= g_strdup(stripped_sender
);
911 gcc
= purple_conversations_find_chat_with_account(
912 zt2
->name
, purple_connection_get_account(gc
));
913 #ifndef INET_ADDRSTRLEN
914 #define INET_ADDRSTRLEN 16
916 if (!purple_chat_conversation_has_user(gcc
, stripped_sender
)) {
917 gchar ipaddr
[INET_ADDRSTRLEN
];
918 #ifdef HAVE_INET_NTOP
919 inet_ntop(AF_INET
, ¬ice
.z_sender_addr
.s_addr
, ipaddr
, sizeof(ipaddr
));
921 memcpy(ipaddr
,inet_ntoa(notice
.z_sender_addr
),sizeof(ipaddr
));
923 purple_chat_conversation_add_user(gcc
, stripped_sender
, ipaddr
, PURPLE_CHAT_USER_NONE
, TRUE
);
925 purple_serv_got_chat_in(gc
, zt2
->id
, send_inst_utf8
,
926 PURPLE_MESSAGE_RECV
, buf3
, time(NULL
));
927 g_free(send_inst_utf8
);
931 g_free(stripped_sender
);
936 static int free_parse_tree(parse_tree
* tree
) {
942 for(i
=0;i
<tree
->num_children
;i
++){
943 if (tree
->children
[i
]) {
944 free_parse_tree(tree
->children
[i
]);
945 g_free(tree
->children
[i
]);
948 if ((tree
!= &null_parse_tree
) && (tree
->contents
!= NULL
))
949 g_free(tree
->contents
);
955 static parse_tree
*tree_child(parse_tree
* tree
,int index
) {
956 if (index
< tree
->num_children
) {
957 return tree
->children
[index
];
959 return &null_parse_tree
;
963 static parse_tree
*find_node(parse_tree
* ptree
,gchar
* key
)
968 return &null_parse_tree
;
970 tc
= tree_child(ptree
,0)->contents
;
972 /* g_strcasecmp() is deprecated. What is the encoding here??? */
973 if (ptree
->num_children
> 0 && tc
&& !g_ascii_strcasecmp(tc
, key
)) {
976 parse_tree
*result
= &null_parse_tree
;
978 for(i
= 0; i
< ptree
->num_children
; i
++) {
979 result
= find_node(ptree
->children
[i
],key
);
980 if(result
!= &null_parse_tree
) {
988 static parse_tree
*parse_buffer(gchar
* source
, gboolean do_parse
) {
990 parse_tree
*ptree
= g_new0(parse_tree
,1);
991 ptree
->contents
= NULL
;
992 ptree
->num_children
=0;
995 while(p
< strlen(source
)) {
999 /* Eat white space: */
1000 if(g_ascii_isspace(source
[p
]) || source
[p
] == '\001') {
1006 if(source
[p
] == ';') {
1007 while(source
[p
] != '\n' && p
< strlen(source
)) {
1013 if(source
[p
] == '(') {
1015 gboolean in_quote
= FALSE
;
1016 gboolean escape_next
= FALSE
;
1019 while(!(source
[end
] == ')' && nesting
== 0 && !in_quote
) && end
< strlen(source
)) {
1021 if(source
[end
] == '\\') {
1025 if(source
[end
] == '(') {
1028 if(source
[end
] == ')') {
1032 if(source
[end
] == '"') {
1033 in_quote
= !in_quote
;
1036 escape_next
= FALSE
;
1044 if(source
[p
] == '"') {
1053 while(source
[end
] != end_char
&& end
< strlen(source
)) {
1054 if(source
[end
] == '\\')
1059 newstr
= g_new0(gchar
, end
+1-p
);
1060 strncpy(newstr
,source
+p
,end
-p
);
1061 if (ptree
->num_children
< MAXCHILDREN
) {
1062 /* In case we surpass maxchildren, ignore this */
1063 ptree
->children
[ptree
->num_children
++] = parse_buffer( newstr
, do_parse
);
1065 purple_debug_error("zephyr","too many children in tzc output. skipping\n");
1072 /* XXX does this have to be strdup'd */
1073 ptree
->contents
= g_strdup(source
);
1078 static parse_tree
*read_from_tzc(zephyr_account
* zephyr
){
1082 char *buf
= (char *)calloc(bufsize
, 1);
1085 parse_tree
*incoming_msg
;
1088 FD_SET(zephyr
->fromtzc
[ZEPHYR_FD_READ
], &rfds
);
1093 while (select(zephyr
->fromtzc
[ZEPHYR_FD_READ
] + 1, &rfds
, NULL
, NULL
, &tv
)) {
1095 if (read(zephyr
->fromtzc
[ZEPHYR_FD_READ
], bufcur
, 1) != 1) {
1096 purple_debug_error("zephyr", "couldn't read\n");
1097 purple_connection_error(purple_account_get_connection(zephyr
->account
), PURPLE_CONNECTION_ERROR_NETWORK_ERROR
, "couldn't read");
1102 if ((bufcur
- buf
) > (bufsize
- 1)) {
1103 if ((buf
= realloc(buf
, bufsize
* 2)) == NULL
) {
1104 purple_debug_error("zephyr","Ran out of memory\n");
1107 bufcur
= buf
+ bufsize
;
1115 incoming_msg
= parse_buffer(buf
,TRUE
);
1118 return incoming_msg
;
1121 static gint
check_notify_tzc(gpointer data
)
1123 PurpleConnection
*gc
= (PurpleConnection
*)data
;
1124 zephyr_account
* zephyr
= purple_connection_get_protocol_data(gc
);
1125 parse_tree
*newparsetree
= read_from_tzc(zephyr
);
1126 if (newparsetree
!= NULL
) {
1128 if ( (spewtype
= tree_child(find_node(newparsetree
,"tzcspew"),2)->contents
) ) {
1129 if (!g_ascii_strncasecmp(spewtype
,"message",7)) {
1131 parse_tree
*msgnode
= tree_child(find_node(newparsetree
,"message"),2);
1132 parse_tree
*bodynode
= tree_child(msgnode
,1);
1133 /* char *zsig = g_strdup(" "); */ /* purple doesn't care about zsigs */
1134 char *msg
= zephyr_tzc_deescape_str(bodynode
->contents
);
1135 size_t bufsize
= strlen(msg
) + 3;
1136 char *buf
= g_new0(char,bufsize
);
1137 g_snprintf(buf
,1+strlen(msg
)+2," %c%s",'\0',msg
);
1138 memset((char *)¬ice
, 0, sizeof(notice
));
1139 notice
.z_kind
= ACKED
;
1141 notice
.z_opcode
= tree_child(find_node(newparsetree
,"opcode"),2)->contents
;
1142 notice
.z_class
= zephyr_tzc_deescape_str(tree_child(find_node(newparsetree
,"class"),2)->contents
);
1143 notice
.z_class_inst
= tree_child(find_node(newparsetree
,"instance"),2)->contents
;
1144 notice
.z_recipient
= local_zephyr_normalize(zephyr
,tree_child(find_node(newparsetree
,"recipient"),2)->contents
);
1145 notice
.z_sender
= local_zephyr_normalize(zephyr
,tree_child(find_node(newparsetree
,"sender"),2)->contents
);
1146 notice
.z_default_format
= "Class $class, Instance $instance:\n" "To: @bold($recipient) at $time $date\n" "From: @bold($1) <$sender>\n\n$2";
1147 notice
.z_message_len
= strlen(msg
) + 3;
1148 notice
.z_message
= buf
;
1149 handle_message(gc
, ¬ice
);
1153 /* free_parse_tree(msgnode);
1154 free_parse_tree(bodynode);
1160 else if (!g_ascii_strncasecmp(spewtype
,"zlocation",9)) {
1161 /* check_loc or zephyr_zloc respectively */
1167 parse_tree
*locations
;
1169 user
= tree_child(find_node(newparsetree
,"user"),2)->contents
;
1171 if ((b
= purple_blist_find_buddy(purple_connection_get_account(gc
), user
)) == NULL
) {
1172 gchar
*stripped_user
= zephyr_strip_local_realm(zephyr
,user
);
1173 b
= purple_blist_find_buddy(purple_connection_get_account(gc
), stripped_user
);
1174 g_free(stripped_user
);
1176 locations
= find_node(newparsetree
,"locations");
1177 locval
= tree_child(tree_child(tree_child(tree_child(locations
,2),0),0),2)->contents
;
1179 if (!locval
|| !g_ascii_strcasecmp(locval
," ") || !*locval
) {
1185 bname
= b
? purple_buddy_get_name(b
) : NULL
;
1186 if ((b
&& pending_zloc(zephyr
,bname
)) || pending_zloc(zephyr
,user
) || pending_zloc(zephyr
,local_zephyr_normalize(zephyr
,user
))){
1187 PurpleNotifyUserInfo
*user_info
= purple_notify_user_info_new();
1191 /* TODO: Check whether it's correct to call add_pair_html,
1192 or if we should be using add_pair_plaintext */
1193 purple_notify_user_info_add_pair_html(user_info
, _("User"), (b
? bname
: user
));
1195 balias
= b
? purple_buddy_get_local_alias(b
) : NULL
;
1197 purple_notify_user_info_add_pair_plaintext(user_info
, _("Alias"), balias
);
1200 purple_notify_user_info_add_pair_plaintext(user_info
, NULL
, _("Hidden or not logged-in"));
1202 /* TODO: Need to escape the two strings that make up tmp? */
1203 tmp
= g_strdup_printf(_("<br>At %s since %s"),
1204 tree_child(tree_child(tree_child(tree_child(locations
,2),0),0),2)->contents
,
1205 tree_child(tree_child(tree_child(tree_child(locations
,2),0),2),2)->contents
);
1206 purple_notify_user_info_add_pair_html(user_info
, _("Location"), tmp
);
1210 purple_notify_userinfo(gc
, b
? bname
: user
,
1211 user_info
, NULL
, NULL
);
1212 purple_notify_user_info_destroy(user_info
);
1215 purple_protocol_got_user_status(purple_connection_get_account(gc
), b
? bname
: user
, "available", NULL
);
1217 purple_protocol_got_user_status(purple_connection_get_account(gc
), b
? bname
: user
, "offline", NULL
);
1220 else if (!g_ascii_strncasecmp(spewtype
,"subscribed",10)) {
1222 else if (!g_ascii_strncasecmp(spewtype
,"start",5)) {
1224 else if (!g_ascii_strncasecmp(spewtype
,"error",5)) {
1232 free_parse_tree(newparsetree
);
1236 static gint
check_notify_zeph02(gpointer data
)
1238 /* XXX add real error reporting */
1239 PurpleConnection
*gc
= (PurpleConnection
*) data
;
1240 while (ZPending()) {
1242 struct sockaddr_in from
;
1243 /* XXX add real error reporting */
1245 z_call_r(ZReceiveNotice(¬ice
, &from
));
1247 switch (notice
.z_kind
) {
1251 handle_message(gc
, ¬ice
);
1254 if (!(g_ascii_strcasecmp(notice
.z_message
, ZSRVACK_NOTSENT
))) {
1255 message_failed(gc
, ¬ice
, from
);
1259 purple_debug_error("zephyr", "Client ack received\n");
1260 handle_unknown(¬ice
); /* XXX: is it really unknown? */
1263 /* we'll just ignore things for now */
1264 handle_unknown(¬ice
);
1265 purple_debug_error("zephyr", "Unhandled notice.\n");
1268 /* XXX add real error reporting */
1269 ZFreeNotice(¬ice
);
1277 static gint
check_loc(gpointer data
)
1280 ZLocations_t locations
;
1281 PurpleConnection
*gc
= data
;
1282 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
1283 PurpleAccount
*account
= purple_connection_get_account(gc
);
1287 for (buddies
= purple_blist_find_buddies(account
, NULL
); buddies
;
1288 buddies
= g_slist_delete_link(buddies
, buddies
)) {
1289 PurpleBuddy
*b
= buddies
->data
;
1291 const char *bname
= purple_buddy_get_name(b
);
1292 chk
= local_zephyr_normalize(bname
);
1293 ZLocateUser(chk
,&numlocs
, ZAUTH
);
1296 for(i
=0;i
<numlocs
;i
++) {
1297 ZGetLocations(&locations
,&one
);
1298 serv_got_update(zgc
,bname
,1,0,0,0,0);
1308 static gint
check_loc(gpointer data
)
1311 ZAsyncLocateData_t ald
;
1312 PurpleConnection
*gc
= (PurpleConnection
*)data
;
1313 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
1314 PurpleAccount
*account
= purple_connection_get_account(gc
);
1316 if (use_zeph02(zephyr
)) {
1318 memset(&(ald
.uid
), 0, sizeof(ZUnique_Id_t
));
1322 for (buddies
= purple_blist_find_buddies(account
, NULL
); buddies
;
1323 buddies
= g_slist_delete_link(buddies
, buddies
)) {
1324 PurpleBuddy
*b
= buddies
->data
;
1327 const char *name
= purple_buddy_get_name(b
);
1329 chk
= local_zephyr_normalize(zephyr
,name
);
1330 purple_debug_info("zephyr","chk: %s b->name %s\n",chk
,name
);
1331 /* XXX add real error reporting */
1332 /* doesn't matter if this fails or not; we'll just move on to the next one */
1333 if (use_zeph02(zephyr
)) {
1337 ZLocateUser(chk
,&numlocs
,ZAUTH
);
1340 for(i
=0;i
<numlocs
;i
++) {
1341 ZGetLocations(&locations
,&one
);
1343 purple_protocol_got_user_status(account
,name
,"available",NULL
);
1345 purple_protocol_got_user_status(account
,name
,"offline",NULL
);
1349 ZRequestLocations(chk
, &ald
, UNACKED
, ZAUTH
);
1351 g_free(ald
.version
);
1354 if (use_tzc(zephyr
)) {
1355 gchar
*zlocstr
= g_strdup_printf("((tzcfodder . zlocate) \"%s\")\n",chk
);
1356 size_t len
= strlen(zlocstr
);
1357 size_t result
= write(zephyr
->totzc
[ZEPHYR_FD_WRITE
],zlocstr
,len
);
1358 if (result
!= len
) {
1359 purple_debug_error("zephyr", "Unable to write a message: %s\n", g_strerror(errno
));
1370 static char *get_exposure_level(void)
1372 /* XXX add real error reporting */
1373 char *exposure
= ZGetVariable("exposure");
1376 return EXPOSE_REALMVIS
;
1377 if (!g_ascii_strcasecmp(exposure
, EXPOSE_NONE
))
1379 if (!g_ascii_strcasecmp(exposure
, EXPOSE_OPSTAFF
))
1380 return EXPOSE_OPSTAFF
;
1381 if (!g_ascii_strcasecmp(exposure
, EXPOSE_REALMANN
))
1382 return EXPOSE_REALMANN
;
1383 if (!g_ascii_strcasecmp(exposure
, EXPOSE_NETVIS
))
1384 return EXPOSE_NETVIS
;
1385 if (!g_ascii_strcasecmp(exposure
, EXPOSE_NETANN
))
1386 return EXPOSE_NETANN
;
1387 return EXPOSE_REALMVIS
;
1390 static void strip_comments(char *str
)
1392 char *tmp
= strchr(str
, '#');
1400 static void zephyr_inithosts(zephyr_account
*zephyr
)
1402 /* XXX This code may not be Win32 clean */
1403 struct hostent
*hent
;
1405 if (gethostname(zephyr
->ourhost
, sizeof(zephyr
->ourhost
)) == -1) {
1406 purple_debug_error("zephyr", "unable to retrieve hostname, %%host%% and %%canon%% will be wrong in subscriptions and have been set to unknown\n");
1407 g_strlcpy(zephyr
->ourhost
, "unknown", sizeof(zephyr
->ourhost
));
1408 g_strlcpy(zephyr
->ourhostcanon
, "unknown", sizeof(zephyr
->ourhostcanon
));
1412 if (!(hent
= gethostbyname(zephyr
->ourhost
))) {
1413 purple_debug_error("zephyr", "unable to resolve hostname, %%canon%% will be wrong in subscriptions.and has been set to the value of %%host%%, %s\n",zephyr
->ourhost
);
1414 g_strlcpy(zephyr
->ourhostcanon
, zephyr
->ourhost
, sizeof(zephyr
->ourhostcanon
));
1418 g_strlcpy(zephyr
->ourhostcanon
, hent
->h_name
, sizeof(zephyr
->ourhostcanon
));
1423 static void process_zsubs(zephyr_account
*zephyr
)
1425 /* Loads zephyr chats "(subscriptions) from ~/.zephyr.subs, and
1426 registers (subscribes to) them on the server */
1428 /* XXX deal with unsubscriptions */
1429 /* XXX deal with punts */
1435 fname
= g_strdup_printf("%s/.zephyr.subs", purple_home_dir());
1436 f
= g_fopen(fname
, "r");
1442 char *z_galaxy
= NULL
;
1444 while (fgets(buff
, BUFSIZ
, f
)) {
1445 strip_comments(buff
);
1447 triple
= g_strsplit(buff
, ",", 3);
1448 if (triple
[0] && triple
[1]) {
1449 char *tmp
= g_strdup_printf("%s", zephyr
->username
);
1452 if (triple
[2] == NULL
) {
1453 recip
= g_malloc0(1);
1454 } else if (!g_ascii_strcasecmp(triple
[2], "%me%")) {
1455 recip
= g_strdup_printf("%s", zephyr
->username
);
1456 } else if (!g_ascii_strcasecmp(triple
[2], "*")) {
1458 * form of class,instance,* */
1459 recip
= g_malloc0(1);
1460 } else if (!g_ascii_strcasecmp(triple
[2], tmp
)) {
1461 /* form of class,instance,aatharuv@ATHENA.MIT.EDU */
1462 recip
= g_strdup(triple
[2]);
1463 } else if ((atptr
= strchr(triple
[2], '@')) != NULL
) {
1464 /* form of class,instance,*@ANDREW.CMU.EDU
1465 * class,instance,@ANDREW.CMU.EDU
1466 * If realm is local realm, blank recipient, else
1469 char *realmat
= g_strdup_printf("@%s",zephyr
->realm
);
1471 if (!g_ascii_strcasecmp(atptr
, realmat
))
1472 recip
= g_malloc0(1);
1474 recip
= g_strdup(atptr
);
1477 recip
= g_strdup(triple
[2]);
1481 if (!g_ascii_strcasecmp(triple
[0],"%host%")) {
1482 z_class
= g_strdup(zephyr
->ourhost
);
1483 } else if (!g_ascii_strcasecmp(triple
[0],"%canon%")) {
1484 z_class
= g_strdup(zephyr
->ourhostcanon
);
1486 z_class
= g_strdup(triple
[0]);
1489 if (!g_ascii_strcasecmp(triple
[1],"%host%")) {
1490 z_instance
= g_strdup(zephyr
->ourhost
);
1491 } else if (!g_ascii_strcasecmp(triple
[1],"%canon%")) {
1492 z_instance
= g_strdup(zephyr
->ourhostcanon
);
1494 z_instance
= g_strdup(triple
[1]);
1497 /* There should be some sort of error report listing classes that couldn't be subbed to.
1498 Not important right now though */
1500 if (zephyr_subscribe_to(zephyr
,z_class
, z_instance
, recip
,z_galaxy
) != ZERR_NONE
) {
1502 purple_debug_error("zephyr", "Couldn't subscribe to %s, %s, %s\n", z_class
,z_instance
,recip
);
1505 zephyr
->subscrips
= g_slist_append(zephyr
->subscrips
, new_triple(zephyr
,z_class
,z_instance
,recip
));
1506 /* g_hash_table_destroy(sub_hash_table); */
1519 static void process_anyone(PurpleConnection
*gc
)
1521 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
1523 gchar buff
[BUFSIZ
], *filename
;
1527 if (!(g
= purple_blist_find_group(_("Anyone")))) {
1528 g
= purple_group_new(_("Anyone"));
1529 purple_blist_add_group(g
, NULL
);
1532 filename
= g_strconcat(purple_home_dir(), "/.anyone", NULL
);
1533 if ((fd
= g_fopen(filename
, "r")) != NULL
) {
1534 while (fgets(buff
, BUFSIZ
, fd
)) {
1535 strip_comments(buff
);
1537 if (!purple_blist_find_buddy(purple_connection_get_account(gc
), buff
)) {
1538 char *stripped_user
= zephyr_strip_local_realm(zephyr
,buff
);
1539 purple_debug_info("zephyr","stripped_user %s\n",stripped_user
);
1540 if (!purple_blist_find_buddy(purple_connection_get_account(gc
),stripped_user
)) {
1541 b
= purple_buddy_new(purple_connection_get_account(gc
), stripped_user
, NULL
);
1542 purple_blist_add_buddy(b
, NULL
, g
, NULL
);
1544 g_free(stripped_user
);
1553 static char* normalize_zephyr_exposure(const char* exposure
) {
1554 char *exp2
= g_strstrip(g_ascii_strup(exposure
,-1));
1557 return EXPOSE_REALMVIS
;
1558 if (!g_ascii_strcasecmp(exp2
, EXPOSE_NONE
))
1560 if (!g_ascii_strcasecmp(exp2
, EXPOSE_OPSTAFF
))
1561 return EXPOSE_OPSTAFF
;
1562 if (!g_ascii_strcasecmp(exp2
, EXPOSE_REALMANN
))
1563 return EXPOSE_REALMANN
;
1564 if (!g_ascii_strcasecmp(exp2
, EXPOSE_NETVIS
))
1565 return EXPOSE_NETVIS
;
1566 if (!g_ascii_strcasecmp(exp2
, EXPOSE_NETANN
))
1567 return EXPOSE_NETANN
;
1568 return EXPOSE_REALMVIS
;
1571 static void zephyr_login(PurpleAccount
* account
)
1573 PurpleConnection
*gc
;
1574 zephyr_account
*zephyr
;
1575 gboolean read_anyone
;
1576 gboolean read_zsubs
;
1579 gc
= purple_account_get_connection(account
);
1580 read_anyone
= purple_account_get_bool(purple_connection_get_account(gc
),"read_anyone",TRUE
);
1581 read_zsubs
= purple_account_get_bool(purple_connection_get_account(gc
),"read_zsubs",TRUE
);
1582 exposure
= (gchar
*)purple_account_get_string(purple_connection_get_account(gc
), "exposure_level", EXPOSE_REALMVIS
);
1585 username
= purple_account_get_username(account
);
1587 purple_connection_set_flags(gc
, PURPLE_CONNECTION_FLAG_AUTO_RESP
|
1588 PURPLE_CONNECTION_FLAG_HTML
| PURPLE_CONNECTION_FLAG_NO_BGCOLOR
|
1589 PURPLE_CONNECTION_FLAG_NO_URLDESC
| PURPLE_CONNECTION_FLAG_NO_IMAGES
);
1590 zephyr
= g_new0(zephyr_account
, 1);
1591 purple_connection_set_protocol_data(gc
, zephyr
);
1593 zephyr
->account
= account
;
1595 /* Make sure that the exposure (visibility) is set to a sane value */
1596 zephyr
->exposure
=g_strdup(normalize_zephyr_exposure(exposure
));
1598 if (purple_account_get_bool(purple_connection_get_account(gc
),"use_tzc",0)) {
1599 zephyr
->connection_type
= PURPLE_ZEPHYR_TZC
;
1601 zephyr
->connection_type
= PURPLE_ZEPHYR_KRB4
;
1604 zephyr
->encoding
= (char *)purple_account_get_string(purple_connection_get_account(gc
), "encoding", ZEPHYR_FALLBACK_CHARSET
);
1605 purple_connection_update_progress(gc
, _("Connecting"), 0, 8);
1607 /* XXX z_call_s should actually try to report the com_err determined error */
1608 if (use_tzc(zephyr
)) {
1610 /* purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, "tzc not supported yet"); */
1611 if ((pipe(zephyr
->totzc
) != 0) || (pipe(zephyr
->fromtzc
) != 0)) {
1612 purple_debug_error("zephyr", "pipe creation failed. killing\n");
1619 purple_debug_error("zephyr", "forking failed\n");
1624 gboolean found_ps
= FALSE
;
1625 gchar
** tzc_cmd_array
= g_strsplit(purple_account_get_string(purple_connection_get_account(gc
),"tzc_command","/usr/bin/tzc -e %s")," ",0);
1626 if (close(1) == -1) {
1629 if (dup2(zephyr
->fromtzc
[1], 1) == -1) {
1632 if (close(zephyr
->fromtzc
[1]) == -1) {
1635 if (close(0) == -1) {
1638 if (dup2(zephyr
->totzc
[0], 0) == -1) {
1641 if (close(zephyr
->totzc
[0]) == -1) {
1644 /* tzc_command should really be of the form
1647 ssh username@hostname pathtotzc -e %s
1648 -- this should not require a password, and ideally should be kerberized ssh --
1650 fsh username@hostname pathtotzc -e %s
1652 while(tzc_cmd_array
[i
] != NULL
){
1653 if (!g_ascii_strncasecmp(tzc_cmd_array
[i
],"%s",2)) {
1654 /* fprintf(stderr,"replacing %%s with %s\n",zephyr->exposure); */
1655 tzc_cmd_array
[i
] = g_strdup(zephyr
->exposure
);
1659 /* fprintf(stderr,"keeping %s\n",tzc_cmd_array[i]); */
1668 execvp(tzc_cmd_array
[0], tzc_cmd_array
);
1674 char *buf
= (char *)calloc(bufsize
, 1);
1683 zephyr
->tzc_pid
= pid
;
1684 /* wait till we have data to read from ssh */
1686 FD_SET(zephyr
->fromtzc
[ZEPHYR_FD_READ
], &rfds
);
1691 purple_debug_info("zephyr", "about to read from tzc\n");
1693 if (waitpid(pid
, NULL
, WNOHANG
) == 0) { /* Only select if tzc is still running */
1694 purple_debug_info("zephyr", "about to read from tzc\n");
1695 select_status
= select(zephyr
->fromtzc
[ZEPHYR_FD_READ
] + 1, &rfds
, NULL
, NULL
, NULL
);
1698 purple_debug_info("zephyr", "tzc exited early\n");
1703 FD_SET(zephyr
->fromtzc
[ZEPHYR_FD_READ
], &rfds
);
1704 while (select_status
> 0 &&
1705 select(zephyr
->fromtzc
[ZEPHYR_FD_READ
] + 1, &rfds
, NULL
, NULL
, &tv
) > 0) {
1706 if (read(zephyr
->fromtzc
[ZEPHYR_FD_READ
], bufcur
, 1) != 1) {
1707 purple_debug_error("zephyr", "couldn't read\n");
1708 purple_connection_error(gc
, PURPLE_CONNECTION_ERROR_NETWORK_ERROR
, "couldn't read");
1713 if ((bufcur
- buf
) > (bufsize
- 1)) {
1714 if ((buf
= realloc(buf
, bufsize
* 2)) == NULL
) {
1717 bufcur
= buf
+ bufsize
;
1722 FD_SET(zephyr
->fromtzc
[ZEPHYR_FD_READ
], &rfds
);
1727 /* fprintf(stderr, "read from tzc\n"); */
1731 /* ignore all tzcoutput till we've received the first (*/
1732 while (ptr
< bufcur
&& (*ptr
!='(')) {
1736 purple_connection_error(gc
, PURPLE_CONNECTION_ERROR_NETWORK_ERROR
, "invalid output by tzc (or bad parsing code)");
1741 while(ptr
< bufcur
) {
1745 else if (*ptr
== ')') {
1748 purple_debug_info("zephyr","tzc parenlevel is %d\n",parenlevel
);
1749 switch (parenlevel
) {
1753 /* Search for next beginning (, or for the ending */
1755 while((*ptr
!= '(') && (*ptr
!= ')') && (ptr
<bufcur
))
1758 purple_debug_error("zephyr","tzc parsing error\n");
1761 /* You are probably at
1762 (foo . bar ) or (foo . "bar") or (foo . chars) or (foo . numbers) or (foo . () )
1763 Parse all the data between the first and last f, and move past )
1765 tempstr
= g_malloc0(20000);
1767 while(parenlevel
>1) {
1773 if (parenlevel
> 1) {
1774 tempstr
[tempstridx
++]=*ptr
;
1779 purple_debug_info("zephyr","tempstr parsed\n");
1780 /* tempstr should now be a tempstridx length string containing all characters
1781 from that after the first ( to the one before the last paren ). */
1782 /* We should have the following possible lisp strings but we don't care
1783 (tzcspew . start) (version . "something") (pid . number)*/
1784 /* We care about 'zephyrid . "username@REALM.NAME"' and 'exposure . "SOMETHING"' */
1786 if (!g_ascii_strncasecmp(tempstr
,"zephyrid",8)) {
1787 gchar
* username
= g_malloc0(100);
1790 purple_debug_info("zephyr","zephyrid found\n");
1792 while(tempstr
[tempstridx
] !='"' && tempstridx
< 20000)
1795 while(tempstr
[tempstridx
] !='"' && tempstridx
< 20000)
1796 username
[username_idx
++]=tempstr
[tempstridx
++];
1798 zephyr
->username
= g_strdup_printf("%s",username
);
1799 if ((realm
= strchr(username
,'@')))
1800 zephyr
->realm
= g_strdup_printf("%s",realm
+1);
1802 realm
= (gchar
*)purple_account_get_string(purple_connection_get_account(gc
),"realm","");
1804 realm
= "local-realm";
1806 zephyr
->realm
= g_strdup(realm
);
1807 g_strlcpy(__Zephyr_realm
, (const char*)zephyr
->realm
, REALM_SZ
-1);
1810 zephyr->realm = g_strdup("local-realm");
1815 purple_debug_info("zephyr", "something that's not zephyr id found %s\n",tempstr
);
1818 /* We don't care about anything else yet */
1822 purple_debug_info("zephyr","parenlevel is not 1 or 2\n");
1823 /* This shouldn't be happening */
1828 } /* while (ptr < bufcur) */
1829 purple_debug_info("zephyr", "tzc startup done\n");
1833 else if ( use_zeph02(zephyr
)) {
1835 z_call_s(ZInitialize(), "Couldn't initialize zephyr");
1836 z_call_s(ZOpenPort(&(zephyr
->port
)), "Couldn't open port");
1837 z_call_s(ZSetLocation((char *)zephyr
->exposure
), "Couldn't set location");
1839 realm
= (gchar
*)purple_account_get_string(purple_connection_get_account(gc
),"realm","");
1841 realm
= ZGetRealm();
1843 zephyr
->realm
= g_strdup(realm
);
1844 g_strlcpy(__Zephyr_realm
, (const char*)zephyr
->realm
, REALM_SZ
-1);
1845 zephyr
->username
= g_strdup(ZGetSender());
1847 /* zephyr->realm = g_strdup(ZGetRealm()); */
1848 purple_debug_info("zephyr","realm: %s\n",zephyr
->realm
);
1851 purple_connection_error(gc
, PURPLE_CONNECTION_ERROR_NETWORK_ERROR
, "Only ZEPH0.2 supported currently");
1854 purple_debug_info("zephyr","does it get here\n");
1855 purple_debug_info("zephyr"," realm: %s username:%s\n", zephyr
->realm
, zephyr
->username
);
1858 zephyr
->galaxy
= NULL
;
1859 zephyr
->krbtkfile
= NULL
;
1860 zephyr_inithosts(zephyr
);
1862 if (zephyr_subscribe_to(zephyr
,"MESSAGE","PERSONAL",zephyr
->username
,NULL
) != ZERR_NONE
) {
1863 /* XXX don't translate this yet. It could be written better */
1864 /* XXX error messages could be handled with more detail */
1865 purple_notify_error(purple_account_get_connection(account
), NULL
,
1866 "Unable to subscribe to messages", "Unable to subscribe to initial messages",
1867 purple_request_cpar_from_connection(gc
));
1871 purple_connection_set_state(gc
, PURPLE_CONNECTION_CONNECTED
);
1876 process_zsubs(zephyr
);
1878 if (use_zeph02(zephyr
)) {
1879 zephyr
->nottimer
= purple_timeout_add(100, check_notify_zeph02
, gc
);
1880 } else if (use_tzc(zephyr
)) {
1881 zephyr
->nottimer
= purple_timeout_add(100, check_notify_tzc
, gc
);
1883 zephyr
->loctimer
= purple_timeout_add_seconds(20, check_loc
, gc
);
1887 static void write_zsubs(zephyr_account
*zephyr
)
1889 /* Exports subscription (chat) list back to
1891 * XXX deal with %host%, %canon%, unsubscriptions, and negative subscriptions (punts?)
1894 GSList
*s
= zephyr
->subscrips
;
1901 fname
= g_strdup_printf("%s/.zephyr.subs", purple_home_dir());
1902 fd
= g_fopen(fname
, "w");
1910 char *zclass
, *zinst
, *zrecip
;
1912 triple
= g_strsplit(zt
->name
, ",", 3);
1914 /* deal with classes */
1915 if (!g_ascii_strcasecmp(triple
[0],zephyr
->ourhost
)) {
1916 zclass
= g_strdup("%host%");
1917 } else if (!g_ascii_strcasecmp(triple
[0],zephyr
->ourhostcanon
)) {
1918 zclass
= g_strdup("%canon%");
1920 zclass
= g_strdup(triple
[0]);
1923 /* deal with instances */
1925 if (!g_ascii_strcasecmp(triple
[1],zephyr
->ourhost
)) {
1926 zinst
= g_strdup("%host%");
1927 } else if (!g_ascii_strcasecmp(triple
[1],zephyr
->ourhostcanon
)) {
1928 zinst
= g_strdup("%canon%");;
1930 zinst
= g_strdup(triple
[1]);
1933 /* deal with recipients */
1934 if (triple
[2] == NULL
) {
1935 zrecip
= g_strdup("*");
1936 } else if (!g_ascii_strcasecmp(triple
[2],"")){
1937 zrecip
= g_strdup("*");
1938 } else if (!g_ascii_strcasecmp(triple
[2], zephyr
->username
)) {
1939 zrecip
= g_strdup("%me%");
1941 zrecip
= g_strdup(triple
[2]);
1944 fprintf(fd
, "%s,%s,%s\n",zclass
,zinst
,zrecip
);
1956 static void write_anyone(zephyr_account
*zephyr
)
1961 PurpleAccount
*account
;
1962 fname
= g_strdup_printf("%s/.anyone", purple_home_dir());
1963 fd
= g_fopen(fname
, "w");
1969 account
= zephyr
->account
;
1970 for (buddies
= purple_blist_find_buddies(account
, NULL
); buddies
;
1971 buddies
= g_slist_delete_link(buddies
, buddies
)) {
1972 PurpleBuddy
*b
= buddies
->data
;
1973 gchar
*stripped_user
= zephyr_strip_local_realm(zephyr
, purple_buddy_get_name(b
));
1974 fprintf(fd
, "%s\n", stripped_user
);
1975 g_free(stripped_user
);
1982 static void zephyr_close(PurpleConnection
* gc
)
1986 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
1987 pid_t tzc_pid
= zephyr
->tzc_pid
;
1989 l
= zephyr
->pending_zloc_names
;
1991 g_free((char *)l
->data
);
1994 g_list_free(zephyr
->pending_zloc_names
);
1996 if (purple_account_get_bool(purple_connection_get_account(gc
), "write_anyone", FALSE
))
1997 write_anyone(zephyr
);
1999 if (purple_account_get_bool(purple_connection_get_account(gc
), "write_zsubs", FALSE
))
2000 write_zsubs(zephyr
);
2002 s
= zephyr
->subscrips
;
2004 free_triple((zephyr_triple
*) s
->data
);
2007 g_slist_free(zephyr
->subscrips
);
2009 if (zephyr
->nottimer
)
2010 purple_timeout_remove(zephyr
->nottimer
);
2011 zephyr
->nottimer
= 0;
2012 if (zephyr
->loctimer
)
2013 purple_timeout_remove(zephyr
->loctimer
);
2014 zephyr
->loctimer
= 0;
2016 if (use_zeph02(zephyr
)) {
2017 z_call(ZCancelSubscriptions(0));
2018 z_call(ZUnsetLocation());
2019 z_call(ZClosePort());
2022 if (kill(tzc_pid
,SIGTERM
) == -1) {
2025 purple_debug_error("zephyr","An invalid signal was specified when killing tzc\n");
2027 else if (err
==ESRCH
) {
2028 purple_debug_error("zephyr","Tzc's pid didn't exist while killing tzc\n");
2030 else if (err
==EPERM
) {
2031 purple_debug_error("zephyr","purple didn't have permission to kill tzc\n");
2034 purple_debug_error("zephyr","miscellaneous error while attempting to close tzc\n");
2040 static int zephyr_send_message(zephyr_account
*zephyr
,char* zclass
, char* instance
, char* recipient
, const char *im
,
2041 const char *sig
, char *opcode
) ;
2043 static const char * zephyr_get_signature(void)
2045 /* XXX add zephyr error reporting */
2046 const char * sig
=ZGetVariable("zwrite-signature");
2048 sig
= g_get_real_name();
2053 static int zephyr_chat_send(PurpleConnection
* gc
, int id
, PurpleMessage
*msg
)
2057 PurpleChatConversation
*gcc
;
2060 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
2062 zt
= find_sub_by_id(zephyr
,id
);
2064 /* this should never happen. */
2067 sig
= zephyr_get_signature();
2069 gcc
= purple_conversations_find_chat_with_account(zt
->name
,
2070 purple_connection_get_account(gc
));
2072 if (!(inst
= (char *)purple_chat_conversation_get_topic(gcc
)))
2073 inst
= g_strdup("PERSONAL");
2075 if (!g_ascii_strcasecmp(zt
->recipient
, "*"))
2076 recipient
= local_zephyr_normalize(zephyr
,"");
2078 recipient
= local_zephyr_normalize(zephyr
,zt
->recipient
);
2080 zephyr_send_message(zephyr
, zt
->class, inst
, recipient
,
2081 purple_message_get_contents(msg
), sig
, "");
2086 static int zephyr_send_im(PurpleConnection
*gc
, PurpleMessage
*msg
)
2089 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
2091 if (purple_message_get_flags(msg
) & PURPLE_MESSAGE_AUTO_RESP
) {
2092 sig
= "Automated reply:";
2094 sig
= zephyr_get_signature();
2096 zephyr_send_message(zephyr
, "MESSAGE", "PERSONAL",
2097 local_zephyr_normalize(zephyr
, purple_message_get_recipient(msg
)),
2098 purple_message_get_contents(msg
), sig
, "");
2103 /* Munge the outgoing zephyr so that any quotes or backslashes are
2104 escaped and do not confuse tzc: */
2106 static char* zephyr_tzc_escape_msg(const char *message
)
2108 gsize pos
= 0, pos2
= 0;
2111 if (message
&& *message
) {
2112 newmsg
= g_new0(char,1+strlen(message
)*2);
2113 while(pos
< strlen(message
)) {
2114 if (message
[pos
]=='\\') {
2116 newmsg
[pos2
+1]='\\';
2119 else if (message
[pos
]=='"') {
2125 newmsg
[pos2
] = message
[pos
];
2131 newmsg
= g_strdup("");
2133 /* fprintf(stderr,"newmsg %s message %s\n",newmsg,message); */
2137 char* zephyr_tzc_deescape_str(const char *message
)
2139 gsize pos
= 0, pos2
= 0;
2142 if (message
&& *message
) {
2143 newmsg
= g_new0(char,strlen(message
)+1);
2144 while(pos
< strlen(message
)) {
2145 if (message
[pos
]=='\\') {
2148 newmsg
[pos2
] = message
[pos
];
2153 newmsg
= g_strdup("");
2159 static int zephyr_send_message(zephyr_account
*zephyr
,char* zclass
, char* instance
, char* recipient
, const char *im
,
2160 const char *sig
, char *opcode
)
2163 /* (From the tzc source)
2164 * emacs sends something of the form:
2165 * ((class . "MESSAGE")
2167 * (recipients ("PERSONAL" . "bovik") ("test" . ""))
2168 * (sender . "bovik")
2169 * (message . ("Harry Bovik" "my zgram"))
2174 html_buf
= html_to_zephyr(im
);
2175 html_buf2
= purple_unescape_html(html_buf
);
2177 if(use_tzc(zephyr
)) {
2181 /* CMU cclub tzc doesn't grok opcodes for now */
2182 char* tzc_sig
= zephyr_tzc_escape_msg(sig
);
2183 char *tzc_body
= zephyr_tzc_escape_msg(html_buf2
);
2184 zsendstr
= g_strdup_printf("((tzcfodder . send) (class . \"%s\") (auth . t) (recipients (\"%s\" . \"%s\")) (message . (\"%s\" \"%s\")) ) \n",
2185 zclass
, instance
, recipient
, tzc_sig
, tzc_body
);
2186 /* fprintf(stderr,"zsendstr = %s\n",zsendstr); */
2187 len
= strlen(zsendstr
);
2188 result
= write(zephyr
->totzc
[ZEPHYR_FD_WRITE
], zsendstr
, len
);
2189 if (result
!= len
) {
2200 } else if (use_zeph02(zephyr
)) {
2202 char *buf
= g_strdup_printf("%s%c%s", sig
, '\0', html_buf2
);
2203 memset((char *)¬ice
, 0, sizeof(notice
));
2205 notice
.z_kind
= ACKED
;
2207 notice
.z_opcode
= "";
2208 notice
.z_class
= zclass
;
2209 notice
.z_class_inst
= instance
;
2210 notice
.z_recipient
= recipient
;
2211 notice
.z_sender
= 0;
2212 notice
.z_default_format
= "Class $class, Instance $instance:\n" "To: @bold($recipient) at $time $date\n" "From: @bold($1) <$sender>\n\n$2";
2213 notice
.z_message_len
= strlen(html_buf2
) + strlen(sig
) + 2;
2214 notice
.z_message
= buf
;
2215 notice
.z_opcode
= g_strdup(opcode
);
2216 purple_debug_info("zephyr","About to send notice\n");
2217 if (! ZSendNotice(¬ice
, ZAUTH
) == ZERR_NONE
) {
2218 /* XXX handle errors here */
2224 purple_debug_info("zephyr","notice sent\n");
2234 char *local_zephyr_normalize(zephyr_account
*zephyr
,const char *orig
)
2237 Basically the inverse of zephyr_strip_local_realm
2241 if (!g_ascii_strcasecmp(orig
, "")) {
2242 return g_strdup("");
2245 if (strchr(orig
,'@')) {
2246 buf
= g_strdup_printf("%s",orig
);
2248 buf
= g_strdup_printf("%s@%s",orig
,zephyr
->realm
);
2253 static const char *zephyr_normalize(const PurpleAccount
*account
, const char *who
)
2255 static char buf
[BUF_LEN
];
2256 PurpleConnection
*gc
;
2259 if (account
== NULL
) {
2260 if (strlen(who
) >= sizeof(buf
))
2265 gc
= purple_account_get_connection(account
);
2269 tmp
= local_zephyr_normalize(purple_connection_get_protocol_data(gc
), who
);
2271 if (strlen(tmp
) >= sizeof(buf
)) {
2276 g_strlcpy(buf
, tmp
, sizeof(buf
));
2282 static void zephyr_zloc(PurpleConnection
*gc
, const char *who
)
2284 ZAsyncLocateData_t ald
;
2285 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
2286 gchar
* normalized_who
= local_zephyr_normalize(zephyr
,who
);
2288 if (use_zeph02(zephyr
)) {
2289 if (ZRequestLocations(normalized_who
, &ald
, UNACKED
, ZAUTH
) == ZERR_NONE
) {
2290 zephyr
->pending_zloc_names
= g_list_append(zephyr
->pending_zloc_names
,
2291 g_strdup(normalized_who
));
2293 /* XXX deal with errors somehow */
2295 } else if (use_tzc(zephyr
)) {
2298 char* zlocstr
= g_strdup_printf("((tzcfodder . zlocate) \"%s\")\n",normalized_who
);
2299 zephyr
->pending_zloc_names
= g_list_append(zephyr
->pending_zloc_names
, g_strdup(normalized_who
));
2300 len
= strlen(zlocstr
);
2301 result
= write(zephyr
->totzc
[ZEPHYR_FD_WRITE
],zlocstr
,len
);
2302 if (result
!= len
) {
2303 purple_debug_error("zephyr", "Unable to write a message: %s\n", g_strerror(errno
));
2309 static void zephyr_set_status(PurpleAccount
*account
, PurpleStatus
*status
) {
2312 PurpleConnection
*gc
= purple_account_get_connection(account
);
2313 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
2314 PurpleStatusPrimitive primitive
= purple_status_type_get_primitive(purple_status_get_status_type(status
));
2316 g_free(zephyr
->away
);
2317 zephyr
->away
= NULL
;
2319 if (primitive
== PURPLE_STATUS_AWAY
) {
2320 zephyr
->away
= g_strdup(purple_status_get_attr_string(status
,"message"));
2322 else if (primitive
== PURPLE_STATUS_AVAILABLE
) {
2323 if (use_zeph02(zephyr
)) {
2324 ZSetLocation(zephyr
->exposure
);
2327 char *zexpstr
= g_strdup_printf("((tzcfodder . set-location) (hostname . \"%s\") (exposure . \"%s\"))\n",zephyr
->ourhost
,zephyr
->exposure
);
2328 len
= strlen(zexpstr
);
2329 result
= write(zephyr
->totzc
[ZEPHYR_FD_WRITE
],zexpstr
,len
);
2330 if (result
!= len
) {
2331 purple_debug_error("zephyr", "Unable to write message: %s\n", g_strerror(errno
));
2336 else if (primitive
== PURPLE_STATUS_INVISIBLE
) {
2337 /* XXX handle errors */
2338 if (use_zeph02(zephyr
)) {
2339 ZSetLocation(EXPOSE_OPSTAFF
);
2341 char *zexpstr
= g_strdup_printf("((tzcfodder . set-location) (hostname . \"%s\") (exposure . \"%s\"))\n",zephyr
->ourhost
,EXPOSE_OPSTAFF
);
2342 len
= strlen(zexpstr
);
2343 result
= write(zephyr
->totzc
[ZEPHYR_FD_WRITE
],zexpstr
,len
);
2344 if (result
!= len
) {
2345 purple_debug_error("zephyr", "Unable to write message: %s\n", g_strerror(errno
));
2352 static GList
*zephyr_status_types(PurpleAccount
*account
)
2354 PurpleStatusType
*type
;
2355 GList
*types
= NULL
;
2357 /* zephyr has several exposures
2358 NONE (where you are hidden, and zephyrs to you are in practice silently dropped -- yes this is wrong)
2360 REALM-VISIBLE visible to people in local realm
2361 REALM-ANNOUNCED REALM-VISIBLE+ plus your logins/logouts are announced to <login,username,*>
2362 NET-VISIBLE REALM-ANNOUNCED, plus visible to people in foreign realm
2363 NET-ANNOUNCED NET-VISIBLE, plus logins/logouts are announced to <login,username,*>
2365 Online will set the user to the exposure they have in their options (defaulting to REALM-VISIBLE),
2366 Hidden, will set the user's exposure to OPSTAFF
2368 Away won't change their exposure but will set an auto away message (for IMs only)
2371 type
= purple_status_type_new(PURPLE_STATUS_AVAILABLE
, NULL
, NULL
, TRUE
);
2372 types
= g_list_append(types
,type
);
2374 type
= purple_status_type_new(PURPLE_STATUS_INVISIBLE
, NULL
, NULL
, TRUE
);
2375 types
= g_list_append(types
,type
);
2377 type
= purple_status_type_new_with_attrs(
2378 PURPLE_STATUS_AWAY
, NULL
, NULL
, TRUE
, TRUE
, FALSE
,
2379 "message", _("Message"), purple_value_new(G_TYPE_STRING
),
2381 types
= g_list_append(types
, type
);
2383 type
= purple_status_type_new(PURPLE_STATUS_OFFLINE
, NULL
, NULL
, TRUE
);
2384 types
= g_list_append(types
,type
);
2389 static GList
*zephyr_chat_info(PurpleConnection
* gc
)
2392 PurpleProtocolChatEntry
*pce
;
2394 pce
= g_new0(PurpleProtocolChatEntry
, 1);
2396 pce
->label
= _("_Class:");
2397 pce
->identifier
= "class";
2398 m
= g_list_append(m
, pce
);
2400 pce
= g_new0(PurpleProtocolChatEntry
, 1);
2402 pce
->label
= _("_Instance:");
2403 pce
->identifier
= "instance";
2404 m
= g_list_append(m
, pce
);
2406 pce
= g_new0(PurpleProtocolChatEntry
, 1);
2408 pce
->label
= _("_Recipient:");
2409 pce
->identifier
= "recipient";
2410 m
= g_list_append(m
, pce
);
2415 /* Called when the server notifies us a message couldn't get sent */
2417 static void zephyr_subscribe_failed(PurpleConnection
*gc
,char * z_class
, char *z_instance
, char * z_recipient
, char* z_galaxy
)
2419 gchar
* subscribe_failed
= g_strdup_printf(_("Attempt to subscribe to %s,%s,%s failed"), z_class
, z_instance
,z_recipient
);
2420 purple_notify_error(gc
,"", subscribe_failed
, NULL
, purple_request_cpar_from_connection(gc
));
2421 g_free(subscribe_failed
);
2424 static char *zephyr_get_chat_name(GHashTable
*data
) {
2425 gchar
* zclass
= g_hash_table_lookup(data
,"class");
2426 gchar
* inst
= g_hash_table_lookup(data
,"instance");
2427 gchar
* recipient
= g_hash_table_lookup(data
, "recipient");
2428 if (!zclass
) /* This should never happen */
2434 return g_strdup_printf("%s,%s,%s",zclass
,inst
,recipient
);
2438 static void zephyr_join_chat(PurpleConnection
* gc
, GHashTable
* data
)
2440 /* ZSubscription_t sub; */
2441 zephyr_triple
*zt1
, *zt2
;
2442 const char *classname
;
2443 const char *instname
;
2445 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
2446 classname
= g_hash_table_lookup(data
, "class");
2447 instname
= g_hash_table_lookup(data
, "instance");
2448 recip
= g_hash_table_lookup(data
, "recipient");
2454 if (!g_ascii_strcasecmp(classname
,"%host%"))
2455 classname
= g_strdup(zephyr
->ourhost
);
2456 if (!g_ascii_strcasecmp(classname
,"%canon%"))
2457 classname
= g_strdup(zephyr
->ourhostcanon
);
2459 if (!instname
|| *instname
== '\0')
2462 if (!g_ascii_strcasecmp(instname
,"%host%"))
2463 instname
= g_strdup(zephyr
->ourhost
);
2464 if (!g_ascii_strcasecmp(instname
,"%canon%"))
2465 instname
= g_strdup(zephyr
->ourhostcanon
);
2467 if (!recip
|| (*recip
== '*'))
2469 if (!g_ascii_strcasecmp(recip
, "%me%"))
2470 recip
= zephyr
->username
;
2472 zt1
= new_triple(zephyr
,classname
, instname
, recip
);
2473 zt2
= find_sub_by_triple(zephyr
,zt1
);
2477 if (!g_ascii_strcasecmp(instname
,"*"))
2478 instname
= "PERSONAL";
2479 purple_serv_got_joined_chat(gc
, zt2
->id
, zt2
->name
);
2480 zephyr_chat_set_topic(gc
,zt2
->id
,instname
);
2486 /* sub.zsub_class = zt1->class;
2487 sub.zsub_classinst = zt1->instance;
2488 sub.zsub_recipient = zt1->recipient; */
2490 if (zephyr_subscribe_to(zephyr
,zt1
->class,zt1
->instance
,zt1
->recipient
,NULL
) != ZERR_NONE
) {
2491 /* XXX output better subscription information */
2492 zephyr_subscribe_failed(gc
,zt1
->class,zt1
->instance
,zt1
->recipient
,NULL
);
2497 zephyr
->subscrips
= g_slist_append(zephyr
->subscrips
, zt1
);
2499 purple_serv_got_joined_chat(gc
, zt1
->id
, zt1
->name
);
2500 if (!g_ascii_strcasecmp(instname
,"*"))
2501 instname
= "PERSONAL";
2502 zephyr_chat_set_topic(gc
,zt1
->id
,instname
);
2505 static void zephyr_chat_leave(PurpleConnection
* gc
, int id
)
2508 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
2509 zt
= find_sub_by_id(zephyr
,id
);
2513 zt
->id
= ++(zephyr
->last_id
);
2517 static PurpleChat
*zephyr_find_blist_chat(PurpleAccount
*account
, const char *name
)
2519 PurpleBlistNode
*gnode
, *cnode
;
2521 /* XXX needs to be %host%,%canon%, and %me% clean */
2522 for(gnode
= purple_blist_get_root(); gnode
;
2523 gnode
= purple_blist_node_get_sibling_next(gnode
)) {
2524 for(cnode
= purple_blist_node_get_first_child(gnode
);
2526 cnode
= purple_blist_node_get_sibling_next(cnode
)) {
2527 PurpleChat
*chat
= (PurpleChat
*)cnode
;
2528 char *zclass
, *inst
, *recip
;
2530 GHashTable
*components
;
2531 if(!PURPLE_IS_CHAT(cnode
))
2533 if(purple_chat_get_account(chat
) != account
)
2535 components
= purple_chat_get_components(chat
);
2536 if(!(zclass
= g_hash_table_lookup(components
, "class")))
2538 if(!(inst
= g_hash_table_lookup(components
, "instance")))
2539 inst
= g_strdup("");
2540 if(!(recip
= g_hash_table_lookup(components
, "recipient")))
2541 recip
= g_strdup("");
2542 /* purple_debug_info("zephyr","in zephyr_find_blist_chat name: %s\n",name?name:""); */
2543 triple
= g_strsplit(name
,",",3);
2544 if (!g_ascii_strcasecmp(triple
[0],zclass
) && !g_ascii_strcasecmp(triple
[1],inst
) && !g_ascii_strcasecmp(triple
[2],recip
))
2551 static const char *zephyr_list_icon(PurpleAccount
* a
, PurpleBuddy
* b
)
2556 static unsigned int zephyr_send_typing(PurpleConnection
*gc
, const char *who
, PurpleIMTypingState state
) {
2558 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
2559 if (use_tzc(zephyr
))
2562 if (state
== PURPLE_IM_NOT_TYPING
)
2565 /* XXX We probably should care if this fails. Or maybe we don't want to */
2567 purple_debug_info("zephyr", "who is null\n");
2568 recipient
= local_zephyr_normalize(zephyr
,"");
2570 char *comma
= strrchr(who
, ',');
2571 /* Don't ping broadcast (chat) recipients */
2572 /* The strrchr case finds a realm-stripped broadcast subscription
2573 e.g. comma is the last character in the string */
2574 if (comma
&& ( (*(comma
+1) == '\0') || (*(comma
+1) == '@')))
2577 recipient
= local_zephyr_normalize(zephyr
,who
);
2580 purple_debug_info("zephyr","about to send typing notification to %s\n",recipient
);
2581 zephyr_send_message(zephyr
,"MESSAGE","PERSONAL",recipient
,"","","PING");
2582 purple_debug_info("zephyr","sent typing notification\n");
2585 * TODO: Is this correct? It means we will call
2586 * purple_serv_send_typing(gc, who, PURPLE_IM_TYPING) once every 15 seconds
2587 * until the Purple user stops typing.
2589 return ZEPHYR_TYPING_SEND_TIMEOUT
;
2594 static void zephyr_chat_set_topic(PurpleConnection
* gc
, int id
, const char *topic
)
2597 PurpleChatConversation
*gcc
;
2599 zephyr_account
* zephyr
= purple_connection_get_protocol_data(gc
);
2600 char *sender
= (char *)zephyr
->username
;
2602 zt
= find_sub_by_id(zephyr
,id
);
2603 /* find_sub_by_id can return NULL */
2606 gcc
= purple_conversations_find_chat_with_account(zt
->name
,
2607 purple_connection_get_account(gc
));
2609 topic_utf8
= zephyr_recv_convert(gc
,(gchar
*)topic
);
2610 purple_chat_conversation_set_topic(gcc
,sender
,topic_utf8
);
2617 static PurpleCmdRet
zephyr_purple_cmd_msg(PurpleConversation
*conv
,
2618 const char *cmd
, char **args
, char **error
, void *data
)
2622 PurpleConnection
*gc
= purple_conversation_get_connection(conv
);
2623 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);;
2624 if (!g_ascii_strcasecmp(args
[0],"*"))
2625 return PURPLE_CMD_RET_FAILED
; /* "*" is not a valid argument */
2627 recipient
= local_zephyr_normalize(zephyr
,args
[0]);
2629 if (strlen(recipient
) < 1) {
2631 return PURPLE_CMD_RET_FAILED
; /* a null recipient is a chat message, not an IM */
2634 if (zephyr_send_message(zephyr
,"MESSAGE","PERSONAL",recipient
,args
[1],zephyr_get_signature(),""))
2635 ret
= PURPLE_CMD_RET_OK
;
2637 ret
= PURPLE_CMD_RET_FAILED
;
2642 static PurpleCmdRet
zephyr_purple_cmd_zlocate(PurpleConversation
*conv
,
2643 const char *cmd
, char **args
, char **error
, void *data
)
2645 zephyr_zloc(purple_conversation_get_connection(conv
),args
[0]);
2646 return PURPLE_CMD_RET_OK
;
2649 static PurpleCmdRet
zephyr_purple_cmd_instance(PurpleConversation
*conv
,
2650 const char *cmd
, char **args
, char **error
, void *data
)
2652 /* Currently it sets the instance with leading spaces and
2653 * all. This might not be the best thing to do, though having
2654 * one word isn't ideal either. */
2656 const char* instance
= args
[0];
2657 zephyr_chat_set_topic(purple_conversation_get_connection(conv
),
2658 purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(conv
)),instance
);
2659 return PURPLE_CMD_RET_OK
;
2662 static PurpleCmdRet
zephyr_purple_cmd_joinchat_cir(PurpleConversation
*conv
,
2663 const char *cmd
, char **args
, char **error
, void *data
)
2665 /* Join a new zephyr chat */
2666 GHashTable
*triple
= g_hash_table_new(NULL
,NULL
);
2667 g_hash_table_insert(triple
,"class",args
[0]);
2668 g_hash_table_insert(triple
,"instance",args
[1]);
2669 g_hash_table_insert(triple
,"recipient",args
[2]);
2670 zephyr_join_chat(purple_conversation_get_connection(conv
),triple
);
2671 return PURPLE_CMD_RET_OK
;
2674 static PurpleCmdRet
zephyr_purple_cmd_zi(PurpleConversation
*conv
,
2675 const char *cmd
, char **args
, char **error
, void *data
)
2677 /* args = instance, message */
2678 PurpleConnection
*gc
= purple_conversation_get_connection(conv
);
2679 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
2680 if ( zephyr_send_message(zephyr
,"message",args
[0],"",args
[1],zephyr_get_signature(),""))
2681 return PURPLE_CMD_RET_OK
;
2683 return PURPLE_CMD_RET_FAILED
;
2686 static PurpleCmdRet
zephyr_purple_cmd_zci(PurpleConversation
*conv
,
2687 const char *cmd
, char **args
, char **error
, void *data
)
2689 /* args = class, instance, message */
2690 PurpleConnection
*gc
= purple_conversation_get_connection(conv
);
2691 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
2692 if ( zephyr_send_message(zephyr
,args
[0],args
[1],"",args
[2],zephyr_get_signature(),""))
2693 return PURPLE_CMD_RET_OK
;
2695 return PURPLE_CMD_RET_FAILED
;
2698 static PurpleCmdRet
zephyr_purple_cmd_zcir(PurpleConversation
*conv
,
2699 const char *cmd
, char **args
, char **error
, void *data
)
2701 /* args = class, instance, recipient, message */
2702 PurpleConnection
*gc
= purple_conversation_get_connection(conv
);
2703 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
2704 if ( zephyr_send_message(zephyr
,args
[0],args
[1],args
[2],args
[3],zephyr_get_signature(),""))
2705 return PURPLE_CMD_RET_OK
;
2707 return PURPLE_CMD_RET_FAILED
;
2710 static PurpleCmdRet
zephyr_purple_cmd_zir(PurpleConversation
*conv
,
2711 const char *cmd
, char **args
, char **error
, void *data
)
2713 /* args = instance, recipient, message */
2714 PurpleConnection
*gc
= purple_conversation_get_connection(conv
);
2715 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
2716 if ( zephyr_send_message(zephyr
,"message",args
[0],args
[1],args
[2],zephyr_get_signature(),""))
2717 return PURPLE_CMD_RET_OK
;
2719 return PURPLE_CMD_RET_FAILED
;
2722 static PurpleCmdRet
zephyr_purple_cmd_zc(PurpleConversation
*conv
,
2723 const char *cmd
, char **args
, char **error
, void *data
)
2725 /* args = class, message */
2726 PurpleConnection
*gc
= purple_conversation_get_connection(conv
);
2727 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
2728 if ( zephyr_send_message(zephyr
,args
[0],"PERSONAL","",args
[1],zephyr_get_signature(),""))
2729 return PURPLE_CMD_RET_OK
;
2731 return PURPLE_CMD_RET_FAILED
;
2734 static void zephyr_register_slash_commands(void)
2738 id
= purple_cmd_register("msg","ws", PURPLE_CMD_P_PROTOCOL
,
2739 PURPLE_CMD_FLAG_IM
| PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
,
2741 zephyr_purple_cmd_msg
, _("msg <nick> <message>: Send a private message to a user"), NULL
);
2742 cmds
= g_slist_prepend(cmds
, GUINT_TO_POINTER(id
));
2744 id
= purple_cmd_register("zlocate","w", PURPLE_CMD_P_PROTOCOL
,
2745 PURPLE_CMD_FLAG_IM
| PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
,
2747 zephyr_purple_cmd_zlocate
, _("zlocate <nick>: Locate user"), NULL
);
2748 cmds
= g_slist_prepend(cmds
, GUINT_TO_POINTER(id
));
2750 id
= purple_cmd_register("zl","w", PURPLE_CMD_P_PROTOCOL
,
2751 PURPLE_CMD_FLAG_IM
| PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
,
2753 zephyr_purple_cmd_zlocate
, _("zl <nick>: Locate user"), NULL
);
2754 cmds
= g_slist_prepend(cmds
, GUINT_TO_POINTER(id
));
2756 id
= purple_cmd_register("instance","s", PURPLE_CMD_P_PROTOCOL
,
2757 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
,
2759 zephyr_purple_cmd_instance
, _("instance <instance>: Set the instance to be used on this class"), NULL
);
2760 cmds
= g_slist_prepend(cmds
, GUINT_TO_POINTER(id
));
2762 id
= purple_cmd_register("inst","s", PURPLE_CMD_P_PROTOCOL
,
2763 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
,
2765 zephyr_purple_cmd_instance
, _("inst <instance>: Set the instance to be used on this class"), NULL
);
2766 cmds
= g_slist_prepend(cmds
, GUINT_TO_POINTER(id
));
2768 id
= purple_cmd_register("topic","s", PURPLE_CMD_P_PROTOCOL
,
2769 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
,
2771 zephyr_purple_cmd_instance
, _("topic <instance>: Set the instance to be used on this class"), NULL
);
2772 cmds
= g_slist_prepend(cmds
, GUINT_TO_POINTER(id
));
2774 id
= purple_cmd_register("sub", "www", PURPLE_CMD_P_PROTOCOL
,
2775 PURPLE_CMD_FLAG_IM
| PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
,
2777 zephyr_purple_cmd_joinchat_cir
,
2778 _("sub <class> <instance> <recipient>: Join a new chat"), NULL
);
2779 cmds
= g_slist_prepend(cmds
, GUINT_TO_POINTER(id
));
2781 id
= purple_cmd_register("zi","ws", PURPLE_CMD_P_PROTOCOL
,
2782 PURPLE_CMD_FLAG_IM
| PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
,
2784 zephyr_purple_cmd_zi
, _("zi <instance>: Send a message to <message,<i>instance</i>,*>"), NULL
);
2785 cmds
= g_slist_prepend(cmds
, GUINT_TO_POINTER(id
));
2787 id
= purple_cmd_register("zci","wws",PURPLE_CMD_P_PROTOCOL
,
2788 PURPLE_CMD_FLAG_IM
| PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
,
2790 zephyr_purple_cmd_zci
,
2791 _("zci <class> <instance>: Send a message to <<i>class</i>,<i>instance</i>,*>"), NULL
);
2792 cmds
= g_slist_prepend(cmds
, GUINT_TO_POINTER(id
));
2794 id
= purple_cmd_register("zcir","wwws",PURPLE_CMD_P_PROTOCOL
,
2795 PURPLE_CMD_FLAG_IM
| PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
,
2797 zephyr_purple_cmd_zcir
,
2798 _("zcir <class> <instance> <recipient>: Send a message to <<i>class</i>,<i>instance</i>,<i>recipient</i>>"), NULL
);
2799 cmds
= g_slist_prepend(cmds
, GUINT_TO_POINTER(id
));
2801 id
= purple_cmd_register("zir","wws",PURPLE_CMD_P_PROTOCOL
,
2802 PURPLE_CMD_FLAG_IM
| PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
,
2804 zephyr_purple_cmd_zir
,
2805 _("zir <instance> <recipient>: Send a message to <MESSAGE,<i>instance</i>,<i>recipient</i>>"), NULL
);
2806 cmds
= g_slist_prepend(cmds
, GUINT_TO_POINTER(id
));
2808 id
= purple_cmd_register("zc","ws", PURPLE_CMD_P_PROTOCOL
,
2809 PURPLE_CMD_FLAG_IM
| PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
,
2811 zephyr_purple_cmd_zc
, _("zc <class>: Send a message to <<i>class</i>,PERSONAL,*>"), NULL
);
2812 cmds
= g_slist_prepend(cmds
, GUINT_TO_POINTER(id
));
2816 static void zephyr_unregister_slash_commands(void)
2819 PurpleCmdId id
= GPOINTER_TO_UINT(cmds
->data
);
2820 purple_cmd_unregister(id
);
2821 cmds
= g_slist_delete_link(cmds
, cmds
);
2826 static int zephyr_resubscribe(PurpleConnection
*gc
)
2828 /* Resubscribe to the in-memory list of subscriptions and also
2830 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
2831 GSList
*s
= zephyr
->subscrips
;
2835 /* XXX We really should care if this fails */
2836 zephyr_subscribe_to(zephyr
,zt
->class,zt
->instance
,zt
->recipient
,NULL
);
2839 /* XXX handle unsubscriptions */
2844 static void zephyr_action_resubscribe(PurpleProtocolAction
*action
)
2847 PurpleConnection
*gc
= action
->connection
;
2848 zephyr_resubscribe(gc
);
2852 static void zephyr_action_get_subs_from_server(PurpleProtocolAction
*action
)
2854 PurpleConnection
*gc
= action
->connection
;
2855 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
2857 int retval
, nsubs
, one
,i
;
2858 ZSubscription_t subs
;
2859 if (use_zeph02(zephyr
)) {
2860 GString
* subout
= g_string_new("Subscription list<br>");
2862 title
= g_strdup_printf("Server subscriptions for %s", zephyr
->username
);
2864 if (zephyr
->port
== 0) {
2866 purple_debug_error("zephyr", "error while retrieving port\n");
2869 if ((retval
= ZRetrieveSubscriptions(zephyr
->port
,&nsubs
)) != ZERR_NONE
) {
2871 /* XXX better error handling */
2872 purple_debug_error("zephyr", "error while retrieving subscriptions from server\n");
2875 for(i
=0;i
<nsubs
;i
++) {
2877 if ((retval
= ZGetSubscriptions(&subs
,&one
)) != ZERR_NONE
) {
2878 /* XXX better error handling */
2880 purple_debug_error("zephyr", "error while retrieving individual subscription\n");
2883 g_string_append_printf(subout
, "Class %s Instance %s Recipient %s<br>",
2884 subs
.zsub_class
, subs
.zsub_classinst
,
2885 subs
.zsub_recipient
);
2887 purple_notify_formatted(gc
, title
, title
, NULL
, subout
->str
, NULL
, NULL
);
2889 g_string_free(subout
, TRUE
);
2892 purple_notify_error(gc
, "", "tzc doesn't support this action",
2893 NULL
, purple_request_cpar_from_connection(gc
));
2898 static GList
*zephyr_get_actions(PurpleConnection
*gc
)
2901 PurpleProtocolAction
*act
= NULL
;
2903 act
= purple_protocol_action_new(_("Resubscribe"), zephyr_action_resubscribe
);
2904 list
= g_list_append(list
, act
);
2906 act
= purple_protocol_action_new(_("Retrieve subscriptions from server"), zephyr_action_get_subs_from_server
);
2907 list
= g_list_append(list
,act
);
2914 zephyr_protocol_init(PurpleProtocol
*protocol
)
2916 PurpleAccountOption
*option
;
2917 char *tmp
= get_exposure_level();
2919 protocol
->id
= "prpl-zephyr";
2920 protocol
->name
= "Zephyr";
2921 protocol
->options
= OPT_PROTO_CHAT_TOPIC
| OPT_PROTO_NO_PASSWORD
;
2923 option
= purple_account_option_bool_new(_("Use tzc"), "use_tzc", FALSE
);
2924 protocol
->account_options
= g_list_append(protocol
->account_options
, option
);
2926 option
= purple_account_option_string_new(_("tzc command"), "tzc_command", "/usr/bin/tzc -e %s");
2927 protocol
->account_options
= g_list_append(protocol
->account_options
, option
);
2929 option
= purple_account_option_bool_new(_("Export to .anyone"), "write_anyone", FALSE
);
2930 protocol
->account_options
= g_list_append(protocol
->account_options
, option
);
2932 option
= purple_account_option_bool_new(_("Export to .zephyr.subs"), "write_zsubs", FALSE
);
2933 protocol
->account_options
= g_list_append(protocol
->account_options
, option
);
2935 option
= purple_account_option_bool_new(_("Import from .anyone"), "read_anyone", TRUE
);
2936 protocol
->account_options
= g_list_append(protocol
->account_options
, option
);
2938 option
= purple_account_option_bool_new(_("Import from .zephyr.subs"), "read_zsubs", TRUE
);
2939 protocol
->account_options
= g_list_append(protocol
->account_options
, option
);
2941 option
= purple_account_option_string_new(_("Realm"), "realm", "");
2942 protocol
->account_options
= g_list_append(protocol
->account_options
, option
);
2944 option
= purple_account_option_string_new(_("Exposure"), "exposure_level", tmp
?tmp
: EXPOSE_REALMVIS
);
2945 protocol
->account_options
= g_list_append(protocol
->account_options
, option
);
2947 option
= purple_account_option_string_new(_("Encoding"), "encoding", ZEPHYR_FALLBACK_CHARSET
);
2948 protocol
->account_options
= g_list_append(protocol
->account_options
, option
);
2953 zephyr_protocol_class_init(PurpleProtocolClass
*klass
)
2955 klass
->login
= zephyr_login
;
2956 klass
->close
= zephyr_close
;
2957 klass
->status_types
= zephyr_status_types
;
2958 klass
->list_icon
= zephyr_list_icon
;
2963 zephyr_protocol_client_iface_init(PurpleProtocolClientIface
*client_iface
)
2965 client_iface
->get_actions
= zephyr_get_actions
;
2966 client_iface
->normalize
= zephyr_normalize
;
2967 client_iface
->find_blist_chat
= zephyr_find_blist_chat
;
2972 zephyr_protocol_server_iface_init(PurpleProtocolServerIface
*server_iface
)
2974 server_iface
->get_info
= zephyr_zloc
;
2975 server_iface
->set_status
= zephyr_set_status
;
2977 server_iface
->set_info
= NULL
; /* XXX Location? */
2978 server_iface
->set_buddy_icon
= NULL
; /* XXX */
2983 zephyr_protocol_im_iface_init(PurpleProtocolIMIface
*im_iface
)
2985 im_iface
->send
= zephyr_send_im
;
2986 im_iface
->send_typing
= zephyr_send_typing
;
2991 zephyr_protocol_chat_iface_init(PurpleProtocolChatIface
*chat_iface
)
2993 chat_iface
->info
= zephyr_chat_info
;
2994 chat_iface
->join
= zephyr_join_chat
;
2995 chat_iface
->get_name
= zephyr_get_chat_name
;
2996 chat_iface
->leave
= zephyr_chat_leave
;
2997 chat_iface
->send
= zephyr_chat_send
;
2998 chat_iface
->set_topic
= zephyr_chat_set_topic
;
3000 chat_iface
->get_user_real_name
= NULL
; /* XXX */
3004 PURPLE_DEFINE_TYPE_EXTENDED(
3005 ZephyrProtocol
, zephyr_protocol
, PURPLE_TYPE_PROTOCOL
, 0,
3007 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE
,
3008 zephyr_protocol_client_iface_init
)
3010 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE
,
3011 zephyr_protocol_server_iface_init
)
3013 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE
,
3014 zephyr_protocol_im_iface_init
)
3016 PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE
,
3017 zephyr_protocol_chat_iface_init
)
3021 static PurplePluginInfo
*plugin_query(GError
**error
)
3023 return purple_plugin_info_new(
3024 "id", "prpl-zephyr",
3025 "name", "Zephyr Protocol",
3026 "version", DISPLAY_VERSION
,
3027 "category", N_("Protocol"),
3028 "summary", N_("Zephyr Protocol Plugin"),
3029 "description", N_("Zephyr Protocol Plugin"),
3030 "website", PURPLE_WEBSITE
,
3031 "abi-version", PURPLE_ABI_VERSION
,
3032 "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL
|
3033 PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD
,
3040 plugin_load(PurplePlugin
*plugin
, GError
**error
)
3042 zephyr_protocol_register_type(plugin
);
3044 my_protocol
= purple_protocols_add(ZEPHYR_TYPE_PROTOCOL
, error
);
3048 zephyr_register_slash_commands();
3055 plugin_unload(PurplePlugin
*plugin
, GError
**error
)
3057 zephyr_unregister_slash_commands();
3059 if (!purple_protocols_remove(my_protocol
, error
))
3066 PURPLE_PLUGIN_INIT(zephyr
, plugin_query
, plugin_load
, plugin_unload
);