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 "purpleaccountoption.h"
46 #define ZEPHYR_FALLBACK_CHARSET "ISO-8859-1"
48 /* these are deliberately high, since most people don't send multiple "PING"s */
49 #define ZEPHYR_TYPING_SEND_TIMEOUT 15
50 #define ZEPHYR_TYPING_RECV_TIMEOUT 10
51 #define ZEPHYR_FD_READ 0
52 #define ZEPHYR_FD_WRITE 1
54 static PurpleProtocol
*my_protocol
= NULL
;
55 static GSList
*cmds
= NULL
;
57 extern Code_t
ZGetLocations(ZLocations_t
*, int *);
58 extern Code_t
ZSetLocation(char *);
59 extern Code_t
ZUnsetLocation(void);
60 extern Code_t
ZGetSubscriptions(ZSubscription_t
*, int*);
61 extern char __Zephyr_realm
[];
62 typedef struct _zframe zframe
;
63 typedef struct _zephyr_triple zephyr_triple
;
64 typedef struct _zephyr_account zephyr_account
;
65 typedef struct _parse_tree parse_tree
;
68 PURPLE_ZEPHYR_NONE
, /* Non-kerberized ZEPH0.2 */
69 PURPLE_ZEPHYR_KRB4
, /* ZEPH0.2 w/ KRB4 support */
70 PURPLE_ZEPHYR_TZC
, /* tzc executable proxy */
71 PURPLE_ZEPHYR_INTERGALACTIC_KRB4
/* Kerberized ZEPH0.3 */
72 } zephyr_connection_type
;
74 struct _zephyr_account
{
75 PurpleAccount
* account
;
79 char* galaxy
; /* not yet useful */
80 char* krbtkfile
; /* not yet useful */
83 GList
*pending_zloc_names
;
87 char ourhost
[HOST_NAME_MAX
+ 1];
88 char ourhostcanon
[HOST_NAME_MAX
+ 1];
89 zephyr_connection_type connection_type
;
97 #define MAXCHILDREN 20
101 parse_tree
*children
[MAXCHILDREN
];
105 parse_tree null_parse_tree
= {
111 #define use_none(zephyr) ((zephyr->connection_type == PURPLE_ZEPHYR_NONE)?1:0)
112 #define use_krb4(zephyr) ((zephyr->connection_type == PURPLE_ZEPHYR_KRB4)?1:0)
113 #define use_tzc(zephyr) ((zephyr->connection_type == PURPLE_ZEPHYR_TZC)?1:0)
115 #define use_zeph02(zephyr) ( (zephyr->connection_type == PURPLE_ZEPHYR_NONE)?1: ((zephyr->connection_type == PURPLE_ZEPHYR_KRB4)?1:0))
117 /* struct I need for zephyr_to_html */
119 /* true for everything but @color, since inside the parens of that one is
124 /* }=1, ]=2, )=4, >=8 */
128 /* </i>, </font>, </b>, etc. */
130 /* text including the opening html thingie. */
135 struct _zframe
*enclosing
;
138 struct _zephyr_triple
{
147 #define z_call(func) if (func != ZERR_NONE)\
149 #define z_call_r(func) if (func != ZERR_NONE)\
152 #define z_call_s(func, err) if (func != ZERR_NONE) {\
153 purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, err);\
158 extern const char *username
;
161 static Code_t
zephyr_subscribe_to(zephyr_account
* zephyr
, char* class, char *instance
, char *recipient
, char* galaxy
) {
165 if (use_tzc(zephyr
)) {
166 /* ((tzcfodder . subscribe) ("class" "instance" "recipient")) */
167 gchar
*zsubstr
= g_strdup_printf("((tzcfodder . subscribe) (\"%s\" \"%s\" \"%s\"))\n",class,instance
,recipient
);
168 size_t len
= strlen(zsubstr
);
169 result
= write(zephyr
->totzc
[ZEPHYR_FD_WRITE
],zsubstr
,len
);
171 purple_debug_error("zephyr", "Unable to write a message: %s\n", g_strerror(errno
));
178 if (use_zeph02(zephyr
)) {
180 sub
.zsub_class
= class;
181 sub
.zsub_classinst
= instance
;
182 sub
.zsub_recipient
= recipient
;
183 ret_val
= ZSubscribeTo(&sub
,1,0);
189 char *local_zephyr_normalize(zephyr_account
* zephyr
,const char *);
190 static void zephyr_chat_set_topic(PurpleConnection
* gc
, int id
, const char *topic
);
191 char* zephyr_tzc_deescape_str(const char *message
);
193 static char *zephyr_strip_local_realm(zephyr_account
* zephyr
,const char* user
){
195 Takes in a username of the form username or username@realm
197 username, if there is no realm, or the realm is the local realm
199 username@realm if there is a realm and it is foreign
201 char *tmp
= g_strdup(user
);
202 char *at
= strchr(tmp
,'@');
203 if (at
&& !g_ascii_strcasecmp(at
+1,zephyr
->realm
)) {
204 /* We're passed in a username of the form user@users-realm */
207 tmp2
= g_strdup(tmp
);
212 /* We're passed in a username of the form user or user@foreign-realm */
217 /* this is so bad, and if Zephyr weren't so fucked up to begin with I
218 * wouldn't do this. but it is so i will. */
220 /* just for debugging */
221 static void handle_unknown(ZNotice_t
*notice
)
223 purple_debug_error("zephyr","z_packet: %s\n", notice
->z_packet
);
224 purple_debug_error("zephyr","z_version: %s\n", notice
->z_version
);
225 purple_debug_error("zephyr","z_kind: %d\n", (int)(notice
->z_kind
));
226 purple_debug_error("zephyr","z_class: %s\n", notice
->z_class
);
227 purple_debug_error("zephyr","z_class_inst: %s\n", notice
->z_class_inst
);
228 purple_debug_error("zephyr","z_opcode: %s\n", notice
->z_opcode
);
229 purple_debug_error("zephyr","z_sender: %s\n", notice
->z_sender
);
230 purple_debug_error("zephyr","z_recipient: %s\n", notice
->z_recipient
);
231 purple_debug_error("zephyr","z_message: %s\n", notice
->z_message
);
232 purple_debug_error("zephyr","z_message_len: %d\n", notice
->z_message_len
);
236 static zephyr_triple
*new_triple(zephyr_account
*zephyr
,const char *c
, const char *i
, const char *r
)
240 zt
= g_new0(zephyr_triple
, 1);
241 zt
->class = g_strdup(c
);
242 zt
->instance
= g_strdup(i
);
243 zt
->recipient
= g_strdup(r
);
244 zt
->name
= g_strdup_printf("%s,%s,%s", c
, i
?i
:"", r
?r
:"");
245 zt
->id
= ++(zephyr
->last_id
);
250 static void free_triple(zephyr_triple
* zt
)
253 g_free(zt
->instance
);
254 g_free(zt
->recipient
);
259 /* returns true if zt1 is a subset of zt2. This function is used to
260 determine whether a zephyr sent to zt1 should be placed in the chat
263 zt1 is a subset of zt2
264 iff. the classnames are identical ignoring case
265 AND. the instance names are identical (ignoring case), or zt2->instance is *.
266 AND. the recipient names are identical
269 static gboolean
triple_subset(zephyr_triple
* zt1
, zephyr_triple
* zt2
)
273 purple_debug_error("zephyr","zt2 doesn't exist\n");
277 purple_debug_error("zephyr","zt1 doesn't exist\n");
281 purple_debug_error("zephyr","zt1c doesn't exist\n");
284 if (!(zt1
->instance
)) {
285 purple_debug_error("zephyr","zt1i doesn't exist\n");
288 if (!(zt1
->recipient
)) {
289 purple_debug_error("zephyr","zt1r doesn't exist\n");
293 purple_debug_error("zephyr","zt2c doesn't exist\n");
296 if (!(zt2
->recipient
)) {
297 purple_debug_error("zephyr","zt2r doesn't exist\n");
300 if (!(zt2
->instance
)) {
301 purple_debug_error("zephyr","zt2i doesn't exist\n");
305 if (g_ascii_strcasecmp(zt2
->class, zt1
->class)) {
308 if (g_ascii_strcasecmp(zt2
->instance
, zt1
->instance
) && g_ascii_strcasecmp(zt2
->instance
, "*")) {
311 if (g_ascii_strcasecmp(zt2
->recipient
, zt1
->recipient
)) {
314 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
);
318 static zephyr_triple
*find_sub_by_triple(zephyr_account
*zephyr
,zephyr_triple
* zt
)
320 zephyr_triple
*curr_t
;
321 GSList
*curr
= zephyr
->subscrips
;
325 if (triple_subset(zt
, curr_t
))
332 static zephyr_triple
*find_sub_by_id(zephyr_account
*zephyr
,int id
)
335 GSList
*curr
= zephyr
->subscrips
;
347 Converts strings to utf-8 if necessary using user specified encoding
350 static gchar
*zephyr_recv_convert(PurpleConnection
*gc
, gchar
*string
)
354 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
355 if (g_utf8_validate(string
, -1, NULL
)) {
356 return g_strdup(string
);
358 utf8
= g_convert(string
, -1, "UTF-8", zephyr
->encoding
, NULL
, NULL
, &err
);
360 purple_debug_error("zephyr", "recv conversion error: %s\n", err
->message
);
361 utf8
= g_strdup(_("(There was an error converting this message. Check the 'Encoding' option in the Account Editor)"));
369 /* This parses HTML formatting (put out by one of the gtkimhtml widgets
370 And converts it to zephyr formatting.
371 It currently deals properly with <b>, <br>, <i>, <font face=...>, <font color=...>,
372 It ignores <font back=...>
374 <font size = "1 or 2" -> @small
377 <a href is dealt with by outputting "description <link>" or just "description" as appropriate
380 static char *html_to_zephyr(const char *message
)
382 zframe
*frames
, *new_f
;
385 if (*message
== '\0')
388 frames
= g_new(zframe
, 1);
389 frames
->text
= g_string_new("");
391 frames
->is_href
= FALSE
;
392 frames
->enclosing
= NULL
;
393 frames
->closing
= NULL
;
395 frames
->has_closer
= FALSE
;
396 frames
->closer_mask
= 15;
398 purple_debug_info("zephyr","html received %s\n",message
);
400 if (frames
->closing
&& !g_ascii_strncasecmp(message
, frames
->closing
, strlen(frames
->closing
))) {
402 message
+= strlen(frames
->closing
);
404 frames
= frames
->enclosing
;
405 if (popped
->is_href
) {
406 frames
->href
= popped
->text
;
408 g_string_append(frames
->text
, popped
->env
);
409 if (popped
->has_closer
) {
410 g_string_append_c(frames
->text
,
411 (popped
->closer_mask
& 1) ? '{' :
412 (popped
->closer_mask
& 2) ? '[' :
413 (popped
->closer_mask
& 4) ? '(' :
416 g_string_append(frames
->text
, popped
->text
->str
);
419 int text_len
= strlen(popped
->text
->str
), href_len
= strlen(popped
->href
->str
);
420 if (!((text_len
== href_len
&& !strncmp(popped
->href
->str
, popped
->text
->str
, text_len
)) ||
421 (7 + text_len
== href_len
&& !strncmp(popped
->href
->str
, "http://", 7) &&
422 !strncmp(popped
->href
->str
+ 7, popped
->text
->str
, text_len
)) ||
423 (7 + text_len
== href_len
&& !strncmp(popped
->href
->str
, "mailto:", 7) &&
424 !strncmp(popped
->href
->str
+ 7, popped
->text
->str
, text_len
)))) {
425 g_string_append(frames
->text
, " <");
426 g_string_append(frames
->text
, popped
->href
->str
);
427 if (popped
->closer_mask
& ~8) {
428 g_string_append_c(frames
->text
, '>');
429 popped
->closer_mask
&= ~8;
431 g_string_append(frames
->text
, "@{>}");
434 g_string_free(popped
->href
, TRUE
);
436 if (popped
->has_closer
) {
437 g_string_append_c(frames
->text
,
438 (popped
->closer_mask
& 1) ? '}' :
439 (popped
->closer_mask
& 2) ? ']' :
440 (popped
->closer_mask
& 4) ? ')' :
443 if (!popped
->has_closer
)
444 frames
->closer_mask
= popped
->closer_mask
;
445 g_string_free(popped
->text
, TRUE
);
448 } else if (*message
== '<') {
449 if (!g_ascii_strncasecmp(message
+ 1, "i>", 2)) {
450 new_f
= g_new(zframe
, 1);
451 new_f
->enclosing
= frames
;
452 new_f
->text
= g_string_new("");
454 new_f
->is_href
= FALSE
;
455 new_f
->closing
= "</i>";
457 new_f
->has_closer
= TRUE
;
458 new_f
->closer_mask
= 15;
461 } else if (!g_ascii_strncasecmp(message
+ 1, "b>", 2)) {
462 new_f
= g_new(zframe
, 1);
463 new_f
->enclosing
= frames
;
464 new_f
->text
= g_string_new("");
466 new_f
->is_href
= FALSE
;
467 new_f
->closing
= "</b>";
469 new_f
->has_closer
= TRUE
;
470 new_f
->closer_mask
= 15;
473 } else if (!g_ascii_strncasecmp(message
+ 1, "br>", 3)) {
474 g_string_append_c(frames
->text
, '\n');
476 } else if (!g_ascii_strncasecmp(message
+ 1, "a href=\"", 8)) {
478 new_f
= g_new(zframe
, 1);
479 new_f
->enclosing
= frames
;
480 new_f
->text
= g_string_new("");
482 new_f
->is_href
= FALSE
;
483 new_f
->closing
= "</a>";
485 new_f
->has_closer
= FALSE
;
486 new_f
->closer_mask
= frames
->closer_mask
;
488 new_f
= g_new(zframe
, 1);
489 new_f
->enclosing
= frames
;
490 new_f
->text
= g_string_new("");
492 new_f
->is_href
= TRUE
;
493 new_f
->closing
= "\">";
494 new_f
->has_closer
= FALSE
;
495 new_f
->closer_mask
= frames
->closer_mask
;
497 } else if (!g_ascii_strncasecmp(message
+ 1, "font", 4)) {
498 new_f
= g_new(zframe
, 1);
499 new_f
->enclosing
= frames
;
500 new_f
->text
= g_string_new("");
502 new_f
->is_href
= FALSE
;
503 new_f
->closing
= "</font>";
504 new_f
->has_closer
= TRUE
;
505 new_f
->closer_mask
= 15;
507 while (*message
== ' ')
509 if (!g_ascii_strncasecmp(message
, "color=\"", 7)) {
513 new_f
= g_new(zframe
, 1);
514 new_f
->enclosing
= frames
;
515 new_f
->env
= "@color";
516 new_f
->text
= g_string_new("");
518 new_f
->is_href
= FALSE
;
519 new_f
->closing
= "\">";
520 new_f
->has_closer
= TRUE
;
521 new_f
->closer_mask
= 15;
522 } else if (!g_ascii_strncasecmp(message
, "face=\"", 6)) {
526 new_f
= g_new(zframe
, 1);
527 new_f
->enclosing
= frames
;
528 new_f
->env
= "@font";
529 new_f
->text
= g_string_new("");
531 new_f
->is_href
= FALSE
;
532 new_f
->closing
= "\">";
533 new_f
->has_closer
= TRUE
;
534 new_f
->closer_mask
= 15;
535 } else if (!g_ascii_strncasecmp(message
, "size=\"", 6)) {
537 if ((*message
== '1') || (*message
== '2')) {
538 new_f
->env
= "@small";
539 } else if ((*message
== '3')
540 || (*message
== '4')) {
541 new_f
->env
= "@medium";
542 } else if ((*message
== '5')
544 || (*message
== '7')) {
545 new_f
->env
= "@large";
548 new_f
->has_closer
= FALSE
;
549 new_f
->closer_mask
= frames
->closer_mask
;
553 /* Drop all unrecognized/misparsed font tags */
555 new_f
->has_closer
= FALSE
;
556 new_f
->closer_mask
= frames
->closer_mask
;
557 while (g_ascii_strncasecmp(message
, "\">", 2) != 0) {
560 if (*message
!= '\0')
565 /* Catch all for all unrecognized/misparsed <foo> tage */
566 g_string_append_c(frames
->text
, *message
++);
568 } else if (*message
== '@') {
569 g_string_append(frames
->text
, "@@");
571 } else if (*message
== '}') {
572 if (frames
->closer_mask
& ~1) {
573 frames
->closer_mask
&= ~1;
574 g_string_append_c(frames
->text
, *message
++);
576 g_string_append(frames
->text
, "@[}]");
579 } else if (*message
== ']') {
580 if (frames
->closer_mask
& ~2) {
581 frames
->closer_mask
&= ~2;
582 g_string_append_c(frames
->text
, *message
++);
584 g_string_append(frames
->text
, "@{]}");
587 } else if (*message
== ')') {
588 if (frames
->closer_mask
& ~4) {
589 frames
->closer_mask
&= ~4;
590 g_string_append_c(frames
->text
, *message
++);
592 g_string_append(frames
->text
, "@{)}");
595 } else if (!g_ascii_strncasecmp(message
, ">", 4)) {
596 if (frames
->closer_mask
& ~8) {
597 frames
->closer_mask
&= ~8;
598 g_string_append_c(frames
->text
, *message
++);
600 g_string_append(frames
->text
, "@{>}");
604 g_string_append_c(frames
->text
, *message
++);
607 ret
= frames
->text
->str
;
608 g_string_free(frames
->text
, FALSE
);
610 purple_debug_info("zephyr","zephyr outputted %s\n",ret
);
614 /* this parses zephyr formatting and converts it to html. For example, if
615 * you pass in "@{@color(blue)@i(hello)}" you should get out
616 * "<font color=blue><i>hello</i></font>". */
617 static char *zephyr_to_html(const char *message
)
619 zframe
*frames
, *curr
;
622 frames
= g_new(zframe
, 1);
623 frames
->text
= g_string_new("");
624 frames
->enclosing
= NULL
;
625 frames
->closing
= "";
626 frames
->has_closer
= FALSE
;
627 frames
->closer
= NULL
;
630 if (*message
== '@' && message
[1] == '@') {
631 g_string_append(frames
->text
, "@");
633 } else if (*message
== '@') {
635 for (end
= 1; message
[end
] && (isalnum(message
[end
]) || message
[end
] == '_'); end
++);
637 (message
[end
] == '{' || message
[end
] == '[' || message
[end
] == '(' ||
638 !g_ascii_strncasecmp(message
+ end
, "<", 4))) {
641 buf
= g_new0(char, end
);
642 g_snprintf(buf
, end
, "%s", message
+ 1);
644 new_f
= g_new(zframe
, 1);
645 new_f
->enclosing
= frames
;
646 new_f
->has_closer
= TRUE
;
647 new_f
->closer
= (*message
== '{' ? "}" :
648 *message
== '[' ? "]" :
649 *message
== '(' ? ")" :
651 message
+= (*message
== '&' ? 4 : 1);
652 if (!g_ascii_strcasecmp(buf
, "italic") || !g_ascii_strcasecmp(buf
, "i")) {
653 new_f
->text
= g_string_new("<i>");
654 new_f
->closing
= "</i>";
655 } else if (!g_ascii_strcasecmp(buf
, "small")) {
656 new_f
->text
= g_string_new("<font size=\"1\">");
657 new_f
->closing
= "</font>";
658 } else if (!g_ascii_strcasecmp(buf
, "medium")) {
659 new_f
->text
= g_string_new("<font size=\"3\">");
660 new_f
->closing
= "</font>";
661 } else if (!g_ascii_strcasecmp(buf
, "large")) {
662 new_f
->text
= g_string_new("<font size=\"7\">");
663 new_f
->closing
= "</font>";
664 } else if (!g_ascii_strcasecmp(buf
, "bold")
665 || !g_ascii_strcasecmp(buf
, "b")) {
666 new_f
->text
= g_string_new("<b>");
667 new_f
->closing
= "</b>";
668 } else if (!g_ascii_strcasecmp(buf
, "font")) {
670 extra_f
= g_new(zframe
, 1);
671 extra_f
->enclosing
= frames
;
672 new_f
->enclosing
= extra_f
;
673 extra_f
->text
= g_string_new("");
674 extra_f
->has_closer
= FALSE
;
675 extra_f
->closer
= frames
->closer
;
676 extra_f
->closing
= "</font>";
677 new_f
->text
= g_string_new("<font face=\"");
678 new_f
->closing
= "\">";
679 } else if (!g_ascii_strcasecmp(buf
, "color")) {
681 extra_f
= g_new(zframe
, 1);
682 extra_f
->enclosing
= frames
;
683 new_f
->enclosing
= extra_f
;
684 extra_f
->text
= g_string_new("");
685 extra_f
->has_closer
= FALSE
;
686 extra_f
->closer
= frames
->closer
;
687 extra_f
->closing
= "</font>";
688 new_f
->text
= g_string_new("<font color=\"");
689 new_f
->closing
= "\">";
691 new_f
->text
= g_string_new("");
697 /* Not a formatting tag, add the character as normal. */
698 g_string_append_c(frames
->text
, *message
++);
700 } else if (frames
->closer
&& !g_ascii_strncasecmp(message
, frames
->closer
, strlen(frames
->closer
))) {
702 gboolean last_had_closer
;
704 message
+= strlen(frames
->closer
);
705 if (frames
->enclosing
) {
708 frames
= frames
->enclosing
;
709 g_string_append(frames
->text
, popped
->text
->str
);
710 g_string_append(frames
->text
, popped
->closing
);
711 g_string_free(popped
->text
, TRUE
);
712 last_had_closer
= popped
->has_closer
;
714 } while (frames
->enclosing
&& !last_had_closer
);
716 g_string_append_c(frames
->text
, *message
);
718 } else if (*message
== '\n') {
719 g_string_append(frames
->text
, "<br>");
722 g_string_append_c(frames
->text
, *message
++);
725 /* go through all the stuff that they didn't close */
726 while (frames
->enclosing
) {
728 g_string_append(frames
->enclosing
->text
, frames
->text
->str
);
729 g_string_append(frames
->enclosing
->text
, frames
->closing
);
730 g_string_free(frames
->text
, TRUE
);
731 frames
= frames
->enclosing
;
734 ret
= frames
->text
->str
;
735 g_string_free(frames
->text
, FALSE
);
740 static gboolean
pending_zloc(zephyr_account
*zephyr
, const char *who
)
743 char* normalized_who
= local_zephyr_normalize(zephyr
,who
);
745 curr
= g_list_find_custom(zephyr
->pending_zloc_names
, normalized_who
, (GCompareFunc
)g_ascii_strcasecmp
);
746 g_free(normalized_who
);
750 g_free((char *)curr
->data
);
751 zephyr
->pending_zloc_names
= g_list_delete_link(zephyr
->pending_zloc_names
, curr
);
755 /* Called when the server notifies us a message couldn't get sent */
757 static void message_failed(PurpleConnection
*gc
, ZNotice_t
*notice
, struct sockaddr_in from
)
759 if (g_ascii_strcasecmp(notice
->z_class
, "message")) {
760 gchar
* chat_failed
= g_strdup_printf(
761 _("Unable to send to chat %s,%s,%s"),
762 notice
->z_class
, notice
->z_class_inst
,
763 notice
->z_recipient
);
764 purple_notify_error(gc
,"",chat_failed
,NULL
,
765 purple_request_cpar_from_connection(gc
));
768 purple_notify_error(gc
, notice
->z_recipient
,
769 _("User is offline"), NULL
,
770 purple_request_cpar_from_connection(gc
));
774 static void handle_message(PurpleConnection
*gc
, ZNotice_t
*notice_p
)
777 zephyr_account
* zephyr
= purple_connection_get_protocol_data(gc
);
779 memcpy(¬ice
, notice_p
, sizeof(notice
)); /* TODO - use pointer? */
781 if (!g_ascii_strcasecmp(notice
.z_class
, LOGIN_CLASS
)) {
782 /* well, we'll be updating in 20 seconds anyway, might as well ignore this. */
783 } else if (!g_ascii_strcasecmp(notice
.z_class
, LOCATE_CLASS
)) {
784 if (!g_ascii_strcasecmp(notice
.z_opcode
, LOCATE_LOCATE
)) {
790 /* XXX add real error reporting */
791 if (ZParseLocations(¬ice
, NULL
, &nlocs
, &user
) != ZERR_NONE
)
794 if ((b
= purple_blist_find_buddy(purple_connection_get_account(gc
), user
)) == NULL
) {
795 char* stripped_user
= zephyr_strip_local_realm(zephyr
,user
);
796 b
= purple_blist_find_buddy(purple_connection_get_account(gc
),stripped_user
);
797 g_free(stripped_user
);
800 bname
= b
? purple_buddy_get_name(b
) : NULL
;
801 if ((b
&& pending_zloc(zephyr
,bname
)) || pending_zloc(zephyr
,user
)) {
804 PurpleNotifyUserInfo
*user_info
= purple_notify_user_info_new();
808 /* TODO: Check whether it's correct to call add_pair_html,
809 or if we should be using add_pair_plaintext */
810 purple_notify_user_info_add_pair_html(user_info
, _("User"), (b
? bname
: user
));
811 balias
= purple_buddy_get_local_alias(b
);
813 purple_notify_user_info_add_pair_plaintext(user_info
, _("Alias"), balias
);
816 purple_notify_user_info_add_pair_plaintext(user_info
, NULL
, _("Hidden or not logged-in"));
818 for (; nlocs
> 0; nlocs
--) {
819 /* XXX add real error reporting */
821 ZGetLocations(&locs
, &one
);
822 /* TODO: Need to escape locs.host and locs.time? */
823 tmp
= g_strdup_printf(_("<br>At %s since %s"), locs
.host
, locs
.time
);
824 purple_notify_user_info_add_pair_html(user_info
, _("Location"), tmp
);
827 purple_notify_userinfo(gc
, (b
? bname
: user
),
828 user_info
, NULL
, NULL
);
829 purple_notify_user_info_destroy(user_info
);
832 purple_protocol_got_user_status(purple_connection_get_account(gc
), b
? bname
: user
, "available", NULL
);
834 purple_protocol_got_user_status(purple_connection_get_account(gc
), b
? bname
: user
, "offline", NULL
);
840 char *buf
, *buf2
, *buf3
;
842 PurpleChatConversation
*gcc
;
843 char *ptr
= (char *) notice
.z_message
+ (strlen(notice
.z_message
) + 1);
845 char *stripped_sender
;
846 int signature_length
= strlen(notice
.z_message
);
847 PurpleMessageFlags flags
= 0;
850 /* Need to deal with 0 length messages to handle typing notification (OPCODE) ping messages */
851 /* One field zephyrs would have caused purple to crash */
852 if ( (notice
.z_message_len
== 0) || (signature_length
>= notice
.z_message_len
- 1)) {
854 purple_debug_info("zephyr","message_size %d %d %d\n",len
,notice
.z_message_len
,signature_length
);
858 len
= notice
.z_message_len
- ( signature_length
+1);
859 purple_debug_info("zephyr","message_size %d %d %d\n",len
,notice
.z_message_len
,signature_length
);
860 buf
= g_malloc(len
+ 1);
861 g_snprintf(buf
, len
+ 1, "%s", ptr
);
863 tmpescape
= g_markup_escape_text(buf
, -1);
865 buf2
= zephyr_to_html(tmpescape
);
866 buf3
= zephyr_recv_convert(gc
, buf2
);
871 stripped_sender
= zephyr_strip_local_realm(zephyr
,notice
.z_sender
);
873 if (!g_ascii_strcasecmp(notice
.z_class
, "MESSAGE") && !g_ascii_strcasecmp(notice
.z_class_inst
, "PERSONAL")
874 && !g_ascii_strcasecmp(notice
.z_recipient
,zephyr
->username
)) {
875 if (!g_ascii_strcasecmp(notice
.z_message
, "Automated reply:"))
876 flags
|= PURPLE_MESSAGE_AUTO_RESP
;
878 if (!g_ascii_strcasecmp(notice
.z_opcode
,"PING"))
879 purple_serv_got_typing(gc
,stripped_sender
,ZEPHYR_TYPING_RECV_TIMEOUT
, PURPLE_IM_TYPING
);
881 purple_serv_got_im(gc
, stripped_sender
, buf3
, flags
, time(NULL
));
884 zephyr_triple
*zt1
, *zt2
;
885 gchar
*send_inst_utf8
;
886 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
887 zt1
= new_triple(zephyr
,notice
.z_class
, notice
.z_class_inst
, notice
.z_recipient
);
888 zt2
= find_sub_by_triple(zephyr
,zt1
);
890 /* This is a server supplied subscription */
891 zephyr
->subscrips
= g_slist_append(zephyr
->subscrips
, new_triple(zephyr
,zt1
->class,zt1
->instance
,zt1
->recipient
));
892 zt2
= find_sub_by_triple(zephyr
,zt1
);
897 purple_serv_got_joined_chat(gc
, zt2
->id
, zt2
->name
);
898 zephyr_chat_set_topic(gc
,zt2
->id
,notice
.z_class_inst
);
901 if (!g_ascii_strcasecmp(notice
.z_class_inst
,"PERSONAL"))
902 send_inst_utf8
= g_strdup(stripped_sender
);
904 send_inst
= g_strdup_printf("[%s] %s",notice
.z_class_inst
,stripped_sender
);
905 send_inst_utf8
= zephyr_recv_convert(gc
,send_inst
);
907 if (!send_inst_utf8
) {
908 purple_debug_error("zephyr","Failed to convert instance for sender %s.\n", stripped_sender
);
909 send_inst_utf8
= g_strdup(stripped_sender
);
913 gcc
= purple_conversations_find_chat_with_account(
914 zt2
->name
, purple_connection_get_account(gc
));
915 #ifndef INET_ADDRSTRLEN
916 #define INET_ADDRSTRLEN 16
918 if (!purple_chat_conversation_has_user(gcc
, stripped_sender
)) {
919 gchar ipaddr
[INET_ADDRSTRLEN
];
920 #ifdef HAVE_INET_NTOP
921 inet_ntop(AF_INET
, ¬ice
.z_sender_addr
.s_addr
, ipaddr
, sizeof(ipaddr
));
923 memcpy(ipaddr
,inet_ntoa(notice
.z_sender_addr
),sizeof(ipaddr
));
925 purple_chat_conversation_add_user(gcc
, stripped_sender
, ipaddr
, PURPLE_CHAT_USER_NONE
, TRUE
);
927 purple_serv_got_chat_in(gc
, zt2
->id
, send_inst_utf8
,
928 PURPLE_MESSAGE_RECV
, buf3
, time(NULL
));
929 g_free(send_inst_utf8
);
933 g_free(stripped_sender
);
938 static int free_parse_tree(parse_tree
* tree
) {
944 for(i
=0;i
<tree
->num_children
;i
++){
945 if (tree
->children
[i
]) {
946 free_parse_tree(tree
->children
[i
]);
949 if (tree
!= &null_parse_tree
) {
950 g_free(tree
->contents
);
957 static parse_tree
*tree_child(parse_tree
* tree
,int index
) {
958 if (index
< tree
->num_children
) {
959 return tree
->children
[index
];
961 return &null_parse_tree
;
965 static parse_tree
*find_node(parse_tree
* ptree
,gchar
* key
)
970 return &null_parse_tree
;
972 tc
= tree_child(ptree
,0)->contents
;
974 /* g_strcasecmp() is deprecated. What is the encoding here??? */
975 if (ptree
->num_children
> 0 && tc
&& !g_ascii_strcasecmp(tc
, key
)) {
978 parse_tree
*result
= &null_parse_tree
;
980 for(i
= 0; i
< ptree
->num_children
; i
++) {
981 result
= find_node(ptree
->children
[i
],key
);
982 if(result
!= &null_parse_tree
) {
990 static parse_tree
*parse_buffer(gchar
* source
, gboolean do_parse
) {
992 parse_tree
*ptree
= g_new0(parse_tree
,1);
993 ptree
->contents
= NULL
;
994 ptree
->num_children
=0;
997 while(p
< strlen(source
)) {
1001 /* Eat white space: */
1002 if(g_ascii_isspace(source
[p
]) || source
[p
] == '\001') {
1008 if(source
[p
] == ';') {
1009 while(source
[p
] != '\n' && p
< strlen(source
)) {
1015 if(source
[p
] == '(') {
1017 gboolean in_quote
= FALSE
;
1018 gboolean escape_next
= FALSE
;
1021 while(!(source
[end
] == ')' && nesting
== 0 && !in_quote
) && end
< strlen(source
)) {
1023 if(source
[end
] == '\\') {
1027 if(source
[end
] == '(') {
1030 if(source
[end
] == ')') {
1034 if(source
[end
] == '"') {
1035 in_quote
= !in_quote
;
1038 escape_next
= FALSE
;
1046 if(source
[p
] == '"') {
1055 while(source
[end
] != end_char
&& end
< strlen(source
)) {
1056 if(source
[end
] == '\\')
1061 newstr
= g_new0(gchar
, end
+1-p
);
1062 strncpy(newstr
,source
+p
,end
-p
);
1063 if (ptree
->num_children
< MAXCHILDREN
) {
1064 /* In case we surpass maxchildren, ignore this */
1065 ptree
->children
[ptree
->num_children
++] = parse_buffer( newstr
, do_parse
);
1067 purple_debug_error("zephyr","too many children in tzc output. skipping\n");
1074 /* XXX does this have to be strdup'd */
1075 ptree
->contents
= g_strdup(source
);
1080 static parse_tree
*read_from_tzc(zephyr_account
* zephyr
){
1084 char *buf
= (char *)calloc(bufsize
, 1);
1087 parse_tree
*incoming_msg
;
1090 FD_SET(zephyr
->fromtzc
[ZEPHYR_FD_READ
], &rfds
);
1095 while (select(zephyr
->fromtzc
[ZEPHYR_FD_READ
] + 1, &rfds
, NULL
, NULL
, &tv
)) {
1097 if (read(zephyr
->fromtzc
[ZEPHYR_FD_READ
], bufcur
, 1) != 1) {
1098 purple_debug_error("zephyr", "couldn't read\n");
1099 purple_connection_error(purple_account_get_connection(zephyr
->account
), PURPLE_CONNECTION_ERROR_NETWORK_ERROR
, "couldn't read");
1104 if ((bufcur
- buf
) > (bufsize
- 1)) {
1105 if ((buf
= realloc(buf
, bufsize
* 2)) == NULL
) {
1106 purple_debug_error("zephyr","Ran out of memory\n");
1109 bufcur
= buf
+ bufsize
;
1117 incoming_msg
= parse_buffer(buf
,TRUE
);
1120 return incoming_msg
;
1123 static gint
check_notify_tzc(gpointer data
)
1125 PurpleConnection
*gc
= (PurpleConnection
*)data
;
1126 zephyr_account
* zephyr
= purple_connection_get_protocol_data(gc
);
1127 parse_tree
*newparsetree
= read_from_tzc(zephyr
);
1128 if (newparsetree
!= NULL
) {
1130 if ( (spewtype
= tree_child(find_node(newparsetree
,"tzcspew"),2)->contents
) ) {
1131 if (!g_ascii_strncasecmp(spewtype
,"message",7)) {
1133 parse_tree
*msgnode
= tree_child(find_node(newparsetree
,"message"),2);
1134 parse_tree
*bodynode
= tree_child(msgnode
,1);
1135 /* char *zsig = g_strdup(" "); */ /* purple doesn't care about zsigs */
1136 char *msg
= zephyr_tzc_deescape_str(bodynode
->contents
);
1137 size_t bufsize
= strlen(msg
) + 3;
1138 char *buf
= g_new0(char,bufsize
);
1139 g_snprintf(buf
,1+strlen(msg
)+2," %c%s",'\0',msg
);
1140 memset((char *)¬ice
, 0, sizeof(notice
));
1141 notice
.z_kind
= ACKED
;
1143 notice
.z_opcode
= tree_child(find_node(newparsetree
,"opcode"),2)->contents
;
1144 notice
.z_class
= zephyr_tzc_deescape_str(tree_child(find_node(newparsetree
,"class"),2)->contents
);
1145 notice
.z_class_inst
= tree_child(find_node(newparsetree
,"instance"),2)->contents
;
1146 notice
.z_recipient
= local_zephyr_normalize(zephyr
,tree_child(find_node(newparsetree
,"recipient"),2)->contents
);
1147 notice
.z_sender
= local_zephyr_normalize(zephyr
,tree_child(find_node(newparsetree
,"sender"),2)->contents
);
1148 notice
.z_default_format
= "Class $class, Instance $instance:\n" "To: @bold($recipient) at $time $date\n" "From: @bold($1) <$sender>\n\n$2";
1149 notice
.z_message_len
= strlen(msg
) + 3;
1150 notice
.z_message
= buf
;
1151 handle_message(gc
, ¬ice
);
1155 /* free_parse_tree(msgnode);
1156 free_parse_tree(bodynode);
1162 else if (!g_ascii_strncasecmp(spewtype
,"zlocation",9)) {
1163 /* check_loc or zephyr_zloc respectively */
1169 parse_tree
*locations
;
1171 user
= tree_child(find_node(newparsetree
,"user"),2)->contents
;
1173 if ((b
= purple_blist_find_buddy(purple_connection_get_account(gc
), user
)) == NULL
) {
1174 gchar
*stripped_user
= zephyr_strip_local_realm(zephyr
,user
);
1175 b
= purple_blist_find_buddy(purple_connection_get_account(gc
), stripped_user
);
1176 g_free(stripped_user
);
1178 locations
= find_node(newparsetree
,"locations");
1179 locval
= tree_child(tree_child(tree_child(tree_child(locations
,2),0),0),2)->contents
;
1181 if (!locval
|| !g_ascii_strcasecmp(locval
," ") || !*locval
) {
1187 bname
= b
? purple_buddy_get_name(b
) : NULL
;
1188 if ((b
&& pending_zloc(zephyr
,bname
)) || pending_zloc(zephyr
,user
) || pending_zloc(zephyr
,local_zephyr_normalize(zephyr
,user
))){
1189 PurpleNotifyUserInfo
*user_info
= purple_notify_user_info_new();
1193 /* TODO: Check whether it's correct to call add_pair_html,
1194 or if we should be using add_pair_plaintext */
1195 purple_notify_user_info_add_pair_html(user_info
, _("User"), (b
? bname
: user
));
1197 balias
= b
? purple_buddy_get_local_alias(b
) : NULL
;
1199 purple_notify_user_info_add_pair_plaintext(user_info
, _("Alias"), balias
);
1202 purple_notify_user_info_add_pair_plaintext(user_info
, NULL
, _("Hidden or not logged-in"));
1204 /* TODO: Need to escape the two strings that make up tmp? */
1205 tmp
= g_strdup_printf(_("<br>At %s since %s"),
1206 tree_child(tree_child(tree_child(tree_child(locations
,2),0),0),2)->contents
,
1207 tree_child(tree_child(tree_child(tree_child(locations
,2),0),2),2)->contents
);
1208 purple_notify_user_info_add_pair_html(user_info
, _("Location"), tmp
);
1212 purple_notify_userinfo(gc
, b
? bname
: user
,
1213 user_info
, NULL
, NULL
);
1214 purple_notify_user_info_destroy(user_info
);
1217 purple_protocol_got_user_status(purple_connection_get_account(gc
), b
? bname
: user
, "available", NULL
);
1219 purple_protocol_got_user_status(purple_connection_get_account(gc
), b
? bname
: user
, "offline", NULL
);
1222 else if (!g_ascii_strncasecmp(spewtype
,"subscribed",10)) {
1224 else if (!g_ascii_strncasecmp(spewtype
,"start",5)) {
1226 else if (!g_ascii_strncasecmp(spewtype
,"error",5)) {
1234 free_parse_tree(newparsetree
);
1238 static gint
check_notify_zeph02(gpointer data
)
1240 /* XXX add real error reporting */
1241 PurpleConnection
*gc
= (PurpleConnection
*) data
;
1242 while (ZPending()) {
1244 struct sockaddr_in from
;
1245 /* XXX add real error reporting */
1247 z_call_r(ZReceiveNotice(¬ice
, &from
));
1249 switch (notice
.z_kind
) {
1253 handle_message(gc
, ¬ice
);
1256 if (!(g_ascii_strcasecmp(notice
.z_message
, ZSRVACK_NOTSENT
))) {
1257 message_failed(gc
, ¬ice
, from
);
1261 purple_debug_error("zephyr", "Client ack received\n");
1262 handle_unknown(¬ice
); /* XXX: is it really unknown? */
1265 /* we'll just ignore things for now */
1266 handle_unknown(¬ice
);
1267 purple_debug_error("zephyr", "Unhandled notice.\n");
1270 /* XXX add real error reporting */
1271 ZFreeNotice(¬ice
);
1279 static gint
check_loc(gpointer data
)
1282 ZLocations_t locations
;
1283 PurpleConnection
*gc
= data
;
1284 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
1285 PurpleAccount
*account
= purple_connection_get_account(gc
);
1289 for (buddies
= purple_blist_find_buddies(account
, NULL
); buddies
;
1290 buddies
= g_slist_delete_link(buddies
, buddies
)) {
1291 PurpleBuddy
*b
= buddies
->data
;
1293 const char *bname
= purple_buddy_get_name(b
);
1294 chk
= local_zephyr_normalize(bname
);
1295 ZLocateUser(chk
,&numlocs
, ZAUTH
);
1298 for(i
=0;i
<numlocs
;i
++) {
1299 ZGetLocations(&locations
,&one
);
1300 serv_got_update(zgc
,bname
,1,0,0,0,0);
1310 static gint
check_loc(gpointer data
)
1313 ZAsyncLocateData_t ald
;
1314 PurpleConnection
*gc
= (PurpleConnection
*)data
;
1315 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
1316 PurpleAccount
*account
= purple_connection_get_account(gc
);
1318 if (use_zeph02(zephyr
)) {
1320 memset(&(ald
.uid
), 0, sizeof(ZUnique_Id_t
));
1324 for (buddies
= purple_blist_find_buddies(account
, NULL
); buddies
;
1325 buddies
= g_slist_delete_link(buddies
, buddies
)) {
1326 PurpleBuddy
*b
= buddies
->data
;
1329 const char *name
= purple_buddy_get_name(b
);
1331 chk
= local_zephyr_normalize(zephyr
,name
);
1332 purple_debug_info("zephyr","chk: %s b->name %s\n",chk
,name
);
1333 /* XXX add real error reporting */
1334 /* doesn't matter if this fails or not; we'll just move on to the next one */
1335 if (use_zeph02(zephyr
)) {
1339 ZLocateUser(chk
,&numlocs
,ZAUTH
);
1342 for(i
=0;i
<numlocs
;i
++) {
1343 ZGetLocations(&locations
,&one
);
1345 purple_protocol_got_user_status(account
,name
,"available",NULL
);
1347 purple_protocol_got_user_status(account
,name
,"offline",NULL
);
1351 ZRequestLocations(chk
, &ald
, UNACKED
, ZAUTH
);
1353 g_free(ald
.version
);
1356 if (use_tzc(zephyr
)) {
1357 gchar
*zlocstr
= g_strdup_printf("((tzcfodder . zlocate) \"%s\")\n",chk
);
1358 size_t len
= strlen(zlocstr
);
1359 size_t result
= write(zephyr
->totzc
[ZEPHYR_FD_WRITE
],zlocstr
,len
);
1360 if (result
!= len
) {
1361 purple_debug_error("zephyr", "Unable to write a message: %s\n", g_strerror(errno
));
1372 static const gchar
*
1373 get_exposure_level(void)
1375 /* XXX add real error reporting */
1376 const gchar
*exposure
= ZGetVariable("exposure");
1379 return EXPOSE_REALMVIS
;
1380 if (!g_ascii_strcasecmp(exposure
, EXPOSE_NONE
))
1382 if (!g_ascii_strcasecmp(exposure
, EXPOSE_OPSTAFF
))
1383 return EXPOSE_OPSTAFF
;
1384 if (!g_ascii_strcasecmp(exposure
, EXPOSE_REALMANN
))
1385 return EXPOSE_REALMANN
;
1386 if (!g_ascii_strcasecmp(exposure
, EXPOSE_NETVIS
))
1387 return EXPOSE_NETVIS
;
1388 if (!g_ascii_strcasecmp(exposure
, EXPOSE_NETANN
))
1389 return EXPOSE_NETANN
;
1390 return EXPOSE_REALMVIS
;
1393 static void strip_comments(char *str
)
1395 char *tmp
= strchr(str
, '#');
1403 static void zephyr_inithosts(zephyr_account
*zephyr
)
1405 /* XXX This code may not be Win32 clean */
1406 struct hostent
*hent
;
1408 if (gethostname(zephyr
->ourhost
, sizeof(zephyr
->ourhost
)) == -1) {
1409 purple_debug_error("zephyr", "unable to retrieve hostname, %%host%% and %%canon%% will be wrong in subscriptions and have been set to unknown\n");
1410 g_strlcpy(zephyr
->ourhost
, "unknown", sizeof(zephyr
->ourhost
));
1411 g_strlcpy(zephyr
->ourhostcanon
, "unknown", sizeof(zephyr
->ourhostcanon
));
1415 if (!(hent
= gethostbyname(zephyr
->ourhost
))) {
1416 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
);
1417 g_strlcpy(zephyr
->ourhostcanon
, zephyr
->ourhost
, sizeof(zephyr
->ourhostcanon
));
1421 g_strlcpy(zephyr
->ourhostcanon
, hent
->h_name
, sizeof(zephyr
->ourhostcanon
));
1426 static void process_zsubs(zephyr_account
*zephyr
)
1428 /* Loads zephyr chats "(subscriptions) from ~/.zephyr.subs, and
1429 registers (subscribes to) them on the server */
1431 /* XXX deal with unsubscriptions */
1432 /* XXX deal with punts */
1438 fname
= g_strdup_printf("%s/.zephyr.subs", purple_home_dir());
1439 f
= g_fopen(fname
, "r");
1445 char *z_galaxy
= NULL
;
1447 while (fgets(buff
, BUFSIZ
, f
)) {
1448 strip_comments(buff
);
1450 triple
= g_strsplit(buff
, ",", 3);
1451 if (triple
[0] && triple
[1]) {
1452 char *tmp
= g_strdup_printf("%s", zephyr
->username
);
1455 if (triple
[2] == NULL
) {
1456 recip
= g_malloc0(1);
1457 } else if (!g_ascii_strcasecmp(triple
[2], "%me%")) {
1458 recip
= g_strdup_printf("%s", zephyr
->username
);
1459 } else if (!g_ascii_strcasecmp(triple
[2], "*")) {
1461 * form of class,instance,* */
1462 recip
= g_malloc0(1);
1463 } else if (!g_ascii_strcasecmp(triple
[2], tmp
)) {
1464 /* form of class,instance,aatharuv@ATHENA.MIT.EDU */
1465 recip
= g_strdup(triple
[2]);
1466 } else if ((atptr
= strchr(triple
[2], '@')) != NULL
) {
1467 /* form of class,instance,*@ANDREW.CMU.EDU
1468 * class,instance,@ANDREW.CMU.EDU
1469 * If realm is local realm, blank recipient, else
1472 char *realmat
= g_strdup_printf("@%s",zephyr
->realm
);
1474 if (!g_ascii_strcasecmp(atptr
, realmat
))
1475 recip
= g_malloc0(1);
1477 recip
= g_strdup(atptr
);
1480 recip
= g_strdup(triple
[2]);
1484 if (!g_ascii_strcasecmp(triple
[0],"%host%")) {
1485 z_class
= g_strdup(zephyr
->ourhost
);
1486 } else if (!g_ascii_strcasecmp(triple
[0],"%canon%")) {
1487 z_class
= g_strdup(zephyr
->ourhostcanon
);
1489 z_class
= g_strdup(triple
[0]);
1492 if (!g_ascii_strcasecmp(triple
[1],"%host%")) {
1493 z_instance
= g_strdup(zephyr
->ourhost
);
1494 } else if (!g_ascii_strcasecmp(triple
[1],"%canon%")) {
1495 z_instance
= g_strdup(zephyr
->ourhostcanon
);
1497 z_instance
= g_strdup(triple
[1]);
1500 /* There should be some sort of error report listing classes that couldn't be subbed to.
1501 Not important right now though */
1503 if (zephyr_subscribe_to(zephyr
,z_class
, z_instance
, recip
,z_galaxy
) != ZERR_NONE
) {
1505 purple_debug_error("zephyr", "Couldn't subscribe to %s, %s, %s\n", z_class
,z_instance
,recip
);
1508 zephyr
->subscrips
= g_slist_append(zephyr
->subscrips
, new_triple(zephyr
,z_class
,z_instance
,recip
));
1509 /* g_hash_table_destroy(sub_hash_table); */
1522 static void process_anyone(PurpleConnection
*gc
)
1524 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
1526 gchar buff
[BUFSIZ
], *filename
;
1530 if (!(g
= purple_blist_find_group(_("Anyone")))) {
1531 g
= purple_group_new(_("Anyone"));
1532 purple_blist_add_group(g
, NULL
);
1535 filename
= g_strconcat(purple_home_dir(), "/.anyone", NULL
);
1536 if ((fd
= g_fopen(filename
, "r")) != NULL
) {
1537 while (fgets(buff
, BUFSIZ
, fd
)) {
1538 strip_comments(buff
);
1540 if (!purple_blist_find_buddy(purple_connection_get_account(gc
), buff
)) {
1541 char *stripped_user
= zephyr_strip_local_realm(zephyr
,buff
);
1542 purple_debug_info("zephyr","stripped_user %s\n",stripped_user
);
1543 if (!purple_blist_find_buddy(purple_connection_get_account(gc
),stripped_user
)) {
1544 b
= purple_buddy_new(purple_connection_get_account(gc
), stripped_user
, NULL
);
1545 purple_blist_add_buddy(b
, NULL
, g
, NULL
);
1547 g_free(stripped_user
);
1557 normalize_zephyr_exposure(const gchar
*exposure
)
1559 gchar
*exp2
= g_strstrip(g_ascii_strup(exposure
, -1));
1562 return g_strdup(EXPOSE_REALMVIS
);
1564 if (g_str_equal(exp2
, EXPOSE_NONE
) || g_str_equal(exp2
, EXPOSE_OPSTAFF
) ||
1565 g_str_equal(exp2
, EXPOSE_REALMANN
) ||
1566 g_str_equal(exp2
, EXPOSE_NETVIS
) || g_str_equal(exp2
, EXPOSE_NETANN
)) {
1569 return g_strdup(EXPOSE_REALMVIS
);
1572 static void zephyr_login(PurpleAccount
* account
)
1574 PurpleConnection
*gc
;
1575 zephyr_account
*zephyr
;
1576 gboolean read_anyone
;
1577 gboolean read_zsubs
;
1580 gc
= purple_account_get_connection(account
);
1581 read_anyone
= purple_account_get_bool(purple_connection_get_account(gc
),"read_anyone",TRUE
);
1582 read_zsubs
= purple_account_get_bool(purple_connection_get_account(gc
),"read_zsubs",TRUE
);
1583 exposure
= (gchar
*)purple_account_get_string(purple_connection_get_account(gc
), "exposure_level", EXPOSE_REALMVIS
);
1586 username
= purple_account_get_username(account
);
1588 purple_connection_set_flags(gc
, PURPLE_CONNECTION_FLAG_AUTO_RESP
|
1589 PURPLE_CONNECTION_FLAG_HTML
| PURPLE_CONNECTION_FLAG_NO_BGCOLOR
|
1590 PURPLE_CONNECTION_FLAG_NO_URLDESC
| PURPLE_CONNECTION_FLAG_NO_IMAGES
);
1591 zephyr
= g_new0(zephyr_account
, 1);
1592 purple_connection_set_protocol_data(gc
, zephyr
);
1594 zephyr
->account
= account
;
1596 /* Make sure that the exposure (visibility) is set to a sane value */
1597 zephyr
->exposure
= normalize_zephyr_exposure(exposure
);
1599 if (purple_account_get_bool(purple_connection_get_account(gc
),"use_tzc",0)) {
1600 zephyr
->connection_type
= PURPLE_ZEPHYR_TZC
;
1602 zephyr
->connection_type
= PURPLE_ZEPHYR_KRB4
;
1605 zephyr
->encoding
= (char *)purple_account_get_string(purple_connection_get_account(gc
), "encoding", ZEPHYR_FALLBACK_CHARSET
);
1606 purple_connection_update_progress(gc
, _("Connecting"), 0, 8);
1608 /* XXX z_call_s should actually try to report the com_err determined error */
1609 if (use_tzc(zephyr
)) {
1611 /* purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, "tzc not supported yet"); */
1612 if ((pipe(zephyr
->totzc
) != 0) || (pipe(zephyr
->fromtzc
) != 0)) {
1613 purple_debug_error("zephyr", "pipe creation failed. killing\n");
1620 purple_debug_error("zephyr", "forking failed\n");
1625 gboolean found_ps
= FALSE
;
1626 gchar
** tzc_cmd_array
= g_strsplit(purple_account_get_string(purple_connection_get_account(gc
),"tzc_command","/usr/bin/tzc -e %s")," ",0);
1627 if (close(1) == -1) {
1630 if (dup2(zephyr
->fromtzc
[1], 1) == -1) {
1633 if (close(zephyr
->fromtzc
[1]) == -1) {
1636 if (close(0) == -1) {
1639 if (dup2(zephyr
->totzc
[0], 0) == -1) {
1642 if (close(zephyr
->totzc
[0]) == -1) {
1645 /* tzc_command should really be of the form
1648 ssh username@hostname pathtotzc -e %s
1649 -- this should not require a password, and ideally should be kerberized ssh --
1651 fsh username@hostname pathtotzc -e %s
1653 while(tzc_cmd_array
[i
] != NULL
){
1654 if (!g_ascii_strncasecmp(tzc_cmd_array
[i
],"%s",2)) {
1655 /* fprintf(stderr,"replacing %%s with %s\n",zephyr->exposure); */
1656 tzc_cmd_array
[i
] = g_strdup(zephyr
->exposure
);
1660 /* fprintf(stderr,"keeping %s\n",tzc_cmd_array[i]); */
1669 execvp(tzc_cmd_array
[0], tzc_cmd_array
);
1675 char *buf
= (char *)calloc(bufsize
, 1);
1684 zephyr
->tzc_pid
= pid
;
1685 /* wait till we have data to read from ssh */
1687 FD_SET(zephyr
->fromtzc
[ZEPHYR_FD_READ
], &rfds
);
1692 purple_debug_info("zephyr", "about to read from tzc\n");
1694 if (waitpid(pid
, NULL
, WNOHANG
) == 0) { /* Only select if tzc is still running */
1695 purple_debug_info("zephyr", "about to read from tzc\n");
1696 select_status
= select(zephyr
->fromtzc
[ZEPHYR_FD_READ
] + 1, &rfds
, NULL
, NULL
, NULL
);
1699 purple_debug_info("zephyr", "tzc exited early\n");
1704 FD_SET(zephyr
->fromtzc
[ZEPHYR_FD_READ
], &rfds
);
1705 while (select_status
> 0 &&
1706 select(zephyr
->fromtzc
[ZEPHYR_FD_READ
] + 1, &rfds
, NULL
, NULL
, &tv
) > 0) {
1707 if (read(zephyr
->fromtzc
[ZEPHYR_FD_READ
], bufcur
, 1) != 1) {
1708 purple_debug_error("zephyr", "couldn't read\n");
1709 purple_connection_error(gc
, PURPLE_CONNECTION_ERROR_NETWORK_ERROR
, "couldn't read");
1714 if ((bufcur
- buf
) > (bufsize
- 1)) {
1715 if ((buf
= realloc(buf
, bufsize
* 2)) == NULL
) {
1718 bufcur
= buf
+ bufsize
;
1723 FD_SET(zephyr
->fromtzc
[ZEPHYR_FD_READ
], &rfds
);
1728 /* fprintf(stderr, "read from tzc\n"); */
1732 /* ignore all tzcoutput till we've received the first (*/
1733 while (ptr
< bufcur
&& (*ptr
!='(')) {
1737 purple_connection_error(gc
, PURPLE_CONNECTION_ERROR_NETWORK_ERROR
, "invalid output by tzc (or bad parsing code)");
1742 while(ptr
< bufcur
) {
1746 else if (*ptr
== ')') {
1749 purple_debug_info("zephyr","tzc parenlevel is %d\n",parenlevel
);
1750 switch (parenlevel
) {
1754 /* Search for next beginning (, or for the ending */
1756 while((*ptr
!= '(') && (*ptr
!= ')') && (ptr
<bufcur
))
1759 purple_debug_error("zephyr","tzc parsing error\n");
1762 /* You are probably at
1763 (foo . bar ) or (foo . "bar") or (foo . chars) or (foo . numbers) or (foo . () )
1764 Parse all the data between the first and last f, and move past )
1766 tempstr
= g_malloc0(20000);
1768 while(parenlevel
>1) {
1774 if (parenlevel
> 1) {
1775 tempstr
[tempstridx
++]=*ptr
;
1780 purple_debug_info("zephyr","tempstr parsed\n");
1781 /* tempstr should now be a tempstridx length string containing all characters
1782 from that after the first ( to the one before the last paren ). */
1783 /* We should have the following possible lisp strings but we don't care
1784 (tzcspew . start) (version . "something") (pid . number)*/
1785 /* We care about 'zephyrid . "username@REALM.NAME"' and 'exposure . "SOMETHING"' */
1787 if (!g_ascii_strncasecmp(tempstr
,"zephyrid",8)) {
1788 gchar
* username
= g_malloc0(100);
1791 purple_debug_info("zephyr","zephyrid found\n");
1793 while(tempstr
[tempstridx
] !='"' && tempstridx
< 20000)
1796 while(tempstr
[tempstridx
] !='"' && tempstridx
< 20000)
1797 username
[username_idx
++]=tempstr
[tempstridx
++];
1799 zephyr
->username
= g_strdup_printf("%s",username
);
1800 if ((realm
= strchr(username
,'@')))
1801 zephyr
->realm
= g_strdup_printf("%s",realm
+1);
1803 realm
= (gchar
*)purple_account_get_string(purple_connection_get_account(gc
),"realm","");
1805 realm
= "local-realm";
1807 zephyr
->realm
= g_strdup(realm
);
1808 g_strlcpy(__Zephyr_realm
, (const char*)zephyr
->realm
, REALM_SZ
-1);
1811 zephyr->realm = g_strdup("local-realm");
1816 purple_debug_info("zephyr", "something that's not zephyr id found %s\n",tempstr
);
1819 /* We don't care about anything else yet */
1823 purple_debug_info("zephyr","parenlevel is not 1 or 2\n");
1824 /* This shouldn't be happening */
1829 } /* while (ptr < bufcur) */
1830 purple_debug_info("zephyr", "tzc startup done\n");
1834 else if ( use_zeph02(zephyr
)) {
1836 z_call_s(ZInitialize(), "Couldn't initialize zephyr");
1837 z_call_s(ZOpenPort(&(zephyr
->port
)), "Couldn't open port");
1838 z_call_s(ZSetLocation((char *)zephyr
->exposure
), "Couldn't set location");
1840 realm
= (gchar
*)purple_account_get_string(purple_connection_get_account(gc
),"realm","");
1842 realm
= ZGetRealm();
1844 zephyr
->realm
= g_strdup(realm
);
1845 g_strlcpy(__Zephyr_realm
, (const char*)zephyr
->realm
, REALM_SZ
-1);
1846 zephyr
->username
= g_strdup(ZGetSender());
1848 /* zephyr->realm = g_strdup(ZGetRealm()); */
1849 purple_debug_info("zephyr","realm: %s\n",zephyr
->realm
);
1852 purple_connection_error(gc
, PURPLE_CONNECTION_ERROR_NETWORK_ERROR
, "Only ZEPH0.2 supported currently");
1855 purple_debug_info("zephyr","does it get here\n");
1856 purple_debug_info("zephyr"," realm: %s username:%s\n", zephyr
->realm
, zephyr
->username
);
1859 zephyr
->galaxy
= NULL
;
1860 zephyr
->krbtkfile
= NULL
;
1861 zephyr_inithosts(zephyr
);
1863 if (zephyr_subscribe_to(zephyr
,"MESSAGE","PERSONAL",zephyr
->username
,NULL
) != ZERR_NONE
) {
1864 /* XXX don't translate this yet. It could be written better */
1865 /* XXX error messages could be handled with more detail */
1866 purple_notify_error(purple_account_get_connection(account
), NULL
,
1867 "Unable to subscribe to messages", "Unable to subscribe to initial messages",
1868 purple_request_cpar_from_connection(gc
));
1872 purple_connection_set_state(gc
, PURPLE_CONNECTION_CONNECTED
);
1877 process_zsubs(zephyr
);
1879 if (use_zeph02(zephyr
)) {
1880 zephyr
->nottimer
= g_timeout_add(100, check_notify_zeph02
, gc
);
1881 } else if (use_tzc(zephyr
)) {
1882 zephyr
->nottimer
= g_timeout_add(100, check_notify_tzc
, gc
);
1884 zephyr
->loctimer
= g_timeout_add_seconds(20, check_loc
, gc
);
1888 static void write_zsubs(zephyr_account
*zephyr
)
1890 /* Exports subscription (chat) list back to
1892 * XXX deal with %host%, %canon%, unsubscriptions, and negative subscriptions (punts?)
1895 GSList
*s
= zephyr
->subscrips
;
1902 fname
= g_strdup_printf("%s/.zephyr.subs", purple_home_dir());
1903 fd
= g_fopen(fname
, "w");
1911 char *zclass
, *zinst
, *zrecip
;
1913 triple
= g_strsplit(zt
->name
, ",", 3);
1915 /* deal with classes */
1916 if (!g_ascii_strcasecmp(triple
[0],zephyr
->ourhost
)) {
1917 zclass
= g_strdup("%host%");
1918 } else if (!g_ascii_strcasecmp(triple
[0],zephyr
->ourhostcanon
)) {
1919 zclass
= g_strdup("%canon%");
1921 zclass
= g_strdup(triple
[0]);
1924 /* deal with instances */
1926 if (!g_ascii_strcasecmp(triple
[1],zephyr
->ourhost
)) {
1927 zinst
= g_strdup("%host%");
1928 } else if (!g_ascii_strcasecmp(triple
[1],zephyr
->ourhostcanon
)) {
1929 zinst
= g_strdup("%canon%");;
1931 zinst
= g_strdup(triple
[1]);
1934 /* deal with recipients */
1935 if (triple
[2] == NULL
) {
1936 zrecip
= g_strdup("*");
1937 } else if (!g_ascii_strcasecmp(triple
[2],"")){
1938 zrecip
= g_strdup("*");
1939 } else if (!g_ascii_strcasecmp(triple
[2], zephyr
->username
)) {
1940 zrecip
= g_strdup("%me%");
1942 zrecip
= g_strdup(triple
[2]);
1945 fprintf(fd
, "%s,%s,%s\n",zclass
,zinst
,zrecip
);
1957 static void write_anyone(zephyr_account
*zephyr
)
1962 PurpleAccount
*account
;
1963 fname
= g_strdup_printf("%s/.anyone", purple_home_dir());
1964 fd
= g_fopen(fname
, "w");
1970 account
= zephyr
->account
;
1971 for (buddies
= purple_blist_find_buddies(account
, NULL
); buddies
;
1972 buddies
= g_slist_delete_link(buddies
, buddies
)) {
1973 PurpleBuddy
*b
= buddies
->data
;
1974 gchar
*stripped_user
= zephyr_strip_local_realm(zephyr
, purple_buddy_get_name(b
));
1975 fprintf(fd
, "%s\n", stripped_user
);
1976 g_free(stripped_user
);
1983 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 g_list_free_full(zephyr
->pending_zloc_names
, g_free
);
1991 if (purple_account_get_bool(purple_connection_get_account(gc
), "write_anyone", FALSE
))
1992 write_anyone(zephyr
);
1994 if (purple_account_get_bool(purple_connection_get_account(gc
), "write_zsubs", FALSE
))
1995 write_zsubs(zephyr
);
1997 s
= zephyr
->subscrips
;
1999 free_triple((zephyr_triple
*) s
->data
);
2002 g_slist_free(zephyr
->subscrips
);
2004 if (zephyr
->nottimer
)
2005 g_source_remove(zephyr
->nottimer
);
2006 zephyr
->nottimer
= 0;
2007 if (zephyr
->loctimer
)
2008 g_source_remove(zephyr
->loctimer
);
2009 zephyr
->loctimer
= 0;
2011 if (use_zeph02(zephyr
)) {
2012 z_call(ZCancelSubscriptions(0));
2013 z_call(ZUnsetLocation());
2014 z_call(ZClosePort());
2017 if (kill(tzc_pid
,SIGTERM
) == -1) {
2020 purple_debug_error("zephyr","An invalid signal was specified when killing tzc\n");
2022 else if (err
==ESRCH
) {
2023 purple_debug_error("zephyr","Tzc's pid didn't exist while killing tzc\n");
2025 else if (err
==EPERM
) {
2026 purple_debug_error("zephyr","purple didn't have permission to kill tzc\n");
2029 purple_debug_error("zephyr","miscellaneous error while attempting to close tzc\n");
2035 static int zephyr_send_message(zephyr_account
*zephyr
,char* zclass
, char* instance
, char* recipient
, const char *im
,
2036 const char *sig
, char *opcode
) ;
2038 static const char * zephyr_get_signature(void)
2040 /* XXX add zephyr error reporting */
2041 const char * sig
=ZGetVariable("zwrite-signature");
2043 sig
= g_get_real_name();
2048 static int zephyr_chat_send(PurpleConnection
* gc
, int id
, PurpleMessage
*msg
)
2052 PurpleChatConversation
*gcc
;
2055 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
2057 zt
= find_sub_by_id(zephyr
,id
);
2059 /* this should never happen. */
2062 sig
= zephyr_get_signature();
2064 gcc
= purple_conversations_find_chat_with_account(zt
->name
,
2065 purple_connection_get_account(gc
));
2067 if (!(inst
= (char *)purple_chat_conversation_get_topic(gcc
)))
2068 inst
= g_strdup("PERSONAL");
2070 if (!g_ascii_strcasecmp(zt
->recipient
, "*"))
2071 recipient
= local_zephyr_normalize(zephyr
,"");
2073 recipient
= local_zephyr_normalize(zephyr
,zt
->recipient
);
2075 zephyr_send_message(zephyr
, zt
->class, inst
, recipient
,
2076 purple_message_get_contents(msg
), sig
, "");
2081 static int zephyr_send_im(PurpleConnection
*gc
, PurpleMessage
*msg
)
2084 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
2086 if (purple_message_get_flags(msg
) & PURPLE_MESSAGE_AUTO_RESP
) {
2087 sig
= "Automated reply:";
2089 sig
= zephyr_get_signature();
2091 zephyr_send_message(zephyr
, "MESSAGE", "PERSONAL",
2092 local_zephyr_normalize(zephyr
, purple_message_get_recipient(msg
)),
2093 purple_message_get_contents(msg
), sig
, "");
2098 /* Munge the outgoing zephyr so that any quotes or backslashes are
2099 escaped and do not confuse tzc: */
2101 static char* zephyr_tzc_escape_msg(const char *message
)
2103 gsize pos
= 0, pos2
= 0;
2106 if (message
&& *message
) {
2107 newmsg
= g_new0(char,1+strlen(message
)*2);
2108 while(pos
< strlen(message
)) {
2109 if (message
[pos
]=='\\') {
2111 newmsg
[pos2
+1]='\\';
2114 else if (message
[pos
]=='"') {
2120 newmsg
[pos2
] = message
[pos
];
2126 newmsg
= g_strdup("");
2128 /* fprintf(stderr,"newmsg %s message %s\n",newmsg,message); */
2132 char* zephyr_tzc_deescape_str(const char *message
)
2134 gsize pos
= 0, pos2
= 0;
2137 if (message
&& *message
) {
2138 newmsg
= g_new0(char,strlen(message
)+1);
2139 while(pos
< strlen(message
)) {
2140 if (message
[pos
]=='\\') {
2143 newmsg
[pos2
] = message
[pos
];
2148 newmsg
= g_strdup("");
2154 static int zephyr_send_message(zephyr_account
*zephyr
,char* zclass
, char* instance
, char* recipient
, const char *im
,
2155 const char *sig
, char *opcode
)
2158 /* (From the tzc source)
2159 * emacs sends something of the form:
2160 * ((class . "MESSAGE")
2162 * (recipients ("PERSONAL" . "bovik") ("test" . ""))
2163 * (sender . "bovik")
2164 * (message . ("Harry Bovik" "my zgram"))
2169 html_buf
= html_to_zephyr(im
);
2170 html_buf2
= purple_unescape_html(html_buf
);
2172 if(use_tzc(zephyr
)) {
2176 /* CMU cclub tzc doesn't grok opcodes for now */
2177 char* tzc_sig
= zephyr_tzc_escape_msg(sig
);
2178 char *tzc_body
= zephyr_tzc_escape_msg(html_buf2
);
2179 zsendstr
= g_strdup_printf("((tzcfodder . send) (class . \"%s\") (auth . t) (recipients (\"%s\" . \"%s\")) (message . (\"%s\" \"%s\")) ) \n",
2180 zclass
, instance
, recipient
, tzc_sig
, tzc_body
);
2181 /* fprintf(stderr,"zsendstr = %s\n",zsendstr); */
2182 len
= strlen(zsendstr
);
2183 result
= write(zephyr
->totzc
[ZEPHYR_FD_WRITE
], zsendstr
, len
);
2184 if (result
!= len
) {
2195 } else if (use_zeph02(zephyr
)) {
2197 char *buf
= g_strdup_printf("%s%c%s", sig
, '\0', html_buf2
);
2198 memset((char *)¬ice
, 0, sizeof(notice
));
2200 notice
.z_kind
= ACKED
;
2202 notice
.z_class
= zclass
;
2203 notice
.z_class_inst
= instance
;
2204 notice
.z_recipient
= recipient
;
2205 notice
.z_sender
= 0;
2206 notice
.z_default_format
= "Class $class, Instance $instance:\n" "To: @bold($recipient) at $time $date\n" "From: @bold($1) <$sender>\n\n$2";
2207 notice
.z_message_len
= strlen(html_buf2
) + strlen(sig
) + 2;
2208 notice
.z_message
= buf
;
2209 notice
.z_opcode
= g_strdup(opcode
);
2210 purple_debug_info("zephyr","About to send notice\n");
2211 if (! ZSendNotice(¬ice
, ZAUTH
) == ZERR_NONE
) {
2212 /* XXX handle errors here */
2218 purple_debug_info("zephyr","notice sent\n");
2228 char *local_zephyr_normalize(zephyr_account
*zephyr
,const char *orig
)
2231 Basically the inverse of zephyr_strip_local_realm
2235 if (!g_ascii_strcasecmp(orig
, "")) {
2236 return g_strdup("");
2239 if (strchr(orig
,'@')) {
2240 buf
= g_strdup_printf("%s",orig
);
2242 buf
= g_strdup_printf("%s@%s",orig
,zephyr
->realm
);
2247 static const char *zephyr_normalize(const PurpleAccount
*account
, const char *who
)
2249 static char buf
[BUF_LEN
];
2250 PurpleConnection
*gc
;
2253 if (account
== NULL
) {
2254 if (strlen(who
) >= sizeof(buf
))
2259 gc
= purple_account_get_connection((PurpleAccount
*)account
);
2263 tmp
= local_zephyr_normalize(purple_connection_get_protocol_data(gc
), who
);
2265 if (strlen(tmp
) >= sizeof(buf
)) {
2270 g_strlcpy(buf
, tmp
, sizeof(buf
));
2276 static void zephyr_zloc(PurpleConnection
*gc
, const char *who
)
2278 ZAsyncLocateData_t ald
;
2279 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
2280 gchar
* normalized_who
= local_zephyr_normalize(zephyr
,who
);
2282 if (use_zeph02(zephyr
)) {
2283 if (ZRequestLocations(normalized_who
, &ald
, UNACKED
, ZAUTH
) == ZERR_NONE
) {
2284 zephyr
->pending_zloc_names
= g_list_append(zephyr
->pending_zloc_names
,
2285 g_strdup(normalized_who
));
2287 /* XXX deal with errors somehow */
2289 } else if (use_tzc(zephyr
)) {
2292 char* zlocstr
= g_strdup_printf("((tzcfodder . zlocate) \"%s\")\n",normalized_who
);
2293 zephyr
->pending_zloc_names
= g_list_append(zephyr
->pending_zloc_names
, g_strdup(normalized_who
));
2294 len
= strlen(zlocstr
);
2295 result
= write(zephyr
->totzc
[ZEPHYR_FD_WRITE
],zlocstr
,len
);
2296 if (result
!= len
) {
2297 purple_debug_error("zephyr", "Unable to write a message: %s\n", g_strerror(errno
));
2303 static void zephyr_set_status(PurpleAccount
*account
, PurpleStatus
*status
) {
2306 PurpleConnection
*gc
= purple_account_get_connection(account
);
2307 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
2308 PurpleStatusPrimitive primitive
= purple_status_type_get_primitive(purple_status_get_status_type(status
));
2310 g_free(zephyr
->away
);
2311 zephyr
->away
= NULL
;
2313 if (primitive
== PURPLE_STATUS_AWAY
) {
2314 zephyr
->away
= g_strdup(purple_status_get_attr_string(status
,"message"));
2316 else if (primitive
== PURPLE_STATUS_AVAILABLE
) {
2317 if (use_zeph02(zephyr
)) {
2318 ZSetLocation(zephyr
->exposure
);
2321 char *zexpstr
= g_strdup_printf("((tzcfodder . set-location) (hostname . \"%s\") (exposure . \"%s\"))\n",zephyr
->ourhost
,zephyr
->exposure
);
2322 len
= strlen(zexpstr
);
2323 result
= write(zephyr
->totzc
[ZEPHYR_FD_WRITE
],zexpstr
,len
);
2324 if (result
!= len
) {
2325 purple_debug_error("zephyr", "Unable to write message: %s\n", g_strerror(errno
));
2330 else if (primitive
== PURPLE_STATUS_INVISIBLE
) {
2331 /* XXX handle errors */
2332 if (use_zeph02(zephyr
)) {
2333 ZSetLocation(EXPOSE_OPSTAFF
);
2335 char *zexpstr
= g_strdup_printf("((tzcfodder . set-location) (hostname . \"%s\") (exposure . \"%s\"))\n",zephyr
->ourhost
,EXPOSE_OPSTAFF
);
2336 len
= strlen(zexpstr
);
2337 result
= write(zephyr
->totzc
[ZEPHYR_FD_WRITE
],zexpstr
,len
);
2338 if (result
!= len
) {
2339 purple_debug_error("zephyr", "Unable to write message: %s\n", g_strerror(errno
));
2346 static GList
*zephyr_status_types(PurpleAccount
*account
)
2348 PurpleStatusType
*type
;
2349 GList
*types
= NULL
;
2351 /* zephyr has several exposures
2352 NONE (where you are hidden, and zephyrs to you are in practice silently dropped -- yes this is wrong)
2354 REALM-VISIBLE visible to people in local realm
2355 REALM-ANNOUNCED REALM-VISIBLE+ plus your logins/logouts are announced to <login,username,*>
2356 NET-VISIBLE REALM-ANNOUNCED, plus visible to people in foreign realm
2357 NET-ANNOUNCED NET-VISIBLE, plus logins/logouts are announced to <login,username,*>
2359 Online will set the user to the exposure they have in their options (defaulting to REALM-VISIBLE),
2360 Hidden, will set the user's exposure to OPSTAFF
2362 Away won't change their exposure but will set an auto away message (for IMs only)
2365 type
= purple_status_type_new(PURPLE_STATUS_AVAILABLE
, NULL
, NULL
, TRUE
);
2366 types
= g_list_append(types
,type
);
2368 type
= purple_status_type_new(PURPLE_STATUS_INVISIBLE
, NULL
, NULL
, TRUE
);
2369 types
= g_list_append(types
,type
);
2371 type
= purple_status_type_new_with_attrs(
2372 PURPLE_STATUS_AWAY
, NULL
, NULL
, TRUE
, TRUE
, FALSE
,
2373 "message", _("Message"), purple_value_new(G_TYPE_STRING
),
2375 types
= g_list_append(types
, type
);
2377 type
= purple_status_type_new(PURPLE_STATUS_OFFLINE
, NULL
, NULL
, TRUE
);
2378 types
= g_list_append(types
,type
);
2383 static GList
*zephyr_chat_info(PurpleConnection
* gc
)
2386 PurpleProtocolChatEntry
*pce
;
2388 pce
= g_new0(PurpleProtocolChatEntry
, 1);
2390 pce
->label
= _("_Class:");
2391 pce
->identifier
= "class";
2392 m
= g_list_append(m
, pce
);
2394 pce
= g_new0(PurpleProtocolChatEntry
, 1);
2396 pce
->label
= _("_Instance:");
2397 pce
->identifier
= "instance";
2398 m
= g_list_append(m
, pce
);
2400 pce
= g_new0(PurpleProtocolChatEntry
, 1);
2402 pce
->label
= _("_Recipient:");
2403 pce
->identifier
= "recipient";
2404 m
= g_list_append(m
, pce
);
2409 /* Called when the server notifies us a message couldn't get sent */
2411 static void zephyr_subscribe_failed(PurpleConnection
*gc
,char * z_class
, char *z_instance
, char * z_recipient
, char* z_galaxy
)
2413 gchar
* subscribe_failed
= g_strdup_printf(_("Attempt to subscribe to %s,%s,%s failed"), z_class
, z_instance
,z_recipient
);
2414 purple_notify_error(gc
,"", subscribe_failed
, NULL
, purple_request_cpar_from_connection(gc
));
2415 g_free(subscribe_failed
);
2418 static char *zephyr_get_chat_name(GHashTable
*data
) {
2419 gchar
* zclass
= g_hash_table_lookup(data
,"class");
2420 gchar
* inst
= g_hash_table_lookup(data
,"instance");
2421 gchar
* recipient
= g_hash_table_lookup(data
, "recipient");
2422 if (!zclass
) /* This should never happen */
2428 return g_strdup_printf("%s,%s,%s",zclass
,inst
,recipient
);
2432 static void zephyr_join_chat(PurpleConnection
* gc
, GHashTable
* data
)
2434 /* ZSubscription_t sub; */
2435 zephyr_triple
*zt1
, *zt2
;
2436 const char *classname
;
2437 const char *instname
;
2439 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
2440 classname
= g_hash_table_lookup(data
, "class");
2441 instname
= g_hash_table_lookup(data
, "instance");
2442 recip
= g_hash_table_lookup(data
, "recipient");
2448 if (!g_ascii_strcasecmp(classname
,"%host%"))
2449 classname
= zephyr
->ourhost
;
2450 if (!g_ascii_strcasecmp(classname
,"%canon%"))
2451 classname
= zephyr
->ourhostcanon
;
2453 if (!instname
|| *instname
== '\0')
2456 if (!g_ascii_strcasecmp(instname
,"%host%"))
2457 instname
= zephyr
->ourhost
;
2458 if (!g_ascii_strcasecmp(instname
,"%canon%"))
2459 instname
= zephyr
->ourhostcanon
;
2461 if (!recip
|| (*recip
== '*'))
2463 if (!g_ascii_strcasecmp(recip
, "%me%"))
2464 recip
= zephyr
->username
;
2466 zt1
= new_triple(zephyr
,classname
, instname
, recip
);
2467 zt2
= find_sub_by_triple(zephyr
,zt1
);
2471 if (!g_ascii_strcasecmp(instname
,"*"))
2472 instname
= "PERSONAL";
2473 purple_serv_got_joined_chat(gc
, zt2
->id
, zt2
->name
);
2474 zephyr_chat_set_topic(gc
,zt2
->id
,instname
);
2480 /* sub.zsub_class = zt1->class;
2481 sub.zsub_classinst = zt1->instance;
2482 sub.zsub_recipient = zt1->recipient; */
2484 if (zephyr_subscribe_to(zephyr
,zt1
->class,zt1
->instance
,zt1
->recipient
,NULL
) != ZERR_NONE
) {
2485 /* XXX output better subscription information */
2486 zephyr_subscribe_failed(gc
,zt1
->class,zt1
->instance
,zt1
->recipient
,NULL
);
2491 zephyr
->subscrips
= g_slist_append(zephyr
->subscrips
, zt1
);
2493 purple_serv_got_joined_chat(gc
, zt1
->id
, zt1
->name
);
2494 if (!g_ascii_strcasecmp(instname
,"*"))
2495 instname
= "PERSONAL";
2496 zephyr_chat_set_topic(gc
,zt1
->id
,instname
);
2499 static void zephyr_chat_leave(PurpleConnection
* gc
, int id
)
2502 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
2503 zt
= find_sub_by_id(zephyr
,id
);
2507 zt
->id
= ++(zephyr
->last_id
);
2511 static PurpleChat
*zephyr_find_blist_chat(PurpleAccount
*account
, const char *name
)
2513 PurpleBlistNode
*gnode
, *cnode
;
2515 /* XXX needs to be %host%,%canon%, and %me% clean */
2516 for (gnode
= purple_blist_get_default_root(); gnode
;
2517 gnode
= purple_blist_node_get_sibling_next(gnode
)) {
2518 for(cnode
= purple_blist_node_get_first_child(gnode
);
2520 cnode
= purple_blist_node_get_sibling_next(cnode
)) {
2521 PurpleChat
*chat
= (PurpleChat
*)cnode
;
2522 const gchar
*zclass
, *inst
, *recip
;
2524 GHashTable
*components
;
2525 if(!PURPLE_IS_CHAT(cnode
))
2527 if(purple_chat_get_account(chat
) != account
)
2529 components
= purple_chat_get_components(chat
);
2530 if(!(zclass
= g_hash_table_lookup(components
, "class")))
2532 if(!(inst
= g_hash_table_lookup(components
, "instance")))
2534 if(!(recip
= g_hash_table_lookup(components
, "recipient")))
2536 /* purple_debug_info("zephyr","in zephyr_find_blist_chat name: %s\n",name?name:""); */
2537 triple
= g_strsplit(name
,",",3);
2538 if (!g_ascii_strcasecmp(triple
[0], zclass
) &&
2539 !g_ascii_strcasecmp(triple
[1], inst
) &&
2540 !g_ascii_strcasecmp(triple
[2], recip
)) {
2549 static const char *zephyr_list_icon(PurpleAccount
* a
, PurpleBuddy
* b
)
2554 static unsigned int zephyr_send_typing(PurpleConnection
*gc
, const char *who
, PurpleIMTypingState state
) {
2556 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
2557 if (use_tzc(zephyr
))
2560 if (state
== PURPLE_IM_NOT_TYPING
)
2563 /* XXX We probably should care if this fails. Or maybe we don't want to */
2565 purple_debug_info("zephyr", "who is null\n");
2566 recipient
= local_zephyr_normalize(zephyr
,"");
2568 char *comma
= strrchr(who
, ',');
2569 /* Don't ping broadcast (chat) recipients */
2570 /* The strrchr case finds a realm-stripped broadcast subscription
2571 e.g. comma is the last character in the string */
2572 if (comma
&& ( (*(comma
+1) == '\0') || (*(comma
+1) == '@')))
2575 recipient
= local_zephyr_normalize(zephyr
,who
);
2578 purple_debug_info("zephyr","about to send typing notification to %s\n",recipient
);
2579 zephyr_send_message(zephyr
,"MESSAGE","PERSONAL",recipient
,"","","PING");
2580 purple_debug_info("zephyr","sent typing notification\n");
2583 * TODO: Is this correct? It means we will call
2584 * purple_serv_send_typing(gc, who, PURPLE_IM_TYPING) once every 15 seconds
2585 * until the Purple user stops typing.
2587 return ZEPHYR_TYPING_SEND_TIMEOUT
;
2592 static void zephyr_chat_set_topic(PurpleConnection
* gc
, int id
, const char *topic
)
2595 PurpleChatConversation
*gcc
;
2597 zephyr_account
* zephyr
= purple_connection_get_protocol_data(gc
);
2598 char *sender
= (char *)zephyr
->username
;
2600 zt
= find_sub_by_id(zephyr
,id
);
2601 /* find_sub_by_id can return NULL */
2604 gcc
= purple_conversations_find_chat_with_account(zt
->name
,
2605 purple_connection_get_account(gc
));
2607 topic_utf8
= zephyr_recv_convert(gc
,(gchar
*)topic
);
2608 purple_chat_conversation_set_topic(gcc
,sender
,topic_utf8
);
2615 static PurpleCmdRet
zephyr_purple_cmd_msg(PurpleConversation
*conv
,
2616 const char *cmd
, char **args
, char **error
, void *data
)
2620 PurpleConnection
*gc
= purple_conversation_get_connection(conv
);
2621 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);;
2622 if (!g_ascii_strcasecmp(args
[0],"*"))
2623 return PURPLE_CMD_RET_FAILED
; /* "*" is not a valid argument */
2625 recipient
= local_zephyr_normalize(zephyr
,args
[0]);
2627 if (strlen(recipient
) < 1) {
2629 return PURPLE_CMD_RET_FAILED
; /* a null recipient is a chat message, not an IM */
2632 if (zephyr_send_message(zephyr
,"MESSAGE","PERSONAL",recipient
,args
[1],zephyr_get_signature(),""))
2633 ret
= PURPLE_CMD_RET_OK
;
2635 ret
= PURPLE_CMD_RET_FAILED
;
2640 static PurpleCmdRet
zephyr_purple_cmd_zlocate(PurpleConversation
*conv
,
2641 const char *cmd
, char **args
, char **error
, void *data
)
2643 zephyr_zloc(purple_conversation_get_connection(conv
),args
[0]);
2644 return PURPLE_CMD_RET_OK
;
2647 static PurpleCmdRet
zephyr_purple_cmd_instance(PurpleConversation
*conv
,
2648 const char *cmd
, char **args
, char **error
, void *data
)
2650 /* Currently it sets the instance with leading spaces and
2651 * all. This might not be the best thing to do, though having
2652 * one word isn't ideal either. */
2654 const char* instance
= args
[0];
2655 zephyr_chat_set_topic(purple_conversation_get_connection(conv
),
2656 purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(conv
)),instance
);
2657 return PURPLE_CMD_RET_OK
;
2660 static PurpleCmdRet
zephyr_purple_cmd_joinchat_cir(PurpleConversation
*conv
,
2661 const char *cmd
, char **args
, char **error
, void *data
)
2663 /* Join a new zephyr chat */
2664 GHashTable
*triple
= g_hash_table_new(NULL
,NULL
);
2665 g_hash_table_insert(triple
,"class",args
[0]);
2666 g_hash_table_insert(triple
,"instance",args
[1]);
2667 g_hash_table_insert(triple
,"recipient",args
[2]);
2668 zephyr_join_chat(purple_conversation_get_connection(conv
),triple
);
2669 return PURPLE_CMD_RET_OK
;
2672 static PurpleCmdRet
zephyr_purple_cmd_zi(PurpleConversation
*conv
,
2673 const char *cmd
, char **args
, char **error
, void *data
)
2675 /* args = instance, message */
2676 PurpleConnection
*gc
= purple_conversation_get_connection(conv
);
2677 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
2678 if ( zephyr_send_message(zephyr
,"message",args
[0],"",args
[1],zephyr_get_signature(),""))
2679 return PURPLE_CMD_RET_OK
;
2681 return PURPLE_CMD_RET_FAILED
;
2684 static PurpleCmdRet
zephyr_purple_cmd_zci(PurpleConversation
*conv
,
2685 const char *cmd
, char **args
, char **error
, void *data
)
2687 /* args = class, instance, message */
2688 PurpleConnection
*gc
= purple_conversation_get_connection(conv
);
2689 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
2690 if ( zephyr_send_message(zephyr
,args
[0],args
[1],"",args
[2],zephyr_get_signature(),""))
2691 return PURPLE_CMD_RET_OK
;
2693 return PURPLE_CMD_RET_FAILED
;
2696 static PurpleCmdRet
zephyr_purple_cmd_zcir(PurpleConversation
*conv
,
2697 const char *cmd
, char **args
, char **error
, void *data
)
2699 /* args = class, instance, recipient, message */
2700 PurpleConnection
*gc
= purple_conversation_get_connection(conv
);
2701 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
2702 if ( zephyr_send_message(zephyr
,args
[0],args
[1],args
[2],args
[3],zephyr_get_signature(),""))
2703 return PURPLE_CMD_RET_OK
;
2705 return PURPLE_CMD_RET_FAILED
;
2708 static PurpleCmdRet
zephyr_purple_cmd_zir(PurpleConversation
*conv
,
2709 const char *cmd
, char **args
, char **error
, void *data
)
2711 /* args = instance, recipient, message */
2712 PurpleConnection
*gc
= purple_conversation_get_connection(conv
);
2713 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
2714 if ( zephyr_send_message(zephyr
,"message",args
[0],args
[1],args
[2],zephyr_get_signature(),""))
2715 return PURPLE_CMD_RET_OK
;
2717 return PURPLE_CMD_RET_FAILED
;
2720 static PurpleCmdRet
zephyr_purple_cmd_zc(PurpleConversation
*conv
,
2721 const char *cmd
, char **args
, char **error
, void *data
)
2723 /* args = class, message */
2724 PurpleConnection
*gc
= purple_conversation_get_connection(conv
);
2725 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
2726 if ( zephyr_send_message(zephyr
,args
[0],"PERSONAL","",args
[1],zephyr_get_signature(),""))
2727 return PURPLE_CMD_RET_OK
;
2729 return PURPLE_CMD_RET_FAILED
;
2732 static void zephyr_register_slash_commands(void)
2736 id
= purple_cmd_register("msg","ws", PURPLE_CMD_P_PROTOCOL
,
2737 PURPLE_CMD_FLAG_IM
| PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
,
2739 zephyr_purple_cmd_msg
, _("msg <nick> <message>: Send a private message to a user"), NULL
);
2740 cmds
= g_slist_prepend(cmds
, GUINT_TO_POINTER(id
));
2742 id
= purple_cmd_register("zlocate","w", PURPLE_CMD_P_PROTOCOL
,
2743 PURPLE_CMD_FLAG_IM
| PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
,
2745 zephyr_purple_cmd_zlocate
, _("zlocate <nick>: Locate user"), NULL
);
2746 cmds
= g_slist_prepend(cmds
, GUINT_TO_POINTER(id
));
2748 id
= purple_cmd_register("zl","w", PURPLE_CMD_P_PROTOCOL
,
2749 PURPLE_CMD_FLAG_IM
| PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
,
2751 zephyr_purple_cmd_zlocate
, _("zl <nick>: Locate user"), NULL
);
2752 cmds
= g_slist_prepend(cmds
, GUINT_TO_POINTER(id
));
2754 id
= purple_cmd_register("instance","s", PURPLE_CMD_P_PROTOCOL
,
2755 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
,
2757 zephyr_purple_cmd_instance
, _("instance <instance>: Set the instance to be used on this class"), NULL
);
2758 cmds
= g_slist_prepend(cmds
, GUINT_TO_POINTER(id
));
2760 id
= purple_cmd_register("inst","s", PURPLE_CMD_P_PROTOCOL
,
2761 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
,
2763 zephyr_purple_cmd_instance
, _("inst <instance>: Set the instance to be used on this class"), NULL
);
2764 cmds
= g_slist_prepend(cmds
, GUINT_TO_POINTER(id
));
2766 id
= purple_cmd_register("topic","s", PURPLE_CMD_P_PROTOCOL
,
2767 PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
,
2769 zephyr_purple_cmd_instance
, _("topic <instance>: Set the instance to be used on this class"), NULL
);
2770 cmds
= g_slist_prepend(cmds
, GUINT_TO_POINTER(id
));
2772 id
= purple_cmd_register("sub", "www", PURPLE_CMD_P_PROTOCOL
,
2773 PURPLE_CMD_FLAG_IM
| PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
,
2775 zephyr_purple_cmd_joinchat_cir
,
2776 _("sub <class> <instance> <recipient>: Join a new chat"), NULL
);
2777 cmds
= g_slist_prepend(cmds
, GUINT_TO_POINTER(id
));
2779 id
= purple_cmd_register("zi","ws", PURPLE_CMD_P_PROTOCOL
,
2780 PURPLE_CMD_FLAG_IM
| PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
,
2782 zephyr_purple_cmd_zi
, _("zi <instance>: Send a message to <message,<i>instance</i>,*>"), NULL
);
2783 cmds
= g_slist_prepend(cmds
, GUINT_TO_POINTER(id
));
2785 id
= purple_cmd_register("zci","wws",PURPLE_CMD_P_PROTOCOL
,
2786 PURPLE_CMD_FLAG_IM
| PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
,
2788 zephyr_purple_cmd_zci
,
2789 _("zci <class> <instance>: Send a message to <<i>class</i>,<i>instance</i>,*>"), NULL
);
2790 cmds
= g_slist_prepend(cmds
, GUINT_TO_POINTER(id
));
2792 id
= purple_cmd_register("zcir","wwws",PURPLE_CMD_P_PROTOCOL
,
2793 PURPLE_CMD_FLAG_IM
| PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
,
2795 zephyr_purple_cmd_zcir
,
2796 _("zcir <class> <instance> <recipient>: Send a message to <<i>class</i>,<i>instance</i>,<i>recipient</i>>"), NULL
);
2797 cmds
= g_slist_prepend(cmds
, GUINT_TO_POINTER(id
));
2799 id
= purple_cmd_register("zir","wws",PURPLE_CMD_P_PROTOCOL
,
2800 PURPLE_CMD_FLAG_IM
| PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
,
2802 zephyr_purple_cmd_zir
,
2803 _("zir <instance> <recipient>: Send a message to <MESSAGE,<i>instance</i>,<i>recipient</i>>"), NULL
);
2804 cmds
= g_slist_prepend(cmds
, GUINT_TO_POINTER(id
));
2806 id
= purple_cmd_register("zc","ws", PURPLE_CMD_P_PROTOCOL
,
2807 PURPLE_CMD_FLAG_IM
| PURPLE_CMD_FLAG_CHAT
| PURPLE_CMD_FLAG_PROTOCOL_ONLY
,
2809 zephyr_purple_cmd_zc
, _("zc <class>: Send a message to <<i>class</i>,PERSONAL,*>"), NULL
);
2810 cmds
= g_slist_prepend(cmds
, GUINT_TO_POINTER(id
));
2814 static void zephyr_unregister_slash_commands(void)
2817 PurpleCmdId id
= GPOINTER_TO_UINT(cmds
->data
);
2818 purple_cmd_unregister(id
);
2819 cmds
= g_slist_delete_link(cmds
, cmds
);
2824 static int zephyr_resubscribe(PurpleConnection
*gc
)
2826 /* Resubscribe to the in-memory list of subscriptions and also
2828 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
2829 GSList
*s
= zephyr
->subscrips
;
2833 /* XXX We really should care if this fails */
2834 zephyr_subscribe_to(zephyr
,zt
->class,zt
->instance
,zt
->recipient
,NULL
);
2837 /* XXX handle unsubscriptions */
2842 static void zephyr_action_resubscribe(PurpleProtocolAction
*action
)
2845 PurpleConnection
*gc
= action
->connection
;
2846 zephyr_resubscribe(gc
);
2850 static void zephyr_action_get_subs_from_server(PurpleProtocolAction
*action
)
2852 PurpleConnection
*gc
= action
->connection
;
2853 zephyr_account
*zephyr
= purple_connection_get_protocol_data(gc
);
2855 int retval
, nsubs
, one
,i
;
2856 ZSubscription_t subs
;
2857 if (use_zeph02(zephyr
)) {
2860 if (zephyr
->port
== 0) {
2861 purple_debug_error("zephyr", "error while retrieving port\n");
2864 if ((retval
= ZRetrieveSubscriptions(zephyr
->port
,&nsubs
)) != ZERR_NONE
) {
2865 /* XXX better error handling */
2866 purple_debug_error("zephyr", "error while retrieving subscriptions from server\n");
2870 title
= g_strdup_printf("Server subscriptions for %s",
2872 subout
= g_string_new("Subscription list<br>");
2873 for(i
=0;i
<nsubs
;i
++) {
2875 if ((retval
= ZGetSubscriptions(&subs
,&one
)) != ZERR_NONE
) {
2876 /* XXX better error handling */
2878 g_string_free(subout
, TRUE
);
2879 purple_debug_error("zephyr", "error while retrieving individual subscription\n");
2882 g_string_append_printf(subout
, "Class %s Instance %s Recipient %s<br>",
2883 subs
.zsub_class
, subs
.zsub_classinst
,
2884 subs
.zsub_recipient
);
2886 purple_notify_formatted(gc
, title
, title
, NULL
, subout
->str
, NULL
, NULL
);
2888 g_string_free(subout
, TRUE
);
2891 purple_notify_error(gc
, "", "tzc doesn't support this action",
2892 NULL
, purple_request_cpar_from_connection(gc
));
2897 static GList
*zephyr_get_actions(PurpleConnection
*gc
)
2900 PurpleProtocolAction
*act
= NULL
;
2902 act
= purple_protocol_action_new(_("Resubscribe"), zephyr_action_resubscribe
);
2903 list
= g_list_append(list
, act
);
2905 act
= purple_protocol_action_new(_("Retrieve subscriptions from server"), zephyr_action_get_subs_from_server
);
2906 list
= g_list_append(list
,act
);
2913 zephyr_protocol_init(ZephyrProtocol
*self
)
2915 PurpleProtocol
*protocol
= PURPLE_PROTOCOL(self
);
2916 PurpleAccountOption
*option
;
2917 const gchar
*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",
2946 protocol
->account_options
= g_list_append(protocol
->account_options
, option
);
2948 option
= purple_account_option_string_new(_("Encoding"), "encoding", ZEPHYR_FALLBACK_CHARSET
);
2949 protocol
->account_options
= g_list_append(protocol
->account_options
, option
);
2954 zephyr_protocol_class_init(ZephyrProtocolClass
*klass
)
2956 PurpleProtocolClass
*protocol_class
= PURPLE_PROTOCOL_CLASS(klass
);
2958 protocol_class
->login
= zephyr_login
;
2959 protocol_class
->close
= zephyr_close
;
2960 protocol_class
->status_types
= zephyr_status_types
;
2961 protocol_class
->list_icon
= zephyr_list_icon
;
2966 zephyr_protocol_class_finalize(G_GNUC_UNUSED ZephyrProtocolClass
*klass
)
2972 zephyr_protocol_client_iface_init(PurpleProtocolClientInterface
*client_iface
)
2974 client_iface
->get_actions
= zephyr_get_actions
;
2975 client_iface
->normalize
= zephyr_normalize
;
2976 client_iface
->find_blist_chat
= zephyr_find_blist_chat
;
2981 zephyr_protocol_server_iface_init(PurpleProtocolServerInterface
*server_iface
)
2983 server_iface
->get_info
= zephyr_zloc
;
2984 server_iface
->set_status
= zephyr_set_status
;
2986 server_iface
->set_info
= NULL
; /* XXX Location? */
2987 server_iface
->set_buddy_icon
= NULL
; /* XXX */
2992 zephyr_protocol_im_iface_init(PurpleProtocolIMInterface
*im_iface
)
2994 im_iface
->send
= zephyr_send_im
;
2995 im_iface
->send_typing
= zephyr_send_typing
;
3000 zephyr_protocol_chat_iface_init(PurpleProtocolChatInterface
*chat_iface
)
3002 chat_iface
->info
= zephyr_chat_info
;
3003 chat_iface
->join
= zephyr_join_chat
;
3004 chat_iface
->get_name
= zephyr_get_chat_name
;
3005 chat_iface
->leave
= zephyr_chat_leave
;
3006 chat_iface
->send
= zephyr_chat_send
;
3007 chat_iface
->set_topic
= zephyr_chat_set_topic
;
3009 chat_iface
->get_user_real_name
= NULL
; /* XXX */
3013 G_DEFINE_DYNAMIC_TYPE_EXTENDED(
3014 ZephyrProtocol
, zephyr_protocol
, PURPLE_TYPE_PROTOCOL
, 0,
3016 G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT
,
3017 zephyr_protocol_client_iface_init
)
3019 G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_SERVER
,
3020 zephyr_protocol_server_iface_init
)
3022 G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_IM
,
3023 zephyr_protocol_im_iface_init
)
3025 G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CHAT
,
3026 zephyr_protocol_chat_iface_init
));
3028 static PurplePluginInfo
*plugin_query(GError
**error
)
3030 return purple_plugin_info_new(
3031 "id", "prpl-zephyr",
3032 "name", "Zephyr Protocol",
3033 "version", DISPLAY_VERSION
,
3034 "category", N_("Protocol"),
3035 "summary", N_("Zephyr Protocol Plugin"),
3036 "description", N_("Zephyr Protocol Plugin"),
3037 "website", PURPLE_WEBSITE
,
3038 "abi-version", PURPLE_ABI_VERSION
,
3039 "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL
|
3040 PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD
,
3047 plugin_load(PurplePlugin
*plugin
, GError
**error
)
3049 zephyr_protocol_register_type(G_TYPE_MODULE(plugin
));
3051 my_protocol
= purple_protocols_add(ZEPHYR_TYPE_PROTOCOL
, error
);
3055 zephyr_register_slash_commands();
3062 plugin_unload(PurplePlugin
*plugin
, GError
**error
)
3064 zephyr_unregister_slash_commands();
3066 if (!purple_protocols_remove(my_protocol
, error
))
3073 PURPLE_PLUGIN_INIT(zephyr
, plugin_query
, plugin_load
, plugin_unload
);