/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ * Copyright (C) 1998-2001, Mark Spencer <markster@marko.net> * Some code borrowed from GtkZephyr, by * Jag/Sean Dilda <agrajag@linuxpower.org>/<smdilda@unity.ncsu.edu> * http://gtkzephyr.linuxpower.org/ * Some code borrowed from kzephyr, by * Chris Colohan <colohan+@cs.cmu.edu> * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA #include "libpurple/internal.h" #include "purpleaccountoption.h" #define ZEPHYR_FALLBACK_CHARSET "ISO-8859-1" /* these are deliberately high, since most people don't send multiple "PING"s */ #define ZEPHYR_TYPING_SEND_TIMEOUT 15 #define ZEPHYR_TYPING_RECV_TIMEOUT 10 #define ZEPHYR_FD_WRITE 1 static PurpleProtocol * my_protocol = NULL ; static GSList * cmds = NULL ; extern Code_t ZGetLocations ( ZLocations_t * , int * ); extern Code_t ZSetLocation ( char * ); extern Code_t ZUnsetLocation ( void ); extern Code_t ZGetSubscriptions ( ZSubscription_t * , int * ); extern char __Zephyr_realm []; typedef struct _zframe zframe ; typedef struct _zephyr_triple zephyr_triple ; typedef struct _zephyr_account zephyr_account ; typedef struct _parse_tree parse_tree ; PURPLE_ZEPHYR_NONE , /* Non-kerberized ZEPH0.2 */ PURPLE_ZEPHYR_KRB4 , /* ZEPH0.2 w/ KRB4 support */ PURPLE_ZEPHYR_TZC , /* tzc executable proxy */ PURPLE_ZEPHYR_INTERGALACTIC_KRB4 /* Kerberized ZEPH0.3 */ } zephyr_connection_type ; char * galaxy ; /* not yet useful */ char * krbtkfile ; /* not yet useful */ GList * pending_zloc_names ; char ourhost [ HOST_NAME_MAX + 1 ]; char ourhostcanon [ HOST_NAME_MAX + 1 ]; zephyr_connection_type connection_type ; parse_tree * children [ MAXCHILDREN ]; parse_tree null_parse_tree = { #define use_none(zephyr) ((zephyr->connection_type == PURPLE_ZEPHYR_NONE)?1:0) #define use_krb4(zephyr) ((zephyr->connection_type == PURPLE_ZEPHYR_KRB4)?1:0) #define use_tzc(zephyr) ((zephyr->connection_type == PURPLE_ZEPHYR_TZC)?1:0) #define use_zeph02(zephyr) ( (zephyr->connection_type == PURPLE_ZEPHYR_NONE)?1: ((zephyr->connection_type == PURPLE_ZEPHYR_KRB4)?1:0)) /* struct I need for zephyr_to_html */ /* true for everything but @color, since inside the parens of that one is /* </i>, </font>, </b>, etc. */ /* text including the opening html thingie. */ struct _zframe * enclosing ; #define z_call(func) if (func != ZERR_NONE)\ #define z_call_r(func) if (func != ZERR_NONE)\ #define z_call_s(func, err) if (func != ZERR_NONE) {\ purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, err);\ extern const char * username ; static Code_t zephyr_subscribe_to ( zephyr_account * zephyr , char * class , char * instance , char * recipient , char * galaxy ) { /* ((tzcfodder . subscribe) ("class" "instance" "recipient")) */ gchar * zsubstr = g_strdup_printf ( "((tzcfodder . subscribe) ( \" %s \" \" %s \" \" %s \" )) \n " , class , instance , recipient ); size_t len = strlen ( zsubstr ); result = write ( zephyr -> totzc [ ZEPHYR_FD_WRITE ], zsubstr , len ); purple_debug_error ( "zephyr" , "Unable to write a message: %s \n " , g_strerror ( errno )); if ( use_zeph02 ( zephyr )) { sub . zsub_classinst = instance ; sub . zsub_recipient = recipient ; ret_val = ZSubscribeTo ( & sub , 1 , 0 ); char * local_zephyr_normalize ( zephyr_account * zephyr , const char * ); static void zephyr_chat_set_topic ( PurpleConnection * gc , int id , const char * topic ); char * zephyr_tzc_deescape_str ( const char * message ); static char * zephyr_strip_local_realm ( zephyr_account * zephyr , const char * user ){ Takes in a username of the form username or username@realm username, if there is no realm, or the realm is the local realm username@realm if there is a realm and it is foreign char * tmp = g_strdup ( user ); char * at = strchr ( tmp , '@' ); if ( at && ! g_ascii_strcasecmp ( at + 1 , zephyr -> realm )) { /* We're passed in a username of the form user@users-realm */ /* We're passed in a username of the form user or user@foreign-realm */ /* this is so bad, and if Zephyr weren't so fucked up to begin with I * wouldn't do this. but it is so i will. */ static void handle_unknown ( ZNotice_t * notice ) purple_debug_error ( "zephyr" , "z_packet: %s \n " , notice -> z_packet ); purple_debug_error ( "zephyr" , "z_version: %s \n " , notice -> z_version ); purple_debug_error ( "zephyr" , "z_kind: %d \n " , ( int )( notice -> z_kind )); purple_debug_error ( "zephyr" , "z_class: %s \n " , notice -> z_class ); purple_debug_error ( "zephyr" , "z_class_inst: %s \n " , notice -> z_class_inst ); purple_debug_error ( "zephyr" , "z_opcode: %s \n " , notice -> z_opcode ); purple_debug_error ( "zephyr" , "z_sender: %s \n " , notice -> z_sender ); purple_debug_error ( "zephyr" , "z_recipient: %s \n " , notice -> z_recipient ); purple_debug_error ( "zephyr" , "z_message: %s \n " , notice -> z_message ); purple_debug_error ( "zephyr" , "z_message_len: %d \n " , notice -> z_message_len ); static zephyr_triple * new_triple ( zephyr_account * zephyr , const char * c , const char * i , const char * r ) zt = g_new0 ( zephyr_triple , 1 ); zt -> instance = g_strdup ( i ); zt -> recipient = g_strdup ( r ); zt -> name = g_strdup_printf ( "%s,%s,%s" , c , i ? i : "" , r ? r : "" ); zt -> id = ++ ( zephyr -> last_id ); static void free_triple ( zephyr_triple * zt ) /* returns true if zt1 is a subset of zt2. This function is used to determine whether a zephyr sent to zt1 should be placed in the chat iff. the classnames are identical ignoring case AND. the instance names are identical (ignoring case), or zt2->instance is *. AND. the recipient names are identical static gboolean triple_subset ( zephyr_triple * zt1 , zephyr_triple * zt2 ) purple_debug_error ( "zephyr" , "zt2 doesn't exist \n " ); purple_debug_error ( "zephyr" , "zt1 doesn't exist \n " ); purple_debug_error ( "zephyr" , "zt1c doesn't exist \n " ); purple_debug_error ( "zephyr" , "zt1i doesn't exist \n " ); purple_debug_error ( "zephyr" , "zt1r doesn't exist \n " ); purple_debug_error ( "zephyr" , "zt2c doesn't exist \n " ); purple_debug_error ( "zephyr" , "zt2r doesn't exist \n " ); purple_debug_error ( "zephyr" , "zt2i doesn't exist \n " ); if ( g_ascii_strcasecmp ( zt2 -> class , zt1 -> class )) { if ( g_ascii_strcasecmp ( zt2 -> instance , zt1 -> instance ) && g_ascii_strcasecmp ( zt2 -> instance , "*" )) { if ( g_ascii_strcasecmp ( zt2 -> recipient , zt1 -> recipient )) { 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 ); static zephyr_triple * find_sub_by_triple ( zephyr_account * zephyr , zephyr_triple * zt ) GSList * curr = zephyr -> subscrips ; if ( triple_subset ( zt , curr_t )) static zephyr_triple * find_sub_by_id ( zephyr_account * zephyr , int id ) GSList * curr = zephyr -> subscrips ; Converts strings to utf-8 if necessary using user specified encoding static gchar * zephyr_recv_convert ( PurpleConnection * gc , gchar * string ) zephyr_account * zephyr = purple_connection_get_protocol_data ( gc ); if ( g_utf8_validate ( string , -1 , NULL )) { utf8 = g_convert ( string , -1 , "UTF-8" , zephyr -> encoding , NULL , NULL , & err ); purple_debug_error ( "zephyr" , "recv conversion error: %s \n " , err -> message ); utf8 = g_strdup ( _ ( "(There was an error converting this message. Check the 'Encoding' option in the Account Editor)" )); /* This parses HTML formatting (put out by one of the gtkimhtml widgets And converts it to zephyr formatting. It currently deals properly with <b>, <br>, <i>, <font face=...>, <font color=...>, It ignores <font back=...> <font size = "1 or 2" -> @small <a href is dealt with by outputting "description <link>" or just "description" as appropriate static char * html_to_zephyr ( const char * message ) frames = g_new ( zframe , 1 ); frames -> text = g_string_new ( "" ); frames -> enclosing = NULL ; frames -> has_closer = FALSE ; frames -> closer_mask = 15 ; purple_debug_info ( "zephyr" , "html received %s \n " , message ); if ( frames -> closing && ! g_ascii_strncasecmp ( message , frames -> closing , strlen ( frames -> closing ))) { message += strlen ( frames -> closing ); frames = frames -> enclosing ; frames -> href = popped -> text ; g_string_append ( frames -> text , popped -> env ); if ( popped -> has_closer ) { g_string_append_c ( frames -> text , ( popped -> closer_mask & 1 ) ? '{' : ( popped -> closer_mask & 2 ) ? '[' : ( popped -> closer_mask & 4 ) ? '(' : g_string_append ( frames -> text , popped -> text -> str ); int text_len = strlen ( popped -> text -> str ), href_len = strlen ( popped -> href -> str ); if ( ! (( text_len == href_len && ! strncmp ( popped -> href -> str , popped -> text -> str , text_len )) || ( 7 + text_len == href_len && ! strncmp ( popped -> href -> str , "http://" , 7 ) && ! strncmp ( popped -> href -> str + 7 , popped -> text -> str , text_len )) || ( 7 + text_len == href_len && ! strncmp ( popped -> href -> str , "mailto:" , 7 ) && ! strncmp ( popped -> href -> str + 7 , popped -> text -> str , text_len )))) { g_string_append ( frames -> text , " <" ); g_string_append ( frames -> text , popped -> href -> str ); if ( popped -> closer_mask & ~ 8 ) { g_string_append_c ( frames -> text , '>' ); popped -> closer_mask &= ~ 8 ; g_string_append ( frames -> text , "@{>}" ); g_string_free ( popped -> href , TRUE ); if ( popped -> has_closer ) { g_string_append_c ( frames -> text , ( popped -> closer_mask & 1 ) ? '}' : ( popped -> closer_mask & 2 ) ? ']' : ( popped -> closer_mask & 4 ) ? ')' : frames -> closer_mask = popped -> closer_mask ; g_string_free ( popped -> text , TRUE ); } else if ( * message == '<' ) { if ( ! g_ascii_strncasecmp ( message + 1 , "i>" , 2 )) { new_f = g_new ( zframe , 1 ); new_f -> enclosing = frames ; new_f -> text = g_string_new ( "" ); new_f -> has_closer = TRUE ; } else if ( ! g_ascii_strncasecmp ( message + 1 , "b>" , 2 )) { new_f = g_new ( zframe , 1 ); new_f -> enclosing = frames ; new_f -> text = g_string_new ( "" ); new_f -> has_closer = TRUE ; } else if ( ! g_ascii_strncasecmp ( message + 1 , "br>" , 3 )) { g_string_append_c ( frames -> text , '\n' ); } else if ( ! g_ascii_strncasecmp ( message + 1 , "a href= \" " , 8 )) { new_f = g_new ( zframe , 1 ); new_f -> enclosing = frames ; new_f -> text = g_string_new ( "" ); new_f -> has_closer = FALSE ; new_f -> closer_mask = frames -> closer_mask ; new_f = g_new ( zframe , 1 ); new_f -> enclosing = frames ; new_f -> text = g_string_new ( "" ); new_f -> has_closer = FALSE ; new_f -> closer_mask = frames -> closer_mask ; } else if ( ! g_ascii_strncasecmp ( message + 1 , "font" , 4 )) { new_f = g_new ( zframe , 1 ); new_f -> enclosing = frames ; new_f -> text = g_string_new ( "" ); new_f -> closing = "</font>" ; new_f -> has_closer = TRUE ; if ( ! g_ascii_strncasecmp ( message , "color= \" " , 7 )) { new_f = g_new ( zframe , 1 ); new_f -> enclosing = frames ; new_f -> text = g_string_new ( "" ); new_f -> has_closer = TRUE ; } else if ( ! g_ascii_strncasecmp ( message , "face= \" " , 6 )) { new_f = g_new ( zframe , 1 ); new_f -> enclosing = frames ; new_f -> text = g_string_new ( "" ); new_f -> has_closer = TRUE ; } else if ( ! g_ascii_strncasecmp ( message , "size= \" " , 6 )) { if (( * message == '1' ) || ( * message == '2' )) { } else if (( * message == '3' ) } else if (( * message == '5' ) new_f -> has_closer = FALSE ; new_f -> closer_mask = frames -> closer_mask ; /* Drop all unrecognized/misparsed font tags */ new_f -> has_closer = FALSE ; new_f -> closer_mask = frames -> closer_mask ; while ( g_ascii_strncasecmp ( message , " \" >" , 2 ) != 0 ) { /* Catch all for all unrecognized/misparsed <foo> tage */ g_string_append_c ( frames -> text , * message ++ ); } else if ( * message == '@' ) { g_string_append ( frames -> text , "@@" ); } else if ( * message == '}' ) { if ( frames -> closer_mask & ~ 1 ) { frames -> closer_mask &= ~ 1 ; g_string_append_c ( frames -> text , * message ++ ); g_string_append ( frames -> text , "@[}]" ); } else if ( * message == ']' ) { if ( frames -> closer_mask & ~ 2 ) { frames -> closer_mask &= ~ 2 ; g_string_append_c ( frames -> text , * message ++ ); g_string_append ( frames -> text , "@{]}" ); } else if ( * message == ')' ) { if ( frames -> closer_mask & ~ 4 ) { frames -> closer_mask &= ~ 4 ; g_string_append_c ( frames -> text , * message ++ ); g_string_append ( frames -> text , "@{)}" ); } else if ( ! g_ascii_strncasecmp ( message , ">" , 4 )) { if ( frames -> closer_mask & ~ 8 ) { frames -> closer_mask &= ~ 8 ; g_string_append_c ( frames -> text , * message ++ ); g_string_append ( frames -> text , "@{>}" ); g_string_append_c ( frames -> text , * message ++ ); g_string_free ( frames -> text , FALSE ); purple_debug_info ( "zephyr" , "zephyr outputted %s \n " , ret ); /* this parses zephyr formatting and converts it to html. For example, if * you pass in "@{@color(blue)@i(hello)}" you should get out * "<font color=blue><i>hello</i></font>". */ static char * zephyr_to_html ( const char * message ) frames = g_new ( zframe , 1 ); frames -> text = g_string_new ( "" ); frames -> enclosing = NULL ; frames -> has_closer = FALSE ; if ( * message == '@' && message [ 1 ] == '@' ) { g_string_append ( frames -> text , "@" ); } else if ( * message == '@' ) { for ( end = 1 ; message [ end ] && ( isalnum ( message [ end ]) || message [ end ] == '_' ); end ++ ); ( message [ end ] == '{' || message [ end ] == '[' || message [ end ] == '(' || ! g_ascii_strncasecmp ( message + end , "<" , 4 ))) { g_snprintf ( buf , end , "%s" , message + 1 ); new_f = g_new ( zframe , 1 ); new_f -> enclosing = frames ; new_f -> has_closer = TRUE ; new_f -> closer = ( * message == '{' ? "}" : message += ( * message == '&' ? 4 : 1 ); if ( ! g_ascii_strcasecmp ( buf , "italic" ) || ! g_ascii_strcasecmp ( buf , "i" )) { new_f -> text = g_string_new ( "<i>" ); } else if ( ! g_ascii_strcasecmp ( buf , "small" )) { new_f -> text = g_string_new ( "<font size= \" 1 \" >" ); new_f -> closing = "</font>" ; } else if ( ! g_ascii_strcasecmp ( buf , "medium" )) { new_f -> text = g_string_new ( "<font size= \" 3 \" >" ); new_f -> closing = "</font>" ; } else if ( ! g_ascii_strcasecmp ( buf , "large" )) { new_f -> text = g_string_new ( "<font size= \" 7 \" >" ); new_f -> closing = "</font>" ; } else if ( ! g_ascii_strcasecmp ( buf , "bold" ) || ! g_ascii_strcasecmp ( buf , "b" )) { new_f -> text = g_string_new ( "<b>" ); } else if ( ! g_ascii_strcasecmp ( buf , "font" )) { extra_f = g_new ( zframe , 1 ); extra_f -> enclosing = frames ; new_f -> enclosing = extra_f ; extra_f -> text = g_string_new ( "" ); extra_f -> has_closer = FALSE ; extra_f -> closer = frames -> closer ; extra_f -> closing = "</font>" ; new_f -> text = g_string_new ( "<font face= \" " ); } else if ( ! g_ascii_strcasecmp ( buf , "color" )) { extra_f = g_new ( zframe , 1 ); extra_f -> enclosing = frames ; new_f -> enclosing = extra_f ; extra_f -> text = g_string_new ( "" ); extra_f -> has_closer = FALSE ; extra_f -> closer = frames -> closer ; extra_f -> closing = "</font>" ; new_f -> text = g_string_new ( "<font color= \" " ); new_f -> text = g_string_new ( "" ); /* Not a formatting tag, add the character as normal. */ g_string_append_c ( frames -> text , * message ++ ); } else if ( frames -> closer && ! g_ascii_strncasecmp ( message , frames -> closer , strlen ( frames -> closer ))) { gboolean last_had_closer ; message += strlen ( frames -> closer ); frames = frames -> enclosing ; g_string_append ( frames -> text , popped -> text -> str ); g_string_append ( frames -> text , popped -> closing ); g_string_free ( popped -> text , TRUE ); last_had_closer = popped -> has_closer ; } while ( frames -> enclosing && ! last_had_closer ); g_string_append_c ( frames -> text , * message ); } else if ( * message == '\n' ) { g_string_append ( frames -> text , "<br>" ); g_string_append_c ( frames -> text , * message ++ ); /* go through all the stuff that they didn't close */ while ( frames -> enclosing ) { g_string_append ( frames -> enclosing -> text , frames -> text -> str ); g_string_append ( frames -> enclosing -> text , frames -> closing ); g_string_free ( frames -> text , TRUE ); frames = frames -> enclosing ; g_string_free ( frames -> text , FALSE ); static gboolean pending_zloc ( zephyr_account * zephyr , const char * who ) char * normalized_who = local_zephyr_normalize ( zephyr , who ); curr = g_list_find_custom ( zephyr -> pending_zloc_names , normalized_who , ( GCompareFunc ) g_ascii_strcasecmp ); g_free (( char * ) curr -> data ); zephyr -> pending_zloc_names = g_list_delete_link ( zephyr -> pending_zloc_names , curr ); /* Called when the server notifies us a message couldn't get sent */ static void message_failed ( PurpleConnection * gc , ZNotice_t * notice , struct sockaddr_in from ) if ( g_ascii_strcasecmp ( notice -> z_class , "message" )) { gchar * chat_failed = g_strdup_printf ( _ ( "Unable to send to chat %s,%s,%s" ), notice -> z_class , notice -> z_class_inst , purple_notify_error ( gc , "" , chat_failed , NULL , purple_request_cpar_from_connection ( gc )); purple_notify_error ( gc , notice -> z_recipient , _ ( "User is offline" ), NULL , purple_request_cpar_from_connection ( gc )); static void handle_message ( PurpleConnection * gc , ZNotice_t * notice_p ) zephyr_account * zephyr = purple_connection_get_protocol_data ( gc ); memcpy ( & notice , notice_p , sizeof ( notice )); /* TODO - use pointer? */ if ( ! g_ascii_strcasecmp ( notice . z_class , LOGIN_CLASS )) { /* well, we'll be updating in 20 seconds anyway, might as well ignore this. */ } else if ( ! g_ascii_strcasecmp ( notice . z_class , LOCATE_CLASS )) { if ( ! g_ascii_strcasecmp ( notice . z_opcode , LOCATE_LOCATE )) { /* XXX add real error reporting */ if ( ZParseLocations ( & notice , NULL , & nlocs , & user ) != ZERR_NONE ) if (( b = purple_blist_find_buddy ( purple_connection_get_account ( gc ), user )) == NULL ) { char * stripped_user = zephyr_strip_local_realm ( zephyr , user ); b = purple_blist_find_buddy ( purple_connection_get_account ( gc ), stripped_user ); bname = b ? purple_buddy_get_name ( b ) : NULL ; if (( b && pending_zloc ( zephyr , bname )) || pending_zloc ( zephyr , user )) { PurpleNotifyUserInfo * user_info = purple_notify_user_info_new (); /* TODO: Check whether it's correct to call add_pair_html, or if we should be using add_pair_plaintext */ purple_notify_user_info_add_pair_html ( user_info , _ ( "User" ), ( b ? bname : user )); balias = purple_buddy_get_local_alias ( b ); purple_notify_user_info_add_pair_plaintext ( user_info , _ ( "Alias" ), balias ); purple_notify_user_info_add_pair_plaintext ( user_info , NULL , _ ( "Hidden or not logged-in" )); for (; nlocs > 0 ; nlocs -- ) { /* XXX add real error reporting */ ZGetLocations ( & locs , & one ); /* TODO: Need to escape locs.host and locs.time? */ tmp = g_strdup_printf ( _ ( "<br>At %s since %s" ), locs . host , locs . time ); purple_notify_user_info_add_pair_html ( user_info , _ ( "Location" ), tmp ); purple_notify_userinfo ( gc , ( b ? bname : user ), purple_notify_user_info_destroy ( user_info ); purple_protocol_got_user_status ( purple_connection_get_account ( gc ), b ? bname : user , "available" , NULL ); purple_protocol_got_user_status ( purple_connection_get_account ( gc ), b ? bname : user , "offline" , NULL ); PurpleChatConversation * gcc ; char * ptr = ( char * ) notice . z_message + ( strlen ( notice . z_message ) + 1 ); int signature_length = strlen ( notice . z_message ); PurpleMessageFlags flags = 0 ; /* Need to deal with 0 length messages to handle typing notification (OPCODE) ping messages */ /* One field zephyrs would have caused purple to crash */ if ( ( notice . z_message_len == 0 ) || ( signature_length >= notice . z_message_len - 1 )) { purple_debug_info ( "zephyr" , "message_size %d %d %d \n " , len , notice . z_message_len , signature_length ); len = notice . z_message_len - ( signature_length + 1 ); purple_debug_info ( "zephyr" , "message_size %d %d %d \n " , len , notice . z_message_len , signature_length ); g_snprintf ( buf , len + 1 , "%s" , ptr ); tmpescape = g_markup_escape_text ( buf , -1 ); buf2 = zephyr_to_html ( tmpescape ); buf3 = zephyr_recv_convert ( gc , buf2 ); stripped_sender = zephyr_strip_local_realm ( zephyr , notice . z_sender ); if ( ! g_ascii_strcasecmp ( notice . z_class , "MESSAGE" ) && ! g_ascii_strcasecmp ( notice . z_class_inst , "PERSONAL" ) && ! g_ascii_strcasecmp ( notice . z_recipient , zephyr -> username )) { if ( ! g_ascii_strcasecmp ( notice . z_message , "Automated reply:" )) flags |= PURPLE_MESSAGE_AUTO_RESP ; if ( ! g_ascii_strcasecmp ( notice . z_opcode , "PING" )) purple_serv_got_typing ( gc , stripped_sender , ZEPHYR_TYPING_RECV_TIMEOUT , PURPLE_IM_TYPING ); purple_serv_got_im ( gc , stripped_sender , buf3 , flags , time ( NULL )); zephyr_triple * zt1 , * zt2 ; zephyr_account * zephyr = purple_connection_get_protocol_data ( gc ); zt1 = new_triple ( zephyr , notice . z_class , notice . z_class_inst , notice . z_recipient ); zt2 = find_sub_by_triple ( zephyr , zt1 ); /* This is a server supplied subscription */ zephyr -> subscrips = g_slist_append ( zephyr -> subscrips , new_triple ( zephyr , zt1 -> class , zt1 -> instance , zt1 -> recipient )); zt2 = find_sub_by_triple ( zephyr , zt1 ); purple_serv_got_joined_chat ( gc , zt2 -> id , zt2 -> name ); zephyr_chat_set_topic ( gc , zt2 -> id , notice . z_class_inst ); if ( ! g_ascii_strcasecmp ( notice . z_class_inst , "PERSONAL" )) send_inst_utf8 = g_strdup ( stripped_sender ); send_inst = g_strdup_printf ( "[%s] %s" , notice . z_class_inst , stripped_sender ); send_inst_utf8 = zephyr_recv_convert ( gc , send_inst ); purple_debug_error ( "zephyr" , "Failed to convert instance for sender %s. \n " , stripped_sender ); send_inst_utf8 = g_strdup ( stripped_sender ); gcc = purple_conversations_find_chat_with_account ( zt2 -> name , purple_connection_get_account ( gc )); #define INET_ADDRSTRLEN 16 if ( ! purple_chat_conversation_has_user ( gcc , stripped_sender )) { gchar ipaddr [ INET_ADDRSTRLEN ]; inet_ntop ( AF_INET , & notice . z_sender_addr . s_addr , ipaddr , sizeof ( ipaddr )); memcpy ( ipaddr , inet_ntoa ( notice . z_sender_addr ), sizeof ( ipaddr )); purple_chat_conversation_add_user ( gcc , stripped_sender , ipaddr , PURPLE_CHAT_USER_NONE , TRUE ); purple_serv_got_chat_in ( gc , zt2 -> id , send_inst_utf8 , PURPLE_MESSAGE_RECV , buf3 , time ( NULL )); static int free_parse_tree ( parse_tree * tree ) { for ( i = 0 ; i < tree -> num_children ; i ++ ){ free_parse_tree ( tree -> children [ i ]); if ( tree != & null_parse_tree ) { static parse_tree * tree_child ( parse_tree * tree , int index ) { if ( index < tree -> num_children ) { return tree -> children [ index ]; static parse_tree * find_node ( parse_tree * ptree , gchar * key ) tc = tree_child ( ptree , 0 ) -> contents ; /* g_strcasecmp() is deprecated. What is the encoding here??? */ if ( ptree -> num_children > 0 && tc && ! g_ascii_strcasecmp ( tc , key )) { parse_tree * result = & null_parse_tree ; for ( i = 0 ; i < ptree -> num_children ; i ++ ) { result = find_node ( ptree -> children [ i ], key ); if ( result != & null_parse_tree ) { static parse_tree * parse_buffer ( gchar * source , gboolean do_parse ) { parse_tree * ptree = g_new0 ( parse_tree , 1 ); while ( p < strlen ( source )) { if ( g_ascii_isspace ( source [ p ]) || source [ p ] == '\001' ) { while ( source [ p ] != '\n' && p < strlen ( source )) { gboolean in_quote = FALSE ; gboolean escape_next = FALSE ; while ( ! ( source [ end ] == ')' && nesting == 0 && ! in_quote ) && end < strlen ( source )) { if ( source [ end ] == '\\' ) { while ( source [ end ] != end_char && end < strlen ( source )) { newstr = g_new0 ( gchar , end + 1 - p ); strncpy ( newstr , source + p , end - p ); if ( ptree -> num_children < MAXCHILDREN ) { /* In case we surpass maxchildren, ignore this */ ptree -> children [ ptree -> num_children ++ ] = parse_buffer ( newstr , do_parse ); purple_debug_error ( "zephyr" , "too many children in tzc output. skipping \n " ); /* XXX does this have to be strdup'd */ ptree -> contents = g_strdup ( source ); static parse_tree * read_from_tzc ( zephyr_account * zephyr ){ char * buf = ( char * ) calloc ( bufsize , 1 ); parse_tree * incoming_msg ; FD_SET ( zephyr -> fromtzc [ ZEPHYR_FD_READ ], & rfds ); while ( select ( zephyr -> fromtzc [ ZEPHYR_FD_READ ] + 1 , & rfds , NULL , NULL , & tv )) { if ( read ( zephyr -> fromtzc [ ZEPHYR_FD_READ ], bufcur , 1 ) != 1 ) { purple_debug_error ( "zephyr" , "couldn't read \n " ); purple_connection_error ( purple_account_get_connection ( zephyr -> account ), PURPLE_CONNECTION_ERROR_NETWORK_ERROR , "couldn't read" ); if (( bufcur - buf ) > ( bufsize - 1 )) { if (( buf = realloc ( buf , bufsize * 2 )) == NULL ) { purple_debug_error ( "zephyr" , "Ran out of memory \n " ); incoming_msg = parse_buffer ( buf , TRUE ); static gint check_notify_tzc ( gpointer data ) PurpleConnection * gc = ( PurpleConnection * ) data ; zephyr_account * zephyr = purple_connection_get_protocol_data ( gc ); parse_tree * newparsetree = read_from_tzc ( zephyr ); if ( newparsetree != NULL ) { if ( ( spewtype = tree_child ( find_node ( newparsetree , "tzcspew" ), 2 ) -> contents ) ) { if ( ! g_ascii_strncasecmp ( spewtype , "message" , 7 )) { parse_tree * msgnode = tree_child ( find_node ( newparsetree , "message" ), 2 ); parse_tree * bodynode = tree_child ( msgnode , 1 ); /* char *zsig = g_strdup(" "); */ /* purple doesn't care about zsigs */ char * msg = zephyr_tzc_deescape_str ( bodynode -> contents ); size_t bufsize = strlen ( msg ) + 3 ; char * buf = g_new0 ( char , bufsize ); g_snprintf ( buf , 1 + strlen ( msg ) + 2 , " %c%s" , '\0' , msg ); memset (( char * ) & notice , 0 , sizeof ( notice )); notice . z_opcode = tree_child ( find_node ( newparsetree , "opcode" ), 2 ) -> contents ; notice . z_class = zephyr_tzc_deescape_str ( tree_child ( find_node ( newparsetree , "class" ), 2 ) -> contents ); notice . z_class_inst = tree_child ( find_node ( newparsetree , "instance" ), 2 ) -> contents ; notice . z_recipient = local_zephyr_normalize ( zephyr , tree_child ( find_node ( newparsetree , "recipient" ), 2 ) -> contents ); notice . z_sender = local_zephyr_normalize ( zephyr , tree_child ( find_node ( newparsetree , "sender" ), 2 ) -> contents ); notice . z_default_format = "Class $class, Instance $instance: \n " "To: @bold($recipient) at $time $date \n " "From: @bold($1) <$sender> \n\n $2" ; notice . z_message_len = strlen ( msg ) + 3 ; handle_message ( gc , & notice ); /* free_parse_tree(msgnode); free_parse_tree(bodynode); else if ( ! g_ascii_strncasecmp ( spewtype , "zlocation" , 9 )) { /* check_loc or zephyr_zloc respectively */ user = tree_child ( find_node ( newparsetree , "user" ), 2 ) -> contents ; if (( b = purple_blist_find_buddy ( purple_connection_get_account ( gc ), user )) == NULL ) { gchar * stripped_user = zephyr_strip_local_realm ( zephyr , user ); b = purple_blist_find_buddy ( purple_connection_get_account ( gc ), stripped_user ); locations = find_node ( newparsetree , "locations" ); locval = tree_child ( tree_child ( tree_child ( tree_child ( locations , 2 ), 0 ), 0 ), 2 ) -> contents ; if ( ! locval || ! g_ascii_strcasecmp ( locval , " " ) || !* locval ) { bname = b ? purple_buddy_get_name ( b ) : NULL ; if (( b && pending_zloc ( zephyr , bname )) || pending_zloc ( zephyr , user ) || pending_zloc ( zephyr , local_zephyr_normalize ( zephyr , user ))){ PurpleNotifyUserInfo * user_info = purple_notify_user_info_new (); /* TODO: Check whether it's correct to call add_pair_html, or if we should be using add_pair_plaintext */ purple_notify_user_info_add_pair_html ( user_info , _ ( "User" ), ( b ? bname : user )); balias = b ? purple_buddy_get_local_alias ( b ) : NULL ; purple_notify_user_info_add_pair_plaintext ( user_info , _ ( "Alias" ), balias ); purple_notify_user_info_add_pair_plaintext ( user_info , NULL , _ ( "Hidden or not logged-in" )); /* TODO: Need to escape the two strings that make up tmp? */ tmp = g_strdup_printf ( _ ( "<br>At %s since %s" ), tree_child ( tree_child ( tree_child ( tree_child ( locations , 2 ), 0 ), 0 ), 2 ) -> contents , tree_child ( tree_child ( tree_child ( tree_child ( locations , 2 ), 0 ), 2 ), 2 ) -> contents ); purple_notify_user_info_add_pair_html ( user_info , _ ( "Location" ), tmp ); purple_notify_userinfo ( gc , b ? bname : user , purple_notify_user_info_destroy ( user_info ); purple_protocol_got_user_status ( purple_connection_get_account ( gc ), b ? bname : user , "available" , NULL ); purple_protocol_got_user_status ( purple_connection_get_account ( gc ), b ? bname : user , "offline" , NULL ); else if ( ! g_ascii_strncasecmp ( spewtype , "subscribed" , 10 )) { else if ( ! g_ascii_strncasecmp ( spewtype , "start" , 5 )) { else if ( ! g_ascii_strncasecmp ( spewtype , "error" , 5 )) { free_parse_tree ( newparsetree ); static gint check_notify_zeph02 ( gpointer data ) /* XXX add real error reporting */ PurpleConnection * gc = ( PurpleConnection * ) data ; /* XXX add real error reporting */ z_call_r ( ZReceiveNotice ( & notice , & from )); handle_message ( gc , & notice ); if ( ! ( g_ascii_strcasecmp ( notice . z_message , ZSRVACK_NOTSENT ))) { message_failed ( gc , & notice , from ); purple_debug_error ( "zephyr" , "Client ack received \n " ); handle_unknown ( & notice ); /* XXX: is it really unknown? */ /* we'll just ignore things for now */ purple_debug_error ( "zephyr" , "Unhandled notice. \n " ); /* XXX add real error reporting */ static gint check_loc ( gpointer data ) PurpleConnection * gc = data ; zephyr_account * zephyr = purple_connection_get_protocol_data ( gc ); PurpleAccount * account = purple_connection_get_account ( gc ); for ( buddies = purple_blist_find_buddies ( account , NULL ); buddies ; buddies = g_slist_delete_link ( buddies , buddies )) { PurpleBuddy * b = buddies -> data ; const char * bname = purple_buddy_get_name ( b ); chk = local_zephyr_normalize ( bname ); ZLocateUser ( chk , & numlocs , ZAUTH ); ZGetLocations ( & locations , & one ); serv_got_update ( zgc , bname , 1 , 0 , 0 , 0 , 0 ); static gint check_loc ( gpointer data ) PurpleConnection * gc = ( PurpleConnection * ) data ; zephyr_account * zephyr = purple_connection_get_protocol_data ( gc ); PurpleAccount * account = purple_connection_get_account ( gc ); if ( use_zeph02 ( zephyr )) { memset ( & ( ald . uid ), 0 , sizeof ( ZUnique_Id_t )); for ( buddies = purple_blist_find_buddies ( account , NULL ); buddies ; buddies = g_slist_delete_link ( buddies , buddies )) { PurpleBuddy * b = buddies -> data ; const char * name = purple_buddy_get_name ( b ); chk = local_zephyr_normalize ( zephyr , name ); purple_debug_info ( "zephyr" , "chk: %s b->name %s \n " , chk , name ); /* XXX add real error reporting */ /* doesn't matter if this fails or not; we'll just move on to the next one */ if ( use_zeph02 ( zephyr )) { ZLocateUser ( chk , & numlocs , ZAUTH ); ZGetLocations ( & locations , & one ); purple_protocol_got_user_status ( account , name , "available" , NULL ); purple_protocol_got_user_status ( account , name , "offline" , NULL ); ZRequestLocations ( chk , & ald , UNACKED , ZAUTH ); gchar * zlocstr = g_strdup_printf ( "((tzcfodder . zlocate) \" %s \" ) \n " , chk ); size_t len = strlen ( zlocstr ); size_t result = write ( zephyr -> totzc [ ZEPHYR_FD_WRITE ], zlocstr , len ); purple_debug_error ( "zephyr" , "Unable to write a message: %s \n " , g_strerror ( errno )); /* XXX add real error reporting */ const gchar * exposure = ZGetVariable ( "exposure" ); if ( ! g_ascii_strcasecmp ( exposure , EXPOSE_NONE )) if ( ! g_ascii_strcasecmp ( exposure , EXPOSE_OPSTAFF )) if ( ! g_ascii_strcasecmp ( exposure , EXPOSE_REALMANN )) if ( ! g_ascii_strcasecmp ( exposure , EXPOSE_NETVIS )) if ( ! g_ascii_strcasecmp ( exposure , EXPOSE_NETANN )) static void strip_comments ( char * str ) char * tmp = strchr ( str , '#' ); static void zephyr_inithosts ( zephyr_account * zephyr ) /* XXX This code may not be Win32 clean */ if ( gethostname ( zephyr -> ourhost , sizeof ( zephyr -> ourhost )) == -1 ) { purple_debug_error ( "zephyr" , "unable to retrieve hostname, %%host%% and %%canon%% will be wrong in subscriptions and have been set to unknown \n " ); g_strlcpy ( zephyr -> ourhost , "unknown" , sizeof ( zephyr -> ourhost )); g_strlcpy ( zephyr -> ourhostcanon , "unknown" , sizeof ( zephyr -> ourhostcanon )); if ( ! ( hent = gethostbyname ( zephyr -> ourhost ))) { 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 ); g_strlcpy ( zephyr -> ourhostcanon , zephyr -> ourhost , sizeof ( zephyr -> ourhostcanon )); g_strlcpy ( zephyr -> ourhostcanon , hent -> h_name , sizeof ( zephyr -> ourhostcanon )); static void process_zsubs ( zephyr_account * zephyr ) /* Loads zephyr chats "(subscriptions) from ~/.zephyr.subs, and registers (subscribes to) them on the server */ /* XXX deal with unsubscriptions */ /* XXX deal with punts */ fname = g_strdup_printf ( "%s/.zephyr.subs" , purple_home_dir ()); while ( fgets ( buff , BUFSIZ , f )) { triple = g_strsplit ( buff , "," , 3 ); if ( triple [ 0 ] && triple [ 1 ]) { char * tmp = g_strdup_printf ( "%s" , zephyr -> username ); } else if ( ! g_ascii_strcasecmp ( triple [ 2 ], "%me%" )) { recip = g_strdup_printf ( "%s" , zephyr -> username ); } else if ( ! g_ascii_strcasecmp ( triple [ 2 ], "*" )) { * form of class,instance,* */ } else if ( ! g_ascii_strcasecmp ( triple [ 2 ], tmp )) { /* form of class,instance,aatharuv@ATHENA.MIT.EDU */ recip = g_strdup ( triple [ 2 ]); } else if (( atptr = strchr ( triple [ 2 ], '@' )) != NULL ) { /* form of class,instance,*@ANDREW.CMU.EDU * class,instance,@ANDREW.CMU.EDU * If realm is local realm, blank recipient, else char * realmat = g_strdup_printf ( "@%s" , zephyr -> realm ); if ( ! g_ascii_strcasecmp ( atptr , realmat )) recip = g_strdup ( triple [ 2 ]); if ( ! g_ascii_strcasecmp ( triple [ 0 ], "%host%" )) { z_class = g_strdup ( zephyr -> ourhost ); } else if ( ! g_ascii_strcasecmp ( triple [ 0 ], "%canon%" )) { z_class = g_strdup ( zephyr -> ourhostcanon ); z_class = g_strdup ( triple [ 0 ]); if ( ! g_ascii_strcasecmp ( triple [ 1 ], "%host%" )) { z_instance = g_strdup ( zephyr -> ourhost ); } else if ( ! g_ascii_strcasecmp ( triple [ 1 ], "%canon%" )) { z_instance = g_strdup ( zephyr -> ourhostcanon ); z_instance = g_strdup ( triple [ 1 ]); /* There should be some sort of error report listing classes that couldn't be subbed to. Not important right now though */ if ( zephyr_subscribe_to ( zephyr , z_class , z_instance , recip , z_galaxy ) != ZERR_NONE ) { purple_debug_error ( "zephyr" , "Couldn't subscribe to %s, %s, %s \n " , z_class , z_instance , recip ); zephyr -> subscrips = g_slist_append ( zephyr -> subscrips , new_triple ( zephyr , z_class , z_instance , recip )); /* g_hash_table_destroy(sub_hash_table); */ static void process_anyone ( PurpleConnection * gc ) zephyr_account * zephyr = purple_connection_get_protocol_data ( gc ); gchar buff [ BUFSIZ ], * filename ; if ( ! ( g = purple_blist_find_group ( _ ( "Anyone" )))) { g = purple_group_new ( _ ( "Anyone" )); purple_blist_add_group ( g , NULL ); filename = g_strconcat ( purple_home_dir (), "/.anyone" , NULL ); if (( fd = g_fopen ( filename , "r" )) != NULL ) { while ( fgets ( buff , BUFSIZ , fd )) { if ( ! purple_blist_find_buddy ( purple_connection_get_account ( gc ), buff )) { char * stripped_user = zephyr_strip_local_realm ( zephyr , buff ); purple_debug_info ( "zephyr" , "stripped_user %s \n " , stripped_user ); if ( ! purple_blist_find_buddy ( purple_connection_get_account ( gc ), stripped_user )) { b = purple_buddy_new ( purple_connection_get_account ( gc ), stripped_user , NULL ); purple_blist_add_buddy ( b , NULL , g , NULL ); normalize_zephyr_exposure ( const gchar * exposure ) gchar * exp2 = g_strstrip ( g_ascii_strup ( exposure , -1 )); return g_strdup ( EXPOSE_REALMVIS ); if ( g_str_equal ( exp2 , EXPOSE_NONE ) || g_str_equal ( exp2 , EXPOSE_OPSTAFF ) || g_str_equal ( exp2 , EXPOSE_REALMANN ) || g_str_equal ( exp2 , EXPOSE_NETVIS ) || g_str_equal ( exp2 , EXPOSE_NETANN )) { return g_strdup ( EXPOSE_REALMVIS ); static void zephyr_login ( PurpleAccount * account ) gc = purple_account_get_connection ( account ); read_anyone = purple_account_get_bool ( purple_connection_get_account ( gc ), "read_anyone" , TRUE ); read_zsubs = purple_account_get_bool ( purple_connection_get_account ( gc ), "read_zsubs" , TRUE ); exposure = ( gchar * ) purple_account_get_string ( purple_connection_get_account ( gc ), "exposure_level" , EXPOSE_REALMVIS ); username = purple_account_get_username ( account ); purple_connection_set_flags ( gc , PURPLE_CONNECTION_FLAG_AUTO_RESP | PURPLE_CONNECTION_FLAG_HTML | PURPLE_CONNECTION_FLAG_NO_BGCOLOR | PURPLE_CONNECTION_FLAG_NO_URLDESC | PURPLE_CONNECTION_FLAG_NO_IMAGES ); zephyr = g_new0 ( zephyr_account , 1 ); purple_connection_set_protocol_data ( gc , zephyr ); zephyr -> account = account ; /* Make sure that the exposure (visibility) is set to a sane value */ zephyr -> exposure = normalize_zephyr_exposure ( exposure ); if ( purple_account_get_bool ( purple_connection_get_account ( gc ), "use_tzc" , 0 )) { zephyr -> connection_type = PURPLE_ZEPHYR_TZC ; zephyr -> connection_type = PURPLE_ZEPHYR_KRB4 ; zephyr -> encoding = ( char * ) purple_account_get_string ( purple_connection_get_account ( gc ), "encoding" , ZEPHYR_FALLBACK_CHARSET ); purple_connection_update_progress ( gc , _ ( "Connecting" ), 0 , 8 ); /* XXX z_call_s should actually try to report the com_err determined error */ /* purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, "tzc not supported yet"); */ if (( pipe ( zephyr -> totzc ) != 0 ) || ( pipe ( zephyr -> fromtzc ) != 0 )) { purple_debug_error ( "zephyr" , "pipe creation failed. killing \n " ); purple_debug_error ( "zephyr" , "forking failed \n " ); gboolean found_ps = FALSE ; gchar ** tzc_cmd_array = g_strsplit ( purple_account_get_string ( purple_connection_get_account ( gc ), "tzc_command" , "/usr/bin/tzc -e %s" ), " " , 0 ); if ( dup2 ( zephyr -> fromtzc [ 1 ], 1 ) == -1 ) { if ( close ( zephyr -> fromtzc [ 1 ]) == -1 ) { if ( dup2 ( zephyr -> totzc [ 0 ], 0 ) == -1 ) { if ( close ( zephyr -> totzc [ 0 ]) == -1 ) { /* tzc_command should really be of the form ssh username@hostname pathtotzc -e %s -- this should not require a password, and ideally should be kerberized ssh -- fsh username@hostname pathtotzc -e %s while ( tzc_cmd_array [ i ] != NULL ){ if ( ! g_ascii_strncasecmp ( tzc_cmd_array [ i ], "%s" , 2 )) { /* fprintf(stderr,"replacing %%s with %s\n",zephyr->exposure); */ tzc_cmd_array [ i ] = g_strdup ( zephyr -> exposure ); /* fprintf(stderr,"keeping %s\n",tzc_cmd_array[i]); */ execvp ( tzc_cmd_array [ 0 ], tzc_cmd_array ); char * buf = ( char * ) calloc ( bufsize , 1 ); /* wait till we have data to read from ssh */ FD_SET ( zephyr -> fromtzc [ ZEPHYR_FD_READ ], & rfds ); purple_debug_info ( "zephyr" , "about to read from tzc \n " ); if ( waitpid ( pid , NULL , WNOHANG ) == 0 ) { /* Only select if tzc is still running */ purple_debug_info ( "zephyr" , "about to read from tzc \n " ); select_status = select ( zephyr -> fromtzc [ ZEPHYR_FD_READ ] + 1 , & rfds , NULL , NULL , NULL ); purple_debug_info ( "zephyr" , "tzc exited early \n " ); FD_SET ( zephyr -> fromtzc [ ZEPHYR_FD_READ ], & rfds ); while ( select_status > 0 && select ( zephyr -> fromtzc [ ZEPHYR_FD_READ ] + 1 , & rfds , NULL , NULL , & tv ) > 0 ) { if ( read ( zephyr -> fromtzc [ ZEPHYR_FD_READ ], bufcur , 1 ) != 1 ) { purple_debug_error ( "zephyr" , "couldn't read \n " ); purple_connection_error ( gc , PURPLE_CONNECTION_ERROR_NETWORK_ERROR , "couldn't read" ); if (( bufcur - buf ) > ( bufsize - 1 )) { if (( buf = realloc ( buf , bufsize * 2 )) == NULL ) { FD_SET ( zephyr -> fromtzc [ ZEPHYR_FD_READ ], & rfds ); /* fprintf(stderr, "read from tzc\n"); */ /* ignore all tzcoutput till we've received the first (*/ while ( ptr < bufcur && ( * ptr != '(' )) { purple_connection_error ( gc , PURPLE_CONNECTION_ERROR_NETWORK_ERROR , "invalid output by tzc (or bad parsing code)" ); purple_debug_info ( "zephyr" , "tzc parenlevel is %d \n " , parenlevel ); /* Search for next beginning (, or for the ending */ while (( * ptr != '(' ) && ( * ptr != ')' ) && ( ptr < bufcur )) purple_debug_error ( "zephyr" , "tzc parsing error \n " ); (foo . bar ) or (foo . "bar") or (foo . chars) or (foo . numbers) or (foo . () ) Parse all the data between the first and last f, and move past ) tempstr = g_malloc0 ( 20000 ); tempstr [ tempstridx ++ ] =* ptr ; purple_debug_info ( "zephyr" , "tempstr parsed \n " ); /* tempstr should now be a tempstridx length string containing all characters from that after the first ( to the one before the last paren ). */ /* We should have the following possible lisp strings but we don't care (tzcspew . start) (version . "something") (pid . number)*/ /* We care about 'zephyrid . "username@REALM.NAME"' and 'exposure . "SOMETHING"' */ if ( ! g_ascii_strncasecmp ( tempstr , "zephyrid" , 8 )) { gchar * username = g_malloc0 ( 100 ); purple_debug_info ( "zephyr" , "zephyrid found \n " ); while ( tempstr [ tempstridx ] != '"' && tempstridx < 20000 ) while ( tempstr [ tempstridx ] != '"' && tempstridx < 20000 ) username [ username_idx ++ ] = tempstr [ tempstridx ++ ]; zephyr -> username = g_strdup_printf ( "%s" , username ); if (( realm = strchr ( username , '@' ))) zephyr -> realm = g_strdup_printf ( "%s" , realm + 1 ); realm = ( gchar * ) purple_account_get_string ( purple_connection_get_account ( gc ), "realm" , "" ); zephyr -> realm = g_strdup ( realm ); g_strlcpy ( __Zephyr_realm , ( const char * ) zephyr -> realm , REALM_SZ -1 ); zephyr->realm = g_strdup("local-realm"); purple_debug_info ( "zephyr" , "something that's not zephyr id found %s \n " , tempstr ); /* We don't care about anything else yet */ purple_debug_info ( "zephyr" , "parenlevel is not 1 or 2 \n " ); /* This shouldn't be happening */ } /* while (ptr < bufcur) */ purple_debug_info ( "zephyr" , "tzc startup done \n " ); else if ( use_zeph02 ( zephyr )) { z_call_s ( ZInitialize (), "Couldn't initialize zephyr" ); z_call_s ( ZOpenPort ( & ( zephyr -> port )), "Couldn't open port" ); z_call_s ( ZSetLocation (( char * ) zephyr -> exposure ), "Couldn't set location" ); realm = ( gchar * ) purple_account_get_string ( purple_connection_get_account ( gc ), "realm" , "" ); zephyr -> realm = g_strdup ( realm ); g_strlcpy ( __Zephyr_realm , ( const char * ) zephyr -> realm , REALM_SZ -1 ); zephyr -> username = g_strdup ( ZGetSender ()); /* zephyr->realm = g_strdup(ZGetRealm()); */ purple_debug_info ( "zephyr" , "realm: %s \n " , zephyr -> realm ); purple_connection_error ( gc , PURPLE_CONNECTION_ERROR_NETWORK_ERROR , "Only ZEPH0.2 supported currently" ); purple_debug_info ( "zephyr" , "does it get here \n " ); purple_debug_info ( "zephyr" , " realm: %s username:%s \n " , zephyr -> realm , zephyr -> username ); zephyr -> krbtkfile = NULL ; zephyr_inithosts ( zephyr ); if ( zephyr_subscribe_to ( zephyr , "MESSAGE" , "PERSONAL" , zephyr -> username , NULL ) != ZERR_NONE ) { /* XXX don't translate this yet. It could be written better */ /* XXX error messages could be handled with more detail */ purple_notify_error ( purple_account_get_connection ( account ), NULL , "Unable to subscribe to messages" , "Unable to subscribe to initial messages" , purple_request_cpar_from_connection ( gc )); purple_connection_set_state ( gc , PURPLE_CONNECTION_CONNECTED ); if ( use_zeph02 ( zephyr )) { zephyr -> nottimer = g_timeout_add ( 100 , check_notify_zeph02 , gc ); } else if ( use_tzc ( zephyr )) { zephyr -> nottimer = g_timeout_add ( 100 , check_notify_tzc , gc ); zephyr -> loctimer = g_timeout_add_seconds ( 20 , check_loc , gc ); static void write_zsubs ( zephyr_account * zephyr ) /* Exports subscription (chat) list back to * XXX deal with %host%, %canon%, unsubscriptions, and negative subscriptions (punts?) GSList * s = zephyr -> subscrips ; fname = g_strdup_printf ( "%s/.zephyr.subs" , purple_home_dir ()); fd = g_fopen ( fname , "w" ); char * zclass , * zinst , * zrecip ; triple = g_strsplit ( zt -> name , "," , 3 ); if ( ! g_ascii_strcasecmp ( triple [ 0 ], zephyr -> ourhost )) { zclass = g_strdup ( "%host%" ); } else if ( ! g_ascii_strcasecmp ( triple [ 0 ], zephyr -> ourhostcanon )) { zclass = g_strdup ( "%canon%" ); zclass = g_strdup ( triple [ 0 ]); /* deal with instances */ if ( ! g_ascii_strcasecmp ( triple [ 1 ], zephyr -> ourhost )) { zinst = g_strdup ( "%host%" ); } else if ( ! g_ascii_strcasecmp ( triple [ 1 ], zephyr -> ourhostcanon )) { zinst = g_strdup ( "%canon%" );; zinst = g_strdup ( triple [ 1 ]); /* deal with recipients */ } else if ( ! g_ascii_strcasecmp ( triple [ 2 ], "" )){ } else if ( ! g_ascii_strcasecmp ( triple [ 2 ], zephyr -> username )) { zrecip = g_strdup ( "%me%" ); zrecip = g_strdup ( triple [ 2 ]); fprintf ( fd , "%s,%s,%s \n " , zclass , zinst , zrecip ); static void write_anyone ( zephyr_account * zephyr ) fname = g_strdup_printf ( "%s/.anyone" , purple_home_dir ()); fd = g_fopen ( fname , "w" ); account = zephyr -> account ; for ( buddies = purple_blist_find_buddies ( account , NULL ); buddies ; buddies = g_slist_delete_link ( buddies , buddies )) { PurpleBuddy * b = buddies -> data ; gchar * stripped_user = zephyr_strip_local_realm ( zephyr , purple_buddy_get_name ( b )); fprintf ( fd , "%s \n " , stripped_user ); static void zephyr_close ( PurpleConnection * gc ) zephyr_account * zephyr = purple_connection_get_protocol_data ( gc ); pid_t tzc_pid = zephyr -> tzc_pid ; g_list_free_full ( zephyr -> pending_zloc_names , g_free ); if ( purple_account_get_bool ( purple_connection_get_account ( gc ), "write_anyone" , FALSE )) if ( purple_account_get_bool ( purple_connection_get_account ( gc ), "write_zsubs" , FALSE )) g_slist_free_full ( zephyr -> subscrips , ( GDestroyNotify ) free_triple ); g_source_remove ( zephyr -> nottimer ); g_source_remove ( zephyr -> loctimer ); if ( use_zeph02 ( zephyr )) { z_call ( ZCancelSubscriptions ( 0 )); z_call ( ZUnsetLocation ()); if ( kill ( tzc_pid , SIGTERM ) == -1 ) { purple_debug_error ( "zephyr" , "An invalid signal was specified when killing tzc \n " ); purple_debug_error ( "zephyr" , "Tzc's pid didn't exist while killing tzc \n " ); purple_debug_error ( "zephyr" , "purple didn't have permission to kill tzc \n " ); purple_debug_error ( "zephyr" , "miscellaneous error while attempting to close tzc \n " ); static int zephyr_send_message ( zephyr_account * zephyr , char * zclass , char * instance , char * recipient , const char * im , const char * sig , char * opcode ) ; static const char * zephyr_get_signature ( void ) /* XXX add zephyr error reporting */ const char * sig = ZGetVariable ( "zwrite-signature" ); static int zephyr_chat_send ( PurpleConnection * gc , int id , PurpleMessage * msg ) PurpleChatConversation * gcc ; zephyr_account * zephyr = purple_connection_get_protocol_data ( gc ); zt = find_sub_by_id ( zephyr , id ); /* this should never happen. */ sig = zephyr_get_signature (); gcc = purple_conversations_find_chat_with_account ( zt -> name , purple_connection_get_account ( gc )); if ( ! ( inst = ( char * ) purple_chat_conversation_get_topic ( gcc ))) inst = g_strdup ( "PERSONAL" ); if ( ! g_ascii_strcasecmp ( zt -> recipient , "*" )) recipient = local_zephyr_normalize ( zephyr , "" ); recipient = local_zephyr_normalize ( zephyr , zt -> recipient ); zephyr_send_message ( zephyr , zt -> class , inst , recipient , purple_message_get_contents ( msg ), sig , "" ); static int zephyr_send_im ( PurpleConnection * gc , PurpleMessage * msg ) zephyr_account * zephyr = purple_connection_get_protocol_data ( gc ); if ( purple_message_get_flags ( msg ) & PURPLE_MESSAGE_AUTO_RESP ) { sig = "Automated reply:" ; sig = zephyr_get_signature (); zephyr_send_message ( zephyr , "MESSAGE" , "PERSONAL" , local_zephyr_normalize ( zephyr , purple_message_get_recipient ( msg )), purple_message_get_contents ( msg ), sig , "" ); /* Munge the outgoing zephyr so that any quotes or backslashes are escaped and do not confuse tzc: */ static char * zephyr_tzc_escape_msg ( const char * message ) if ( message && * message ) { newmsg = g_new0 ( char , 1 + strlen ( message ) * 2 ); while ( pos < strlen ( message )) { if ( message [ pos ] == '\\' ) { else if ( message [ pos ] == '"' ) { newmsg [ pos2 ] = message [ pos ]; /* fprintf(stderr,"newmsg %s message %s\n",newmsg,message); */ char * zephyr_tzc_deescape_str ( const char * message ) if ( message && * message ) { newmsg = g_new0 ( char , strlen ( message ) + 1 ); while ( pos < strlen ( message )) { if ( message [ pos ] == '\\' ) { newmsg [ pos2 ] = message [ pos ]; static int zephyr_send_message ( zephyr_account * zephyr , char * zclass , char * instance , char * recipient , const char * im , const char * sig , char * opcode ) * emacs sends something of the form: * (recipients ("PERSONAL" . "bovik") ("test" . "")) * (message . ("Harry Bovik" "my zgram")) html_buf = html_to_zephyr ( im ); html_buf2 = purple_unescape_html ( html_buf ); /* CMU cclub tzc doesn't grok opcodes for now */ char * tzc_sig = zephyr_tzc_escape_msg ( sig ); char * tzc_body = zephyr_tzc_escape_msg ( html_buf2 ); zsendstr = g_strdup_printf ( "((tzcfodder . send) (class . \" %s \" ) (auth . t) (recipients ( \" %s \" . \" %s \" )) (message . ( \" %s \" \" %s \" )) ) \n " , zclass , instance , recipient , tzc_sig , tzc_body ); /* fprintf(stderr,"zsendstr = %s\n",zsendstr); */ result = write ( zephyr -> totzc [ ZEPHYR_FD_WRITE ], zsendstr , len ); } else if ( use_zeph02 ( zephyr )) { char * buf = g_strdup_printf ( "%s%c%s" , sig , '\0' , html_buf2 ); memset (( char * ) & notice , 0 , sizeof ( notice )); notice . z_class_inst = instance ; notice . z_recipient = recipient ; notice . z_default_format = "Class $class, Instance $instance: \n " "To: @bold($recipient) at $time $date \n " "From: @bold($1) <$sender> \n\n $2" ; notice . z_message_len = strlen ( html_buf2 ) + strlen ( sig ) + 2 ; notice . z_opcode = g_strdup ( opcode ); purple_debug_info ( "zephyr" , "About to send notice \n " ); if ( ! ZSendNotice ( & notice , ZAUTH ) == ZERR_NONE ) { /* XXX handle errors here */ purple_debug_info ( "zephyr" , "notice sent \n " ); char * local_zephyr_normalize ( zephyr_account * zephyr , const char * orig ) Basically the inverse of zephyr_strip_local_realm if ( ! g_ascii_strcasecmp ( orig , "" )) { buf = g_strdup_printf ( "%s" , orig ); buf = g_strdup_printf ( "%s@%s" , orig , zephyr -> realm ); static const char * zephyr_normalize ( const PurpleAccount * account , const char * who ) static char buf [ BUF_LEN ]; if ( strlen ( who ) >= sizeof ( buf )) gc = purple_account_get_connection (( PurpleAccount * ) account ); tmp = local_zephyr_normalize ( purple_connection_get_protocol_data ( gc ), who ); if ( strlen ( tmp ) >= sizeof ( buf )) { g_strlcpy ( buf , tmp , sizeof ( buf )); static void zephyr_zloc ( PurpleConnection * gc , const char * who ) zephyr_account * zephyr = purple_connection_get_protocol_data ( gc ); gchar * normalized_who = local_zephyr_normalize ( zephyr , who ); if ( use_zeph02 ( zephyr )) { if ( ZRequestLocations ( normalized_who , & ald , UNACKED , ZAUTH ) == ZERR_NONE ) { zephyr -> pending_zloc_names = g_list_append ( zephyr -> pending_zloc_names , g_strdup ( normalized_who )); /* XXX deal with errors somehow */ } else if ( use_tzc ( zephyr )) { char * zlocstr = g_strdup_printf ( "((tzcfodder . zlocate) \" %s \" ) \n " , normalized_who ); zephyr -> pending_zloc_names = g_list_append ( zephyr -> pending_zloc_names , g_strdup ( normalized_who )); result = write ( zephyr -> totzc [ ZEPHYR_FD_WRITE ], zlocstr , len ); purple_debug_error ( "zephyr" , "Unable to write a message: %s \n " , g_strerror ( errno )); static void zephyr_set_status ( PurpleAccount * account , PurpleStatus * status ) { PurpleConnection * gc = purple_account_get_connection ( account ); zephyr_account * zephyr = purple_connection_get_protocol_data ( gc ); PurpleStatusPrimitive primitive = purple_status_type_get_primitive ( purple_status_get_status_type ( status )); if ( primitive == PURPLE_STATUS_AWAY ) { zephyr -> away = g_strdup ( purple_status_get_attr_string ( status , "message" )); else if ( primitive == PURPLE_STATUS_AVAILABLE ) { if ( use_zeph02 ( zephyr )) { ZSetLocation ( zephyr -> exposure ); char * zexpstr = g_strdup_printf ( "((tzcfodder . set-location) (hostname . \" %s \" ) (exposure . \" %s \" )) \n " , zephyr -> ourhost , zephyr -> exposure ); result = write ( zephyr -> totzc [ ZEPHYR_FD_WRITE ], zexpstr , len ); purple_debug_error ( "zephyr" , "Unable to write message: %s \n " , g_strerror ( errno )); else if ( primitive == PURPLE_STATUS_INVISIBLE ) { if ( use_zeph02 ( zephyr )) { ZSetLocation ( EXPOSE_OPSTAFF ); char * zexpstr = g_strdup_printf ( "((tzcfodder . set-location) (hostname . \" %s \" ) (exposure . \" %s \" )) \n " , zephyr -> ourhost , EXPOSE_OPSTAFF ); result = write ( zephyr -> totzc [ ZEPHYR_FD_WRITE ], zexpstr , len ); purple_debug_error ( "zephyr" , "Unable to write message: %s \n " , g_strerror ( errno )); static GList * zephyr_status_types ( PurpleAccount * account ) /* zephyr has several exposures NONE (where you are hidden, and zephyrs to you are in practice silently dropped -- yes this is wrong) REALM-VISIBLE visible to people in local realm REALM-ANNOUNCED REALM-VISIBLE+ plus your logins/logouts are announced to <login,username,*> NET-VISIBLE REALM-ANNOUNCED, plus visible to people in foreign realm NET-ANNOUNCED NET-VISIBLE, plus logins/logouts are announced to <login,username,*> Online will set the user to the exposure they have in their options (defaulting to REALM-VISIBLE), Hidden, will set the user's exposure to OPSTAFF Away won't change their exposure but will set an auto away message (for IMs only) type = purple_status_type_new ( PURPLE_STATUS_AVAILABLE , NULL , NULL , TRUE ); types = g_list_append ( types , type ); type = purple_status_type_new ( PURPLE_STATUS_INVISIBLE , NULL , NULL , TRUE ); types = g_list_append ( types , type ); type = purple_status_type_new_with_attrs ( PURPLE_STATUS_AWAY , NULL , NULL , TRUE , TRUE , FALSE , "message" , _ ( "Message" ), purple_value_new ( G_TYPE_STRING ), types = g_list_append ( types , type ); type = purple_status_type_new ( PURPLE_STATUS_OFFLINE , NULL , NULL , TRUE ); types = g_list_append ( types , type ); static GList * zephyr_chat_info ( PurpleConnection * gc ) PurpleProtocolChatEntry * pce ; pce = g_new0 ( PurpleProtocolChatEntry , 1 ); pce -> label = _ ( "_Class:" ); pce -> identifier = "class" ; m = g_list_append ( m , pce ); pce = g_new0 ( PurpleProtocolChatEntry , 1 ); pce -> label = _ ( "_Instance:" ); pce -> identifier = "instance" ; m = g_list_append ( m , pce ); pce = g_new0 ( PurpleProtocolChatEntry , 1 ); pce -> label = _ ( "_Recipient:" ); pce -> identifier = "recipient" ; m = g_list_append ( m , pce ); /* Called when the server notifies us a message couldn't get sent */ static void zephyr_subscribe_failed ( PurpleConnection * gc , char * z_class , char * z_instance , char * z_recipient , char * z_galaxy ) gchar * subscribe_failed = g_strdup_printf ( _ ( "Attempt to subscribe to %s,%s,%s failed" ), z_class , z_instance , z_recipient ); purple_notify_error ( gc , "" , subscribe_failed , NULL , purple_request_cpar_from_connection ( gc )); g_free ( subscribe_failed ); static char * zephyr_get_chat_name ( GHashTable * data ) { gchar * zclass = g_hash_table_lookup ( data , "class" ); gchar * inst = g_hash_table_lookup ( data , "instance" ); gchar * recipient = g_hash_table_lookup ( data , "recipient" ); if ( ! zclass ) /* This should never happen */ return g_strdup_printf ( "%s,%s,%s" , zclass , inst , recipient ); static void zephyr_join_chat ( PurpleConnection * gc , GHashTable * data ) /* ZSubscription_t sub; */ zephyr_triple * zt1 , * zt2 ; zephyr_account * zephyr = purple_connection_get_protocol_data ( gc ); classname = g_hash_table_lookup ( data , "class" ); instname = g_hash_table_lookup ( data , "instance" ); recip = g_hash_table_lookup ( data , "recipient" ); if ( ! g_ascii_strcasecmp ( classname , "%host%" )) classname = zephyr -> ourhost ; if ( ! g_ascii_strcasecmp ( classname , "%canon%" )) classname = zephyr -> ourhostcanon ; if ( ! instname || * instname == '\0' ) if ( ! g_ascii_strcasecmp ( instname , "%host%" )) instname = zephyr -> ourhost ; if ( ! g_ascii_strcasecmp ( instname , "%canon%" )) instname = zephyr -> ourhostcanon ; if ( ! recip || ( * recip == '*' )) if ( ! g_ascii_strcasecmp ( recip , "%me%" )) recip = zephyr -> username ; zt1 = new_triple ( zephyr , classname , instname , recip ); zt2 = find_sub_by_triple ( zephyr , zt1 ); if ( ! g_ascii_strcasecmp ( instname , "*" )) purple_serv_got_joined_chat ( gc , zt2 -> id , zt2 -> name ); zephyr_chat_set_topic ( gc , zt2 -> id , instname ); /* sub.zsub_class = zt1->class; sub.zsub_classinst = zt1->instance; sub.zsub_recipient = zt1->recipient; */ if ( zephyr_subscribe_to ( zephyr , zt1 -> class , zt1 -> instance , zt1 -> recipient , NULL ) != ZERR_NONE ) { /* XXX output better subscription information */ zephyr_subscribe_failed ( gc , zt1 -> class , zt1 -> instance , zt1 -> recipient , NULL ); zephyr -> subscrips = g_slist_append ( zephyr -> subscrips , zt1 ); purple_serv_got_joined_chat ( gc , zt1 -> id , zt1 -> name ); if ( ! g_ascii_strcasecmp ( instname , "*" )) zephyr_chat_set_topic ( gc , zt1 -> id , instname ); static void zephyr_chat_leave ( PurpleConnection * gc , int id ) zephyr_account * zephyr = purple_connection_get_protocol_data ( gc ); zt = find_sub_by_id ( zephyr , id ); zt -> id = ++ ( zephyr -> last_id ); static PurpleChat * zephyr_find_blist_chat ( PurpleAccount * account , const char * name ) PurpleBlistNode * gnode , * cnode ; /* XXX needs to be %host%,%canon%, and %me% clean */ for ( gnode = purple_blist_get_default_root (); gnode ; gnode = purple_blist_node_get_sibling_next ( gnode )) { for ( cnode = purple_blist_node_get_first_child ( gnode ); cnode = purple_blist_node_get_sibling_next ( cnode )) { PurpleChat * chat = ( PurpleChat * ) cnode ; const gchar * zclass , * inst , * recip ; if ( ! PURPLE_IS_CHAT ( cnode )) if ( purple_chat_get_account ( chat ) != account ) components = purple_chat_get_components ( chat ); if ( ! ( zclass = g_hash_table_lookup ( components , "class" ))) if ( ! ( inst = g_hash_table_lookup ( components , "instance" ))) if ( ! ( recip = g_hash_table_lookup ( components , "recipient" ))) /* purple_debug_info("zephyr","in zephyr_find_blist_chat name: %s\n",name?name:""); */ triple = g_strsplit ( name , "," , 3 ); if ( ! g_ascii_strcasecmp ( triple [ 0 ], zclass ) && ! g_ascii_strcasecmp ( triple [ 1 ], inst ) && ! g_ascii_strcasecmp ( triple [ 2 ], recip )) { static const char * zephyr_list_icon ( PurpleAccount * a , PurpleBuddy * b ) static unsigned int zephyr_send_typing ( PurpleConnection * gc , const char * who , PurpleIMTypingState state ) { zephyr_account * zephyr = purple_connection_get_protocol_data ( gc ); if ( state == PURPLE_IM_NOT_TYPING ) /* XXX We probably should care if this fails. Or maybe we don't want to */ purple_debug_info ( "zephyr" , "who is null \n " ); recipient = local_zephyr_normalize ( zephyr , "" ); char * comma = strrchr ( who , ',' ); /* Don't ping broadcast (chat) recipients */ /* The strrchr case finds a realm-stripped broadcast subscription e.g. comma is the last character in the string */ if ( comma && ( ( * ( comma + 1 ) == '\0' ) || ( * ( comma + 1 ) == '@' ))) recipient = local_zephyr_normalize ( zephyr , who ); purple_debug_info ( "zephyr" , "about to send typing notification to %s \n " , recipient ); zephyr_send_message ( zephyr , "MESSAGE" , "PERSONAL" , recipient , "" , "" , "PING" ); purple_debug_info ( "zephyr" , "sent typing notification \n " ); * TODO: Is this correct? It means we will call * purple_serv_send_typing(gc, who, PURPLE_IM_TYPING) once every 15 seconds * until the Purple user stops typing. return ZEPHYR_TYPING_SEND_TIMEOUT ; static void zephyr_chat_set_topic ( PurpleConnection * gc , int id , const char * topic ) PurpleChatConversation * gcc ; zephyr_account * zephyr = purple_connection_get_protocol_data ( gc ); char * sender = ( char * ) zephyr -> username ; zt = find_sub_by_id ( zephyr , id ); /* find_sub_by_id can return NULL */ gcc = purple_conversations_find_chat_with_account ( zt -> name , purple_connection_get_account ( gc )); topic_utf8 = zephyr_recv_convert ( gc ,( gchar * ) topic ); purple_chat_conversation_set_topic ( gcc , sender , topic_utf8 ); static PurpleCmdRet zephyr_purple_cmd_msg ( PurpleConversation * conv , const char * cmd , char ** args , char ** error , void * data ) PurpleConnection * gc = purple_conversation_get_connection ( conv ); zephyr_account * zephyr = purple_connection_get_protocol_data ( gc );; if ( ! g_ascii_strcasecmp ( args [ 0 ], "*" )) return PURPLE_CMD_RET_FAILED ; /* "*" is not a valid argument */ recipient = local_zephyr_normalize ( zephyr , args [ 0 ]); if ( strlen ( recipient ) < 1 ) { return PURPLE_CMD_RET_FAILED ; /* a null recipient is a chat message, not an IM */ if ( zephyr_send_message ( zephyr , "MESSAGE" , "PERSONAL" , recipient , args [ 1 ], zephyr_get_signature (), "" )) ret = PURPLE_CMD_RET_FAILED ; static PurpleCmdRet zephyr_purple_cmd_zlocate ( PurpleConversation * conv , const char * cmd , char ** args , char ** error , void * data ) zephyr_zloc ( purple_conversation_get_connection ( conv ), args [ 0 ]); return PURPLE_CMD_RET_OK ; static PurpleCmdRet zephyr_purple_cmd_instance ( PurpleConversation * conv , const char * cmd , char ** args , char ** error , void * data ) /* Currently it sets the instance with leading spaces and * all. This might not be the best thing to do, though having * one word isn't ideal either. */ const char * instance = args [ 0 ]; zephyr_chat_set_topic ( purple_conversation_get_connection ( conv ), purple_chat_conversation_get_id ( PURPLE_CHAT_CONVERSATION ( conv )), instance ); return PURPLE_CMD_RET_OK ; static PurpleCmdRet zephyr_purple_cmd_joinchat_cir ( PurpleConversation * conv , const char * cmd , char ** args , char ** error , void * data ) /* Join a new zephyr chat */ GHashTable * triple = g_hash_table_new ( NULL , NULL ); g_hash_table_insert ( triple , "class" , args [ 0 ]); g_hash_table_insert ( triple , "instance" , args [ 1 ]); g_hash_table_insert ( triple , "recipient" , args [ 2 ]); zephyr_join_chat ( purple_conversation_get_connection ( conv ), triple ); return PURPLE_CMD_RET_OK ; static PurpleCmdRet zephyr_purple_cmd_zi ( PurpleConversation * conv , const char * cmd , char ** args , char ** error , void * data ) /* args = instance, message */ PurpleConnection * gc = purple_conversation_get_connection ( conv ); zephyr_account * zephyr = purple_connection_get_protocol_data ( gc ); if ( zephyr_send_message ( zephyr , "message" , args [ 0 ], "" , args [ 1 ], zephyr_get_signature (), "" )) return PURPLE_CMD_RET_OK ; return PURPLE_CMD_RET_FAILED ; static PurpleCmdRet zephyr_purple_cmd_zci ( PurpleConversation * conv , const char * cmd , char ** args , char ** error , void * data ) /* args = class, instance, message */ PurpleConnection * gc = purple_conversation_get_connection ( conv ); zephyr_account * zephyr = purple_connection_get_protocol_data ( gc ); if ( zephyr_send_message ( zephyr , args [ 0 ], args [ 1 ], "" , args [ 2 ], zephyr_get_signature (), "" )) return PURPLE_CMD_RET_OK ; return PURPLE_CMD_RET_FAILED ; static PurpleCmdRet zephyr_purple_cmd_zcir ( PurpleConversation * conv , const char * cmd , char ** args , char ** error , void * data ) /* args = class, instance, recipient, message */ PurpleConnection * gc = purple_conversation_get_connection ( conv ); zephyr_account * zephyr = purple_connection_get_protocol_data ( gc ); if ( zephyr_send_message ( zephyr , args [ 0 ], args [ 1 ], args [ 2 ], args [ 3 ], zephyr_get_signature (), "" )) return PURPLE_CMD_RET_OK ; return PURPLE_CMD_RET_FAILED ; static PurpleCmdRet zephyr_purple_cmd_zir ( PurpleConversation * conv , const char * cmd , char ** args , char ** error , void * data ) /* args = instance, recipient, message */ PurpleConnection * gc = purple_conversation_get_connection ( conv ); zephyr_account * zephyr = purple_connection_get_protocol_data ( gc ); if ( zephyr_send_message ( zephyr , "message" , args [ 0 ], args [ 1 ], args [ 2 ], zephyr_get_signature (), "" )) return PURPLE_CMD_RET_OK ; return PURPLE_CMD_RET_FAILED ; static PurpleCmdRet zephyr_purple_cmd_zc ( PurpleConversation * conv , const char * cmd , char ** args , char ** error , void * data ) /* args = class, message */ PurpleConnection * gc = purple_conversation_get_connection ( conv ); zephyr_account * zephyr = purple_connection_get_protocol_data ( gc ); if ( zephyr_send_message ( zephyr , args [ 0 ], "PERSONAL" , "" , args [ 1 ], zephyr_get_signature (), "" )) return PURPLE_CMD_RET_OK ; return PURPLE_CMD_RET_FAILED ; static void zephyr_register_slash_commands ( void ) id = purple_cmd_register ( "msg" , "ws" , PURPLE_CMD_P_PROTOCOL , PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY , zephyr_purple_cmd_msg , _ ( "msg <nick> <message>: Send a private message to a user" ), NULL ); cmds = g_slist_prepend ( cmds , GUINT_TO_POINTER ( id )); id = purple_cmd_register ( "zlocate" , "w" , PURPLE_CMD_P_PROTOCOL , PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY , zephyr_purple_cmd_zlocate , _ ( "zlocate <nick>: Locate user" ), NULL ); cmds = g_slist_prepend ( cmds , GUINT_TO_POINTER ( id )); id = purple_cmd_register ( "zl" , "w" , PURPLE_CMD_P_PROTOCOL , PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY , zephyr_purple_cmd_zlocate , _ ( "zl <nick>: Locate user" ), NULL ); cmds = g_slist_prepend ( cmds , GUINT_TO_POINTER ( id )); id = purple_cmd_register ( "instance" , "s" , PURPLE_CMD_P_PROTOCOL , PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY , zephyr_purple_cmd_instance , _ ( "instance <instance>: Set the instance to be used on this class" ), NULL ); cmds = g_slist_prepend ( cmds , GUINT_TO_POINTER ( id )); id = purple_cmd_register ( "inst" , "s" , PURPLE_CMD_P_PROTOCOL , PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY , zephyr_purple_cmd_instance , _ ( "inst <instance>: Set the instance to be used on this class" ), NULL ); cmds = g_slist_prepend ( cmds , GUINT_TO_POINTER ( id )); id = purple_cmd_register ( "topic" , "s" , PURPLE_CMD_P_PROTOCOL , PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY , zephyr_purple_cmd_instance , _ ( "topic <instance>: Set the instance to be used on this class" ), NULL ); cmds = g_slist_prepend ( cmds , GUINT_TO_POINTER ( id )); id = purple_cmd_register ( "sub" , "www" , PURPLE_CMD_P_PROTOCOL , PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY , zephyr_purple_cmd_joinchat_cir , _ ( "sub <class> <instance> <recipient>: Join a new chat" ), NULL ); cmds = g_slist_prepend ( cmds , GUINT_TO_POINTER ( id )); id = purple_cmd_register ( "zi" , "ws" , PURPLE_CMD_P_PROTOCOL , PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY , zephyr_purple_cmd_zi , _ ( "zi <instance>: Send a message to <message,<i>instance</i>,*>" ), NULL ); cmds = g_slist_prepend ( cmds , GUINT_TO_POINTER ( id )); id = purple_cmd_register ( "zci" , "wws" , PURPLE_CMD_P_PROTOCOL , PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY , _ ( "zci <class> <instance>: Send a message to <<i>class</i>,<i>instance</i>,*>" ), NULL ); cmds = g_slist_prepend ( cmds , GUINT_TO_POINTER ( id )); id = purple_cmd_register ( "zcir" , "wwws" , PURPLE_CMD_P_PROTOCOL , PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY , _ ( "zcir <class> <instance> <recipient>: Send a message to <<i>class</i>,<i>instance</i>,<i>recipient</i>>" ), NULL ); cmds = g_slist_prepend ( cmds , GUINT_TO_POINTER ( id )); id = purple_cmd_register ( "zir" , "wws" , PURPLE_CMD_P_PROTOCOL , PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY , _ ( "zir <instance> <recipient>: Send a message to <MESSAGE,<i>instance</i>,<i>recipient</i>>" ), NULL ); cmds = g_slist_prepend ( cmds , GUINT_TO_POINTER ( id )); id = purple_cmd_register ( "zc" , "ws" , PURPLE_CMD_P_PROTOCOL , PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PROTOCOL_ONLY , zephyr_purple_cmd_zc , _ ( "zc <class>: Send a message to <<i>class</i>,PERSONAL,*>" ), NULL ); cmds = g_slist_prepend ( cmds , GUINT_TO_POINTER ( id )); static void zephyr_unregister_slash_commands ( void ) g_slist_free_full ( cmds , ( GDestroyNotify ) purple_cmd_unregister ); static int zephyr_resubscribe ( PurpleConnection * gc ) /* Resubscribe to the in-memory list of subscriptions and also zephyr_account * zephyr = purple_connection_get_protocol_data ( gc ); GSList * s = zephyr -> subscrips ; /* XXX We really should care if this fails */ zephyr_subscribe_to ( zephyr , zt -> class , zt -> instance , zt -> recipient , NULL ); /* XXX handle unsubscriptions */ static void zephyr_action_resubscribe ( PurpleProtocolAction * action ) PurpleConnection * gc = action -> connection ; static void zephyr_action_get_subs_from_server ( PurpleProtocolAction * action ) PurpleConnection * gc = action -> connection ; zephyr_account * zephyr = purple_connection_get_protocol_data ( gc ); int retval , nsubs , one , i ; if ( use_zeph02 ( zephyr )) { purple_debug_error ( "zephyr" , "error while retrieving port \n " ); if (( retval = ZRetrieveSubscriptions ( zephyr -> port , & nsubs )) != ZERR_NONE ) { /* XXX better error handling */ purple_debug_error ( "zephyr" , "error while retrieving subscriptions from server \n " ); title = g_strdup_printf ( "Server subscriptions for %s" , subout = g_string_new ( "Subscription list<br>" ); if (( retval = ZGetSubscriptions ( & subs , & one )) != ZERR_NONE ) { /* XXX better error handling */ g_string_free ( subout , TRUE ); purple_debug_error ( "zephyr" , "error while retrieving individual subscription \n " ); g_string_append_printf ( subout , "Class %s Instance %s Recipient %s<br>" , subs . zsub_class , subs . zsub_classinst , purple_notify_formatted ( gc , title , title , NULL , subout -> str , NULL , NULL ); g_string_free ( subout , TRUE ); purple_notify_error ( gc , "" , "tzc doesn't support this action" , NULL , purple_request_cpar_from_connection ( gc )); static GList * zephyr_get_actions ( PurpleConnection * gc ) PurpleProtocolAction * act = NULL ; act = purple_protocol_action_new ( _ ( "Resubscribe" ), zephyr_action_resubscribe ); list = g_list_append ( list , act ); act = purple_protocol_action_new ( _ ( "Retrieve subscriptions from server" ), zephyr_action_get_subs_from_server ); list = g_list_append ( list , act ); zephyr_protocol_init ( ZephyrProtocol * self ) PurpleProtocol * protocol = PURPLE_PROTOCOL ( self ); PurpleAccountOption * option ; const gchar * tmp = get_exposure_level (); protocol -> id = "prpl-zephyr" ; protocol -> name = "Zephyr" ; protocol -> options = OPT_PROTO_CHAT_TOPIC | OPT_PROTO_NO_PASSWORD ; option = purple_account_option_bool_new ( _ ( "Use tzc" ), "use_tzc" , FALSE ); protocol -> account_options = g_list_append ( protocol -> account_options , option ); option = purple_account_option_string_new ( _ ( "tzc command" ), "tzc_command" , "/usr/bin/tzc -e %s" ); protocol -> account_options = g_list_append ( protocol -> account_options , option ); option = purple_account_option_bool_new ( _ ( "Export to .anyone" ), "write_anyone" , FALSE ); protocol -> account_options = g_list_append ( protocol -> account_options , option ); option = purple_account_option_bool_new ( _ ( "Export to .zephyr.subs" ), "write_zsubs" , FALSE ); protocol -> account_options = g_list_append ( protocol -> account_options , option ); option = purple_account_option_bool_new ( _ ( "Import from .anyone" ), "read_anyone" , TRUE ); protocol -> account_options = g_list_append ( protocol -> account_options , option ); option = purple_account_option_bool_new ( _ ( "Import from .zephyr.subs" ), "read_zsubs" , TRUE ); protocol -> account_options = g_list_append ( protocol -> account_options , option ); option = purple_account_option_string_new ( _ ( "Realm" ), "realm" , "" ); protocol -> account_options = g_list_append ( protocol -> account_options , option ); option = purple_account_option_string_new ( _ ( "Exposure" ), "exposure_level" , protocol -> account_options = g_list_append ( protocol -> account_options , option ); option = purple_account_option_string_new ( _ ( "Encoding" ), "encoding" , ZEPHYR_FALLBACK_CHARSET ); protocol -> account_options = g_list_append ( protocol -> account_options , option ); zephyr_protocol_class_init ( ZephyrProtocolClass * klass ) PurpleProtocolClass * protocol_class = PURPLE_PROTOCOL_CLASS ( klass ); protocol_class -> login = zephyr_login ; protocol_class -> close = zephyr_close ; protocol_class -> status_types = zephyr_status_types ; protocol_class -> list_icon = zephyr_list_icon ; zephyr_protocol_class_finalize ( G_GNUC_UNUSED ZephyrProtocolClass * klass ) zephyr_protocol_client_iface_init ( PurpleProtocolClientInterface * client_iface ) client_iface -> get_actions = zephyr_get_actions ; client_iface -> normalize = zephyr_normalize ; client_iface -> find_blist_chat = zephyr_find_blist_chat ; zephyr_protocol_server_iface_init ( PurpleProtocolServerInterface * server_iface ) server_iface -> get_info = zephyr_zloc ; server_iface -> set_status = zephyr_set_status ; server_iface -> set_info = NULL ; /* XXX Location? */ server_iface -> set_buddy_icon = NULL ; /* XXX */ zephyr_protocol_im_iface_init ( PurpleProtocolIMInterface * im_iface ) im_iface -> send = zephyr_send_im ; im_iface -> send_typing = zephyr_send_typing ; zephyr_protocol_chat_iface_init ( PurpleProtocolChatInterface * chat_iface ) chat_iface -> info = zephyr_chat_info ; chat_iface -> join = zephyr_join_chat ; chat_iface -> get_name = zephyr_get_chat_name ; chat_iface -> leave = zephyr_chat_leave ; chat_iface -> send = zephyr_chat_send ; chat_iface -> set_topic = zephyr_chat_set_topic ; chat_iface -> get_user_real_name = NULL ; /* XXX */ G_DEFINE_DYNAMIC_TYPE_EXTENDED ( ZephyrProtocol , zephyr_protocol , PURPLE_TYPE_PROTOCOL , 0 , G_IMPLEMENT_INTERFACE_DYNAMIC ( PURPLE_TYPE_PROTOCOL_CLIENT , zephyr_protocol_client_iface_init ) G_IMPLEMENT_INTERFACE_DYNAMIC ( PURPLE_TYPE_PROTOCOL_SERVER , zephyr_protocol_server_iface_init ) G_IMPLEMENT_INTERFACE_DYNAMIC ( PURPLE_TYPE_PROTOCOL_IM , zephyr_protocol_im_iface_init ) G_IMPLEMENT_INTERFACE_DYNAMIC ( PURPLE_TYPE_PROTOCOL_CHAT , zephyr_protocol_chat_iface_init )); static PurplePluginInfo * plugin_query ( GError ** error ) return purple_plugin_info_new ( "name" , "Zephyr Protocol" , "version" , DISPLAY_VERSION , "category" , N_ ( "Protocol" ), "summary" , N_ ( "Zephyr Protocol Plugin" ), "description" , N_ ( "Zephyr Protocol Plugin" ), "website" , PURPLE_WEBSITE , "abi-version" , PURPLE_ABI_VERSION , "flags" , PURPLE_PLUGIN_INFO_FLAGS_INTERNAL | PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD , plugin_load ( PurplePlugin * plugin , GError ** error ) zephyr_protocol_register_type ( G_TYPE_MODULE ( plugin )); my_protocol = purple_protocols_add ( ZEPHYR_TYPE_PROTOCOL , error ); zephyr_register_slash_commands (); plugin_unload ( PurplePlugin * plugin , GError ** error ) zephyr_unregister_slash_commands (); if ( ! purple_protocols_remove ( my_protocol , error )) PURPLE_PLUGIN_INIT ( zephyr , plugin_query , plugin_load , plugin_unload );