pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Create an AppImage for Pidgin3
2020-10-15, Gary Kramlich
9ce322b6f2d1
Create an AppImage for Pidgin3
Prepare for an eventual finch appimage
Testing Done:
Built the AppImage and verified it works by logging in with bonjour.
Reviewed at https://reviews.imfreedom.org/r/166/
/**
* purple
*
* Copyright (C) 2003, 2012 Ethan Blanton <elb@pidgin.im>
*
* 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
*/
/*
* Note: If you change any of these functions to use additional args you
* MUST ensure the arg count is correct in parse.c. Otherwise it may be
* possible for a malicious server or man-in-the-middle to trigger a crash.
*/
#include
<glib/gi18n-lib.h>
#include
<ctype.h>
#include
<purple.h>
#include
"irc.h"
#include
<stdio.h>
#include
<stdlib.h>
#ifdef HAVE_CYRUS_SASL
#include
<sasl/sasl.h>
#endif
static
char
*
irc_mask_nick
(
const
char
*
mask
);
static
char
*
irc_mask_userhost
(
const
char
*
mask
);
static
void
irc_chat_remove_buddy
(
PurpleChatConversation
*
chat
,
char
*
data
[
2
]);
static
void
irc_buddy_status
(
char
*
name
,
struct
irc_buddy
*
ib
,
struct
irc_conn
*
irc
);
static
void
irc_connected
(
struct
irc_conn
*
irc
,
const
char
*
nick
);
static
void
irc_msg_handle_privmsg
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
const
char
*
to
,
const
char
*
rawmsg
,
gboolean
notice
);
#ifdef HAVE_CYRUS_SASL
static
void
irc_sasl_finish
(
struct
irc_conn
*
irc
);
#endif
static
char
*
irc_mask_nick
(
const
char
*
mask
)
{
char
*
end
,
*
buf
;
end
=
strchr
(
mask
,
'!'
);
if
(
!
end
)
buf
=
g_strdup
(
mask
);
else
buf
=
g_strndup
(
mask
,
end
-
mask
);
return
buf
;
}
static
char
*
irc_mask_userhost
(
const
char
*
mask
)
{
return
g_strdup
(
strchr
(
mask
,
'!'
)
+
1
);
}
static
void
irc_chat_remove_buddy
(
PurpleChatConversation
*
chat
,
char
*
data
[
2
])
{
char
*
message
,
*
stripped
;
stripped
=
data
[
1
]
?
irc_mirc2txt
(
data
[
1
])
:
NULL
;
message
=
g_strdup_printf
(
"quit: %s"
,
stripped
);
g_free
(
stripped
);
if
(
purple_chat_conversation_has_user
(
chat
,
data
[
0
]))
purple_chat_conversation_remove_user
(
chat
,
data
[
0
],
message
);
g_free
(
message
);
}
static
void
irc_connected
(
struct
irc_conn
*
irc
,
const
char
*
nick
)
{
PurpleConnection
*
gc
;
PurpleStatus
*
status
;
GSList
*
buddies
;
PurpleAccount
*
account
;
if
((
gc
=
purple_account_get_connection
(
irc
->
account
))
==
NULL
||
PURPLE_CONNECTION_IS_CONNECTED
(
gc
))
return
;
purple_connection_set_display_name
(
gc
,
nick
);
purple_connection_set_state
(
gc
,
PURPLE_CONNECTION_CONNECTED
);
account
=
purple_connection_get_account
(
gc
);
/* If we're away then set our away message */
status
=
purple_account_get_active_status
(
irc
->
account
);
if
(
purple_status_type_get_primitive
(
purple_status_get_status_type
(
status
))
!=
PURPLE_STATUS_AVAILABLE
)
{
PurpleProtocol
*
protocol
=
purple_connection_get_protocol
(
gc
);
purple_protocol_server_iface_set_status
(
protocol
,
irc
->
account
,
status
);
}
/* this used to be in the core, but it's not now */
for
(
buddies
=
purple_blist_find_buddies
(
account
,
NULL
);
buddies
;
buddies
=
g_slist_delete_link
(
buddies
,
buddies
))
{
PurpleBuddy
*
b
=
buddies
->
data
;
struct
irc_buddy
*
ib
=
g_new0
(
struct
irc_buddy
,
1
);
ib
->
name
=
g_strdup
(
purple_buddy_get_name
(
b
));
ib
->
ref
=
1
;
g_hash_table_replace
(
irc
->
buddies
,
ib
->
name
,
ib
);
}
irc_blist_timeout
(
irc
);
if
(
!
irc
->
timer
)
irc
->
timer
=
g_timeout_add_seconds
(
45
,
(
GSourceFunc
)
irc_blist_timeout
,
(
gpointer
)
irc
);
}
/* This function is ugly, but it's really an error handler. */
void
irc_msg_default
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
int
i
;
const
char
*
end
,
*
cur
,
*
numeric
=
NULL
;
char
*
clean
,
*
tmp
,
*
convname
;
PurpleConversation
*
convo
;
for
(
cur
=
args
[
0
],
i
=
0
;
i
<
4
;
i
++
)
{
end
=
strchr
(
cur
,
' '
);
if
(
end
==
NULL
)
{
goto
undirected
;
}
/* Check for 3-digit numeric in second position */
if
(
i
==
1
)
{
if
(
end
-
cur
!=
3
||
!
isdigit
(
cur
[
0
])
||
!
isdigit
(
cur
[
1
])
||
!
isdigit
(
cur
[
2
]))
{
goto
undirected
;
}
/* Save the numeric for printing to the channel */
numeric
=
cur
;
}
/* Don't advance cur if we're on the final iteration. */
if
(
i
!=
3
)
{
cur
=
end
+
1
;
}
}
/* At this point, cur is the beginning of the fourth position,
* end is the following space, and there are remaining
* arguments. We'll check to see if this argument is a
* currently active conversation (private message or channel,
* either one), and print the numeric to that conversation if it
* is. */
tmp
=
g_strndup
(
cur
,
end
-
cur
);
convname
=
g_utf8_make_valid
(
tmp
,
-1
);
g_free
(
tmp
);
/* Check for an existing conversation */
convo
=
purple_conversations_find_with_account
(
convname
,
irc
->
account
);
g_free
(
convname
);
if
(
convo
==
NULL
)
{
goto
undirected
;
}
/* end + 1 is the first argument past the target. The initial
* arguments we've skipped are routing info, numeric, recipient
* (this account's nick, most likely), and target (this
* channel). If end + 1 is an ASCII :, skip it, because it's
* meaningless in this context. This won't catch all
* :-arguments, but it'll catch the easy case. */
if
(
*++
end
==
':'
)
{
end
++
;
}
/* We then print "numeric: remainder". */
clean
=
g_utf8_make_valid
(
end
,
-1
);
tmp
=
g_strdup_printf
(
"%.3s: %s"
,
numeric
,
clean
);
g_free
(
clean
);
purple_conversation_write_system_message
(
convo
,
tmp
,
PURPLE_MESSAGE_NO_LOG
|
PURPLE_MESSAGE_RAW
|
PURPLE_MESSAGE_NO_LINKIFY
);
g_free
(
tmp
);
return
;
undirected
:
/* This, too, should be escaped somehow (smarter) */
clean
=
g_utf8_make_valid
(
args
[
0
],
-1
);
purple_debug
(
PURPLE_DEBUG_INFO
,
"irc"
,
"Unrecognized message: %s
\n
"
,
clean
);
g_free
(
clean
);
}
void
irc_msg_features
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
gchar
**
features
;
int
i
;
features
=
g_strsplit
(
args
[
1
],
" "
,
-1
);
for
(
i
=
0
;
features
[
i
];
i
++
)
{
char
*
val
;
if
(
!
strncmp
(
features
[
i
],
"PREFIX="
,
7
))
{
if
((
val
=
strchr
(
features
[
i
]
+
7
,
')'
))
!=
NULL
)
irc
->
mode_chars
=
g_strdup
(
val
+
1
);
}
}
g_strfreev
(
features
);
}
void
irc_msg_luser
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
if
(
purple_strequal
(
name
,
"251"
))
{
/* 251 is required, so we pluck our nick from here and
* finalize connection */
irc_connected
(
irc
,
args
[
0
]);
/* Some IRC servers seem to not send a 255 numeric, so
* I guess we can't require it; 251 will do. */
/* } else if (purple_strequal(name, "255")) { */
}
}
void
irc_msg_away
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleConnection
*
gc
;
char
*
msg
;
if
(
irc
->
whois
.
nick
&&
!
purple_utf8_strcasecmp
(
irc
->
whois
.
nick
,
args
[
1
]))
{
/* We're doing a whois, show this in the whois dialog */
irc_msg_whois
(
irc
,
name
,
from
,
args
);
return
;
}
gc
=
purple_account_get_connection
(
irc
->
account
);
if
(
gc
)
{
msg
=
g_markup_escape_text
(
args
[
2
],
-1
);
purple_serv_got_im
(
gc
,
args
[
1
],
msg
,
PURPLE_MESSAGE_AUTO_RESP
,
time
(
NULL
));
g_free
(
msg
);
}
}
void
irc_msg_badmode
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleConnection
*
gc
=
purple_account_get_connection
(
irc
->
account
);
g_return_if_fail
(
gc
);
purple_notify_error
(
gc
,
NULL
,
_
(
"Bad mode"
),
args
[
1
],
purple_request_cpar_from_connection
(
gc
));
}
void
irc_msg_ban
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleChatConversation
*
chat
;
chat
=
purple_conversations_find_chat_with_account
(
args
[
1
],
irc
->
account
);
if
(
purple_strequal
(
name
,
"367"
))
{
char
*
msg
=
NULL
;
/* Ban list entry */
if
(
args
[
3
]
&&
args
[
4
])
{
/* This is an extended syntax, not in RFC 1459 */
int
t1
=
atoi
(
args
[
4
]);
time_t
t2
=
time
(
NULL
);
char
*
time
=
purple_str_seconds_to_string
(
t2
-
t1
);
msg
=
g_strdup_printf
(
_
(
"Ban on %s by %s, set %s ago"
),
args
[
2
],
args
[
3
],
time
);
g_free
(
time
);
}
else
{
msg
=
g_strdup_printf
(
_
(
"Ban on %s"
),
args
[
2
]);
}
if
(
chat
)
{
purple_conversation_write_system_message
(
PURPLE_CONVERSATION
(
chat
),
msg
,
PURPLE_MESSAGE_NO_LOG
);
}
else
{
purple_debug_info
(
"irc"
,
"%s
\n
"
,
msg
);
}
g_free
(
msg
);
}
else
if
(
purple_strequal
(
name
,
"368"
))
{
if
(
!
chat
)
return
;
/* End of ban list */
purple_conversation_write_system_message
(
PURPLE_CONVERSATION
(
chat
),
_
(
"End of ban list"
),
PURPLE_MESSAGE_NO_LOG
);
}
}
void
irc_msg_banned
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleConnection
*
gc
=
purple_account_get_connection
(
irc
->
account
);
char
*
buf
;
g_return_if_fail
(
gc
);
buf
=
g_strdup_printf
(
_
(
"You are banned from %s."
),
args
[
1
]);
purple_notify_error
(
gc
,
_
(
"Banned"
),
_
(
"Banned"
),
buf
,
purple_request_cpar_from_connection
(
gc
));
g_free
(
buf
);
}
void
irc_msg_banfull
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleChatConversation
*
chat
;
char
*
buf
,
*
nick
;
chat
=
purple_conversations_find_chat_with_account
(
args
[
1
],
irc
->
account
);
if
(
!
chat
)
return
;
nick
=
g_markup_escape_text
(
args
[
2
],
-1
);
buf
=
g_strdup_printf
(
_
(
"Cannot ban %s: banlist is full"
),
nick
);
g_free
(
nick
);
purple_conversation_write_system_message
(
PURPLE_CONVERSATION
(
chat
),
buf
,
PURPLE_MESSAGE_NO_LOG
);
g_free
(
buf
);
}
void
irc_msg_chanmode
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleChatConversation
*
chat
;
char
*
buf
,
*
escaped
;
chat
=
purple_conversations_find_chat_with_account
(
args
[
1
],
irc
->
account
);
if
(
!
chat
)
/* XXX punt on channels we are not in for now */
return
;
escaped
=
(
args
[
3
]
!=
NULL
)
?
g_markup_escape_text
(
args
[
3
],
-1
)
:
NULL
;
buf
=
g_strdup_printf
(
"mode for %s: %s %s"
,
args
[
1
],
args
[
2
],
escaped
?
escaped
:
""
);
purple_conversation_write_system_message
(
PURPLE_CONVERSATION
(
chat
),
buf
,
0
);
g_free
(
escaped
);
g_free
(
buf
);
}
void
irc_msg_whois
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
if
(
!
irc
->
whois
.
nick
)
{
purple_debug
(
PURPLE_DEBUG_WARNING
,
"irc"
,
"Unexpected %s reply for %s
\n
"
,
purple_strequal
(
name
,
"314"
)
?
"WHOWAS"
:
"WHOIS"
,
args
[
1
]);
return
;
}
if
(
purple_utf8_strcasecmp
(
irc
->
whois
.
nick
,
args
[
1
]))
{
purple_debug
(
PURPLE_DEBUG_WARNING
,
"irc"
,
"Got %s reply for %s while waiting for %s
\n
"
,
purple_strequal
(
name
,
"314"
)
?
"WHOWAS"
:
"WHOIS"
,
args
[
1
],
irc
->
whois
.
nick
);
return
;
}
if
(
purple_strequal
(
name
,
"301"
))
{
irc
->
whois
.
away
=
g_strdup
(
args
[
2
]);
}
else
if
(
purple_strequal
(
name
,
"311"
)
||
purple_strequal
(
name
,
"314"
))
{
irc
->
whois
.
ident
=
g_strdup
(
args
[
2
]);
irc
->
whois
.
host
=
g_strdup
(
args
[
3
]);
irc
->
whois
.
real
=
g_strdup
(
args
[
5
]);
}
else
if
(
purple_strequal
(
name
,
"312"
))
{
irc
->
whois
.
server
=
g_strdup
(
args
[
2
]);
irc
->
whois
.
serverinfo
=
g_strdup
(
args
[
3
]);
}
else
if
(
purple_strequal
(
name
,
"313"
))
{
irc
->
whois
.
ircop
=
1
;
}
else
if
(
purple_strequal
(
name
,
"317"
))
{
irc
->
whois
.
idle
=
atoi
(
args
[
2
]);
if
(
args
[
3
])
irc
->
whois
.
signon
=
(
time_t
)
atoi
(
args
[
3
]);
}
else
if
(
purple_strequal
(
name
,
"319"
))
{
if
(
irc
->
whois
.
channels
==
NULL
)
{
irc
->
whois
.
channels
=
g_string_new
(
args
[
2
]);
}
else
{
irc
->
whois
.
channels
=
g_string_append
(
irc
->
whois
.
channels
,
args
[
2
]);
}
}
else
if
(
purple_strequal
(
name
,
"320"
))
{
irc
->
whois
.
identified
=
1
;
}
else
if
(
purple_strequal
(
name
,
"330"
))
{
purple_debug
(
PURPLE_DEBUG_INFO
,
"irc"
,
"330 %s: 1=[%s] 2=[%s] 3=[%s]"
,
name
,
args
[
1
],
args
[
2
],
args
[
3
]);
if
(
purple_strequal
(
args
[
3
],
"is logged in as"
))
irc
->
whois
.
login
=
g_strdup
(
args
[
2
]);
}
}
void
irc_msg_endwhois
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleConnection
*
gc
;
char
*
tmp
,
*
tmp2
;
PurpleNotifyUserInfo
*
user_info
;
if
(
!
irc
->
whois
.
nick
)
{
purple_debug
(
PURPLE_DEBUG_WARNING
,
"irc"
,
"Unexpected End of %s for %s
\n
"
,
purple_strequal
(
name
,
"369"
)
?
"WHOWAS"
:
"WHOIS"
,
args
[
1
]);
return
;
}
if
(
purple_utf8_strcasecmp
(
irc
->
whois
.
nick
,
args
[
1
]))
{
purple_debug
(
PURPLE_DEBUG_WARNING
,
"irc"
,
"Received end of %s for %s, expecting %s
\n
"
,
purple_strequal
(
name
,
"369"
)
?
"WHOWAS"
:
"WHOIS"
,
args
[
1
],
irc
->
whois
.
nick
);
return
;
}
user_info
=
purple_notify_user_info_new
();
tmp2
=
g_markup_escape_text
(
args
[
1
],
-1
);
tmp
=
g_strdup_printf
(
"%s%s%s"
,
tmp2
,
(
irc
->
whois
.
ircop
?
_
(
" <i>(ircop)</i>"
)
:
""
),
(
irc
->
whois
.
identified
?
_
(
" <i>(identified)</i>"
)
:
""
));
purple_notify_user_info_add_pair_html
(
user_info
,
_
(
"Nick"
),
tmp
);
g_free
(
tmp2
);
g_free
(
tmp
);
if
(
irc
->
whois
.
away
)
{
purple_notify_user_info_add_pair_plaintext
(
user_info
,
_
(
"Away"
),
irc
->
whois
.
away
);
g_free
(
irc
->
whois
.
away
);
}
if
(
irc
->
whois
.
real
)
{
purple_notify_user_info_add_pair_plaintext
(
user_info
,
_
(
"Real name"
),
irc
->
whois
.
real
);
g_free
(
irc
->
whois
.
real
);
}
if
(
irc
->
whois
.
login
)
{
purple_notify_user_info_add_pair_plaintext
(
user_info
,
_
(
"Login name"
),
irc
->
whois
.
login
);
g_free
(
irc
->
whois
.
login
);
}
if
(
irc
->
whois
.
ident
)
{
purple_notify_user_info_add_pair_plaintext
(
user_info
,
_
(
"Ident name"
),
irc
->
whois
.
ident
);
g_free
(
irc
->
whois
.
ident
);
}
if
(
irc
->
whois
.
host
)
{
purple_notify_user_info_add_pair_plaintext
(
user_info
,
_
(
"Host name"
),
irc
->
whois
.
host
);
g_free
(
irc
->
whois
.
host
);
}
if
(
irc
->
whois
.
server
)
{
tmp
=
g_strdup_printf
(
"%s (%s)"
,
irc
->
whois
.
server
,
irc
->
whois
.
serverinfo
);
purple_notify_user_info_add_pair_plaintext
(
user_info
,
_
(
"Server"
),
tmp
);
g_free
(
tmp
);
g_free
(
irc
->
whois
.
server
);
g_free
(
irc
->
whois
.
serverinfo
);
}
if
(
irc
->
whois
.
channels
)
{
purple_notify_user_info_add_pair_plaintext
(
user_info
,
_
(
"Currently on"
),
irc
->
whois
.
channels
->
str
);
g_string_free
(
irc
->
whois
.
channels
,
TRUE
);
}
if
(
irc
->
whois
.
idle
)
{
gchar
*
timex
=
purple_str_seconds_to_string
(
irc
->
whois
.
idle
);
purple_notify_user_info_add_pair_plaintext
(
user_info
,
_
(
"Idle for"
),
timex
);
g_free
(
timex
);
purple_notify_user_info_add_pair_plaintext
(
user_info
,
_
(
"Online since"
),
purple_date_format_full
(
localtime
(
&
irc
->
whois
.
signon
)));
}
if
(
purple_strequal
(
irc
->
whois
.
nick
,
"elb"
))
{
purple_notify_user_info_add_pair_plaintext
(
user_info
,
_
(
"<b>Defining adjective:</b>"
),
_
(
"Glorious"
));
}
gc
=
purple_account_get_connection
(
irc
->
account
);
purple_notify_userinfo
(
gc
,
irc
->
whois
.
nick
,
user_info
,
NULL
,
NULL
);
purple_notify_user_info_destroy
(
user_info
);
g_free
(
irc
->
whois
.
nick
);
memset
(
&
irc
->
whois
,
0
,
sizeof
(
irc
->
whois
));
}
void
irc_msg_who
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
if
(
purple_strequal
(
name
,
"352"
))
{
PurpleChatConversation
*
chat
;
PurpleChatUser
*
cb
;
char
*
cur
,
*
userhost
,
*
realname
;
PurpleChatUserFlags
flags
;
chat
=
purple_conversations_find_chat_with_account
(
args
[
1
],
irc
->
account
);
if
(
!
chat
)
{
purple_debug
(
PURPLE_DEBUG_ERROR
,
"irc"
,
"Got a WHO response for %s, which doesn't exist
\n
"
,
args
[
1
]);
return
;
}
cb
=
purple_chat_conversation_find_user
(
chat
,
args
[
5
]);
if
(
!
cb
)
{
purple_debug
(
PURPLE_DEBUG_ERROR
,
"irc"
,
"Got a WHO response for %s who isn't a buddy.
\n
"
,
args
[
5
]);
return
;
}
userhost
=
g_strdup_printf
(
"%s@%s"
,
args
[
2
],
args
[
3
]);
/* The final argument is a :-argument, but annoyingly
* contains two "words", the hop count and real name. */
for
(
cur
=
args
[
7
];
*
cur
;
cur
++
)
{
if
(
*
cur
==
' '
)
{
cur
++
;
break
;
}
}
realname
=
g_strdup
(
cur
);
g_object_set_data_full
(
G_OBJECT
(
cb
),
"userhost"
,
userhost
,
g_free
);
g_object_set_data_full
(
G_OBJECT
(
cb
),
"realname"
,
realname
,
g_free
);
flags
=
purple_chat_user_get_flags
(
cb
);
/* FIXME: I'm not sure this is really a good idea, now
* that we no longer do periodic WHO. It seems to me
* like it's more likely to be confusing than not.
* Comments? */
if
(
args
[
6
][
0
]
==
'G'
&&
!
(
flags
&
PURPLE_CHAT_USER_AWAY
))
{
purple_chat_user_set_flags
(
cb
,
flags
|
PURPLE_CHAT_USER_AWAY
);
}
else
if
(
args
[
6
][
0
]
==
'H'
&&
(
flags
&
PURPLE_CHAT_USER_AWAY
))
{
purple_chat_user_set_flags
(
cb
,
flags
&
~
PURPLE_CHAT_USER_AWAY
);
}
}
}
void
irc_msg_list
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
if
(
!
irc
->
roomlist
)
return
;
if
(
purple_strequal
(
name
,
"321"
))
{
purple_roomlist_set_in_progress
(
irc
->
roomlist
,
TRUE
);
return
;
}
if
(
purple_strequal
(
name
,
"323"
))
{
purple_roomlist_set_in_progress
(
irc
->
roomlist
,
FALSE
);
g_object_unref
(
irc
->
roomlist
);
irc
->
roomlist
=
NULL
;
return
;
}
if
(
purple_strequal
(
name
,
"322"
))
{
PurpleRoomlistRoom
*
room
;
char
*
topic
;
if
(
!
purple_roomlist_get_in_progress
(
irc
->
roomlist
))
{
purple_debug_warning
(
"irc"
,
"Buggy server didn't send RPL_LISTSTART.
\n
"
);
purple_roomlist_set_in_progress
(
irc
->
roomlist
,
TRUE
);
}
room
=
purple_roomlist_room_new
(
PURPLE_ROOMLIST_ROOMTYPE_ROOM
,
args
[
1
],
NULL
);
purple_roomlist_room_add_field
(
irc
->
roomlist
,
room
,
args
[
1
]);
purple_roomlist_room_add_field
(
irc
->
roomlist
,
room
,
GINT_TO_POINTER
(
strtol
(
args
[
2
],
NULL
,
10
)));
topic
=
irc_mirc2txt
(
args
[
3
]);
purple_roomlist_room_add_field
(
irc
->
roomlist
,
room
,
topic
);
g_free
(
topic
);
purple_roomlist_room_add
(
irc
->
roomlist
,
room
);
}
}
void
irc_msg_topic
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
char
*
chan
,
*
topic
,
*
msg
,
*
nick
,
*
tmp
,
*
tmp2
;
PurpleChatConversation
*
chat
;
if
(
purple_strequal
(
name
,
"topic"
))
{
chan
=
args
[
0
];
topic
=
irc_mirc2txt
(
args
[
1
]);
}
else
{
chan
=
args
[
1
];
topic
=
irc_mirc2txt
(
args
[
2
]);
}
chat
=
purple_conversations_find_chat_with_account
(
chan
,
irc
->
account
);
if
(
!
chat
)
{
purple_debug
(
PURPLE_DEBUG_ERROR
,
"irc"
,
"Got a topic for %s, which doesn't exist
\n
"
,
chan
);
g_free
(
topic
);
return
;
}
/* If this is an interactive update, print it out */
tmp
=
g_markup_escape_text
(
topic
,
-1
);
tmp2
=
purple_markup_linkify
(
tmp
);
g_free
(
tmp
);
if
(
purple_strequal
(
name
,
"topic"
))
{
const
char
*
current_topic
=
purple_chat_conversation_get_topic
(
chat
);
if
(
!
(
current_topic
!=
NULL
&&
purple_strequal
(
tmp2
,
current_topic
)))
{
char
*
nick_esc
;
nick
=
irc_mask_nick
(
from
);
nick_esc
=
g_markup_escape_text
(
nick
,
-1
);
purple_chat_conversation_set_topic
(
chat
,
nick
,
topic
);
if
(
*
tmp2
)
msg
=
g_strdup_printf
(
_
(
"%s has changed the topic to: %s"
),
nick_esc
,
tmp2
);
else
msg
=
g_strdup_printf
(
_
(
"%s has cleared the topic."
),
nick_esc
);
g_free
(
nick_esc
);
g_free
(
nick
);
purple_conversation_write_system_message
(
PURPLE_CONVERSATION
(
chat
),
msg
,
0
);
g_free
(
msg
);
}
}
else
{
char
*
chan_esc
=
g_markup_escape_text
(
chan
,
-1
);
msg
=
g_strdup_printf
(
_
(
"The topic for %s is: %s"
),
chan_esc
,
tmp2
);
g_free
(
chan_esc
);
purple_chat_conversation_set_topic
(
chat
,
NULL
,
topic
);
purple_conversation_write_system_message
(
PURPLE_CONVERSATION
(
chat
),
msg
,
0
);
g_free
(
msg
);
}
g_free
(
tmp2
);
g_free
(
topic
);
}
void
irc_msg_topicinfo
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleChatConversation
*
chat
;
GDateTime
*
dt
,
*
local
;
gint64
mtime
;
char
*
msg
,
*
timestamp
,
*
datestamp
;
chat
=
purple_conversations_find_chat_with_account
(
args
[
1
],
irc
->
account
);
if
(
!
chat
)
{
purple_debug
(
PURPLE_DEBUG_ERROR
,
"irc"
,
"Got topic info for %s, which doesn't exist
\n
"
,
args
[
1
]);
return
;
}
mtime
=
g_ascii_strtoll
(
args
[
3
],
NULL
,
10
);
if
(
mtime
==
0
||
mtime
==
G_MININT64
||
mtime
==
G_MAXINT64
)
{
purple_debug
(
PURPLE_DEBUG_ERROR
,
"irc"
,
"Got apparently nonsensical topic timestamp %s
\n
"
,
args
[
3
]);
return
;
}
dt
=
g_date_time_new_from_unix_utc
(
mtime
);
if
(
dt
==
NULL
)
{
purple_debug
(
PURPLE_DEBUG_ERROR
,
"irc"
,
"Failed to turn %"
G_GINT64_FORMAT
" into a GDateTime
\n
"
,
mtime
);
return
;
}
local
=
g_date_time_to_local
(
dt
);
g_date_time_unref
(
dt
);
timestamp
=
g_date_time_format
(
local
,
"%X"
);
datestamp
=
g_date_time_format
(
local
,
"%x"
);
msg
=
g_strdup_printf
(
_
(
"Topic for %s set by %s at %s on %s"
),
args
[
1
],
args
[
2
],
timestamp
,
datestamp
);
purple_conversation_write_system_message
(
PURPLE_CONVERSATION
(
chat
),
msg
,
PURPLE_MESSAGE_NO_LINKIFY
);
g_free
(
timestamp
);
g_free
(
datestamp
);
g_free
(
msg
);
g_date_time_unref
(
local
);
}
void
irc_msg_unknown
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleConnection
*
gc
=
purple_account_get_connection
(
irc
->
account
);
char
*
buf
;
g_return_if_fail
(
gc
);
buf
=
g_strdup_printf
(
_
(
"Unknown message '%s'"
),
args
[
1
]);
purple_notify_error
(
gc
,
_
(
"Unknown message"
),
buf
,
_
(
"The IRC server "
"received a message it did not understand."
),
purple_request_cpar_from_connection
(
gc
));
g_free
(
buf
);
}
void
irc_msg_names
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
char
*
names
,
*
cur
,
*
end
,
*
tmp
,
*
msg
;
PurpleConversation
*
convo
;
if
(
purple_strequal
(
name
,
"366"
))
{
convo
=
purple_conversations_find_with_account
(
args
[
1
],
irc
->
account
);
if
(
!
convo
)
{
purple_debug
(
PURPLE_DEBUG_ERROR
,
"irc"
,
"Got a NAMES list for %s, which doesn't exist
\n
"
,
args
[
1
]);
g_string_free
(
irc
->
names
,
TRUE
);
irc
->
names
=
NULL
;
return
;
}
names
=
cur
=
g_string_free
(
irc
->
names
,
FALSE
);
irc
->
names
=
NULL
;
if
(
g_object_get_data
(
G_OBJECT
(
convo
),
IRC_NAMES_FLAG
))
{
msg
=
g_strdup_printf
(
_
(
"Users on %s: %s"
),
args
[
1
],
names
?
names
:
""
);
purple_conversation_write_system_message
(
convo
,
msg
,
PURPLE_MESSAGE_NO_LOG
);
g_free
(
msg
);
}
else
if
(
cur
!=
NULL
)
{
GList
*
users
=
NULL
;
GList
*
flags
=
NULL
;
while
(
*
cur
)
{
PurpleChatUserFlags
f
=
PURPLE_CHAT_USER_NONE
;
end
=
strchr
(
cur
,
' '
);
if
(
!
end
)
end
=
cur
+
strlen
(
cur
);
if
(
*
cur
==
'@'
)
{
f
=
PURPLE_CHAT_USER_OP
;
cur
++
;
}
else
if
(
*
cur
==
'%'
)
{
f
=
PURPLE_CHAT_USER_HALFOP
;
cur
++
;
}
else
if
(
*
cur
==
'+'
)
{
f
=
PURPLE_CHAT_USER_VOICE
;
cur
++
;
}
else
if
(
irc
->
mode_chars
&&
strchr
(
irc
->
mode_chars
,
*
cur
))
{
if
(
*
cur
==
'~'
)
f
=
PURPLE_CHAT_USER_FOUNDER
;
cur
++
;
}
tmp
=
g_strndup
(
cur
,
end
-
cur
);
users
=
g_list_prepend
(
users
,
tmp
);
flags
=
g_list_prepend
(
flags
,
GINT_TO_POINTER
(
f
));
cur
=
end
;
if
(
*
cur
)
cur
++
;
}
if
(
users
!=
NULL
)
{
purple_chat_conversation_add_users
(
PURPLE_CHAT_CONVERSATION
(
convo
),
users
,
NULL
,
flags
,
FALSE
);
g_list_free_full
(
users
,
g_free
);
g_list_free
(
flags
);
}
g_object_set_data
(
G_OBJECT
(
convo
),
IRC_NAMES_FLAG
,
GINT_TO_POINTER
(
TRUE
));
}
g_free
(
names
);
}
else
{
if
(
!
irc
->
names
)
irc
->
names
=
g_string_new
(
""
);
if
(
irc
->
names
->
len
&&
irc
->
names
->
str
[
irc
->
names
->
len
-
1
]
!=
' '
)
irc
->
names
=
g_string_append_c
(
irc
->
names
,
' '
);
irc
->
names
=
g_string_append
(
irc
->
names
,
args
[
3
]);
}
}
void
irc_msg_motd
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
char
*
escaped
;
if
(
purple_strequal
(
name
,
"375"
))
{
if
(
irc
->
motd
)
{
g_string_free
(
irc
->
motd
,
TRUE
);
irc
->
motd
=
NULL
;
}
irc
->
motd
=
g_string_new
(
""
);
return
;
}
else
if
(
purple_strequal
(
name
,
"376"
))
{
/* dircproxy 1.0.5 does not send 251 on reconnection, so
* finalize the connection here if it is not already done. */
irc_connected
(
irc
,
args
[
0
]);
return
;
}
else
if
(
purple_strequal
(
name
,
"422"
))
{
/* in case there is no 251, and no MOTD set, finalize the connection.
* (and clear the motd for good measure). */
if
(
irc
->
motd
)
{
g_string_free
(
irc
->
motd
,
TRUE
);
irc
->
motd
=
NULL
;
}
irc_connected
(
irc
,
args
[
0
]);
return
;
}
if
(
!
irc
->
motd
)
{
purple_debug_error
(
"irc"
,
"IRC server sent MOTD without STARTMOTD
\n
"
);
return
;
}
if
(
!
args
[
1
])
return
;
escaped
=
g_markup_escape_text
(
args
[
1
],
-1
);
g_string_append_printf
(
irc
->
motd
,
"%s<br>"
,
escaped
);
g_free
(
escaped
);
}
void
irc_msg_time
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleConnection
*
gc
;
gc
=
purple_account_get_connection
(
irc
->
account
);
g_return_if_fail
(
gc
);
purple_notify_message
(
gc
,
PURPLE_NOTIFY_MSG_INFO
,
_
(
"Time Response"
),
_
(
"The IRC server's local time is:"
),
args
[
2
],
NULL
,
NULL
,
purple_request_cpar_from_connection
(
gc
));
}
void
irc_msg_nochan
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleConnection
*
gc
=
purple_account_get_connection
(
irc
->
account
);
g_return_if_fail
(
gc
);
purple_notify_error
(
gc
,
NULL
,
_
(
"No such channel"
),
args
[
1
],
purple_request_cpar_from_connection
(
gc
));
}
void
irc_msg_nonick
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleConnection
*
gc
;
PurpleConversation
*
convo
;
convo
=
purple_conversations_find_with_account
(
args
[
1
],
irc
->
account
);
if
(
convo
)
{
purple_conversation_write_system_message
(
convo
,
PURPLE_IS_IM_CONVERSATION
(
convo
)
?
_
(
"User is not logged in"
)
:
_
(
"no such channel"
),
PURPLE_MESSAGE_NO_LOG
);
}
else
{
if
((
gc
=
purple_account_get_connection
(
irc
->
account
))
==
NULL
)
return
;
purple_notify_error
(
gc
,
NULL
,
_
(
"No such nick or channel"
),
args
[
1
],
purple_request_cpar_from_connection
(
gc
));
}
if
(
irc
->
whois
.
nick
&&
!
purple_utf8_strcasecmp
(
irc
->
whois
.
nick
,
args
[
1
]))
{
g_free
(
irc
->
whois
.
nick
);
irc
->
whois
.
nick
=
NULL
;
}
}
void
irc_msg_nosend
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleConnection
*
gc
;
PurpleChatConversation
*
chat
;
chat
=
purple_conversations_find_chat_with_account
(
args
[
1
],
irc
->
account
);
if
(
chat
)
{
purple_conversation_write_system_message
(
PURPLE_CONVERSATION
(
chat
),
args
[
2
],
PURPLE_MESSAGE_NO_LOG
);
}
else
{
if
((
gc
=
purple_account_get_connection
(
irc
->
account
))
==
NULL
)
return
;
purple_notify_error
(
gc
,
NULL
,
_
(
"Could not send"
),
args
[
2
],
purple_request_cpar_from_connection
(
gc
));
}
}
void
irc_msg_notinchan
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleChatConversation
*
chat
=
purple_conversations_find_chat_with_account
(
args
[
1
],
irc
->
account
);
purple_debug
(
PURPLE_DEBUG_INFO
,
"irc"
,
"We're apparently not in %s, but tried to use it
\n
"
,
args
[
1
]);
if
(
chat
)
{
/*g_slist_remove(irc->gc->buddy_chats, chat);
purple_conversation_set_account(chat, NULL);*/
purple_conversation_write_system_message
(
PURPLE_CONVERSATION
(
chat
),
args
[
2
],
PURPLE_MESSAGE_NO_LOG
);
}
}
void
irc_msg_notop
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleChatConversation
*
chat
;
chat
=
purple_conversations_find_chat_with_account
(
args
[
1
],
irc
->
account
);
if
(
!
chat
)
return
;
purple_conversation_write_system_message
(
PURPLE_CONVERSATION
(
chat
),
args
[
2
],
0
);
}
void
irc_msg_invite
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleConnection
*
gc
=
purple_account_get_connection
(
irc
->
account
);
GHashTable
*
components
;
gchar
*
nick
;
g_return_if_fail
(
gc
);
components
=
g_hash_table_new_full
(
g_str_hash
,
g_str_equal
,
g_free
,
g_free
);
nick
=
irc_mask_nick
(
from
);
g_hash_table_insert
(
components
,
g_strdup
(
"channel"
),
g_strdup
(
args
[
1
]));
purple_serv_got_chat_invite
(
gc
,
args
[
1
],
nick
,
NULL
,
components
);
g_free
(
nick
);
}
void
irc_msg_inviteonly
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleConnection
*
gc
=
purple_account_get_connection
(
irc
->
account
);
char
*
buf
;
g_return_if_fail
(
gc
);
buf
=
g_strdup_printf
(
_
(
"Joining %s requires an invitation."
),
args
[
1
]);
purple_notify_error
(
gc
,
_
(
"Invitation only"
),
_
(
"Invitation only"
),
buf
,
purple_request_cpar_from_connection
(
gc
));
g_free
(
buf
);
}
void
irc_msg_ison
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
char
**
nicks
;
struct
irc_buddy
*
ib
;
int
i
;
nicks
=
g_strsplit
(
args
[
1
],
" "
,
-1
);
for
(
i
=
0
;
nicks
[
i
];
i
++
)
{
if
((
ib
=
g_hash_table_lookup
(
irc
->
buddies
,
(
gconstpointer
)
nicks
[
i
]))
==
NULL
)
{
continue
;
}
ib
->
new_online_status
=
TRUE
;
}
g_strfreev
(
nicks
);
if
(
irc
->
ison_outstanding
)
irc_buddy_query
(
irc
);
if
(
!
irc
->
ison_outstanding
)
g_hash_table_foreach
(
irc
->
buddies
,
(
GHFunc
)
irc_buddy_status
,
(
gpointer
)
irc
);
}
static
void
irc_buddy_status
(
char
*
name
,
struct
irc_buddy
*
ib
,
struct
irc_conn
*
irc
)
{
PurpleConnection
*
gc
=
purple_account_get_connection
(
irc
->
account
);
PurpleBuddy
*
buddy
=
purple_blist_find_buddy
(
irc
->
account
,
name
);
if
(
!
gc
||
!
buddy
)
return
;
if
(
ib
->
online
&&
!
ib
->
new_online_status
)
{
purple_protocol_got_user_status
(
irc
->
account
,
name
,
"offline"
,
NULL
);
ib
->
online
=
FALSE
;
}
else
if
(
!
ib
->
online
&&
ib
->
new_online_status
)
{
purple_protocol_got_user_status
(
irc
->
account
,
name
,
"available"
,
NULL
);
ib
->
online
=
TRUE
;
}
}
void
irc_msg_join
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleConnection
*
gc
=
purple_account_get_connection
(
irc
->
account
);
PurpleChatConversation
*
chat
;
PurpleChatUser
*
cb
;
char
*
nick
,
*
userhost
,
*
buf
;
struct
irc_buddy
*
ib
;
static
int
id
=
1
;
g_return_if_fail
(
gc
);
nick
=
irc_mask_nick
(
from
);
if
(
!
purple_utf8_strcasecmp
(
nick
,
purple_connection_get_display_name
(
gc
)))
{
/* We are joining a channel for the first time */
purple_serv_got_joined_chat
(
gc
,
id
++
,
args
[
0
]);
g_free
(
nick
);
chat
=
purple_conversations_find_chat_with_account
(
args
[
0
],
irc
->
account
);
if
(
chat
==
NULL
)
{
purple_debug_error
(
"irc"
,
"tried to join %s but couldn't
\n
"
,
args
[
0
]);
return
;
}
g_object_set_data
(
G_OBJECT
(
chat
),
IRC_NAMES_FLAG
,
GINT_TO_POINTER
(
FALSE
));
// Get the real name and user host for all participants.
buf
=
irc_format
(
irc
,
"vc"
,
"WHO"
,
args
[
0
]);
irc_send
(
irc
,
buf
);
g_free
(
buf
);
/* Until purple_conversation_present does something that
* one would expect in Pidgin, this call produces buggy
* behavior both for the /join and auto-join cases. */
/* purple_conversation_present(chat); */
return
;
}
chat
=
purple_conversations_find_chat_with_account
(
args
[
0
],
irc
->
account
);
if
(
chat
==
NULL
)
{
purple_debug
(
PURPLE_DEBUG_ERROR
,
"irc"
,
"JOIN for %s failed
\n
"
,
args
[
0
]);
g_free
(
nick
);
return
;
}
userhost
=
irc_mask_userhost
(
from
);
purple_chat_conversation_add_user
(
chat
,
nick
,
userhost
,
PURPLE_CHAT_USER_NONE
,
TRUE
);
cb
=
purple_chat_conversation_find_user
(
chat
,
nick
);
if
(
cb
)
{
g_object_set_data_full
(
G_OBJECT
(
cb
),
"userhost"
,
userhost
,
g_free
);
}
if
((
ib
=
g_hash_table_lookup
(
irc
->
buddies
,
nick
))
!=
NULL
)
{
ib
->
new_online_status
=
TRUE
;
irc_buddy_status
(
nick
,
ib
,
irc
);
}
g_free
(
nick
);
}
void
irc_msg_kick
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleConnection
*
gc
=
purple_account_get_connection
(
irc
->
account
);
PurpleChatConversation
*
chat
=
purple_conversations_find_chat_with_account
(
args
[
0
],
irc
->
account
);
char
*
nick
,
*
buf
;
g_return_if_fail
(
gc
);
nick
=
irc_mask_nick
(
from
);
if
(
!
chat
)
{
purple_debug
(
PURPLE_DEBUG_ERROR
,
"irc"
,
"Received a KICK for unknown channel %s
\n
"
,
args
[
0
]);
g_free
(
nick
);
return
;
}
if
(
!
purple_utf8_strcasecmp
(
purple_connection_get_display_name
(
gc
),
args
[
1
]))
{
buf
=
g_strdup_printf
(
_
(
"You have been kicked by %s: (%s)"
),
nick
,
args
[
2
]);
purple_conversation_write_system_message
(
PURPLE_CONVERSATION
(
chat
),
buf
,
0
);
g_free
(
buf
);
purple_serv_got_chat_left
(
gc
,
purple_chat_conversation_get_id
(
chat
));
}
else
{
buf
=
g_strdup_printf
(
_
(
"Kicked by %s (%s)"
),
nick
,
args
[
2
]);
purple_chat_conversation_remove_user
(
chat
,
args
[
1
],
buf
);
g_free
(
buf
);
}
g_free
(
nick
);
}
void
irc_msg_mode
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleChatConversation
*
chat
;
char
*
nick
=
irc_mask_nick
(
from
),
*
buf
;
if
(
*
args
[
0
]
==
'#'
||
*
args
[
0
]
==
'&'
)
{
/* Channel */
char
*
escaped
;
chat
=
purple_conversations_find_chat_with_account
(
args
[
0
],
irc
->
account
);
if
(
!
chat
)
{
purple_debug
(
PURPLE_DEBUG_ERROR
,
"irc"
,
"MODE received for %s, which we are not in
\n
"
,
args
[
0
]);
g_free
(
nick
);
return
;
}
escaped
=
(
args
[
2
]
!=
NULL
)
?
g_markup_escape_text
(
args
[
2
],
-1
)
:
NULL
;
buf
=
g_strdup_printf
(
_
(
"mode (%s %s) by %s"
),
args
[
1
],
escaped
?
escaped
:
""
,
nick
);
purple_conversation_write_system_message
(
PURPLE_CONVERSATION
(
chat
),
buf
,
0
);
g_free
(
escaped
);
g_free
(
buf
);
if
(
args
[
2
])
{
PurpleChatUser
*
cb
;
PurpleChatUserFlags
newflag
,
flags
;
char
*
mcur
,
*
cur
,
*
end
,
*
user
;
gboolean
add
=
FALSE
;
mcur
=
args
[
1
];
cur
=
args
[
2
];
while
(
*
cur
&&
*
mcur
)
{
if
((
*
mcur
==
'+'
)
||
(
*
mcur
==
'-'
))
{
add
=
(
*
mcur
==
'+'
)
?
TRUE
:
FALSE
;
mcur
++
;
continue
;
}
end
=
strchr
(
cur
,
' '
);
if
(
!
end
)
end
=
cur
+
strlen
(
cur
);
user
=
g_strndup
(
cur
,
end
-
cur
);
cb
=
purple_chat_conversation_find_user
(
chat
,
user
);
flags
=
purple_chat_user_get_flags
(
cb
);
newflag
=
PURPLE_CHAT_USER_NONE
;
if
(
*
mcur
==
'o'
)
newflag
=
PURPLE_CHAT_USER_OP
;
else
if
(
*
mcur
==
'h'
)
newflag
=
PURPLE_CHAT_USER_HALFOP
;
else
if
(
*
mcur
==
'v'
)
newflag
=
PURPLE_CHAT_USER_VOICE
;
else
if
(
irc
->
mode_chars
&&
strchr
(
irc
->
mode_chars
,
'~'
)
&&
(
*
mcur
==
'q'
))
newflag
=
PURPLE_CHAT_USER_FOUNDER
;
if
(
newflag
)
{
if
(
add
)
flags
|=
newflag
;
else
flags
&=
~
newflag
;
purple_chat_user_set_flags
(
cb
,
flags
);
}
g_free
(
user
);
cur
=
end
;
if
(
*
cur
)
cur
++
;
if
(
*
mcur
)
mcur
++
;
}
}
}
else
{
/* User */
}
g_free
(
nick
);
}
void
irc_msg_nick
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleConnection
*
gc
=
purple_account_get_connection
(
irc
->
account
);
PurpleIMConversation
*
im
;
GSList
*
chats
;
char
*
nick
=
irc_mask_nick
(
from
);
irc
->
nickused
=
FALSE
;
if
(
!
gc
)
{
g_free
(
nick
);
return
;
}
chats
=
purple_connection_get_active_chats
(
gc
);
if
(
!
purple_utf8_strcasecmp
(
nick
,
purple_connection_get_display_name
(
gc
)))
{
purple_connection_set_display_name
(
gc
,
args
[
0
]);
}
while
(
chats
)
{
PurpleChatConversation
*
chat
=
PURPLE_CHAT_CONVERSATION
(
chats
->
data
);
/* This is ugly ... */
if
(
purple_chat_conversation_has_user
(
chat
,
nick
))
purple_chat_conversation_rename_user
(
chat
,
nick
,
args
[
0
]);
chats
=
chats
->
next
;
}
im
=
purple_conversations_find_im_with_account
(
nick
,
irc
->
account
);
if
(
im
!=
NULL
)
purple_conversation_set_name
(
PURPLE_CONVERSATION
(
im
),
args
[
0
]);
g_free
(
nick
);
}
void
irc_msg_badnick
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleConnection
*
gc
=
purple_account_get_connection
(
irc
->
account
);
if
(
purple_connection_get_state
(
gc
)
==
PURPLE_CONNECTION_CONNECTED
)
{
purple_notify_error
(
gc
,
_
(
"Invalid nickname"
),
_
(
"Invalid "
"nickname"
),
_
(
"Your selected nickname was rejected by "
"the server. It probably contains invalid characters."
),
purple_request_cpar_from_connection
(
gc
));
}
else
{
purple_connection_take_error
(
gc
,
g_error_new_literal
(
PURPLE_CONNECTION_ERROR
,
PURPLE_CONNECTION_ERROR_INVALID_SETTINGS
,
_
(
"Your selected account name was rejected by the server. It probably contains invalid characters."
)));
}
}
void
irc_msg_nickused
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
char
*
newnick
,
*
buf
,
*
end
;
PurpleConnection
*
gc
=
purple_account_get_connection
(
irc
->
account
);
if
(
gc
&&
purple_connection_get_state
(
gc
)
==
PURPLE_CONNECTION_CONNECTED
)
{
/* We only want to do the following dance if the connection
has not been successfully completed. If it has, just
notify the user that their /nick command didn't go. */
buf
=
g_strdup_printf
(
_
(
"The nickname
\"
%s
\"
is already being used."
),
irc
->
reqnick
);
purple_notify_error
(
gc
,
_
(
"Nickname in use"
),
_
(
"Nickname in "
"use"
),
buf
,
purple_request_cpar_from_connection
(
gc
));
g_free
(
buf
);
g_free
(
irc
->
reqnick
);
irc
->
reqnick
=
NULL
;
return
;
}
if
(
strlen
(
args
[
1
])
<
strlen
(
irc
->
reqnick
)
||
irc
->
nickused
)
newnick
=
g_strdup
(
args
[
1
]);
else
newnick
=
g_strdup_printf
(
"%s0"
,
args
[
1
]);
end
=
newnick
+
strlen
(
newnick
)
-
1
;
/* try fallbacks */
if
((
*
end
<
'9'
)
&&
(
*
end
>=
'1'
))
{
*
end
=
*
end
+
1
;
}
else
*
end
=
'1'
;
g_free
(
irc
->
reqnick
);
irc
->
reqnick
=
newnick
;
irc
->
nickused
=
TRUE
;
purple_connection_set_display_name
(
purple_account_get_connection
(
irc
->
account
),
newnick
);
buf
=
irc_format
(
irc
,
"vn"
,
"NICK"
,
newnick
);
irc_send
(
irc
,
buf
);
g_free
(
buf
);
}
void
irc_msg_notice
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
irc_msg_handle_privmsg
(
irc
,
name
,
from
,
args
[
0
],
args
[
1
],
TRUE
);
}
void
irc_msg_nochangenick
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleConnection
*
gc
=
purple_account_get_connection
(
irc
->
account
);
g_return_if_fail
(
gc
);
purple_notify_error
(
gc
,
_
(
"Cannot change nick"
),
_
(
"Could not change nick"
),
args
[
2
],
purple_request_cpar_from_connection
(
gc
));
}
void
irc_msg_part
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleConnection
*
gc
=
purple_account_get_connection
(
irc
->
account
);
PurpleChatConversation
*
chat
;
char
*
nick
,
*
msg
,
*
channel
;
g_return_if_fail
(
gc
);
/* Undernet likes to :-quote the channel name, for no good reason
* that I can see. This catches that. */
channel
=
(
args
[
0
][
0
]
==
':'
)
?
&
args
[
0
][
1
]
:
args
[
0
];
chat
=
purple_conversations_find_chat_with_account
(
channel
,
irc
->
account
);
if
(
!
chat
)
{
purple_debug
(
PURPLE_DEBUG_INFO
,
"irc"
,
"Got a PART on %s, which doesn't exist -- probably closed
\n
"
,
channel
);
return
;
}
nick
=
irc_mask_nick
(
from
);
if
(
!
purple_utf8_strcasecmp
(
nick
,
purple_connection_get_display_name
(
gc
)))
{
char
*
escaped
=
args
[
1
]
?
g_markup_escape_text
(
args
[
1
],
-1
)
:
NULL
;
msg
=
g_strdup_printf
(
_
(
"You have parted the channel%s%s"
),
(
args
[
1
]
&&
*
args
[
1
])
?
": "
:
""
,
(
escaped
&&
*
escaped
)
?
escaped
:
""
);
g_free
(
escaped
);
purple_conversation_write_system_message
(
PURPLE_CONVERSATION
(
chat
),
msg
,
0
);
g_free
(
msg
);
purple_serv_got_chat_left
(
gc
,
purple_chat_conversation_get_id
(
chat
));
}
else
{
msg
=
args
[
1
]
?
irc_mirc2txt
(
args
[
1
])
:
NULL
;
purple_chat_conversation_remove_user
(
chat
,
nick
,
msg
);
g_free
(
msg
);
}
g_free
(
nick
);
}
void
irc_msg_ping
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
char
*
buf
;
buf
=
irc_format
(
irc
,
"v:"
,
"PONG"
,
args
[
0
]);
irc_send
(
irc
,
buf
);
g_free
(
buf
);
}
void
irc_msg_pong
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleConversation
*
convo
;
PurpleConnection
*
gc
;
char
**
parts
,
*
msg
;
gint64
oldstamp
;
parts
=
g_strsplit
(
args
[
1
],
" "
,
2
);
if
(
!
parts
[
0
]
||
!
parts
[
1
])
{
g_strfreev
(
parts
);
return
;
}
if
(
sscanf
(
parts
[
1
],
"%"
G_GINT64_FORMAT
,
&
oldstamp
)
!=
1
)
{
msg
=
g_strdup
(
_
(
"Error: invalid PONG from server"
));
}
else
{
msg
=
g_strdup_printf
(
_
(
"PING reply -- Lag: %f seconds"
),
(
g_get_monotonic_time
()
-
oldstamp
)
/
(
gdouble
)
G_USEC_PER_SEC
);
}
convo
=
purple_conversations_find_with_account
(
parts
[
0
],
irc
->
account
);
g_strfreev
(
parts
);
if
(
convo
)
{
purple_conversation_write_system_message
(
convo
,
msg
,
PURPLE_MESSAGE_NO_LOG
);
}
else
{
gc
=
purple_account_get_connection
(
irc
->
account
);
if
(
!
gc
)
{
g_free
(
msg
);
return
;
}
purple_notify_info
(
gc
,
NULL
,
"PONG"
,
msg
,
purple_request_cpar_from_connection
(
gc
));
}
g_free
(
msg
);
}
void
irc_msg_privmsg
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
irc_msg_handle_privmsg
(
irc
,
name
,
from
,
args
[
0
],
args
[
1
],
FALSE
);
}
static
void
irc_msg_handle_privmsg
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
const
char
*
to
,
const
char
*
rawmsg
,
gboolean
notice
)
{
PurpleConnection
*
gc
=
purple_account_get_connection
(
irc
->
account
);
PurpleChatConversation
*
chat
;
char
*
tmp
;
char
*
msg
;
char
*
nick
;
if
(
!
gc
)
return
;
nick
=
irc_mask_nick
(
from
);
tmp
=
irc_parse_ctcp
(
irc
,
nick
,
to
,
rawmsg
,
notice
);
if
(
!
tmp
)
{
g_free
(
nick
);
return
;
}
msg
=
irc_escape_privmsg
(
tmp
,
-1
);
g_free
(
tmp
);
tmp
=
irc_mirc2html
(
msg
);
g_free
(
msg
);
msg
=
tmp
;
if
(
notice
)
{
tmp
=
g_strdup_printf
(
"(notice) %s"
,
msg
);
g_free
(
msg
);
msg
=
tmp
;
}
if
(
!
purple_utf8_strcasecmp
(
to
,
purple_connection_get_display_name
(
gc
)))
{
purple_serv_got_im
(
gc
,
nick
,
msg
,
0
,
time
(
NULL
));
}
else
{
chat
=
purple_conversations_find_chat_with_account
(
irc_nick_skip_mode
(
irc
,
to
),
irc
->
account
);
if
(
chat
)
{
purple_serv_got_chat_in
(
gc
,
purple_chat_conversation_get_id
(
chat
),
nick
,
PURPLE_MESSAGE_RECV
,
msg
,
time
(
NULL
));
}
else
purple_debug_error
(
"irc"
,
"Got a %s on %s, which does not exist
\n
"
,
notice
?
"NOTICE"
:
"PRIVMSG"
,
to
);
}
g_free
(
msg
);
g_free
(
nick
);
}
void
irc_msg_regonly
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleConnection
*
gc
=
purple_account_get_connection
(
irc
->
account
);
char
*
msg
;
g_return_if_fail
(
gc
);
if
(
purple_conversations_find_chat_with_account
(
args
[
1
],
irc
->
account
))
{
/* This is a channel we're already in; for some reason,
* freenode feels the need to notify us that in some
* hypothetical other situation this might not have
* succeeded. Suppress that. */
return
;
}
msg
=
g_strdup_printf
(
_
(
"Cannot join %s: Registration is required."
),
args
[
1
]);
purple_notify_error
(
gc
,
_
(
"Cannot join channel"
),
msg
,
args
[
2
],
purple_request_cpar_from_connection
(
gc
));
g_free
(
msg
);
}
void
irc_msg_quit
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleConnection
*
gc
=
purple_account_get_connection
(
irc
->
account
);
struct
irc_buddy
*
ib
;
char
*
data
[
2
];
g_return_if_fail
(
gc
);
data
[
0
]
=
irc_mask_nick
(
from
);
data
[
1
]
=
args
[
0
];
/* XXX this should have an API, I shouldn't grab this directly */
g_slist_foreach
(
purple_connection_get_active_chats
(
gc
),
(
GFunc
)
irc_chat_remove_buddy
,
data
);
if
((
ib
=
g_hash_table_lookup
(
irc
->
buddies
,
data
[
0
]))
!=
NULL
)
{
ib
->
new_online_status
=
FALSE
;
irc_buddy_status
(
data
[
0
],
ib
,
irc
);
}
g_free
(
data
[
0
]);
}
void
irc_msg_unavailable
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleConnection
*
gc
=
purple_account_get_connection
(
irc
->
account
);
purple_notify_error
(
gc
,
NULL
,
_
(
"Nick or channel is temporarily "
"unavailable."
),
args
[
1
],
purple_request_cpar_from_connection
(
gc
));
}
void
irc_msg_wallops
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleConnection
*
gc
=
purple_account_get_connection
(
irc
->
account
);
char
*
nick
,
*
msg
;
g_return_if_fail
(
gc
);
nick
=
irc_mask_nick
(
from
);
msg
=
g_strdup_printf
(
_
(
"Wallops from %s"
),
nick
);
g_free
(
nick
);
purple_notify_info
(
gc
,
NULL
,
msg
,
args
[
0
],
purple_request_cpar_from_connection
(
gc
));
g_free
(
msg
);
}
#ifdef HAVE_CYRUS_SASL
static
int
irc_sasl_cb_secret
(
sasl_conn_t
*
conn
,
void
*
ctx
,
int
id
,
sasl_secret_t
**
secret
)
{
struct
irc_conn
*
irc
=
ctx
;
sasl_secret_t
*
sasl_secret
;
const
char
*
pw
;
size_t
len
;
pw
=
purple_connection_get_password
(
purple_account_get_connection
(
irc
->
account
));
if
(
!
conn
||
!
secret
||
id
!=
SASL_CB_PASS
)
return
SASL_BADPARAM
;
len
=
strlen
(
pw
);
/* Not an off-by-one because sasl_secret_t defines char data[1] */
/* TODO: This can probably be moved to glib's allocator */
sasl_secret
=
malloc
(
sizeof
(
sasl_secret_t
)
+
len
);
if
(
!
sasl_secret
)
return
SASL_NOMEM
;
sasl_secret
->
len
=
len
;
strcpy
((
char
*
)
sasl_secret
->
data
,
pw
);
*
secret
=
sasl_secret
;
return
SASL_OK
;
}
static
int
irc_sasl_cb_log
(
void
*
context
,
int
level
,
const
char
*
message
)
{
if
(
level
<=
SASL_LOG_TRACE
)
purple_debug_info
(
"sasl"
,
"%s
\n
"
,
message
);
return
SASL_OK
;
}
static
int
irc_sasl_cb_simple
(
void
*
ctx
,
int
id
,
const
char
**
res
,
unsigned
*
len
)
{
struct
irc_conn
*
irc
=
ctx
;
PurpleConnection
*
gc
=
purple_account_get_connection
(
irc
->
account
);
switch
(
id
)
{
case
SASL_CB_AUTHNAME
:
*
res
=
purple_connection_get_display_name
(
gc
);
break
;
case
SASL_CB_USER
:
*
res
=
""
;
break
;
default
:
return
SASL_BADPARAM
;
}
if
(
len
)
*
len
=
strlen
((
char
*
)
*
res
);
return
SASL_OK
;
}
static
void
irc_auth_start_cyrus
(
struct
irc_conn
*
irc
)
{
int
ret
=
0
;
char
*
buf
;
sasl_security_properties_t
secprops
;
PurpleAccount
*
account
=
irc
->
account
;
PurpleConnection
*
gc
=
purple_account_get_connection
(
account
);
gboolean
again
=
FALSE
;
/* Set up security properties and options */
secprops
.
min_ssf
=
0
;
secprops
.
security_flags
=
SASL_SEC_NOANONYMOUS
;
if
(
!
G_IS_TLS_CONNECTION
(
irc
->
conn
))
{
gboolean
plaintext
;
secprops
.
max_ssf
=
-1
;
secprops
.
maxbufsize
=
4096
;
plaintext
=
purple_account_get_bool
(
account
,
"auth_plain_in_clear"
,
FALSE
);
if
(
!
plaintext
)
secprops
.
security_flags
|=
SASL_SEC_NOPLAINTEXT
;
}
else
{
secprops
.
max_ssf
=
0
;
secprops
.
maxbufsize
=
0
;
}
secprops
.
property_names
=
0
;
secprops
.
property_values
=
0
;
do
{
again
=
FALSE
;
ret
=
sasl_client_new
(
"irc"
,
irc
->
server
,
NULL
,
NULL
,
irc
->
sasl_cb
,
0
,
&
irc
->
sasl_conn
);
if
(
ret
!=
SASL_OK
)
{
purple_debug_error
(
"irc"
,
"sasl_client_new failed: %d
\n
"
,
ret
);
purple_connection_take_error
(
gc
,
g_error_new
(
PURPLE_CONNECTION_ERROR
,
PURPLE_CONNECTION_ERROR_OTHER_ERROR
,
(
"Failed to initialize SASL authentication: %s"
),
sasl_errdetail
(
irc
->
sasl_conn
)));
return
;
}
sasl_setprop
(
irc
->
sasl_conn
,
SASL_AUTH_EXTERNAL
,
purple_account_get_username
(
irc
->
account
));
sasl_setprop
(
irc
->
sasl_conn
,
SASL_SEC_PROPS
,
&
secprops
);
ret
=
sasl_client_start
(
irc
->
sasl_conn
,
irc
->
sasl_mechs
->
str
,
NULL
,
NULL
,
NULL
,
&
irc
->
current_mech
);
switch
(
ret
)
{
case
SASL_OK
:
case
SASL_CONTINUE
:
irc
->
mech_works
=
FALSE
;
break
;
case
SASL_NOMECH
:
purple_connection_take_error
(
gc
,
g_error_new_literal
(
PURPLE_CONNECTION_ERROR
,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE
,
_
(
"SASL authentication failed: No worthy authentication mechanisms found."
)));
irc_sasl_finish
(
irc
);
return
;
case
SASL_BADPARAM
:
case
SASL_NOMEM
:
purple_connection_take_error
(
gc
,
g_error_new
(
PURPLE_CONNECTION_ERROR
,
PURPLE_CONNECTION_ERROR_OTHER_ERROR
,
_
(
"SASL authentication failed: %s"
),
sasl_errdetail
(
irc
->
sasl_conn
)));
irc_sasl_finish
(
irc
);
return
;
default
:
purple_debug_error
(
"irc"
,
"sasl_client_start failed: %s
\n
"
,
sasl_errdetail
(
irc
->
sasl_conn
));
if
(
irc
->
current_mech
&&
*
irc
->
current_mech
)
{
char
*
pos
;
if
((
pos
=
strstr
(
irc
->
sasl_mechs
->
str
,
irc
->
current_mech
)))
{
size_t
index
=
pos
-
irc
->
sasl_mechs
->
str
;
g_string_erase
(
irc
->
sasl_mechs
,
index
,
strlen
(
irc
->
current_mech
));
/* Remove space which separated this mech from the next */
if
((
irc
->
sasl_mechs
->
str
)[
index
]
==
' '
)
{
g_string_erase
(
irc
->
sasl_mechs
,
index
,
1
);
}
}
again
=
TRUE
;
}
irc_sasl_finish
(
irc
);
}
}
while
(
again
);
purple_debug_info
(
"irc"
,
"Using SASL: %s
\n
"
,
irc
->
current_mech
);
buf
=
irc_format
(
irc
,
"vv"
,
"AUTHENTICATE"
,
irc
->
current_mech
);
irc_send
(
irc
,
buf
);
g_free
(
buf
);
}
/* SASL authentication */
void
irc_msg_cap
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
int
ret
=
0
;
int
id
=
0
;
PurpleConnection
*
gc
=
purple_account_get_connection
(
irc
->
account
);
const
char
*
mech_list
=
NULL
;
char
*
pos
;
size_t
index
;
if
(
strncmp
(
g_strstrip
(
args
[
2
]),
"sasl"
,
5
))
return
;
if
(
strncmp
(
args
[
1
],
"ACK"
,
4
))
{
purple_connection_take_error
(
gc
,
g_error_new_literal
(
PURPLE_CONNECTION_ERROR
,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE
,
_
(
"SASL authentication failed: Server does not support SASL authentication."
)));
irc_sasl_finish
(
irc
);
return
;
}
if
((
ret
=
sasl_client_init
(
NULL
))
!=
SASL_OK
)
{
purple_connection_take_error
(
gc
,
g_error_new_literal
(
PURPLE_CONNECTION_ERROR
,
PURPLE_CONNECTION_ERROR_OTHER_ERROR
,
_
(
"SASL authentication failed: Initializing SASL failed."
)));
return
;
}
irc
->
sasl_cb
=
g_new0
(
sasl_callback_t
,
5
);
irc
->
sasl_cb
[
id
].
id
=
SASL_CB_AUTHNAME
;
irc
->
sasl_cb
[
id
].
proc
=
(
int
(
*
)(
void
))
irc_sasl_cb_simple
;
/* sasl_getsimple_t */
irc
->
sasl_cb
[
id
].
context
=
(
void
*
)
irc
;
id
++
;
irc
->
sasl_cb
[
id
].
id
=
SASL_CB_USER
;
irc
->
sasl_cb
[
id
].
proc
=
(
int
(
*
)(
void
))
irc_sasl_cb_simple
;
/* sasl_getsimple_t */
irc
->
sasl_cb
[
id
].
context
=
(
void
*
)
irc
;
id
++
;
irc
->
sasl_cb
[
id
].
id
=
SASL_CB_PASS
;
irc
->
sasl_cb
[
id
].
proc
=
(
int
(
*
)(
void
))
irc_sasl_cb_secret
;
/* sasl_getsecret_t */
irc
->
sasl_cb
[
id
].
context
=
(
void
*
)
irc
;
id
++
;
irc
->
sasl_cb
[
id
].
id
=
SASL_CB_LOG
;
irc
->
sasl_cb
[
id
].
proc
=
(
int
(
*
)(
void
))
irc_sasl_cb_log
;
/* sasl_log_t */
irc
->
sasl_cb
[
id
].
context
=
(
void
*
)
irc
;
id
++
;
irc
->
sasl_cb
[
id
].
id
=
SASL_CB_LIST_END
;
/* We need to do this to be able to list the mechanisms. */
ret
=
sasl_client_new
(
"irc"
,
irc
->
server
,
NULL
,
NULL
,
irc
->
sasl_cb
,
0
,
&
irc
->
sasl_conn
);
sasl_listmech
(
irc
->
sasl_conn
,
NULL
,
""
,
" "
,
""
,
&
mech_list
,
NULL
,
NULL
);
purple_debug_info
(
"irc"
,
"SASL: we have available: %s
\n
"
,
mech_list
);
if
(
ret
!=
SASL_OK
)
{
purple_debug_error
(
"irc"
,
"sasl_client_new failed: %d
\n
"
,
ret
);
purple_connection_take_error
(
gc
,
g_error_new
(
PURPLE_CONNECTION_ERROR
,
PURPLE_CONNECTION_ERROR_OTHER_ERROR
,
_
(
"Failed to initialize SASL authentication: %s"
),
sasl_errdetail
(
irc
->
sasl_conn
)));
return
;
}
irc
->
sasl_mechs
=
g_string_new
(
mech_list
);
/* Drop EXTERNAL mechanism since we don't support it */
if
((
pos
=
strstr
(
irc
->
sasl_mechs
->
str
,
"EXTERNAL"
)))
{
index
=
pos
-
irc
->
sasl_mechs
->
str
;
g_string_erase
(
irc
->
sasl_mechs
,
index
,
strlen
(
"EXTERNAL"
));
/* Remove space which separated this mech from the next */
if
((
irc
->
sasl_mechs
->
str
)[
index
]
==
' '
)
{
g_string_erase
(
irc
->
sasl_mechs
,
index
,
1
);
}
}
irc_auth_start_cyrus
(
irc
);
}
void
irc_msg_auth
(
struct
irc_conn
*
irc
,
char
*
arg
)
{
PurpleConnection
*
gc
=
purple_account_get_connection
(
irc
->
account
);
char
*
buf
,
*
authinfo
;
char
*
serverin
=
NULL
;
gsize
serverinlen
=
0
;
const
gchar
*
c_out
;
unsigned
int
clen
;
int
ret
;
irc
->
mech_works
=
TRUE
;
if
(
!
arg
)
return
;
if
(
arg
[
0
]
!=
'+'
)
serverin
=
(
char
*
)
g_base64_decode
(
arg
,
&
serverinlen
);
ret
=
sasl_client_step
(
irc
->
sasl_conn
,
serverin
,
serverinlen
,
NULL
,
&
c_out
,
&
clen
);
if
(
ret
!=
SASL_OK
&&
ret
!=
SASL_CONTINUE
)
{
purple_connection_take_error
(
gc
,
g_error_new
(
PURPLE_CONNECTION_ERROR
,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE
,
_
(
"SASL authentication failed: %s"
),
sasl_errdetail
(
irc
->
sasl_conn
)));
irc_sasl_finish
(
irc
);
g_free
(
serverin
);
return
;
}
if
(
clen
>
0
)
authinfo
=
g_base64_encode
((
const
guchar
*
)
c_out
,
clen
);
else
authinfo
=
g_strdup
(
"+"
);
buf
=
irc_format
(
irc
,
"vv"
,
"AUTHENTICATE"
,
authinfo
);
irc_send
(
irc
,
buf
);
g_free
(
buf
);
g_free
(
authinfo
);
g_free
(
serverin
);
}
void
irc_msg_authenticate
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
irc_msg_auth
(
irc
,
args
[
0
]);
}
void
irc_msg_authok
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
char
*
buf
;
sasl_dispose
(
&
irc
->
sasl_conn
);
irc
->
sasl_conn
=
NULL
;
purple_debug_info
(
"irc"
,
"Succesfully authenticated using SASL.
\n
"
);
/* Finish auth session */
buf
=
irc_format
(
irc
,
"vv"
,
"CAP"
,
"END"
);
irc_send
(
irc
,
buf
);
g_free
(
buf
);
}
void
irc_msg_authtryagain
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleConnection
*
gc
=
purple_account_get_connection
(
irc
->
account
);
/* We already received at least one AUTHENTICATE reply from the
* server. This suggests it supports this mechanism, but the
* password was incorrect. It would be better to abort and inform
* the user than to try again with a different mechanism, so they
* aren't told the server supports no worthy mechanisms.
*/
if
(
irc
->
mech_works
)
{
purple_connection_take_error
(
gc
,
g_error_new_literal
(
PURPLE_CONNECTION_ERROR
,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED
,
_
(
"Incorrect Password"
)));
irc_sasl_finish
(
irc
);
return
;
}
if
(
irc
->
current_mech
)
{
char
*
pos
;
if
((
pos
=
strstr
(
irc
->
sasl_mechs
->
str
,
irc
->
current_mech
)))
{
size_t
index
=
pos
-
irc
->
sasl_mechs
->
str
;
g_string_erase
(
irc
->
sasl_mechs
,
index
,
strlen
(
irc
->
current_mech
));
/* Remove space which separated this mech from the next */
if
((
irc
->
sasl_mechs
->
str
)[
index
]
==
' '
)
{
g_string_erase
(
irc
->
sasl_mechs
,
index
,
1
);
}
}
}
if
(
*
irc
->
sasl_mechs
->
str
)
{
sasl_dispose
(
&
irc
->
sasl_conn
);
purple_debug_info
(
"irc"
,
"Now trying with %s
\n
"
,
irc
->
sasl_mechs
->
str
);
irc_auth_start_cyrus
(
irc
);
}
else
{
purple_connection_take_error
(
gc
,
g_error_new_literal
(
PURPLE_CONNECTION_ERROR
,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE
,
_
(
"SASL authentication failed: No worthy mechanisms found"
)));
irc_sasl_finish
(
irc
);
}
}
void
irc_msg_authfail
(
struct
irc_conn
*
irc
,
const
char
*
name
,
const
char
*
from
,
char
**
args
)
{
PurpleConnection
*
gc
=
purple_account_get_connection
(
irc
->
account
);
/* Only show an error if we did not abort ourselves. */
if
(
irc
->
sasl_conn
)
{
purple_debug_info
(
"irc"
,
"SASL authentication failed: %s"
,
sasl_errdetail
(
irc
->
sasl_conn
));
purple_connection_take_error
(
gc
,
g_error_new_literal
(
PURPLE_CONNECTION_ERROR
,
PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED
,
_
(
"Incorrect Password"
)));
}
irc_sasl_finish
(
irc
);
}
static
void
irc_sasl_finish
(
struct
irc_conn
*
irc
)
{
char
*
buf
;
sasl_dispose
(
&
irc
->
sasl_conn
);
irc
->
sasl_conn
=
NULL
;
g_free
(
irc
->
sasl_cb
);
irc
->
sasl_cb
=
NULL
;
/* Auth failed, abort */
buf
=
irc_format
(
irc
,
"vv"
,
"CAP"
,
"END"
);
irc_send
(
irc
,
buf
);
g_free
(
buf
);
}
#endif