| 18 * GNU General Public License for more details. |
18 * GNU General Public License for more details. |
| 19 * |
19 * |
| 20 * You should have received a copy of the GNU General Public License |
20 * You should have received a copy of the GNU General Public License |
| 21 * along with this program; if not, write to the Free Software |
21 * along with this program; if not, write to the Free Software |
| 22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 23 * |
|
| 24 */ |
23 */ |
| |
24 |
| 25 #include "internal.h" |
25 #include "internal.h" |
| 26 |
26 |
| 27 #include "account.h" |
27 #include "plugin.h" |
| 28 #include "accountopt.h" |
28 #include "accountopt.h" |
| |
29 #include "multi.h" |
| |
30 #include "prpl.h" |
| 29 #include "conversation.h" |
31 #include "conversation.h" |
| 30 #include "core.h" |
|
| 31 #include "debug.h" |
32 #include "debug.h" |
| 32 #include "ft.h" |
33 #include "blist.h" |
| 33 #include "multi.h" |
34 #include "irc.h" |
| 34 #include "notify.h" |
35 |
| 35 #include "proxy.h" |
36 static void irc_buddy_append(char *name, struct irc_buddy *ib, GString *string); |
| 36 #include "prpl.h" |
37 |
| 37 #include "request.h" |
38 static const char *irc_blist_icon(GaimAccount *a, struct buddy *b); |
| 38 #include "server.h" |
39 static void irc_blist_emblems(struct buddy *b, char **se, char **sw, char **nw, char **ne); |
| 39 #include "util.h" |
40 static GList *irc_away_states(GaimConnection *gc); |
| 40 |
41 /* static GList *irc_chat_info(GaimConnection *gc); */ |
| 41 /* XXX for g_show_info_text(), WEBSITE, etc. */ |
42 static void irc_login(GaimAccount *account); |
| 42 #include "gaim.h" |
43 static void irc_login_cb(gpointer data, gint source, GaimInputCondition cond); |
| 43 |
44 static void irc_close(GaimConnection *gc); |
| 44 #define IRC_BUF_LEN 4096 |
45 static int irc_im_send(GaimConnection *gc, const char *who, const char *what, int len, int flags); |
| 45 #define PDIWORDS 32 |
46 static int irc_chat_send(GaimConnection *gc, int id, const char *what); |
| 46 |
47 static void irc_chat_join (GaimConnection *gc, GHashTable *data); |
| 47 #define DEFAULT_SERVER "irc.freenode.net" |
48 static void irc_input_cb(gpointer data, gint source, GaimInputCondition cond); |
| 48 |
49 |
| 49 static GaimPlugin *my_protocol = NULL; |
50 static guint irc_nick_hash(const char *nick); |
| 50 |
51 static gboolean irc_nick_equal(const char *nick1, const char *nick2); |
| 51 #ifndef INET6_ADDRSTRLEN |
52 static void irc_buddy_free(struct irc_buddy *ib); |
| 52 #define INET6_ADDRSTRLEN 46 |
53 |
| 53 #endif |
54 static GaimPlugin *_irc_plugin = NULL; |
| 54 |
55 |
| 55 /* Datastructs */ |
56 int irc_send(struct irc_conn *irc, const char *buf) |
| 56 struct dcc_chat |
57 { |
| 57 { |
58 if (irc->fd < 0) |
| 58 GaimConnection *gc; |
59 return -1; |
| 59 char ip_address[INET6_ADDRSTRLEN]; |
60 |
| 60 int port; |
61 /* gaim_debug(GAIM_DEBUG_MISC, "irc", "sent: %s", buf); */ |
| 61 int fd; |
62 return write(irc->fd, buf, strlen(buf)); |
| 62 int inpa; |
63 } |
| 63 char nick[80]; |
64 |
| 64 }; |
65 /* XXX I don't like messing directly with these buddies */ |
| 65 |
66 gboolean irc_blist_timeout(struct irc_conn *irc) |
| 66 struct irc_xfer_data |
67 { |
| 67 { |
68 GString *string = g_string_sized_new(512); |
| 68 char *ip; |
69 char *list, *buf; |
| 69 int port; |
70 |
| 70 |
71 g_hash_table_foreach(irc->buddies, (GHFunc)irc_buddy_append, (gpointer)string); |
| 71 struct irc_data *idata; |
72 |
| 72 }; |
73 list = g_string_free(string, FALSE); |
| 73 |
74 if (!list || !strlen(list)) { |
| 74 struct irc_data { |
75 g_free(list); |
| 75 int fd; |
76 return TRUE; |
| 76 gboolean online; |
77 } |
| 77 guint32 timer; |
78 |
| 78 |
79 buf = irc_format(irc, "v:", "ISON", list); |
| 79 char *server; |
80 g_free(list); |
| 80 |
81 irc_send(irc, buf); |
| 81 char *rxqueue; |
82 g_free(buf); |
| 82 int rxlen; |
83 |
| 83 |
84 return TRUE; |
| 84 GString *str; |
85 } |
| 85 int bc; |
86 |
| 86 |
87 static void irc_buddy_append(char *name, struct irc_buddy *ib, GString *string) |
| 87 char *chantypes; |
88 { |
| 88 char *chanmodes; |
89 ib->flag = FALSE; |
| 89 char *nickmodes; |
90 g_string_append_printf(string, "%s ", name); |
| 90 gboolean six_modes; |
91 } |
| 91 |
92 |
| 92 gboolean in_whois; |
93 static const char *irc_blist_icon(GaimAccount *a, struct buddy *b) |
| 93 gboolean in_list; |
94 { |
| 94 GString *liststr; |
95 return "irc"; |
| 95 GSList *file_transfers; |
96 } |
| 96 }; |
97 |
| 97 |
98 static void irc_blist_emblems(struct buddy *b, char **se, char **sw, char **nw, char **ne) |
| 98 /* Prototypes */ |
99 { |
| 99 static void irc_start_chat(GaimConnection *gc, const char *who); |
100 if (b->present == GAIM_BUDDY_OFFLINE) |
| 100 static void irc_ctcp_clientinfo(GaimConnection *gc, const char *who); |
101 *se = "offline"; |
| 101 static void irc_ctcp_userinfo(GaimConnection *gc, const char *who); |
102 } |
| 102 static void irc_ctcp_version(GaimConnection *gc, const char *who); |
103 |
| 103 static void irc_ctcp_ping(GaimConnection *gc, const char *who); |
104 static GList *irc_away_states(GaimConnection *gc) |
| 104 |
105 { |
| 105 static void irc_send_privmsg(GaimConnection *gc, const char *who, const char *what, gboolean fragment); |
106 return g_list_append(NULL, (gpointer)GAIM_AWAY_CUSTOM); |
| 106 static void irc_send_notice(GaimConnection *gc, char *who, char *what); |
107 } |
| 107 |
108 |
| 108 static char *irc_send_convert(GaimConnection *gc, const char *string, int maxlen, int *done); |
109 static GList *irc_buddy_menu(GaimConnection *gc, const char *who) |
| 109 static char *irc_recv_convert(GaimConnection *gc, char *string); |
110 { |
| 110 static void irc_parse_notice(GaimConnection *gc, char *nick, char *ex, |
111 struct irc_conn *irc = gc->proto_data; |
| 111 char *word[], char *word_eol[]); |
112 struct proto_buddy_menu *pbm; |
| 112 static void irc_parse_join(GaimConnection *gc, char *nick, |
113 |
| 113 char *word[], char *word_eol[]); |
|
| 114 static gboolean irc_parse_part(GaimConnection *gc, char *nick, char *cmd, |
|
| 115 char *word[], char *word_eol[]); |
|
| 116 static void irc_parse_topic(GaimConnection *gc, char *nick, |
|
| 117 char *word[], char *word_eol[]); |
|
| 118 |
|
| 119 static void dcc_chat_cancel(struct dcc_chat *); |
|
| 120 |
|
| 121 /* Global variables */ |
|
| 122 GSList *dcc_chat_list = NULL; |
|
| 123 |
|
| 124 struct dcc_chat * |
|
| 125 find_dcc_chat (GaimConnection *gc, const char *nick) |
|
| 126 { |
|
| 127 GSList *tmp; |
|
| 128 struct dcc_chat *data; |
|
| 129 tmp = dcc_chat_list; |
|
| 130 while (tmp != NULL) |
|
| 131 { |
|
| 132 data = (struct dcc_chat *) (tmp)->data; |
|
| 133 if (data |
|
| 134 && data->nick |
|
| 135 && strcmp (nick, data->nick) == 0 |
|
| 136 && gc == data->gc) |
|
| 137 { |
|
| 138 return data; |
|
| 139 } |
|
| 140 tmp = tmp->next; |
|
| 141 } |
|
| 142 return NULL; |
114 return NULL; |
| 143 } |
115 } |
| 144 |
116 |
| 145 static int |
117 static GList *irc_chat_join_info(GaimConnection *gc) |
| 146 irc_write(int fd, char *data, int len) |
|
| 147 { |
|
| 148 gaim_debug(GAIM_DEBUG_MISC, "irc", "C: %s", data); |
|
| 149 return write(fd, data, len); |
|
| 150 } |
|
| 151 |
|
| 152 static char * |
|
| 153 irc_send_convert(GaimConnection *gc, const char *string, int maxlen, int *done) |
|
| 154 { |
|
| 155 char *converted = g_malloc(maxlen + 1); |
|
| 156 gchar *inptr = (gchar*)string, *outptr = converted; |
|
| 157 int inleft = strlen(string), outleft = maxlen; |
|
| 158 GIConv conv; |
|
| 159 |
|
| 160 /* XXX - I think the below line is leaking */ |
|
| 161 conv = g_iconv_open(gaim_account_get_string(gaim_connection_get_account(gc), "charset", "UTF-8") , "UTF-8"); |
|
| 162 if (g_iconv(conv, &inptr, &inleft, &outptr, &outleft) == -1) { |
|
| 163 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Charset conversion error\n"); |
|
| 164 gaim_debug(GAIM_DEBUG_ERROR, "irc", |
|
| 165 "Sending as UTF-8 (this is a hack!)\n"); |
|
| 166 g_free(converted); |
|
| 167 *done = maxlen; |
|
| 168 return(g_strndup(string, maxlen)); |
|
| 169 } |
|
| 170 |
|
| 171 *done = strlen(string) - inleft; |
|
| 172 *outptr = '\0'; |
|
| 173 return(converted); |
|
| 174 } |
|
| 175 |
|
| 176 static char * |
|
| 177 irc_recv_convert(GaimConnection *gc, char *string) |
|
| 178 { |
|
| 179 char *utf8; |
|
| 180 GError *err = NULL; |
|
| 181 |
|
| 182 utf8 = g_convert(string, strlen(string), "UTF-8", |
|
| 183 gaim_account_get_string(gaim_connection_get_account(gc), "charset", "UTF-8") , NULL, NULL, &err); |
|
| 184 if (err) { |
|
| 185 gaim_debug(GAIM_DEBUG_ERROR, "irc", |
|
| 186 "recv conversion error: %s\n", err->message); |
|
| 187 utf8 = g_strdup(_("(There was an error converting this message. Check the 'Encoding' option in the Account Editor)")); |
|
| 188 } |
|
| 189 |
|
| 190 return (utf8); |
|
| 191 } |
|
| 192 |
|
| 193 static GaimConversation * |
|
| 194 irc_find_chat(GaimConnection *gc, const char *name) |
|
| 195 { |
|
| 196 GSList *bcs = gc->buddy_chats; |
|
| 197 |
|
| 198 while (bcs) { |
|
| 199 GaimConversation *b = bcs->data; |
|
| 200 if (!gaim_utf8_strcasecmp(b->name, name)) |
|
| 201 return b; |
|
| 202 bcs = bcs->next; |
|
| 203 } |
|
| 204 return NULL; |
|
| 205 } |
|
| 206 |
|
| 207 static void |
|
| 208 process_data_init(char *buf, char *cmd, char *word[], char *eol[], gboolean quotes) |
|
| 209 { |
|
| 210 int wordcount = 2; |
|
| 211 gboolean space = FALSE; |
|
| 212 gboolean quote = FALSE; |
|
| 213 int j = 0; |
|
| 214 |
|
| 215 word[1] = cmd; |
|
| 216 eol[1] = buf; |
|
| 217 |
|
| 218 while (TRUE) { |
|
| 219 switch (*cmd) { |
|
| 220 case 0: |
|
| 221 buf[j] = 0; |
|
| 222 for (j = wordcount; j < PDIWORDS; j++) { |
|
| 223 word[j] = "\000\000"; |
|
| 224 eol[j] = "\000\000"; |
|
| 225 } |
|
| 226 return; |
|
| 227 case '"': |
|
| 228 if (!quotes) { |
|
| 229 space = FALSE; |
|
| 230 buf[j++] = *cmd; |
|
| 231 break; |
|
| 232 } |
|
| 233 quote = !quote; |
|
| 234 break; |
|
| 235 case ' ': |
|
| 236 if (quote) { |
|
| 237 space = FALSE; |
|
| 238 buf[j++] = *cmd; |
|
| 239 break; |
|
| 240 } |
|
| 241 if (space) |
|
| 242 break; |
|
| 243 buf[j++] = 0; |
|
| 244 word[wordcount] = &buf[j]; |
|
| 245 eol[wordcount++] = cmd + 1; |
|
| 246 if (wordcount == PDIWORDS - 1) { |
|
| 247 buf[j] = 0; |
|
| 248 return; |
|
| 249 } |
|
| 250 space = TRUE; |
|
| 251 break; |
|
| 252 default: |
|
| 253 space = FALSE; |
|
| 254 buf[j++] = *cmd; |
|
| 255 } |
|
| 256 cmd++; |
|
| 257 } |
|
| 258 } |
|
| 259 |
|
| 260 static void |
|
| 261 handle_005(GaimConnection *gc, char *word[], char *word_eol[]) |
|
| 262 { |
|
| 263 int w = 4; |
|
| 264 struct irc_data *id = gc->proto_data; |
|
| 265 |
|
| 266 while (w < PDIWORDS && *word[w]) { |
|
| 267 if (!strncmp(word[w], "MODES=", 5)) { |
|
| 268 if (atoi(word[w] + 6) >= 6) |
|
| 269 id->six_modes = TRUE; |
|
| 270 } else if (!strncmp(word[w], "CHANTYPES=", 10)) { |
|
| 271 g_free(id->chantypes); |
|
| 272 id->chantypes = g_strdup(word[w] + 10); |
|
| 273 } else if (!strncmp(word[w], "CHANMODES=", 10)) { |
|
| 274 g_free(id->chanmodes); |
|
| 275 id->chanmodes = g_strdup(word[w] + 10); |
|
| 276 } else if (!strncmp(word[w], "PREFIX=", 7)) { |
|
| 277 char *pre = strchr(word[w] + 7, ')'); |
|
| 278 if (pre) { |
|
| 279 *pre = 0; |
|
| 280 g_free(id->nickmodes); |
|
| 281 id->nickmodes = g_strdup(word[w] + 8); |
|
| 282 } |
|
| 283 } |
|
| 284 w++; |
|
| 285 } |
|
| 286 } |
|
| 287 |
|
| 288 static const char *irc_colors[] = { |
|
| 289 "#000000", "#ffffff", "#000066", "#006600", |
|
| 290 "#ff0000", "#660000", "#660066", "#666600", |
|
| 291 "#cccc00", "#33cc33", "#00acac", "#00ccac", |
|
| 292 "#0000ff", "#cc00cc", "#666666", "#00ccac" |
|
| 293 }; |
|
| 294 |
|
| 295 #define int_to_col(c) (irc_colors[(((c)<0 || (c)> 15)?0:c)]) |
|
| 296 |
|
| 297 static GString * |
|
| 298 encode_html(char *msg) |
|
| 299 { |
|
| 300 GString *str = g_string_new(""); |
|
| 301 char *cur = msg, *end = msg; |
|
| 302 gboolean bold = FALSE, underline = FALSE, italics = FALSE; |
|
| 303 |
|
| 304 while ((end = strchr(cur, '<'))) { |
|
| 305 *end = 0; |
|
| 306 str = g_string_append(str, cur); |
|
| 307 cur = ++end; |
|
| 308 if (!g_ascii_strncasecmp(cur, "B>", 2)) { |
|
| 309 if (!bold) { |
|
| 310 bold = TRUE; |
|
| 311 str = g_string_append_c(str, '\2'); |
|
| 312 } |
|
| 313 cur = cur + 2; |
|
| 314 } else if (!g_ascii_strncasecmp(cur, "I>", 2)) { /* use bold for italics too */ |
|
| 315 if (!italics) { |
|
| 316 italics = TRUE; |
|
| 317 str = g_string_append_c(str, '\2'); |
|
| 318 } |
|
| 319 cur = cur + 2; |
|
| 320 } else if (!g_ascii_strncasecmp(cur, "U>", 2)) { |
|
| 321 if (!underline) { |
|
| 322 underline = TRUE; |
|
| 323 str = g_string_append_c(str, '\37'); |
|
| 324 } |
|
| 325 cur = cur + 2; |
|
| 326 } else if (!g_ascii_strncasecmp(cur, "/B>", 3)) { |
|
| 327 if (bold) { |
|
| 328 bold = FALSE; |
|
| 329 str = g_string_append_c(str, '\2'); |
|
| 330 } |
|
| 331 cur = cur + 3; |
|
| 332 } else if (!g_ascii_strncasecmp(cur, "/I>", 3)) { |
|
| 333 if (italics) { |
|
| 334 italics = FALSE; |
|
| 335 str = g_string_append_c(str, '\2'); |
|
| 336 } |
|
| 337 cur = cur + 3; |
|
| 338 } else if (!g_ascii_strncasecmp(cur, "/U>", 3)) { |
|
| 339 if (underline) { |
|
| 340 underline = FALSE; |
|
| 341 str = g_string_append_c(str, '\37'); |
|
| 342 } |
|
| 343 cur = cur + 3; |
|
| 344 } else { |
|
| 345 str = g_string_append_c(str, '<'); |
|
| 346 } |
|
| 347 |
|
| 348 } |
|
| 349 str = g_string_append(str, cur); |
|
| 350 return str; |
|
| 351 } |
|
| 352 |
|
| 353 static GString * |
|
| 354 decode_html(char *msg) |
|
| 355 { |
|
| 356 GString /* oo la la */ *str = g_string_new(""); |
|
| 357 char *cur = msg, *end = msg; |
|
| 358 gboolean bold = FALSE, underline = FALSE, fg = FALSE, bg = FALSE; |
|
| 359 int fore, back; |
|
| 360 while (*end) { |
|
| 361 switch (*end) { |
|
| 362 case 02: /* ^B */ |
|
| 363 *end = 0; |
|
| 364 str = g_string_append(str, cur); |
|
| 365 if (bold) |
|
| 366 str = g_string_append(str, "</B>"); |
|
| 367 else |
|
| 368 str = g_string_append(str, "<B>"); |
|
| 369 bold = !bold; |
|
| 370 cur = end + 1; |
|
| 371 break; |
|
| 372 case 03: /* ^C */ |
|
| 373 *end++ = 0; |
|
| 374 str = g_string_append(str, cur); |
|
| 375 fore = back = -1; |
|
| 376 if (isdigit(*end)) { |
|
| 377 fore = *end++ - '0'; |
|
| 378 if (isdigit(*end)) { |
|
| 379 fore *= 10; |
|
| 380 fore += *end++ - '0'; |
|
| 381 } |
|
| 382 if (*end == ',' && isdigit(end[1])) { |
|
| 383 end++; |
|
| 384 back = *end++ - '0'; |
|
| 385 if (isdigit(*end)) { |
|
| 386 back *= 10; |
|
| 387 back += *end++ - '0'; |
|
| 388 } |
|
| 389 } |
|
| 390 } |
|
| 391 if (fore == -1) { |
|
| 392 if (fg) |
|
| 393 str = g_string_append(str, "</FONT>"); |
|
| 394 if (bg) |
|
| 395 str = g_string_append(str, "</FONT>"); |
|
| 396 fg = bg = FALSE; |
|
| 397 } else { |
|
| 398 fore %= 16; |
|
| 399 if (fg) |
|
| 400 str = g_string_append(str, "</FONT>"); |
|
| 401 if (back != -1) { |
|
| 402 if (bg) |
|
| 403 str = g_string_append(str, "</FONT>"); |
|
| 404 back %= 16; |
|
| 405 str = g_string_append(str, "<FONT BACK="); |
|
| 406 str = g_string_append(str, int_to_col(back)); |
|
| 407 str = g_string_append_c(str, '>'); |
|
| 408 bg = TRUE; |
|
| 409 } |
|
| 410 str = g_string_append(str, "<FONT COLOR="); |
|
| 411 str = g_string_append(str, int_to_col(fore)); |
|
| 412 str = g_string_append_c(str, '>'); |
|
| 413 fg = TRUE; |
|
| 414 } |
|
| 415 cur = end--; |
|
| 416 break; |
|
| 417 case 017: /* ^O */ |
|
| 418 if (!bold && !underline && !fg && !bg) |
|
| 419 break; |
|
| 420 *end = 0; |
|
| 421 str = g_string_append(str, cur); |
|
| 422 if (bold) |
|
| 423 str = g_string_append(str, "</B>"); |
|
| 424 if (underline) |
|
| 425 str = g_string_append(str, "</U>"); |
|
| 426 if (fg) |
|
| 427 str = g_string_append(str, "</FONT>"); |
|
| 428 if (bg) |
|
| 429 str = g_string_append(str, "</FONT>"); |
|
| 430 bold = underline = fg = bg = FALSE; |
|
| 431 cur = end + 1; |
|
| 432 break; |
|
| 433 case 037: /* ^_ */ |
|
| 434 *end = 0; |
|
| 435 str = g_string_append(str, cur); |
|
| 436 if (underline) |
|
| 437 str = g_string_append(str, "</U>"); |
|
| 438 else |
|
| 439 str = g_string_append(str, "<U>"); |
|
| 440 underline = !underline; |
|
| 441 cur = end + 1; |
|
| 442 break; |
|
| 443 } |
|
| 444 end++; |
|
| 445 } |
|
| 446 if (*cur) |
|
| 447 str = g_string_append(str, cur); |
|
| 448 return str; |
|
| 449 } |
|
| 450 |
|
| 451 static void |
|
| 452 irc_got_im(GaimConnection *gc, char *who, char *what, int flags, time_t t) |
|
| 453 { |
|
| 454 char *utf8 = irc_recv_convert(gc, what); |
|
| 455 GString *str = decode_html(utf8); |
|
| 456 serv_got_im(gc, who, str->str, flags, t, -1); |
|
| 457 g_string_free(str, TRUE); |
|
| 458 g_free(utf8); |
|
| 459 } |
|
| 460 |
|
| 461 static void |
|
| 462 dcc_chat_cancel(struct dcc_chat *); |
|
| 463 |
|
| 464 void |
|
| 465 dcc_chat_in (gpointer data, gint source, GaimInputCondition condition) |
|
| 466 { |
|
| 467 struct dcc_chat *chat = data; |
|
| 468 gchar buffer[IRC_BUF_LEN]; |
|
| 469 gchar buf[128]; |
|
| 470 int n = 0; |
|
| 471 GaimConversation *convo; |
|
| 472 gaim_debug(GAIM_DEBUG_MISC, "irc", "THIS IS TOO MUCH EFFORT\n"); |
|
| 473 n = read (chat->fd, buffer, IRC_BUF_LEN); |
|
| 474 if (n > 0) { |
|
| 475 |
|
| 476 buffer[n] = 0; |
|
| 477 g_strstrip(buffer); |
|
| 478 |
|
| 479 /* Convert to HTML */ |
|
| 480 if (strlen(buffer)) { |
|
| 481 gaim_debug(GAIM_DEBUG_INFO, "irc", |
|
| 482 "DCC Message from: %s\n", chat->nick); |
|
| 483 irc_got_im(chat->gc, chat->nick, buffer, 0, |
|
| 484 time(NULL)); |
|
| 485 } |
|
| 486 } |
|
| 487 else { |
|
| 488 g_snprintf (buf, sizeof buf, _("DCC Chat with %s closed"), |
|
| 489 chat->nick); |
|
| 490 convo = gaim_conversation_new(GAIM_CONV_IM, chat->gc->account, |
|
| 491 chat->nick); |
|
| 492 gaim_conversation_write(convo, NULL, buf, -1, WFLAG_SYSTEM, |
|
| 493 time(NULL)); |
|
| 494 dcc_chat_cancel (chat); |
|
| 495 } |
|
| 496 } |
|
| 497 |
|
| 498 void |
|
| 499 irc_read_dcc_ack (gpointer data, gint source, GaimInputCondition condition) { |
|
| 500 /* Read ACK Here */ |
|
| 501 |
|
| 502 } |
|
| 503 |
|
| 504 void |
|
| 505 dcc_send_callback (gpointer data, gint source, GaimInputCondition condition) { |
|
| 506 #if 0 |
|
| 507 struct irc_file_transfer *ift = data; |
|
| 508 struct sockaddr_in addr; |
|
| 509 int len = sizeof(addr); |
|
| 510 |
|
| 511 addr.sin_family = AF_INET; |
|
| 512 addr.sin_port = htons(ift->port); |
|
| 513 addr.sin_addr.s_addr = INADDR_ANY; |
|
| 514 |
|
| 515 ift->fd = accept(ift->fd, (struct sockaddr *)&addr, &len); |
|
| 516 if (!ift->fd) { |
|
| 517 /* FIXME: Handle this gracefully XXX */ |
|
| 518 printf("Something bad happened here, bubba!\n"); |
|
| 519 return; |
|
| 520 } |
|
| 521 |
|
| 522 /* ift->awatcher = gaim_input_add(ift->fd, GAIM_INPUT_READ, irc_read_dcc_ack, ift); */ |
|
| 523 |
|
| 524 if (transfer_out_do(ift->xfer, ift->fd, 0)) { |
|
| 525 gaim_input_remove(ift->watcher); |
|
| 526 ift->watcher = 0; |
|
| 527 } |
|
| 528 #endif |
|
| 529 } |
|
| 530 |
|
| 531 void |
|
| 532 dcc_chat_callback (gpointer data, gint source, GaimInputCondition condition) { |
|
| 533 struct dcc_chat *chat = data; |
|
| 534 GaimConversation *convo; |
|
| 535 char buf[IRC_BUF_LEN]; |
|
| 536 |
|
| 537 convo = gaim_conversation_new(GAIM_CONV_IM, chat->gc->account, chat->nick); |
|
| 538 |
|
| 539 chat->fd = source; |
|
| 540 g_snprintf (buf, sizeof buf, |
|
| 541 _("DCC Chat with %s established"), |
|
| 542 chat->nick); |
|
| 543 gaim_conversation_write(convo, NULL, buf, -1, WFLAG_SYSTEM, time(NULL)); |
|
| 544 gaim_debug(GAIM_DEBUG_INFO, "irc", |
|
| 545 "Chat with %s established\n", chat->nick); |
|
| 546 dcc_chat_list = g_slist_append (dcc_chat_list, chat); |
|
| 547 gaim_input_remove(chat->inpa); |
|
| 548 chat->inpa = gaim_input_add(source, GAIM_INPUT_READ, dcc_chat_in, chat); |
|
| 549 } |
|
| 550 |
|
| 551 static void |
|
| 552 irc_got_chat_in(GaimConnection *gc, int id, char *who, int whisper, char *msg, time_t t) |
|
| 553 { |
|
| 554 char *utf8 = irc_recv_convert(gc, msg); |
|
| 555 GString *str = decode_html(utf8); |
|
| 556 serv_got_chat_in(gc, id, who, whisper, str->str, t); |
|
| 557 g_string_free(str, TRUE); |
|
| 558 g_free(utf8); |
|
| 559 } |
|
| 560 |
|
| 561 static void |
|
| 562 handle_list(GaimConnection *gc, char *list) |
|
| 563 { |
|
| 564 struct irc_data *id = gc->proto_data; |
|
| 565 char *tmp; |
|
| 566 GaimBlistNode *gnode, *bnode; |
|
| 567 |
|
| 568 tmp = g_utf8_strdown(list, -1); |
|
| 569 |
|
| 570 id->str = g_string_append_c(id->str, ' '); |
|
| 571 id->str = g_string_append(id->str, tmp); |
|
| 572 id->bc--; |
|
| 573 g_free(tmp); |
|
| 574 if (id->bc) |
|
| 575 return; |
|
| 576 |
|
| 577 |
|
| 578 for(gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) { |
|
| 579 if(!GAIM_BLIST_NODE_IS_GROUP(gnode)) |
|
| 580 continue; |
|
| 581 for(bnode = gnode->child; bnode; bnode = bnode->next) { |
|
| 582 struct buddy *b = (struct buddy *)bnode; |
|
| 583 if(!GAIM_BLIST_NODE_IS_BUDDY(bnode)) |
|
| 584 continue; |
|
| 585 if(b->account->gc == gc) { |
|
| 586 char *tmp = g_utf8_strdown(b->name, -1); |
|
| 587 char *x, *l; |
|
| 588 x = strstr(id->str->str, tmp); |
|
| 589 l = x + strlen(b->name); |
|
| 590 if (x && (*l != ' ' && *l != 0)) |
|
| 591 x = 0; |
|
| 592 if (!GAIM_BUDDY_IS_ONLINE(b) && x) |
|
| 593 serv_got_update(gc, b->name, 1, 0, 0, 0, 0); |
|
| 594 else if (GAIM_BUDDY_IS_ONLINE(b) && !x) |
|
| 595 serv_got_update(gc, b->name, 0, 0, 0, 0, 0); |
|
| 596 g_free(tmp); |
|
| 597 } |
|
| 598 } |
|
| 599 } |
|
| 600 g_string_free(id->str, TRUE); |
|
| 601 id->str = g_string_new(""); |
|
| 602 } |
|
| 603 |
|
| 604 static gboolean |
|
| 605 irc_request_buddy_update(gpointer data) |
|
| 606 { |
|
| 607 GaimConnection *gc = data; |
|
| 608 struct irc_data *id = gc->proto_data; |
|
| 609 char buf[500]; |
|
| 610 int n = g_snprintf(buf, sizeof(buf), "ISON"); |
|
| 611 gboolean found = FALSE; |
|
| 612 |
|
| 613 GaimBlistNode *gnode, *bnode; |
|
| 614 |
|
| 615 if (id->bc) |
|
| 616 return TRUE; |
|
| 617 |
|
| 618 for(gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) { |
|
| 619 if(!GAIM_BLIST_NODE_IS_GROUP(gnode)) |
|
| 620 continue; |
|
| 621 for(bnode = gnode->child; bnode; bnode = bnode->next) { |
|
| 622 struct buddy *b = (struct buddy *)bnode; |
|
| 623 if(!GAIM_BLIST_NODE_IS_BUDDY(bnode)) |
|
| 624 continue; |
|
| 625 if(b->account->gc == gc) { |
|
| 626 if (n + strlen(b->name) + 2 > sizeof(buf)) { |
|
| 627 g_snprintf(buf + n, sizeof(buf) - n, "\r\n"); |
|
| 628 irc_write(id->fd, buf, n); |
|
| 629 id->bc++; |
|
| 630 n = g_snprintf(buf, sizeof(buf), "ISON"); |
|
| 631 } |
|
| 632 n += g_snprintf(buf + n, sizeof(buf) - n, " %s", b->name); |
|
| 633 |
|
| 634 found = TRUE; |
|
| 635 } |
|
| 636 } |
|
| 637 } |
|
| 638 |
|
| 639 if (found) { |
|
| 640 g_snprintf(buf + n, sizeof(buf) - n, "\r\n"); |
|
| 641 irc_write(id->fd, buf, strlen(buf)); |
|
| 642 id->bc++; |
|
| 643 } |
|
| 644 |
|
| 645 return TRUE; |
|
| 646 } |
|
| 647 |
|
| 648 static void |
|
| 649 handle_names(GaimConnection *gc, char *chan, char *names) |
|
| 650 { |
|
| 651 GaimConversation *c = irc_find_chat(gc, chan); |
|
| 652 GaimChat *chat; |
|
| 653 char **buf, **tmp; |
|
| 654 |
|
| 655 if (!c) return; |
|
| 656 if (*names == ':') names++; |
|
| 657 |
|
| 658 chat = GAIM_CHAT(c); |
|
| 659 |
|
| 660 buf = g_strsplit(names, " ", -1); |
|
| 661 |
|
| 662 for (tmp = buf; *tmp; tmp++) |
|
| 663 gaim_chat_add_user(chat, *tmp, NULL); |
|
| 664 |
|
| 665 g_strfreev(buf); |
|
| 666 } |
|
| 667 |
|
| 668 static void |
|
| 669 handle_notopic(GaimConnection *gc, char *text) |
|
| 670 { |
|
| 671 GaimConversation *c; |
|
| 672 |
|
| 673 if ((c = irc_find_chat(gc, text))) { |
|
| 674 char buf[IRC_BUF_LEN]; |
|
| 675 |
|
| 676 g_snprintf(buf, sizeof(buf), _("No topic is set")); |
|
| 677 |
|
| 678 gaim_chat_set_topic(GAIM_CHAT(c), NULL, buf); |
|
| 679 } |
|
| 680 } |
|
| 681 |
|
| 682 static void |
|
| 683 handle_topic(GaimConnection *gc, char *text) |
|
| 684 { |
|
| 685 GaimConversation *c; |
|
| 686 char *po = strchr(text, ' '), *buf; |
|
| 687 |
|
| 688 if (!po) |
|
| 689 return; |
|
| 690 |
|
| 691 *po = 0; |
|
| 692 po += 2; |
|
| 693 |
|
| 694 if ((c = irc_find_chat(gc, text))) { |
|
| 695 po = irc_recv_convert(gc, po); |
|
| 696 gaim_chat_set_topic(GAIM_CHAT(c), NULL, po); |
|
| 697 buf = g_strdup_printf(_("<B>%s has changed the topic to: %s</B>"), text, po); |
|
| 698 gaim_conversation_write(c, NULL, buf, -1, WFLAG_SYSTEM, time(NULL)); |
|
| 699 g_free(buf); |
|
| 700 g_free(po); |
|
| 701 } |
|
| 702 } |
|
| 703 |
|
| 704 static gboolean |
|
| 705 mode_has_arg(GaimConnection *gc, char sign, char mode) |
|
| 706 { |
|
| 707 struct irc_data *id = gc->proto_data; |
|
| 708 char *cm = id->chanmodes; |
|
| 709 int type = 0; |
|
| 710 |
|
| 711 if (strchr(id->nickmodes, mode)) |
|
| 712 return TRUE; |
|
| 713 |
|
| 714 while (*cm) { |
|
| 715 if (*cm == ',') |
|
| 716 type++; |
|
| 717 else if (*cm == mode) { |
|
| 718 switch (type) { |
|
| 719 case 0: |
|
| 720 case 1: |
|
| 721 return TRUE; |
|
| 722 case 2: |
|
| 723 if (sign == '+') |
|
| 724 return TRUE; |
|
| 725 case 3: |
|
| 726 return FALSE; |
|
| 727 } |
|
| 728 } |
|
| 729 cm++; |
|
| 730 } |
|
| 731 |
|
| 732 return FALSE; |
|
| 733 } |
|
| 734 |
|
| 735 static void |
|
| 736 irc_chan_mode(GaimConnection *gc, char *room, char sign, char mode, char *argstr, char *who) |
|
| 737 { |
|
| 738 GaimConversation *c = irc_find_chat(gc, room); |
|
| 739 char buf[IRC_BUF_LEN]; |
|
| 740 char *nick = g_strndup(who, strchr(who, '!') - who); |
|
| 741 |
|
| 742 g_snprintf(buf, sizeof(buf), _("-:- mode/%s [%c%c %s] by %s"), |
|
| 743 room, sign, mode, strlen(argstr) ? argstr : "", |
|
| 744 nick); |
|
| 745 g_free(nick); |
|
| 746 |
|
| 747 gaim_conversation_write(c, NULL, buf, -1, WFLAG_SYSTEM, time(NULL)); |
|
| 748 } |
|
| 749 |
|
| 750 static void |
|
| 751 irc_user_mode(GaimConnection *gc, char *room, char sign, char mode, char *nick) |
|
| 752 { |
|
| 753 GaimConversation *c = irc_find_chat(gc, room); |
|
| 754 GList *r; |
|
| 755 |
|
| 756 if (mode != 'o' && mode != 'v' && mode != 'h') |
|
| 757 return; |
|
| 758 |
|
| 759 if (!c) |
|
| 760 return; |
|
| 761 |
|
| 762 r = gaim_chat_get_users(GAIM_CHAT(c)); |
|
| 763 while (r) { |
|
| 764 gboolean op = FALSE, halfop = FALSE, voice = FALSE; |
|
| 765 char *who = r->data; |
|
| 766 |
|
| 767 if (*who == '@') { |
|
| 768 op = TRUE; |
|
| 769 who++; |
|
| 770 } |
|
| 771 |
|
| 772 if (*who == '%') { |
|
| 773 halfop = TRUE; |
|
| 774 who++; |
|
| 775 } |
|
| 776 |
|
| 777 if (*who == '+') { |
|
| 778 voice = TRUE; |
|
| 779 who++; |
|
| 780 } |
|
| 781 |
|
| 782 if (!strcmp(who, nick)) { |
|
| 783 char *tmp, buf[IRC_BUF_LEN]; |
|
| 784 |
|
| 785 if (mode == 'o') { |
|
| 786 if (sign == '-') |
|
| 787 op = FALSE; |
|
| 788 else |
|
| 789 op = TRUE; |
|
| 790 } |
|
| 791 |
|
| 792 if (mode == 'h') { |
|
| 793 if (sign == '-') |
|
| 794 halfop = FALSE; |
|
| 795 else |
|
| 796 halfop = TRUE; |
|
| 797 } |
|
| 798 |
|
| 799 if (mode == 'v') { |
|
| 800 if (sign == '-') |
|
| 801 voice = FALSE; |
|
| 802 else |
|
| 803 voice = TRUE; |
|
| 804 } |
|
| 805 |
|
| 806 tmp = g_strdup(r->data); |
|
| 807 g_snprintf(buf, sizeof(buf), "%s%s%s", |
|
| 808 (op ? "@" : (halfop ? "%" : "")), |
|
| 809 voice ? "+" : "", nick); |
|
| 810 gaim_chat_rename_user(GAIM_CHAT(c), tmp, buf); |
|
| 811 g_free(tmp); |
|
| 812 return; |
|
| 813 } |
|
| 814 r = r->next; |
|
| 815 } |
|
| 816 } |
|
| 817 |
|
| 818 static void |
|
| 819 handle_mode(GaimConnection *gc, char *word[], char *word_eol[], gboolean n324) |
|
| 820 { |
|
| 821 struct irc_data *id = gc->proto_data; |
|
| 822 int offset = n324 ? 4 : 3; |
|
| 823 char *chan = word[offset]; |
|
| 824 GaimConversation *c = irc_find_chat(gc, chan); |
|
| 825 char *modes = word[offset + 1]; |
|
| 826 int len = strlen(word_eol[offset]) - 1; |
|
| 827 char sign = *modes++; |
|
| 828 int arg = 1; |
|
| 829 char *argstr; |
|
| 830 char *who = word[1]; |
|
| 831 |
|
| 832 if (!c) |
|
| 833 return; |
|
| 834 |
|
| 835 if (word_eol[offset][len] == ' ') |
|
| 836 word_eol[offset][len] = 0; |
|
| 837 |
|
| 838 while (TRUE) { |
|
| 839 switch (*modes) { |
|
| 840 case 0: |
|
| 841 return; |
|
| 842 case '+': |
|
| 843 case '-': |
|
| 844 sign = *modes; |
|
| 845 break; |
|
| 846 default: |
|
| 847 if (mode_has_arg(gc, sign, *modes)) |
|
| 848 argstr = word[++arg + offset]; |
|
| 849 else |
|
| 850 argstr = ""; |
|
| 851 if (strchr(id->nickmodes, *modes)) |
|
| 852 irc_user_mode(gc, chan, sign, *modes, argstr); |
|
| 853 else if (strchr(who, '!')) |
|
| 854 irc_chan_mode(gc, chan, sign, *modes, argstr, who); |
|
| 855 } |
|
| 856 modes++; |
|
| 857 } |
|
| 858 } |
|
| 859 |
|
| 860 static void |
|
| 861 handle_version(GaimConnection *gc, char *word[], char *word_eol[], int num) |
|
| 862 { |
|
| 863 struct irc_data *id = gc->proto_data; |
|
| 864 GString *str; |
|
| 865 |
|
| 866 id->liststr = g_string_new(""); |
|
| 867 |
|
| 868 id->liststr = g_string_append(id->liststr, "<b>Version: </b>"); |
|
| 869 id->liststr = g_string_append(id->liststr, word_eol[4]); |
|
| 870 |
|
| 871 str = decode_html(id->liststr->str); |
|
| 872 g_show_info_text(gc, NULL, 2, str->str, NULL); |
|
| 873 g_string_free(str, TRUE); |
|
| 874 g_string_free(id->liststr, TRUE); |
|
| 875 id->liststr = NULL; |
|
| 876 } |
|
| 877 |
|
| 878 static void |
|
| 879 handle_who(GaimConnection *gc, char *word[], char *word_eol[], int num) |
|
| 880 { |
|
| 881 struct irc_data *id = gc->proto_data; |
|
| 882 char buf[IRC_BUF_LEN]; |
|
| 883 |
|
| 884 if (!id->in_whois) { |
|
| 885 id->in_whois = TRUE; |
|
| 886 id->liststr = g_string_new(""); |
|
| 887 } |
|
| 888 |
|
| 889 switch (num) { |
|
| 890 case 352: |
|
| 891 g_snprintf(buf, sizeof(buf), "<b>%s</b> (%s@%s): %s<br>", |
|
| 892 word[8], word[5], word[6], word_eol[11]); |
|
| 893 id->liststr = g_string_append(id->liststr, buf); |
|
| 894 break; |
|
| 895 } |
|
| 896 } |
|
| 897 |
|
| 898 /* Handle our whois stuff here. You know what, I have a sore throat. You know |
|
| 899 * what I think about that? I'm not too pleased with it. Perhaps I should take |
|
| 900 * some medicine, or perhaps I should go to bed? Blah!! */ |
|
| 901 |
|
| 902 static void |
|
| 903 handle_whois(GaimConnection *gc, char *word[], char *word_eol[], int num) |
|
| 904 { |
|
| 905 struct irc_data *id = gc->proto_data; |
|
| 906 char tmp[1024]; |
|
| 907 |
|
| 908 if (!id->in_whois) { |
|
| 909 id->in_whois = TRUE; |
|
| 910 id->liststr = g_string_new(""); |
|
| 911 } else { |
|
| 912 /* I can't decide if we should have one break or two */ |
|
| 913 id->liststr = g_string_append(id->liststr, "<BR>"); |
|
| 914 id->in_whois = TRUE; |
|
| 915 } |
|
| 916 |
|
| 917 switch (num) { |
|
| 918 case 311: |
|
| 919 g_snprintf(tmp, sizeof(tmp), "<b>%s: </b>", _("User")); |
|
| 920 id->liststr = g_string_append(id->liststr, tmp); |
|
| 921 break; |
|
| 922 case 312: |
|
| 923 g_snprintf(tmp, sizeof(tmp), "<b>%s: </b>", _("Server")); |
|
| 924 id->liststr = g_string_append(id->liststr, tmp); |
|
| 925 break; |
|
| 926 case 313: |
|
| 927 g_snprintf(tmp, sizeof(tmp), "<b>%s:</b> %s ", _("IRC Operator"), word[4]); |
|
| 928 id->liststr = g_string_append(id->liststr, tmp); |
|
| 929 break; |
|
| 930 case 314: |
|
| 931 g_snprintf(tmp, sizeof(tmp), "<b>%s: </b><b>%s</b> (%s@%s) %s", |
|
| 932 _("User"), word[4], word[5], word[6], word_eol[8]); |
|
| 933 id->liststr = g_string_append(id->liststr, tmp); |
|
| 934 return; |
|
| 935 case 317: |
|
| 936 g_snprintf(tmp, sizeof(tmp), "<b>%s: </b>", _("Idle Time")); |
|
| 937 id->liststr = g_string_append(id->liststr, tmp); |
|
| 938 break; |
|
| 939 case 319: |
|
| 940 g_snprintf(tmp, sizeof(tmp), "<b>%s: </b>", _("Channels")); |
|
| 941 id->liststr = g_string_append(id->liststr, tmp); |
|
| 942 break; |
|
| 943 /* Numeric 320 is used by the freenode irc network for showing |
|
| 944 * that a user is identified to services (Jason Straw <misato@wopn.org>)*/ |
|
| 945 case 320: |
|
| 946 g_snprintf(tmp, sizeof(tmp), _("%s is an Identified User"), word[4]); |
|
| 947 id->liststr = g_string_append(id->liststr, tmp); |
|
| 948 return; |
|
| 949 default: |
|
| 950 break; |
|
| 951 } |
|
| 952 |
|
| 953 if (word_eol[5][0] == ':') |
|
| 954 id->liststr = g_string_append(id->liststr, word_eol[5] + 1); |
|
| 955 /* Nicer idle time output, by jonas@birme.se */ |
|
| 956 else if (isdigit(word_eol[5][0])) { |
|
| 957 time_t idle = atol(word_eol[5]); |
|
| 958 time_t signon = atol(strchr(word_eol[5], ' ')); |
|
| 959 |
|
| 960 g_snprintf(tmp, sizeof(tmp), |
|
| 961 _("%ld seconds [signon: %s]"), (idle / 1000), ctime(&signon)); |
|
| 962 id->liststr = g_string_append(id->liststr, tmp); |
|
| 963 } |
|
| 964 else |
|
| 965 id->liststr = g_string_append(id->liststr, word_eol[5]); |
|
| 966 } |
|
| 967 |
|
| 968 static void |
|
| 969 handle_roomlist(GaimConnection *gc, char *word[], char *word_eol[]) |
|
| 970 { |
|
| 971 struct irc_data *id = gc->proto_data; |
|
| 972 |
|
| 973 if (!id->in_list) { |
|
| 974 id->in_list = TRUE; |
|
| 975 id->liststr = g_string_new(""); |
|
| 976 } else { |
|
| 977 id->liststr = g_string_append(id->liststr, "<BR>"); |
|
| 978 id->in_list = TRUE; |
|
| 979 } |
|
| 980 |
|
| 981 id->liststr = g_string_append(id->liststr, word_eol[4]); |
|
| 982 } |
|
| 983 |
|
| 984 static void |
|
| 985 irc_change_nick(void *a, const char *b) { |
|
| 986 GaimConnection *gc = a; |
|
| 987 struct irc_data *id = gc->proto_data; |
|
| 988 char buf[IRC_BUF_LEN]; |
|
| 989 g_snprintf(buf, sizeof(buf), "NICK %s\r\n", b); |
|
| 990 irc_write(id->fd, buf, strlen(buf)); |
|
| 991 gaim_connection_set_display_name(gc, b); |
|
| 992 } |
|
| 993 |
|
| 994 static void |
|
| 995 process_numeric(GaimConnection *gc, char *word[], char *word_eol[]) |
|
| 996 { |
|
| 997 const char *displayname = gaim_connection_get_display_name(gc); |
|
| 998 struct irc_data *id = gc->proto_data; |
|
| 999 char *text = word_eol[3]; |
|
| 1000 int n = atoi(word[2]); |
|
| 1001 char tmp[1024]; |
|
| 1002 |
|
| 1003 if (!g_ascii_strncasecmp(displayname, text, strlen(displayname))) |
|
| 1004 text += strlen(displayname) + 1; |
|
| 1005 if (*text == ':') |
|
| 1006 text++; |
|
| 1007 |
|
| 1008 /* RPL_ and ERR_ */ |
|
| 1009 switch (n) { |
|
| 1010 case 4: |
|
| 1011 if (!strncmp(word[5], "u2.10", 5)) |
|
| 1012 id->six_modes = TRUE; |
|
| 1013 else |
|
| 1014 id->six_modes = FALSE; |
|
| 1015 break; |
|
| 1016 case 5: |
|
| 1017 handle_005(gc, word, word_eol); |
|
| 1018 break; |
|
| 1019 case 301: /* RPL_AWAY */ |
|
| 1020 if (id->in_whois) { |
|
| 1021 g_snprintf(tmp, sizeof(tmp), "<BR><b>%s: </b>", _("Away")); |
|
| 1022 id->liststr = g_string_append(id->liststr, tmp); |
|
| 1023 |
|
| 1024 if (word_eol[5][0] == ':') |
|
| 1025 id->liststr = g_string_append(id->liststr, word_eol[5] + 1); |
|
| 1026 else |
|
| 1027 id->liststr = g_string_append(id->liststr, word_eol[5]); |
|
| 1028 } else |
|
| 1029 irc_got_im(gc, word[4], word_eol[5], IM_FLAG_AWAY, time(NULL)); |
|
| 1030 break; |
|
| 1031 case 303: /* RPL_ISON */ |
|
| 1032 handle_list(gc, &word_eol[4][1]); |
|
| 1033 break; |
|
| 1034 case 311: /* RPL_WHOISUSER */ |
|
| 1035 case 312: /* RPL_WHOISSERVER */ |
|
| 1036 case 313: /* RPL_WHOISOPERATOR */ |
|
| 1037 case 314: /* RPL_WHOWASUSER */ |
|
| 1038 case 317: /* RPL_WHOISIDLE */ |
|
| 1039 case 319: /* RPL_WHOISCHANNELS */ |
|
| 1040 case 320: /* FreeNode Identified */ |
|
| 1041 handle_whois(gc, word, word_eol, n); |
|
| 1042 break; |
|
| 1043 case 322: /* RPL_LIST */ |
|
| 1044 handle_roomlist(gc, word, word_eol); |
|
| 1045 break; |
|
| 1046 case 315: /* RPL_ENDOFWHO */ |
|
| 1047 case 318: /* RPL_ENDOFWHOIS */ |
|
| 1048 case 323: /* RPL_LISTEND */ |
|
| 1049 case 369: /* RPL_ENDOFWHOWAS */ |
|
| 1050 if ((id->in_whois || id->in_list) && id->liststr) { |
|
| 1051 GString *str = decode_html(id->liststr->str); |
|
| 1052 g_show_info_text(gc, NULL, 2, str->str, NULL); |
|
| 1053 g_string_free(str, TRUE); |
|
| 1054 g_string_free(id->liststr, TRUE); |
|
| 1055 id->liststr = NULL; |
|
| 1056 id->in_whois = FALSE; |
|
| 1057 id->in_list = FALSE; |
|
| 1058 } |
|
| 1059 break; |
|
| 1060 case 324: /* RPL_CHANNELMODEIS */ |
|
| 1061 handle_mode(gc, word, word_eol, TRUE); |
|
| 1062 break; |
|
| 1063 case 331: /* RPL_NOTOPIC */ |
|
| 1064 handle_notopic(gc, text); |
|
| 1065 break; |
|
| 1066 case 332: /* RPL_TOPIC */ |
|
| 1067 handle_topic(gc, text); |
|
| 1068 break; |
|
| 1069 case 351: /* RPL_VERSION */ |
|
| 1070 handle_version(gc, word, word_eol, n); |
|
| 1071 break; |
|
| 1072 case 352: /* RPL_WHOREPLY */ |
|
| 1073 handle_who(gc, word, word_eol, n); |
|
| 1074 break; |
|
| 1075 case 353: /* RPL_NAMREPLY */ |
|
| 1076 handle_names(gc, word[5], word_eol[6]); |
|
| 1077 break; |
|
| 1078 case 376: /* RPL_ENDOFMOTD */ |
|
| 1079 irc_request_buddy_update(gc); |
|
| 1080 break; |
|
| 1081 case 382: /* RPL_REHASHING */ |
|
| 1082 gaim_notify_error(gc, NULL, _("Rehashing server"), _("IRC Operator")); |
|
| 1083 break; |
|
| 1084 case 401: /* ERR_NOSUCHNICK */ |
|
| 1085 gaim_notify_error(gc, NULL, _("No such nick/channel"), _("IRC Error")); |
|
| 1086 break; |
|
| 1087 case 402: /* ERR_NOSUCHSERVER */ |
|
| 1088 gaim_notify_error(gc, NULL, _("No such server"), _("IRC Error")); |
|
| 1089 break; |
|
| 1090 case 422: /* ERR_NOMOTD */ |
|
| 1091 break; /* drop it - bringing up dialog for NOMOTD is annoying */ |
|
| 1092 case 431: /* ERR_NONICKNAMEGIVEN */ |
|
| 1093 gaim_notify_error(gc, NULL, _("No nickname given"), _("IRC Error")); |
|
| 1094 break; |
|
| 1095 case 481: /* ERR_NOPRIVILEGES */ |
|
| 1096 gaim_notify_error(gc, NULL, _("You're not an IRC operator!"), |
|
| 1097 _("IRC Error")); |
|
| 1098 break; |
|
| 1099 case 433: |
|
| 1100 gaim_request_input(gc, NULL, _("That nick is already in use. " |
|
| 1101 "Please enter a new nick"), |
|
| 1102 NULL, gaim_connection_get_display_name(gc), |
|
| 1103 FALSE, FALSE, |
|
| 1104 _("OK"), G_CALLBACK(irc_change_nick), |
|
| 1105 _("Cancel"), NULL, gc); |
|
| 1106 break; |
|
| 1107 default: |
|
| 1108 /* Other error messages */ |
|
| 1109 if (n > 400 && n < 502) { |
|
| 1110 char errmsg[IRC_BUF_LEN]; |
|
| 1111 char *errmsg1 = strrchr(text, ':'); |
|
| 1112 |
|
| 1113 g_snprintf(errmsg, sizeof(errmsg), "IRC Error %d", n); |
|
| 1114 |
|
| 1115 if (errmsg) { |
|
| 1116 gaim_notify_error(gc, NULL, errmsg, |
|
| 1117 (errmsg1 ? errmsg1 + 1 : NULL)); |
|
| 1118 } |
|
| 1119 } |
|
| 1120 |
|
| 1121 break; |
|
| 1122 } |
|
| 1123 } |
|
| 1124 |
|
| 1125 static gboolean |
|
| 1126 is_channel(GaimConnection *gc, const char *name) |
|
| 1127 { |
|
| 1128 struct irc_data *id = gc->proto_data; |
|
| 1129 if (strchr(id->chantypes, *name)) |
|
| 1130 return TRUE; |
|
| 1131 return FALSE; |
|
| 1132 } |
|
| 1133 |
|
| 1134 static void |
|
| 1135 irc_rem_chat_bud(GaimConnection *gc, char *nick, GaimConversation *b, char *reason) |
|
| 1136 { |
|
| 1137 |
|
| 1138 GaimChat *chat; |
|
| 1139 |
|
| 1140 if (b) { |
|
| 1141 GList *r; |
|
| 1142 |
|
| 1143 chat = GAIM_CHAT(b); |
|
| 1144 |
|
| 1145 r = gaim_chat_get_users(chat); |
|
| 1146 |
|
| 1147 while (r) { |
|
| 1148 char *who = r->data; |
|
| 1149 if (*who == '@') |
|
| 1150 who++; |
|
| 1151 if (*who == '%') |
|
| 1152 who++; |
|
| 1153 if (*who == '+') |
|
| 1154 who++; |
|
| 1155 if (!gaim_utf8_strcasecmp(who, nick)) { |
|
| 1156 gaim_chat_remove_user(chat, who, reason); |
|
| 1157 break; |
|
| 1158 } |
|
| 1159 r = r->next; |
|
| 1160 } |
|
| 1161 } else { |
|
| 1162 GSList *bcs = gc->buddy_chats; |
|
| 1163 while (bcs) { |
|
| 1164 GaimConversation *bc = bcs->data; |
|
| 1165 irc_rem_chat_bud(gc, nick, bc, reason); |
|
| 1166 bcs = bcs->next; |
|
| 1167 } |
|
| 1168 } |
|
| 1169 } |
|
| 1170 |
|
| 1171 static void |
|
| 1172 irc_change_name(GaimConnection *gc, char *old, char *new) |
|
| 1173 { |
|
| 1174 GSList *bcs = gc->buddy_chats; |
|
| 1175 char buf[IRC_BUF_LEN]; |
|
| 1176 |
|
| 1177 while (bcs) { |
|
| 1178 GaimConversation *b = bcs->data; |
|
| 1179 GaimChat *chat; |
|
| 1180 GList *r; |
|
| 1181 |
|
| 1182 chat = GAIM_CHAT(b); |
|
| 1183 |
|
| 1184 r = gaim_chat_get_users(chat); |
|
| 1185 |
|
| 1186 while (r) { |
|
| 1187 char *who = r->data; |
|
| 1188 int n = 0; |
|
| 1189 if (*who == '@') |
|
| 1190 buf[n++] = *who++; |
|
| 1191 if (*who == '%') |
|
| 1192 buf[n++] = *who++; |
|
| 1193 if (*who == '+') |
|
| 1194 buf[n++] = *who++; |
|
| 1195 g_snprintf(buf + n, sizeof(buf) - n, "%s", new); |
|
| 1196 if (!strcmp(who, old)) { |
|
| 1197 char *tmp = g_strdup(r->data); |
|
| 1198 gaim_chat_rename_user(chat, tmp, buf); |
|
| 1199 r = gaim_chat_get_users(chat); |
|
| 1200 g_free(tmp); |
|
| 1201 break; |
|
| 1202 } else |
|
| 1203 r = r->next; |
|
| 1204 } |
|
| 1205 bcs = bcs->next; |
|
| 1206 } |
|
| 1207 } |
|
| 1208 |
|
| 1209 static void |
|
| 1210 handle_privmsg(GaimConnection *gc, char *to, char *nick, char *msg) |
|
| 1211 { |
|
| 1212 if (is_channel(gc, to)) { |
|
| 1213 GaimConversation *c = irc_find_chat(gc, to); |
|
| 1214 if (!c) |
|
| 1215 return; |
|
| 1216 irc_got_chat_in(gc, gaim_chat_get_id(GAIM_CHAT(c)), |
|
| 1217 nick, 0, msg, time(NULL)); |
|
| 1218 } else { |
|
| 1219 char *tmp = g_malloc(strlen(nick) + 2); |
|
| 1220 g_snprintf(tmp, strlen(nick) + 2, "@%s", nick); |
|
| 1221 if (gaim_find_conversation(tmp)) |
|
| 1222 irc_got_im(gc, tmp, msg, 0, time(NULL)); |
|
| 1223 else { |
|
| 1224 *tmp = '+'; |
|
| 1225 if (gaim_find_conversation(tmp)) |
|
| 1226 irc_got_im(gc, tmp, msg, 0, time(NULL)); |
|
| 1227 else |
|
| 1228 irc_got_im(gc, nick, msg, 0, time(NULL)); |
|
| 1229 } |
|
| 1230 g_free(tmp); |
|
| 1231 } |
|
| 1232 } |
|
| 1233 |
|
| 1234 static void |
|
| 1235 dcc_chat_init(struct dcc_chat *data) { |
|
| 1236 if (g_list_find(gaim_connections_get_all(), data->gc)) { |
|
| 1237 gaim_proxy_connect(data->gc->account, data->ip_address, data->port, dcc_chat_callback, data); |
|
| 1238 } else { |
|
| 1239 g_free(data); |
|
| 1240 } |
|
| 1241 } |
|
| 1242 |
|
| 1243 static void |
|
| 1244 dcc_chat_cancel(struct dcc_chat *data){ |
|
| 1245 if (g_list_find(gaim_connections_get_all(), data->gc) && find_dcc_chat(data->gc, data->nick)) { |
|
| 1246 dcc_chat_list = g_slist_remove(dcc_chat_list, data); |
|
| 1247 gaim_input_remove (data->inpa); |
|
| 1248 close (data->fd); |
|
| 1249 } |
|
| 1250 g_free(data); |
|
| 1251 } |
|
| 1252 |
|
| 1253 static void |
|
| 1254 irc_convo_closed(GaimConnection *gc, const char *who) |
|
| 1255 { |
|
| 1256 struct dcc_chat *dchat = find_dcc_chat(gc, who); |
|
| 1257 if (!dchat) |
|
| 1258 return; |
|
| 1259 |
|
| 1260 dcc_chat_cancel(dchat); |
|
| 1261 } |
|
| 1262 |
|
| 1263 static void |
|
| 1264 irc_xfer_init(GaimXfer *xfer) |
|
| 1265 { |
|
| 1266 struct irc_xfer_data *data = (struct irc_xfer_data *)xfer->data; |
|
| 1267 |
|
| 1268 gaim_xfer_start(xfer, -1, data->ip, data->port); |
|
| 1269 } |
|
| 1270 |
|
| 1271 static void |
|
| 1272 irc_xfer_end(GaimXfer *xfer) |
|
| 1273 { |
|
| 1274 struct irc_xfer_data *data = (struct irc_xfer_data *)xfer->data; |
|
| 1275 |
|
| 1276 data->idata->file_transfers = g_slist_remove(data->idata->file_transfers, |
|
| 1277 xfer); |
|
| 1278 |
|
| 1279 g_free(data); |
|
| 1280 xfer->data = NULL; |
|
| 1281 } |
|
| 1282 |
|
| 1283 static void |
|
| 1284 irc_xfer_cancel_send(GaimXfer *xfer) |
|
| 1285 { |
|
| 1286 struct irc_xfer_data *data = (struct irc_xfer_data *)xfer->data; |
|
| 1287 |
|
| 1288 data->idata->file_transfers = g_slist_remove(data->idata->file_transfers, |
|
| 1289 xfer); |
|
| 1290 |
|
| 1291 g_free(data); |
|
| 1292 xfer->data = NULL; |
|
| 1293 } |
|
| 1294 |
|
| 1295 static void |
|
| 1296 irc_xfer_cancel_recv(GaimXfer *xfer) |
|
| 1297 { |
|
| 1298 struct irc_xfer_data *data = (struct irc_xfer_data *)xfer->data; |
|
| 1299 |
|
| 1300 data->idata->file_transfers = g_slist_remove(data->idata->file_transfers, |
|
| 1301 xfer); |
|
| 1302 |
|
| 1303 g_free(data); |
|
| 1304 xfer->data = NULL; |
|
| 1305 } |
|
| 1306 |
|
| 1307 static void |
|
| 1308 irc_xfer_ack(GaimXfer *xfer, const char *buffer, size_t size) |
|
| 1309 { |
|
| 1310 guint32 pos; |
|
| 1311 |
|
| 1312 pos = htonl(gaim_xfer_get_bytes_sent(xfer)); |
|
| 1313 |
|
| 1314 write(xfer->fd, (char *)&pos, 4); |
|
| 1315 } |
|
| 1316 |
|
| 1317 /* NOTE: This was taken from irssi. Thanks irssi! */ |
|
| 1318 |
|
| 1319 static gboolean |
|
| 1320 is_numeric(const char *str, char end_char) |
|
| 1321 { |
|
| 1322 g_return_val_if_fail(str != NULL, FALSE); |
|
| 1323 |
|
| 1324 if (*str == '\0' || *str == end_char) |
|
| 1325 return FALSE; |
|
| 1326 |
|
| 1327 while (*str != '\0' && *str != end_char) { |
|
| 1328 if (*str < '0' || *str > '9') |
|
| 1329 return FALSE; |
|
| 1330 |
|
| 1331 str++; |
|
| 1332 } |
|
| 1333 |
|
| 1334 return TRUE; |
|
| 1335 } |
|
| 1336 |
|
| 1337 #define get_params_match(params, pos) \ |
|
| 1338 (is_numeric(params[pos], '\0') && \ |
|
| 1339 is_numeric(params[(pos)+1], '\0') && atol(params[(pos)+1]) < 65536 && \ |
|
| 1340 is_numeric(params[(pos)+2], '\0')) |
|
| 1341 |
|
| 1342 /* Return number of parameters in `params' that belong to file name. |
|
| 1343 Normally it's paramcount-3, but I don't think anything forbids of |
|
| 1344 adding some extension where there could be more parameters after |
|
| 1345 file size. |
|
| 1346 |
|
| 1347 MIRC sends filenames with spaces quoted ("file name"), but I'd rather |
|
| 1348 not trust that entirely either. At least some clients that don't really |
|
| 1349 understand the problem with spaces in file names sends the file name |
|
| 1350 without any quotes. */ |
|
| 1351 static int |
|
| 1352 get_file_params_count(char **params, int paramcount) |
|
| 1353 { |
|
| 1354 int pos, best; |
|
| 1355 |
|
| 1356 if (*params[0] == '"') { |
|
| 1357 /* quoted file name? */ |
|
| 1358 for (pos = 0; pos < paramcount - 3; pos++) { |
|
| 1359 if (params[pos][strlen(params[pos]) - 1] == '"' && |
|
| 1360 get_params_match(params, pos + 1)) { |
|
| 1361 |
|
| 1362 return pos + 1; |
|
| 1363 } |
|
| 1364 } |
|
| 1365 } |
|
| 1366 |
|
| 1367 best = paramcount - 3; |
|
| 1368 |
|
| 1369 for (pos = paramcount - 3; pos > 0; pos--) { |
|
| 1370 if (get_params_match(params, pos)) |
|
| 1371 best = pos; |
|
| 1372 } |
|
| 1373 |
|
| 1374 return best; |
|
| 1375 } |
|
| 1376 |
|
| 1377 static void |
|
| 1378 handle_ctcp(GaimConnection *gc, char *to, char *nick, |
|
| 1379 char *msg, char *word[], char *word_eol[]) |
|
| 1380 { |
|
| 1381 struct irc_data *id = gc->proto_data; |
|
| 1382 char buf[IRC_BUF_LEN]; |
|
| 1383 char out[IRC_BUF_LEN]; |
|
| 1384 |
|
| 1385 if (!g_ascii_strncasecmp(msg, "VERSION", 7)) { |
|
| 1386 g_snprintf(buf, sizeof(buf), "\001VERSION Gaim " VERSION ": The Penguin Pimpin' " |
|
| 1387 "Multi-protocol Messaging Client: " WEBSITE "\001"); |
|
| 1388 irc_send_notice (gc, nick, buf); |
|
| 1389 g_snprintf(out, sizeof(out), ">> CTCP VERSION requested from %s", nick); |
|
| 1390 gaim_notify_info(gc, NULL, out, _("IRC CTCP info")); |
|
| 1391 } |
|
| 1392 else if (!g_ascii_strncasecmp(msg, "CLIENTINFO", 10)) { |
|
| 1393 g_snprintf(buf, sizeof(buf), "\001CLIENTINFO USERINFO CLIENTINFO VERSION\001"); |
|
| 1394 irc_send_notice (gc, nick, buf); |
|
| 1395 g_snprintf(out, sizeof(out), ">> CTCP CLIENTINFO requested from %s", nick); |
|
| 1396 gaim_notify_info(gc, NULL, out, _("IRC CTCP info")); |
|
| 1397 } |
|
| 1398 else if (!g_ascii_strncasecmp(msg, "USERINFO", 8)) { |
|
| 1399 g_snprintf(buf, sizeof(buf), "\001USERINFO Alias: %s\001", gc->account->alias); |
|
| 1400 irc_send_notice (gc, nick, buf); |
|
| 1401 g_snprintf(out, sizeof(out), ">> CTCP USERINFO requested from %s", nick); |
|
| 1402 gaim_notify_info(gc, NULL, out, _("IRC CTCP info")); |
|
| 1403 } |
|
| 1404 else if (!g_ascii_strncasecmp(msg, "ACTION", 6)) { |
|
| 1405 char *po = strchr(msg + 6, 1); |
|
| 1406 char *tmp; |
|
| 1407 if (po) *po = 0; |
|
| 1408 tmp = g_strconcat("/me", msg + 6, NULL); |
|
| 1409 handle_privmsg(gc, to, nick, tmp); |
|
| 1410 g_free(tmp); |
|
| 1411 } |
|
| 1412 else if (!g_ascii_strncasecmp(msg, "PING", 4)) { |
|
| 1413 g_snprintf(buf, sizeof(buf), "\001%s\001", msg); |
|
| 1414 irc_send_notice (gc, nick, buf); |
|
| 1415 g_snprintf(out, sizeof(out), ">> CTCP PING requested from %s", nick); |
|
| 1416 gaim_notify_info(gc, NULL, out, _("IRC CTCP info")); |
|
| 1417 } |
|
| 1418 else if (!g_ascii_strncasecmp(msg, "DCC CHAT", 8)) { |
|
| 1419 char **chat_args = g_strsplit(msg, " ", 5); |
|
| 1420 char ask[1024]; |
|
| 1421 struct dcc_chat *dccchat = g_new0(struct dcc_chat, 1); |
|
| 1422 dccchat->gc = gc; |
|
| 1423 g_snprintf(dccchat->ip_address, sizeof(dccchat->ip_address), chat_args[3]); |
|
| 1424 dccchat->port=atoi(chat_args[4]); |
|
| 1425 g_snprintf(dccchat->nick, sizeof(dccchat->nick), nick); |
|
| 1426 g_snprintf(ask, sizeof(ask), _("%s would like to establish a DCC chat"), nick); |
|
| 1427 |
|
| 1428 gaim_request_action(gc, NULL, ask, |
|
| 1429 _("This requires a direct connection to be " |
|
| 1430 "established between the two computers. " |
|
| 1431 "Messages sent will not pass through the " |
|
| 1432 "IRC server"), 0, dccchat, 2, |
|
| 1433 _("Connect"), G_CALLBACK(dcc_chat_init), |
|
| 1434 _("Cancel"), G_CALLBACK(dcc_chat_cancel)); |
|
| 1435 } |
|
| 1436 else if (!g_ascii_strncasecmp(msg, "DCC SEND", 8)) { |
|
| 1437 GaimXfer *xfer; |
|
| 1438 char **send_args; |
|
| 1439 char *ip, *filename; |
|
| 1440 struct irc_xfer_data *xfer_data; |
|
| 1441 size_t size; |
|
| 1442 int param_count, file_params, len; |
|
| 1443 int port; |
|
| 1444 |
|
| 1445 /* Okay, this is ugly, but should get us past "DCC SEND" */ |
|
| 1446 msg = strstr(msg, "DCC SEND"); |
|
| 1447 msg = strchr(msg, ' ') + 1; |
|
| 1448 msg = strchr(msg, ' ') + 1; |
|
| 1449 |
|
| 1450 /* SEND <file name> <address> <port> <size> [...] */ |
|
| 1451 send_args = g_strsplit(msg, " ", -1); |
|
| 1452 |
|
| 1453 for (param_count = 0; send_args[param_count] != NULL; param_count++) |
|
| 1454 ; |
|
| 1455 |
|
| 1456 if (param_count < 4) { |
|
| 1457 char buf[IRC_BUF_LEN]; |
|
| 1458 |
|
| 1459 g_snprintf(buf, sizeof(buf), |
|
| 1460 _("Received an invalid file send request from %s."), |
|
| 1461 nick); |
|
| 1462 |
|
| 1463 gaim_notify_error(gc, NULL, buf, _("IRC Error")); |
|
| 1464 |
|
| 1465 return; |
|
| 1466 } |
|
| 1467 |
|
| 1468 file_params = get_file_params_count(send_args, param_count); |
|
| 1469 |
|
| 1470 /* send_args[paramcount - 1][strlen(send_args[5])-1] = 0; */ |
|
| 1471 |
|
| 1472 /* Give these better names. */ |
|
| 1473 ip = send_args[file_params]; |
|
| 1474 port = atoi(send_args[file_params + 1]); |
|
| 1475 size = atoi(send_args[file_params + 2]); |
|
| 1476 |
|
| 1477 send_args[file_params] = NULL; |
|
| 1478 |
|
| 1479 filename = g_strjoinv(" ", send_args); |
|
| 1480 |
|
| 1481 g_strfreev(send_args); |
|
| 1482 |
|
| 1483 len = strlen(filename); |
|
| 1484 |
|
| 1485 if (len > 1 && *filename == '"' && filename[len - 1] == '"') { |
|
| 1486 /* "file name" - MIRC sends filenames with spaces like this */ |
|
| 1487 filename[len - 1] = '\0'; |
|
| 1488 g_memmove(filename, filename + 1, len); |
|
| 1489 } |
|
| 1490 |
|
| 1491 /* Setup the IRC-specific transfer data. */ |
|
| 1492 xfer_data = g_malloc0(sizeof(struct irc_xfer_data)); |
|
| 1493 xfer_data->ip = ip; |
|
| 1494 xfer_data->port = port; |
|
| 1495 xfer_data->idata = id; |
|
| 1496 |
|
| 1497 /* Build the file transfer handle. */ |
|
| 1498 xfer = gaim_xfer_new(gc->account, GAIM_XFER_RECEIVE, nick); |
|
| 1499 xfer->data = xfer_data; |
|
| 1500 |
|
| 1501 /* Set the info about the incoming file. */ |
|
| 1502 gaim_xfer_set_filename(xfer, filename); |
|
| 1503 gaim_xfer_set_size(xfer, size); |
|
| 1504 |
|
| 1505 g_free(filename); |
|
| 1506 |
|
| 1507 /* Setup our I/O op functions. */ |
|
| 1508 gaim_xfer_set_init_fnc(xfer, irc_xfer_init); |
|
| 1509 gaim_xfer_set_end_fnc(xfer, irc_xfer_end); |
|
| 1510 gaim_xfer_set_cancel_send_fnc(xfer, irc_xfer_cancel_send); |
|
| 1511 gaim_xfer_set_cancel_recv_fnc(xfer, irc_xfer_cancel_recv); |
|
| 1512 gaim_xfer_set_ack_fnc(xfer, irc_xfer_ack); |
|
| 1513 |
|
| 1514 /* Keep track of this transfer for later. */ |
|
| 1515 id->file_transfers = g_slist_append(id->file_transfers, xfer); |
|
| 1516 |
|
| 1517 /* Now perform the request! */ |
|
| 1518 gaim_xfer_request(xfer); |
|
| 1519 } |
|
| 1520 } |
|
| 1521 |
|
| 1522 static gboolean |
|
| 1523 irc_parse(GaimConnection *gc, char *buf) |
|
| 1524 { |
|
| 1525 struct irc_data *idata = gc->proto_data; |
|
| 1526 gchar outbuf[IRC_BUF_LEN]; |
|
| 1527 char *word[PDIWORDS], *word_eol[PDIWORDS]; |
|
| 1528 char pdibuf[522]; |
|
| 1529 char *ex, ip[128], nick[128]; |
|
| 1530 char *cmd; |
|
| 1531 |
|
| 1532 /* Check for errors */ |
|
| 1533 |
|
| 1534 if (*buf != ':') { |
|
| 1535 if (!strncmp(buf, "NOTICE ", 7)) |
|
| 1536 buf += 7; |
|
| 1537 if (!strncmp(buf, "PING ", 5)) { |
|
| 1538 int r = FALSE; |
|
| 1539 g_snprintf(outbuf, sizeof(outbuf), "PONG %s\r\n", buf + 5); |
|
| 1540 if (irc_write(idata->fd, outbuf, strlen(outbuf)) < 0) { |
|
| 1541 gaim_connection_error(gc, _("Unable to write")); |
|
| 1542 r = TRUE; |
|
| 1543 } |
|
| 1544 return r; |
|
| 1545 } |
|
| 1546 /* XXX doesn't handle ERROR */ |
|
| 1547 return FALSE; |
|
| 1548 } |
|
| 1549 |
|
| 1550 if (!idata->online) { |
|
| 1551 /* Now lets sign ourselves on */ |
|
| 1552 gaim_connection_set_state(gc, GAIM_CONNECTED); |
|
| 1553 serv_finish_login(gc); |
|
| 1554 |
|
| 1555 /* we don't call this now because otherwise some IRC servers might not like us */ |
|
| 1556 idata->timer = g_timeout_add(20000, irc_request_buddy_update, gc); |
|
| 1557 idata->online = TRUE; |
|
| 1558 } |
|
| 1559 |
|
| 1560 buf++; |
|
| 1561 |
|
| 1562 process_data_init(pdibuf, buf, word, word_eol, FALSE); |
|
| 1563 |
|
| 1564 if (atoi(word[2])) { |
|
| 1565 if (*word_eol[3]) |
|
| 1566 process_numeric(gc, word, word_eol); |
|
| 1567 return FALSE; |
|
| 1568 } |
|
| 1569 |
|
| 1570 cmd = word[2]; |
|
| 1571 |
|
| 1572 ex = strchr(pdibuf, '!'); |
|
| 1573 if (!ex) { |
|
| 1574 strncpy(ip, pdibuf, sizeof(ip)); |
|
| 1575 ip[sizeof(ip)-1] = 0; |
|
| 1576 strncpy(nick, pdibuf, sizeof(nick)); |
|
| 1577 nick[sizeof(nick)-1] = 0; |
|
| 1578 } else { |
|
| 1579 strncpy(ip, ex + 1, sizeof(ip)); |
|
| 1580 ip[sizeof(ip)-1] = 0; |
|
| 1581 strncpy(nick, pdibuf, sizeof(nick)); |
|
| 1582 nick[sizeof(nick)-1] = 0; |
|
| 1583 if ((ex - pdibuf) < sizeof (nick)) |
|
| 1584 nick[ex - pdibuf] = 0; /* cut the buffer at the '!' */ |
|
| 1585 } |
|
| 1586 |
|
| 1587 if (!strcmp(cmd, "INVITE")) { |
|
| 1588 GHashTable *components = g_hash_table_new_full(g_str_hash, g_str_equal, |
|
| 1589 g_free, g_free); |
|
| 1590 |
|
| 1591 g_hash_table_replace(components, g_strdup("channel"), g_strdup(word[4])); |
|
| 1592 |
|
| 1593 serv_got_chat_invite(gc, word[4] + 1, nick, NULL, components); |
|
| 1594 } else if (!strcmp(cmd, "JOIN")) { |
|
| 1595 irc_parse_join(gc, nick, word, word_eol); |
|
| 1596 } else if (!strcmp(cmd, "KICK")) { |
|
| 1597 if (!strcmp(gaim_connection_get_display_name(gc), word[4])) { |
|
| 1598 GaimConversation *c = irc_find_chat(gc, word[3]); |
|
| 1599 if (!c) |
|
| 1600 return FALSE; |
|
| 1601 gc->buddy_chats = g_slist_remove(gc->buddy_chats, c); |
|
| 1602 gaim_conversation_set_account(c, NULL); |
|
| 1603 g_snprintf(outbuf, sizeof(outbuf), _("You have been kicked from %s: %s"), |
|
| 1604 word[3], *word_eol[5] == ':' ? word_eol[5] + 1 : word_eol[5]); |
|
| 1605 gaim_notify_error(gc, NULL, outbuf, _("IRC Error")); |
|
| 1606 } else { |
|
| 1607 char *reason = *word_eol[5] == ':' ? word_eol[5] + 1 : word_eol[5]; |
|
| 1608 char *msg = g_strdup_printf(_("Kicked by %s: %s"), nick, reason); |
|
| 1609 GaimConversation *c = irc_find_chat(gc, word[3]); |
|
| 1610 irc_rem_chat_bud(gc, word[4], c, msg); |
|
| 1611 g_free(msg); |
|
| 1612 } |
|
| 1613 } else if (!strcmp(cmd, "KILL")) { /* */ |
|
| 1614 } else if (!strcmp(cmd, "MODE")) { |
|
| 1615 handle_mode(gc, word, word_eol, FALSE); |
|
| 1616 } else if (!strcmp(cmd, "NICK")) { |
|
| 1617 char *new = *word_eol[3] == ':' ? word_eol[3] + 1 : word_eol[3]; |
|
| 1618 if (!strcmp(gaim_connection_get_display_name(gc), nick)) |
|
| 1619 gaim_connection_set_display_name(gc, new); |
|
| 1620 irc_change_name(gc, nick, new); |
|
| 1621 } else if (!strcmp(cmd, "NOTICE")) { |
|
| 1622 irc_parse_notice(gc, nick, ex, word, word_eol); |
|
| 1623 } else if (!strcmp(cmd, "PART")) { |
|
| 1624 if (!irc_parse_part(gc, nick, cmd, word, word_eol)) |
|
| 1625 return FALSE; |
|
| 1626 } else if (!strcmp(cmd, "PRIVMSG")) { |
|
| 1627 char *to, *msg; |
|
| 1628 if (!*word[3]) |
|
| 1629 return FALSE; |
|
| 1630 to = word[3]; |
|
| 1631 msg = *word_eol[4] == ':' ? word_eol[4] + 1 : word_eol[4]; |
|
| 1632 if (msg[0] == 1 && msg[strlen (msg) - 1] == 1) { /* ctcp */ |
|
| 1633 if (!g_ascii_strncasecmp(msg + 1, "DCC ", 4)) |
|
| 1634 process_data_init(pdibuf, buf, word, word_eol, TRUE); |
|
| 1635 handle_ctcp(gc, to, nick, msg + 1, word, word_eol); |
|
| 1636 } else { |
|
| 1637 handle_privmsg(gc, to, nick, msg); |
|
| 1638 } |
|
| 1639 } else if (!strcmp(cmd, "PONG")) { /* */ |
|
| 1640 } else if (!strcmp(cmd, "QUIT")) { |
|
| 1641 irc_rem_chat_bud(gc, nick, irc_find_chat(gc, word[3]), *word_eol[3] == ':' ? word_eol[3] + 1 : word_eol[3]); |
|
| 1642 } else if (!strcmp(cmd, "TOPIC")) { |
|
| 1643 irc_parse_topic(gc, nick, word, word_eol); |
|
| 1644 } else if (!strcmp(cmd, "WALLOPS")) { /* Don't know if a dialog box is the right way? */ |
|
| 1645 char *msg = strrchr(word_eol[0], ':'); |
|
| 1646 if (msg) |
|
| 1647 gaim_notify_error(gc, NULL, msg+1, _("IRC Operator")); |
|
| 1648 } |
|
| 1649 |
|
| 1650 return FALSE; |
|
| 1651 } |
|
| 1652 |
|
| 1653 /* CTCP by jonas@birme.se */ |
|
| 1654 static void |
|
| 1655 irc_parse_notice(GaimConnection *gc, char *nick, char *ex, |
|
| 1656 char *word[], char *word_eol[]) |
|
| 1657 { |
|
| 1658 char buf[IRC_BUF_LEN]; |
|
| 1659 |
|
| 1660 if (!g_ascii_strcasecmp(word[4], ":\001CLIENTINFO")) { |
|
| 1661 char *p = g_strrstr(word_eol[5], "\001"); |
|
| 1662 *p = 0; |
|
| 1663 g_snprintf(buf, sizeof(buf), "CTCP Answer: %s", word_eol[5]); |
|
| 1664 gaim_notify_info(gc, NULL, buf, _("CTCP ClientInfo")); |
|
| 1665 |
|
| 1666 } else if (!g_ascii_strcasecmp(word[4], ":\001USERINFO")) { |
|
| 1667 char *p = g_strrstr(word_eol[5], "\001"); |
|
| 1668 *p = 0; |
|
| 1669 g_snprintf(buf, sizeof(buf), "CTCP Answer: %s", word_eol[5]); |
|
| 1670 gaim_notify_info(gc, NULL, buf, _("CTCP UserInfo")); |
|
| 1671 |
|
| 1672 } else if (!g_ascii_strcasecmp(word[4], ":\001VERSION")) { |
|
| 1673 char *p = g_strrstr(word_eol[5], "\001"); |
|
| 1674 *p = 0; |
|
| 1675 g_snprintf(buf, sizeof(buf), "CTCP Answer: %s", word_eol[5]); |
|
| 1676 gaim_notify_info(gc, NULL, buf, _("CTCP Version")); |
|
| 1677 |
|
| 1678 } else if (!g_ascii_strcasecmp(word[4], ":\001PING")) { |
|
| 1679 char *p = g_strrstr(word_eol[5], "\001"); |
|
| 1680 struct timeval ping_time; |
|
| 1681 struct timeval now; |
|
| 1682 gchar **vector; |
|
| 1683 |
|
| 1684 if (p) |
|
| 1685 *p = 0; |
|
| 1686 |
|
| 1687 vector = g_strsplit(word_eol[5], ".", 2); |
|
| 1688 |
|
| 1689 if (gettimeofday(&now, NULL) == 0 && vector != NULL) { |
|
| 1690 if (vector[1] && now.tv_usec - atol(vector[1]) < 0) { |
|
| 1691 ping_time.tv_sec = now.tv_sec - atol(vector[0]) - 1; |
|
| 1692 ping_time.tv_usec = now.tv_usec - atol(vector[1]) + 1000000; |
|
| 1693 |
|
| 1694 } else { |
|
| 1695 ping_time.tv_sec = now.tv_sec - atol(vector[0]); |
|
| 1696 if(vector[1]) |
|
| 1697 ping_time.tv_usec = now.tv_usec - atol(vector[1]); |
|
| 1698 } |
|
| 1699 |
|
| 1700 g_snprintf(buf, sizeof(buf), |
|
| 1701 "CTCP Ping reply from %s: %lu.%.03lu seconds", |
|
| 1702 nick, ping_time.tv_sec, (ping_time.tv_usec/1000)); |
|
| 1703 |
|
| 1704 gaim_notify_info(gc, NULL, buf, _("CTCP Ping")); |
|
| 1705 g_strfreev(vector); |
|
| 1706 } |
|
| 1707 } else { |
|
| 1708 if (*word_eol[4] == ':') word_eol[4]++; |
|
| 1709 if (ex) |
|
| 1710 irc_got_im(gc, nick, word_eol[4], 0, time(NULL)); |
|
| 1711 } |
|
| 1712 } |
|
| 1713 |
|
| 1714 static void |
|
| 1715 irc_parse_join(GaimConnection *gc, char *nick, |
|
| 1716 char *word[], char *word_eol[]) |
|
| 1717 { |
|
| 1718 char *chan = *word[3] == ':' ? word[3] + 1 : word[3]; |
|
| 1719 static int id = 1; |
|
| 1720 GaimConversation *c; |
|
| 1721 char *hostmask, *p; |
|
| 1722 |
|
| 1723 if (!gaim_utf8_strcasecmp(gaim_connection_get_display_name(gc), nick)) { |
|
| 1724 serv_got_joined_chat(gc, id++, chan); |
|
| 1725 } else { |
|
| 1726 c = irc_find_chat(gc, chan); |
|
| 1727 if (c) { |
|
| 1728 hostmask = g_strdup(word[1]); |
|
| 1729 p = strchr(hostmask, '!'); |
|
| 1730 if (p) { |
|
| 1731 char *pend = strchr(p, ' '); |
|
| 1732 if (pend) { |
|
| 1733 *pend = 0; |
|
| 1734 } |
|
| 1735 |
|
| 1736 gaim_chat_add_user(GAIM_CHAT(c), nick, p + 1); |
|
| 1737 |
|
| 1738 g_free(hostmask); |
|
| 1739 } |
|
| 1740 } |
|
| 1741 } |
|
| 1742 } |
|
| 1743 |
|
| 1744 static void |
|
| 1745 irc_parse_topic(GaimConnection *gc, char *nick, |
|
| 1746 char *word[], char *word_eol[]) |
|
| 1747 { |
|
| 1748 GaimConversation *c = irc_find_chat(gc, word[3]); |
|
| 1749 char *topic = irc_recv_convert(gc, *word_eol[4] == ':' ? word_eol[4] + 1 : word_eol[4]); |
|
| 1750 char buf[IRC_BUF_LEN]; |
|
| 1751 |
|
| 1752 if (c) { |
|
| 1753 gaim_chat_set_topic(GAIM_CHAT(c), nick, topic); |
|
| 1754 g_snprintf(buf, sizeof(buf), |
|
| 1755 _("<B>%s has changed the topic to: %s</B>"), nick, topic); |
|
| 1756 |
|
| 1757 gaim_conversation_write(c, NULL, buf, -1, WFLAG_SYSTEM, time(NULL)); |
|
| 1758 } |
|
| 1759 g_free(topic); |
|
| 1760 } |
|
| 1761 |
|
| 1762 static gboolean |
|
| 1763 irc_parse_part(GaimConnection *gc, char *nick, char *cmd, |
|
| 1764 char *word[], char *word_eol[]) |
|
| 1765 { |
|
| 1766 char *chan = cmd + 5; |
|
| 1767 GaimConversation *c; |
|
| 1768 GaimChat *chat; |
|
| 1769 char *reason = word_eol[4]; |
|
| 1770 GList *r; |
|
| 1771 |
|
| 1772 if (*chan == ':') |
|
| 1773 chan++; |
|
| 1774 if (*reason == ':') |
|
| 1775 reason++; |
|
| 1776 if (!(c = irc_find_chat(gc, chan))) |
|
| 1777 return FALSE; |
|
| 1778 if (!strcmp(nick, gaim_connection_get_display_name(gc))) { |
|
| 1779 serv_got_chat_left(gc, gaim_chat_get_id(GAIM_CHAT(c))); |
|
| 1780 return FALSE; |
|
| 1781 } |
|
| 1782 |
|
| 1783 chat = GAIM_CHAT(c); |
|
| 1784 |
|
| 1785 r = gaim_chat_get_users(GAIM_CHAT(c)); |
|
| 1786 |
|
| 1787 while (r) { |
|
| 1788 char *who = r->data; |
|
| 1789 if (*who == '@') |
|
| 1790 who++; |
|
| 1791 if (*who == '%') |
|
| 1792 who++; |
|
| 1793 if (*who == '+') |
|
| 1794 who++; |
|
| 1795 if (!gaim_utf8_strcasecmp(who, nick)) { |
|
| 1796 gaim_chat_remove_user(chat, who, reason); |
|
| 1797 break; |
|
| 1798 } |
|
| 1799 r = r->next; |
|
| 1800 } |
|
| 1801 return TRUE; |
|
| 1802 } |
|
| 1803 |
|
| 1804 static void |
|
| 1805 irc_callback(gpointer data, gint source, GaimInputCondition condition) |
|
| 1806 { |
|
| 1807 GaimConnection *gc = data; |
|
| 1808 struct irc_data *idata = gc->proto_data; |
|
| 1809 int i = 0; |
|
| 1810 gchar buf[1024]; |
|
| 1811 gboolean off; |
|
| 1812 |
|
| 1813 i = read(idata->fd, buf, 1024); |
|
| 1814 if (i <= 0) { |
|
| 1815 gaim_connection_error(gc, _("Read error")); |
|
| 1816 return; |
|
| 1817 } |
|
| 1818 |
|
| 1819 idata->rxqueue = g_realloc(idata->rxqueue, i + idata->rxlen + 1); |
|
| 1820 memcpy(idata->rxqueue + idata->rxlen, buf, i); |
|
| 1821 idata->rxlen += i; |
|
| 1822 idata->rxqueue[idata->rxlen] = 0; |
|
| 1823 |
|
| 1824 while (1) { |
|
| 1825 char *d, *e; |
|
| 1826 int len; |
|
| 1827 |
|
| 1828 if (!idata->rxqueue || ((e = strchr(idata->rxqueue, '\n')) == NULL)) |
|
| 1829 return; |
|
| 1830 |
|
| 1831 len = e - idata->rxqueue + 1; |
|
| 1832 d = g_strndup(idata->rxqueue, len); |
|
| 1833 g_strchomp(d); |
|
| 1834 gaim_debug(GAIM_DEBUG_MISC, "irc", "S: %s\n", d); |
|
| 1835 |
|
| 1836 /* REMOVE ME BEFORE SUBMIT! */ |
|
| 1837 /*fprintf(stderr, "IRC S: %s\n", d);*/ |
|
| 1838 |
|
| 1839 idata->rxlen -= len; |
|
| 1840 if (idata->rxlen) { |
|
| 1841 char *tmp = g_strdup(e + 1); |
|
| 1842 g_free(idata->rxqueue); |
|
| 1843 idata->rxqueue = tmp; |
|
| 1844 } else { |
|
| 1845 g_free(idata->rxqueue); |
|
| 1846 idata->rxqueue = NULL; |
|
| 1847 } |
|
| 1848 |
|
| 1849 off = irc_parse(gc, d); |
|
| 1850 |
|
| 1851 g_free(d); |
|
| 1852 |
|
| 1853 if (off) |
|
| 1854 return; |
|
| 1855 } |
|
| 1856 } |
|
| 1857 |
|
| 1858 static void |
|
| 1859 irc_login_callback(gpointer data, gint source, GaimInputCondition condition) |
|
| 1860 { |
|
| 1861 GaimConnection *gc = data; |
|
| 1862 GaimAccount *account = gaim_connection_get_account(gc); |
|
| 1863 struct irc_data *idata; |
|
| 1864 char hostname[256]; |
|
| 1865 char buf[IRC_BUF_LEN]; |
|
| 1866 char *test; |
|
| 1867 const char *alias; |
|
| 1868 const char *charset = gaim_account_get_string(account, "charset", "UTF-8"); |
|
| 1869 GError *err = NULL; |
|
| 1870 |
|
| 1871 idata = gc->proto_data; |
|
| 1872 |
|
| 1873 if (source < 0) { |
|
| 1874 gaim_connection_error(gc, _("Write error")); |
|
| 1875 return; |
|
| 1876 } |
|
| 1877 idata->fd = source; |
|
| 1878 |
|
| 1879 /* Try a quick conversion to see if the specified encoding is OK */ |
|
| 1880 test = g_convert("test", strlen("test"), charset, |
|
| 1881 "UTF-8", NULL, NULL, &err); |
|
| 1882 if (err) { |
|
| 1883 gaim_debug(GAIM_DEBUG_ERROR, "irc", |
|
| 1884 "Couldn't initialize %s for IRC charset conversion, using ISO-8859-1\n", |
|
| 1885 charset); |
|
| 1886 gaim_account_set_string(account, "charset", "UTF-8"); |
|
| 1887 } |
|
| 1888 |
|
| 1889 g_free(test); |
|
| 1890 |
|
| 1891 gethostname(hostname, sizeof(hostname) - 1); |
|
| 1892 hostname[sizeof(hostname) - 1] = 0; |
|
| 1893 |
|
| 1894 if (!*hostname) |
|
| 1895 g_snprintf(hostname, sizeof(hostname), "localhost"); |
|
| 1896 |
|
| 1897 if (gaim_account_get_password(account) != NULL) { |
|
| 1898 g_snprintf(buf, sizeof(buf), "PASS %s\r\n", |
|
| 1899 gaim_account_get_password(account)); |
|
| 1900 |
|
| 1901 if (irc_write(idata->fd, buf, strlen(buf)) < 0) { |
|
| 1902 gaim_connection_error(gc, _("Write error")); |
|
| 1903 return; |
|
| 1904 } |
|
| 1905 } |
|
| 1906 |
|
| 1907 alias = gaim_account_get_alias(account); |
|
| 1908 |
|
| 1909 g_snprintf(buf, sizeof(buf), "USER %s %s %s :%s\r\n", |
|
| 1910 g_get_user_name(), hostname, |
|
| 1911 idata->server, |
|
| 1912 (alias == NULL ? "gaim" : alias)); |
|
| 1913 |
|
| 1914 if (irc_write(idata->fd, buf, strlen(buf)) < 0) { |
|
| 1915 gaim_connection_error(gc, _("Write error")); |
|
| 1916 return; |
|
| 1917 } |
|
| 1918 |
|
| 1919 g_snprintf(buf, sizeof(buf), "NICK %s\r\n", |
|
| 1920 gaim_connection_get_display_name(gc)); |
|
| 1921 |
|
| 1922 if (irc_write(idata->fd, buf, strlen(buf)) < 0) { |
|
| 1923 gaim_connection_error(gc, _("Write error")); |
|
| 1924 return; |
|
| 1925 } |
|
| 1926 |
|
| 1927 gc->inpa = gaim_input_add(idata->fd, GAIM_INPUT_READ, irc_callback, gc); |
|
| 1928 } |
|
| 1929 |
|
| 1930 static void |
|
| 1931 irc_login(GaimAccount *account) |
|
| 1932 { |
|
| 1933 const char *username = gaim_account_get_username(account); |
|
| 1934 char buf[IRC_BUF_LEN]; |
|
| 1935 int rc; |
|
| 1936 |
|
| 1937 GaimConnection *gc; |
|
| 1938 struct irc_data *idata; |
|
| 1939 char **parts; |
|
| 1940 |
|
| 1941 gc = gaim_account_get_connection(account); |
|
| 1942 idata = gc->proto_data = g_new0(struct irc_data, 1); |
|
| 1943 |
|
| 1944 parts = g_strsplit(username, "@", 2); |
|
| 1945 gaim_connection_set_display_name(gc, parts[0]); |
|
| 1946 idata->server = g_strdup(parts[1]); |
|
| 1947 g_strfreev(parts); |
|
| 1948 |
|
| 1949 g_snprintf(buf, sizeof(buf), _("Signon: %s"), username); |
|
| 1950 gaim_connection_update_progress(gc, buf, 1, 2); |
|
| 1951 |
|
| 1952 idata->chantypes = g_strdup("#&!+"); |
|
| 1953 idata->chanmodes = g_strdup("beI,k,lnt"); |
|
| 1954 idata->nickmodes = g_strdup("ohv"); |
|
| 1955 idata->str = g_string_new(""); |
|
| 1956 idata->fd = -1; |
|
| 1957 |
|
| 1958 rc = gaim_proxy_connect(account, idata->server, |
|
| 1959 gaim_account_get_int(account, "port", 6667), |
|
| 1960 irc_login_callback, gc); |
|
| 1961 |
|
| 1962 if (rc != 0) { |
|
| 1963 gaim_connection_error(gc, _("Unable to create socket")); |
|
| 1964 return; |
|
| 1965 } |
|
| 1966 } |
|
| 1967 |
|
| 1968 static void |
|
| 1969 irc_close(GaimConnection *gc) |
|
| 1970 { |
|
| 1971 struct irc_data *idata = (struct irc_data *)gc->proto_data; |
|
| 1972 |
|
| 1973 gchar buf[IRC_BUF_LEN]; |
|
| 1974 |
|
| 1975 if (idata->str->len > 0) { |
|
| 1976 g_snprintf(buf, sizeof(buf), "QUIT :%s\r\n", idata->str->str); |
|
| 1977 } else { |
|
| 1978 g_snprintf(buf, sizeof(buf), |
|
| 1979 "QUIT :Download Gaim [%s]\r\n", WEBSITE); |
|
| 1980 } |
|
| 1981 irc_write(idata->fd, buf, strlen(buf)); |
|
| 1982 |
|
| 1983 if (idata->rxqueue) |
|
| 1984 g_free(idata->rxqueue); |
|
| 1985 |
|
| 1986 idata->rxqueue = NULL; |
|
| 1987 idata->rxlen = 0; |
|
| 1988 |
|
| 1989 /* Kill any existing transfers */ |
|
| 1990 while (idata->file_transfers) { |
|
| 1991 GaimXfer *xfer; |
|
| 1992 |
|
| 1993 xfer = (GaimXfer *)idata->file_transfers->data; |
|
| 1994 |
|
| 1995 gaim_xfer_end(xfer); |
|
| 1996 gaim_xfer_destroy(xfer); |
|
| 1997 |
|
| 1998 idata->file_transfers = idata->file_transfers->next; |
|
| 1999 } |
|
| 2000 idata->file_transfers = NULL; |
|
| 2001 |
|
| 2002 |
|
| 2003 g_free(idata->chantypes); |
|
| 2004 g_free(idata->chanmodes); |
|
| 2005 g_free(idata->nickmodes); |
|
| 2006 |
|
| 2007 g_string_free(idata->str, TRUE); |
|
| 2008 if (idata->liststr) |
|
| 2009 g_string_free(idata->liststr, TRUE); |
|
| 2010 |
|
| 2011 if (idata->timer) |
|
| 2012 g_source_remove(idata->timer); |
|
| 2013 |
|
| 2014 if (gc->inpa) |
|
| 2015 gaim_input_remove(gc->inpa); |
|
| 2016 |
|
| 2017 close(idata->fd); |
|
| 2018 g_free(gc->proto_data); |
|
| 2019 } |
|
| 2020 |
|
| 2021 static void |
|
| 2022 set_mode_3(GaimConnection *gc, const char *who, int sign, int mode, |
|
| 2023 int start, int end, char *word[]) |
|
| 2024 { |
|
| 2025 struct irc_data *id = gc->proto_data; |
|
| 2026 char buf[IRC_BUF_LEN]; |
|
| 2027 int left; |
|
| 2028 int i = start; |
|
| 2029 |
|
| 2030 while (1) { |
|
| 2031 left = end - i; |
|
| 2032 switch (left) { |
|
| 2033 case 0: |
|
| 2034 return; |
|
| 2035 case 1: |
|
| 2036 g_snprintf(buf, sizeof(buf), "MODE %s %c%c %s\r\n", |
|
| 2037 who, sign, mode, word[i]); |
|
| 2038 i += 1; |
|
| 2039 break; |
|
| 2040 case 2: |
|
| 2041 g_snprintf(buf, sizeof(buf), "MODE %s %c%c%c %s %s\r\n", |
|
| 2042 who, sign, mode, mode, word[i], word[i + 1]); |
|
| 2043 i += 2; |
|
| 2044 break; |
|
| 2045 default: |
|
| 2046 g_snprintf(buf, sizeof(buf), "MODE %s %c%c%c%c %s %s %s\r\n", |
|
| 2047 who, sign, mode, mode, mode, |
|
| 2048 word[i], word[i + 1], word[i + 2]); |
|
| 2049 i += 2; |
|
| 2050 break; |
|
| 2051 } |
|
| 2052 irc_write(id->fd, buf, strlen(buf)); |
|
| 2053 if (left < 3) |
|
| 2054 return; |
|
| 2055 } |
|
| 2056 } |
|
| 2057 |
|
| 2058 static void |
|
| 2059 set_mode_6(GaimConnection *gc, const char *who, int sign, int mode, |
|
| 2060 int start, int end, char *word[]) |
|
| 2061 { |
|
| 2062 struct irc_data *id = gc->proto_data; |
|
| 2063 char buf[IRC_BUF_LEN]; |
|
| 2064 int left; |
|
| 2065 int i = start; |
|
| 2066 |
|
| 2067 while (1) { |
|
| 2068 left = end - i; |
|
| 2069 switch (left) { |
|
| 2070 case 0: |
|
| 2071 return; |
|
| 2072 case 1: |
|
| 2073 g_snprintf(buf, sizeof(buf), "MODE %s %c%c %s\r\n", |
|
| 2074 who, sign, mode, word[i]); |
|
| 2075 i += 1; |
|
| 2076 break; |
|
| 2077 case 2: |
|
| 2078 g_snprintf(buf, sizeof(buf), "MODE %s %c%c%c %s %s\r\n", |
|
| 2079 who, sign, mode, mode, word[i], word[i + 1]); |
|
| 2080 i += 2; |
|
| 2081 break; |
|
| 2082 case 3: |
|
| 2083 g_snprintf(buf, sizeof(buf), "MODE %s %c%c%c%c %s %s %s\r\n", |
|
| 2084 who, sign, mode, mode, mode, |
|
| 2085 word[i], word[i + 1], word[i + 2]); |
|
| 2086 i += 3; |
|
| 2087 break; |
|
| 2088 case 4: |
|
| 2089 g_snprintf(buf, sizeof(buf), "MODE %s %c%c%c%c%c %s %s %s %s\r\n", |
|
| 2090 who, sign, mode, mode, mode, mode, |
|
| 2091 word[i], word[i + 1], word[i + 2], word[i + 3]); |
|
| 2092 i += 4; |
|
| 2093 break; |
|
| 2094 case 5: |
|
| 2095 g_snprintf(buf, sizeof(buf), "MODE %s %c%c%c%c%c%c %s %s %s %s %s\r\n", |
|
| 2096 who, sign, mode, mode, mode, mode, mode, |
|
| 2097 word[i], word[i + 1], word[i + 2], |
|
| 2098 word[i + 3], word[i + 4]); |
|
| 2099 i += 5; |
|
| 2100 break; |
|
| 2101 default: |
|
| 2102 g_snprintf(buf, sizeof(buf), "MODE %s %c%c%c%c%c%c%c %s %s %s %s %s %s\r\n", |
|
| 2103 who, sign, mode, mode, mode, mode, mode, mode, |
|
| 2104 word[i], word[i + 1], word[i + 2], |
|
| 2105 word[i + 3], word[i + 4], word[i + 5]); |
|
| 2106 i += 6; |
|
| 2107 break; |
|
| 2108 } |
|
| 2109 irc_write(id->fd, buf, strlen(buf)); |
|
| 2110 if (left < 6) |
|
| 2111 return; |
|
| 2112 } |
|
| 2113 } |
|
| 2114 |
|
| 2115 static void |
|
| 2116 set_mode(GaimConnection *gc, const char *who, int sign, int mode, char *word[]) |
|
| 2117 { |
|
| 2118 struct irc_data *id = gc->proto_data; |
|
| 2119 int i = 2; |
|
| 2120 |
|
| 2121 while (1) { |
|
| 2122 if (!*word[i]) { |
|
| 2123 if (i == 2) |
|
| 2124 return; |
|
| 2125 if (id->six_modes) |
|
| 2126 set_mode_6(gc, who, sign, mode, 2, i, word); |
|
| 2127 else |
|
| 2128 set_mode_3(gc, who, sign, mode, 2, i, word); |
|
| 2129 return; |
|
| 2130 } |
|
| 2131 i++; |
|
| 2132 } |
|
| 2133 } |
|
| 2134 |
|
| 2135 static void |
|
| 2136 set_chan_mode(GaimConnection *gc, const char *chan, const char *mode_str) |
|
| 2137 { |
|
| 2138 struct irc_data *id = gc->proto_data; |
|
| 2139 char buf[IRC_BUF_LEN]; |
|
| 2140 |
|
| 2141 if ((mode_str[0] == '-') || (mode_str[0] == '+')) { |
|
| 2142 g_snprintf(buf, sizeof(buf), "MODE %s %s\r\n", chan, mode_str); |
|
| 2143 irc_write(id->fd, buf, strlen(buf)); |
|
| 2144 } |
|
| 2145 } |
|
| 2146 |
|
| 2147 static int |
|
| 2148 handle_command(GaimConnection *gc, const char *who, const char *in_what) |
|
| 2149 { |
|
| 2150 char buf[IRC_BUF_LEN]; |
|
| 2151 char pdibuf[IRC_BUF_LEN]; |
|
| 2152 char *word[PDIWORDS], *word_eol[PDIWORDS]; |
|
| 2153 char *tmp = g_strdup(in_what); |
|
| 2154 GString *str = encode_html(tmp); |
|
| 2155 char *intl; |
|
| 2156 int len; |
|
| 2157 struct dcc_chat *dccchat = find_dcc_chat(gc, who); |
|
| 2158 struct irc_data *id = gc->proto_data; |
|
| 2159 char *what = str->str; |
|
| 2160 |
|
| 2161 g_free(tmp); |
|
| 2162 |
|
| 2163 if (*what != '/') { |
|
| 2164 if (dccchat) { |
|
| 2165 intl = irc_send_convert(gc, what, sizeof(buf), &len); |
|
| 2166 g_snprintf(buf, sizeof(buf), "%s\r\n", intl); |
|
| 2167 g_free(intl); |
|
| 2168 irc_write(dccchat->fd, buf, strlen(buf)); |
|
| 2169 g_string_free(str, TRUE); |
|
| 2170 return 1; |
|
| 2171 } |
|
| 2172 irc_send_privmsg (gc, who, what, TRUE); |
|
| 2173 g_string_free(str, TRUE); |
|
| 2174 return 1; |
|
| 2175 } |
|
| 2176 |
|
| 2177 process_data_init(pdibuf, what + 1, word, word_eol, TRUE); |
|
| 2178 g_string_free(str, FALSE); |
|
| 2179 if (!g_ascii_strcasecmp(pdibuf, "ME")) { |
|
| 2180 if (dccchat) { |
|
| 2181 intl = irc_send_convert(gc, word_eol[2], sizeof(buf), &len); |
|
| 2182 g_snprintf(buf, sizeof(buf), "\001ACTION %s\001\r\n", intl); |
|
| 2183 g_free(intl); |
|
| 2184 irc_write(dccchat->fd, buf, strlen(buf)); |
|
| 2185 g_free(what); |
|
| 2186 return 1; |
|
| 2187 } |
|
| 2188 g_snprintf(buf, sizeof(buf), "\001ACTION %s\001", word_eol[2]); |
|
| 2189 irc_send_privmsg (gc, who, buf, FALSE); |
|
| 2190 g_free(what); |
|
| 2191 return 1; |
|
| 2192 } else if (!g_ascii_strcasecmp(pdibuf, "INVITE")) { |
|
| 2193 char buf[IRC_BUF_LEN]; |
|
| 2194 g_snprintf(buf, sizeof(buf), "INVITE %s\r\n", word_eol[2]); |
|
| 2195 irc_write(id->fd, buf, strlen(buf)); |
|
| 2196 } else if (!g_ascii_strcasecmp(pdibuf, "TOPIC")) { |
|
| 2197 if (!*word_eol[2]) { |
|
| 2198 GaimConversation *c; |
|
| 2199 GaimChat *chat; |
|
| 2200 |
|
| 2201 c = irc_find_chat(gc, who); |
|
| 2202 chat = GAIM_CHAT(c); |
|
| 2203 |
|
| 2204 g_snprintf(buf, sizeof(buf), _("Topic for %s is %s"), |
|
| 2205 who, (gaim_chat_get_topic(chat) |
|
| 2206 ? gaim_chat_get_topic(chat) |
|
| 2207 : "(no topic set)")); |
|
| 2208 |
|
| 2209 gaim_conversation_write(c, NULL, buf, -1, |
|
| 2210 WFLAG_SYSTEM | WFLAG_NOLOG, time(NULL)); |
|
| 2211 } else { |
|
| 2212 /* This could be too long */ |
|
| 2213 intl = irc_send_convert(gc, word_eol[2], sizeof(buf), &len); |
|
| 2214 g_snprintf(buf, sizeof(buf), "TOPIC %s :%s\r\n", who, intl); |
|
| 2215 g_free(intl); |
|
| 2216 irc_write(id->fd, buf, strlen(buf)); |
|
| 2217 } |
|
| 2218 } else if (!g_ascii_strcasecmp(pdibuf, "NICK")) { |
|
| 2219 if (!*word_eol[2]) { |
|
| 2220 g_free(what); |
|
| 2221 return -EINVAL; |
|
| 2222 } |
|
| 2223 g_snprintf(buf, sizeof(buf), "NICK %s\r\n", word_eol[2]); |
|
| 2224 irc_write(id->fd, buf, strlen(buf)); |
|
| 2225 } else if (!g_ascii_strcasecmp(pdibuf, "OP")) { |
|
| 2226 set_mode(gc, who, '+', 'o', word); |
|
| 2227 } else if (!g_ascii_strcasecmp(pdibuf, "DEOP")) { |
|
| 2228 set_mode(gc, who, '-', 'o', word); |
|
| 2229 } else if (!g_ascii_strcasecmp(pdibuf, "VOICE")) { |
|
| 2230 set_mode(gc, who, '+', 'v', word); |
|
| 2231 } else if (!g_ascii_strcasecmp(pdibuf, "DEVOICE")) { |
|
| 2232 set_mode(gc, who, '-', 'v', word); |
|
| 2233 } else if (!g_ascii_strcasecmp(pdibuf, "MODE")) { |
|
| 2234 set_chan_mode(gc, who, word_eol[2]); |
|
| 2235 } else if (!g_ascii_strcasecmp(pdibuf, "QUOTE")) { |
|
| 2236 if (!*word_eol[2]) { |
|
| 2237 g_free(what); |
|
| 2238 return -EINVAL; |
|
| 2239 } |
|
| 2240 g_snprintf(buf, sizeof(buf), "%s\r\n", word_eol[2]); |
|
| 2241 irc_write(id->fd, buf, strlen(buf)); |
|
| 2242 } else if (!g_ascii_strcasecmp(pdibuf, "SAY")) { |
|
| 2243 if (!*word_eol[2]) { |
|
| 2244 g_free(what); |
|
| 2245 return -EINVAL; |
|
| 2246 } |
|
| 2247 irc_send_privmsg (gc, who, word_eol[2], TRUE); |
|
| 2248 return 1; |
|
| 2249 } else if (!g_ascii_strcasecmp(pdibuf, "MSG")) { |
|
| 2250 if (!*word[2]) { |
|
| 2251 g_free(what); |
|
| 2252 return -EINVAL; |
|
| 2253 } |
|
| 2254 if (!*word_eol[3]) { |
|
| 2255 g_free(what); |
|
| 2256 return -EINVAL; |
|
| 2257 } |
|
| 2258 irc_send_privmsg (gc, word[2], word_eol[3], TRUE); |
|
| 2259 } else if (!g_ascii_strcasecmp(pdibuf, "KICK")) { |
|
| 2260 if (!*word[2]) { |
|
| 2261 g_free(what); |
|
| 2262 return -EINVAL; |
|
| 2263 } |
|
| 2264 if (*word_eol[3]) { |
|
| 2265 intl = irc_send_convert(gc, word_eol[3], sizeof(buf), &len); |
|
| 2266 g_snprintf(buf, sizeof(buf), "KICK %s %s :%s\r\n", who, word[2], intl); |
|
| 2267 g_free(intl); |
|
| 2268 } else |
|
| 2269 g_snprintf(buf, sizeof(buf), "KICK %s %s\r\n", who, word[2]); |
|
| 2270 irc_write(id->fd, buf, strlen(buf)); |
|
| 2271 } else if (!g_ascii_strcasecmp(pdibuf, "JOIN") || !g_ascii_strcasecmp(pdibuf, "J")) { |
|
| 2272 if (!*word[2]) { |
|
| 2273 g_free(what); |
|
| 2274 return -EINVAL; |
|
| 2275 } |
|
| 2276 if (*word[3]) |
|
| 2277 g_snprintf(buf, sizeof(buf), "JOIN %s %s\r\n", word[2], word[3]); |
|
| 2278 else |
|
| 2279 g_snprintf(buf, sizeof(buf), "JOIN %s\r\n", word[2]); |
|
| 2280 irc_write(id->fd, buf, strlen(buf)); |
|
| 2281 } else if (!g_ascii_strcasecmp(pdibuf, "PART")) { |
|
| 2282 const char *chan = *word[2] ? word[2] : who; |
|
| 2283 char *reason = word_eol[3]; |
|
| 2284 GaimConversation *c; |
|
| 2285 if (!is_channel(gc, chan)) { |
|
| 2286 g_free(what); |
|
| 2287 return -EINVAL; |
|
| 2288 } |
|
| 2289 c = irc_find_chat(gc, chan); |
|
| 2290 if (*reason) { |
|
| 2291 intl = irc_send_convert(gc, reason, sizeof(buf), &len); |
|
| 2292 g_snprintf(buf, sizeof(buf), "PART %s :%s\r\n", chan, intl); |
|
| 2293 g_free(intl); |
|
| 2294 } else |
|
| 2295 g_snprintf(buf, sizeof(buf), "PART %s\r\n", chan); |
|
| 2296 irc_write(id->fd, buf, strlen(buf)); |
|
| 2297 if (c) { |
|
| 2298 gc->buddy_chats = g_slist_remove(gc->buddy_chats, c); |
|
| 2299 gaim_conversation_set_account(c, NULL); |
|
| 2300 g_snprintf(buf, sizeof(buf), _("You have left %s"), chan); |
|
| 2301 gaim_notify_info(gc, NULL, buf, _("IRC Part")); |
|
| 2302 } |
|
| 2303 } else if (!g_ascii_strcasecmp(pdibuf, "WHOIS")) { |
|
| 2304 g_snprintf(buf, sizeof(buf), "WHOIS %s\r\n", word_eol[2]); |
|
| 2305 irc_write(id->fd, buf, strlen(buf)); |
|
| 2306 } else if (!g_ascii_strcasecmp(pdibuf, "WHOWAS")) { |
|
| 2307 g_snprintf(buf, sizeof(buf), "WHOWAS %s\r\n", word_eol[2]); |
|
| 2308 irc_write(id->fd, buf, strlen(buf)); |
|
| 2309 } else if (!g_ascii_strcasecmp(pdibuf, "LIST")) { |
|
| 2310 g_snprintf(buf, sizeof(buf), "LIST\r\n"); |
|
| 2311 irc_write(id->fd, buf, strlen(buf)); |
|
| 2312 } else if (!g_ascii_strcasecmp(pdibuf, "QUIT")) { |
|
| 2313 char *reason = word_eol[2]; |
|
| 2314 id->str = g_string_insert(id->str, 0, reason); |
|
| 2315 gaim_core_quit(); |
|
| 2316 } else if (!g_ascii_strcasecmp(pdibuf, "VERSION")) { |
|
| 2317 g_snprintf(buf, sizeof(buf), "VERSION\r\n"); |
|
| 2318 irc_write(id->fd, buf, strlen(buf)); |
|
| 2319 } else if (!g_ascii_strcasecmp(pdibuf, "W")) { |
|
| 2320 g_snprintf(buf, sizeof(buf), "WHO *\r\n"); |
|
| 2321 irc_write(id->fd, buf, strlen(buf)); |
|
| 2322 } else if (!g_ascii_strcasecmp(pdibuf, "REHASH")) { |
|
| 2323 g_snprintf(buf, sizeof(buf), "REHASH\r\n"); |
|
| 2324 irc_write(id->fd, buf, strlen(buf)); |
|
| 2325 } else if (!g_ascii_strcasecmp(pdibuf, "RESTART")) { |
|
| 2326 g_snprintf(buf, sizeof(buf), "RESTART\r\n"); |
|
| 2327 irc_write(id->fd, buf, strlen(buf)); |
|
| 2328 } else if (!g_ascii_strcasecmp(pdibuf, "CTCP")) { |
|
| 2329 if (!g_ascii_strcasecmp(word[2], "CLIENTINFO")) { |
|
| 2330 if (word[3]) |
|
| 2331 irc_ctcp_clientinfo(gc, word[3]); |
|
| 2332 } else if (!g_ascii_strcasecmp(word[2], "USERINFO")) { |
|
| 2333 if (word[3]) |
|
| 2334 irc_ctcp_userinfo(gc, word[3]); |
|
| 2335 } else if (!g_ascii_strcasecmp(word[2], "VERSION")) { |
|
| 2336 if (word[3]) |
|
| 2337 irc_ctcp_version(gc, word[3]); |
|
| 2338 |
|
| 2339 } else if (!g_ascii_strcasecmp(word[2], "PING")) { |
|
| 2340 if (word[3]) |
|
| 2341 irc_ctcp_ping(gc, word[3]); |
|
| 2342 } |
|
| 2343 } else if (!g_ascii_strcasecmp(pdibuf, "DCC")) { |
|
| 2344 GaimConversation *c = NULL; |
|
| 2345 if (!g_ascii_strcasecmp(word[2], "CHAT")) { |
|
| 2346 if (word[3]) |
|
| 2347 irc_start_chat(gc, word[3]); |
|
| 2348 |
|
| 2349 if (is_channel(gc, who)) { |
|
| 2350 c = irc_find_chat(gc, who); |
|
| 2351 } else { |
|
| 2352 c = gaim_find_conversation(who); |
|
| 2353 } |
|
| 2354 if (c) { |
|
| 2355 gaim_conversation_write(c, NULL, |
|
| 2356 _("<I>Requesting DCC CHAT</I>"), |
|
| 2357 -1, WFLAG_SYSTEM, time(NULL)); |
|
| 2358 } |
|
| 2359 } |
|
| 2360 } else if (!g_ascii_strcasecmp(pdibuf, "HELP")) { |
|
| 2361 GaimConversation *c = NULL; |
|
| 2362 if (is_channel(gc, who)) { |
|
| 2363 c = irc_find_chat(gc, who); |
|
| 2364 } else { |
|
| 2365 c = gaim_find_conversation(who); |
|
| 2366 } |
|
| 2367 if (!c) { |
|
| 2368 g_free(what); |
|
| 2369 return -EINVAL; |
|
| 2370 } |
|
| 2371 if (!g_ascii_strcasecmp(word[2], "OPER")) { |
|
| 2372 gaim_conversation_write(c, NULL, |
|
| 2373 _("<B>Operator commands:<BR>" |
|
| 2374 "REHASH RESTART</B>"), |
|
| 2375 -1, WFLAG_NOLOG, time(NULL)); |
|
| 2376 } else if (!g_ascii_strcasecmp(word[2], "CTCP")) { |
|
| 2377 gaim_conversation_write(c, NULL, |
|
| 2378 _("<B>CTCP commands:<BR>" |
|
| 2379 "CLIENTINFO <nick><BR>" |
|
| 2380 "USERINFO <nick><BR>" |
|
| 2381 "VERSION <nick><BR>" |
|
| 2382 "PING <nick></B><BR>"), |
|
| 2383 -1, WFLAG_NOLOG, time(NULL)); |
|
| 2384 } else if (!g_ascii_strcasecmp(word[2], "DCC")) { |
|
| 2385 gaim_conversation_write(c, NULL, |
|
| 2386 _("<B>DCC commands:<BR>" |
|
| 2387 "CHAT <nick></B>"), |
|
| 2388 -1, WFLAG_NOLOG, time(NULL)); |
|
| 2389 } else { |
|
| 2390 gaim_conversation_write(c, NULL, |
|
| 2391 _("<B>Currently supported commands:<BR>" |
|
| 2392 "WHOIS INVITE NICK LIST<BR>" |
|
| 2393 "JOIN PART TOPIC KICK<BR>" |
|
| 2394 "OP DEOP VOICE DEVOICE<BR>" |
|
| 2395 "ME MSG QUOTE SAY QUIT<BR>" |
|
| 2396 "MODE VERSION W WHOWAS<BR>" |
|
| 2397 "Type /HELP OPER for operator commands<BR>" |
|
| 2398 "Type /HELP CTCP for CTCP commands<BR>" |
|
| 2399 "Type /HELP DCC for DCC commands"), |
|
| 2400 -1, WFLAG_NOLOG, time(NULL)); |
|
| 2401 } |
|
| 2402 } else { |
|
| 2403 GaimConversation *c = NULL; |
|
| 2404 if (is_channel(gc, who)) { |
|
| 2405 c = irc_find_chat(gc, who); |
|
| 2406 } else { |
|
| 2407 c = gaim_find_conversation(who); |
|
| 2408 } |
|
| 2409 if (!c) { |
|
| 2410 g_free(what); |
|
| 2411 return -EINVAL; |
|
| 2412 } |
|
| 2413 |
|
| 2414 gaim_conversation_write(c, NULL, _("<B>Unknown command</B>"), |
|
| 2415 -1, WFLAG_NOLOG, time(NULL)); |
|
| 2416 } |
|
| 2417 g_free(what); |
|
| 2418 return 0; |
|
| 2419 } |
|
| 2420 |
|
| 2421 static int |
|
| 2422 send_msg(GaimConnection *gc, const char *who, const char *what) |
|
| 2423 { |
|
| 2424 char *cr = strchr(what, '\n'); |
|
| 2425 if (cr) { |
|
| 2426 int ret = 0; |
|
| 2427 while (TRUE) { |
|
| 2428 if (cr) |
|
| 2429 *cr = 0; |
|
| 2430 ret = handle_command(gc, who, what); |
|
| 2431 if (!cr) |
|
| 2432 break; |
|
| 2433 what = cr + 1; |
|
| 2434 if (!*what) |
|
| 2435 break; |
|
| 2436 *cr = '\n'; |
|
| 2437 cr = strchr(what, '\n'); |
|
| 2438 } |
|
| 2439 return ret; |
|
| 2440 } else |
|
| 2441 return handle_command(gc, who, what); |
|
| 2442 } |
|
| 2443 |
|
| 2444 static void |
|
| 2445 irc_chat_invite(GaimConnection *gc, int idn, const char *message, const char *name) { |
|
| 2446 char buf[IRC_BUF_LEN]; |
|
| 2447 struct irc_data *id = gc->proto_data; |
|
| 2448 GaimConversation *c = gaim_find_chat(gc, idn); |
|
| 2449 g_snprintf(buf, sizeof(buf), "INVITE %s %s\r\n", name, c->name); |
|
| 2450 irc_write(id->fd, buf, strlen(buf)); |
|
| 2451 } |
|
| 2452 |
|
| 2453 static int |
|
| 2454 irc_send_im(GaimConnection *gc, const char *who, const char *what, int len, int flags) |
|
| 2455 { |
|
| 2456 if (*who == '@' || *who == '%' || *who == '+') |
|
| 2457 return send_msg(gc, who + 1, what); |
|
| 2458 return send_msg(gc, who, what); |
|
| 2459 } |
|
| 2460 |
|
| 2461 /* IRC doesn't have a buddy list, but we can still figure out who's online with ISON */ |
|
| 2462 static void |
|
| 2463 irc_add_buddy(GaimConnection *gc, const char *who) {} |
|
| 2464 static void |
|
| 2465 irc_remove_buddy(GaimConnection *gc, const char *who, const char *group) {} |
|
| 2466 |
|
| 2467 static GList * |
|
| 2468 irc_chat_info(GaimConnection *gc) |
|
| 2469 { |
118 { |
| 2470 GList *m = NULL; |
119 GList *m = NULL; |
| 2471 struct proto_chat_entry *pce; |
120 struct proto_chat_entry *pce; |
| 2472 |
121 |
| 2473 pce = g_new0(struct proto_chat_entry, 1); |
122 pce = g_new0(struct proto_chat_entry, 1); |
| 2476 m = g_list_append(m, pce); |
125 m = g_list_append(m, pce); |
| 2477 |
126 |
| 2478 pce = g_new0(struct proto_chat_entry, 1); |
127 pce = g_new0(struct proto_chat_entry, 1); |
| 2479 pce->label = _("Password:"); |
128 pce->label = _("Password:"); |
| 2480 pce->identifier = "password"; |
129 pce->identifier = "password"; |
| 2481 pce->secret = TRUE; |
|
| 2482 m = g_list_append(m, pce); |
130 m = g_list_append(m, pce); |
| 2483 |
131 |
| 2484 return m; |
132 return m; |
| 2485 } |
133 } |
| 2486 |
134 |
| 2487 static void |
135 static void irc_login(GaimAccount *account) |
| 2488 irc_join_chat(GaimConnection *gc, GHashTable *data) |
136 { |
| 2489 { |
137 GaimConnection *gc; |
| 2490 struct irc_data *id = gc->proto_data; |
138 struct irc_conn *irc; |
| 2491 char buf[IRC_BUF_LEN]; |
139 char *buf, **userparts; |
| 2492 char *name, *pass; |
140 const char *username = gaim_account_get_username(account); |
| 2493 |
141 int err; |
| 2494 if (!data) |
142 |
| 2495 return; |
143 gc = gaim_account_get_connection(account); |
| 2496 |
144 |
| 2497 name = g_hash_table_lookup(data, "channel"); |
145 gc->flags |= OPT_CONN_AUTO_RESP; |
| 2498 pass = g_hash_table_lookup(data, "password"); |
146 |
| 2499 if (pass) { |
147 gc->proto_data = irc = g_new0(struct irc_conn, 1); |
| 2500 g_snprintf(buf, sizeof(buf), "JOIN %s %s\r\n", name, pass); |
148 irc->account = account; |
| 2501 } else |
149 |
| 2502 g_snprintf(buf, sizeof(buf), "JOIN %s\r\n", name); |
150 userparts = g_strsplit(username, "@", 2); |
| 2503 irc_write(id->fd, buf, strlen(buf)); |
151 gaim_connection_set_display_name(gc, userparts[0]); |
| 2504 } |
152 irc->server = g_strdup(userparts[1]); |
| 2505 |
153 g_strfreev(userparts); |
| 2506 static void |
154 |
| 2507 irc_chat_leave(GaimConnection *gc, int id) |
155 irc->buddies = g_hash_table_new_full((GHashFunc)irc_nick_hash, (GEqualFunc)irc_nick_equal, |
| 2508 { |
156 NULL, (GDestroyNotify)irc_buddy_free); |
| 2509 struct irc_data *idata = gc->proto_data; |
157 irc->cmds = g_hash_table_new(g_str_hash, g_str_equal); |
| 2510 GaimConversation *c = gaim_find_chat(gc, id); |
158 irc_cmd_table_build(irc); |
| 2511 char buf[IRC_BUF_LEN]; |
159 irc->msgs = g_hash_table_new(g_str_hash, g_str_equal); |
| 2512 |
160 irc_msg_table_build(irc); |
| 2513 if (!c) return; |
161 |
| 2514 |
162 buf = g_strdup_printf(_("Signon: %s"), username); |
| 2515 g_snprintf(buf, sizeof(buf), "PART %s\r\n", c->name); |
163 gaim_connection_update_progress(gc, buf, 1, 2); |
| 2516 irc_write(idata->fd, buf, strlen(buf)); |
164 g_free(buf); |
| 2517 } |
165 |
| 2518 |
166 err = gaim_proxy_connect(account, irc->server, |
| 2519 static int |
167 gaim_account_get_int(account, "port", IRC_DEFAULT_PORT), |
| 2520 irc_chat_send(GaimConnection *gc, int id, const char *what) |
168 irc_login_cb, gc); |
| 2521 { |
169 |
| 2522 GaimConversation *c = gaim_find_chat(gc, id); |
170 if (err || !account->gc) { |
| 2523 if (!c) |
171 gaim_connection_error(gc, _("Couldn't create socket")); |
| 2524 return -EINVAL; |
172 return; |
| 2525 if (send_msg(gc, c->name, what) > 0) |
173 } |
| 2526 serv_got_chat_in(gc, gaim_chat_get_id(GAIM_CHAT(c)), |
174 } |
| 2527 gaim_connection_get_display_name(gc), 0, what, time(NULL)); |
175 |
| 2528 return 0; |
176 static void irc_login_cb(gpointer data, gint source, GaimInputCondition cond) |
| 2529 } |
177 { |
| 2530 |
178 GaimConnection *gc = data; |
| 2531 static GList * |
179 struct irc_conn *irc = gc->proto_data; |
| 2532 irc_away_states(GaimConnection *gc) |
180 char hostname[256]; |
| 2533 { |
181 char *buf; |
| 2534 return g_list_append(NULL, GAIM_AWAY_CUSTOM); |
182 GList *connections = gaim_connections_get_all(); |
| 2535 } |
183 |
| 2536 |
184 if (source < 0) |
| 2537 static void |
185 return; |
| 2538 irc_set_away(GaimConnection *gc, const char *state, const char *msg) |
186 |
| 2539 { |
187 if (!g_list_find(connections, gc)) { |
| 2540 struct irc_data *idata = gc->proto_data; |
188 close(source); |
| 2541 char buf[IRC_BUF_LEN]; |
189 return; |
| |
190 } |
| |
191 |
| |
192 irc->fd = source; |
| |
193 |
| |
194 if (gc->account->password && *gc->account->password) { |
| |
195 buf = irc_format(irc, "vv", "PASS", gc->account->password); |
| |
196 if (irc_send(irc, buf) < 0) { |
| |
197 gaim_connection_error(gc, "Error sending password"); |
| |
198 return; |
| |
199 } |
| |
200 g_free(buf); |
| |
201 } |
| |
202 |
| |
203 gethostname(hostname, sizeof(hostname)); |
| |
204 hostname[sizeof(hostname) - 1] = '\0'; |
| |
205 buf = irc_format(irc, "vvvv:", "USER", g_get_user_name(), hostname, irc->server, |
| |
206 gc->account->alias && *gc->account->alias ? gc->account->alias : IRC_DEFAULT_ALIAS); |
| |
207 if (irc_send(irc, buf) < 0) { |
| |
208 gaim_connection_error(gc, "Error registering with server"); |
| |
209 return; |
| |
210 } |
| |
211 g_free(buf); |
| |
212 buf = irc_format(irc, "vn", "NICK", gaim_connection_get_display_name(gc)); |
| |
213 if (irc_send(irc, buf) < 0) { |
| |
214 gaim_connection_error(gc, "Error sending nickname"); |
| |
215 return; |
| |
216 } |
| |
217 g_free(buf); |
| |
218 |
| |
219 gc->inpa = gaim_input_add(irc->fd, GAIM_INPUT_READ, irc_input_cb, gc); |
| |
220 } |
| |
221 |
| |
222 static void irc_close(GaimConnection *gc) |
| |
223 { |
| |
224 struct irc_conn *irc = gc->proto_data; |
| |
225 |
| |
226 irc_cmd_quit(irc, "quit", NULL, NULL); |
| |
227 |
| |
228 if (gc->inpa) |
| |
229 g_source_remove(gc->inpa); |
| |
230 |
| |
231 g_free(irc->inbuf); |
| |
232 close(irc->fd); |
| |
233 if (irc->timer) |
| |
234 g_source_remove(irc->timer); |
| |
235 g_hash_table_destroy(irc->cmds); |
| |
236 g_hash_table_destroy(irc->msgs); |
| |
237 if (irc->motd) |
| |
238 g_string_free(irc->motd, TRUE); |
| |
239 g_free(irc->server); |
| |
240 g_free(irc); |
| |
241 } |
| |
242 |
| |
243 static int irc_im_send(GaimConnection *gc, const char *who, const char *what, int len, int flags) |
| |
244 { |
| |
245 struct irc_conn *irc = gc->proto_data; |
| |
246 const char *args[2]; |
| |
247 |
| |
248 if (*who == '@' || *who == '%' || *who == '+') |
| |
249 args[0] = who + 1; |
| |
250 else |
| |
251 args[0] = who; |
| |
252 args[1] = what; |
| |
253 |
| |
254 if (*what == '/') { |
| |
255 return irc_parse_cmd(irc, who, what + 1); |
| |
256 } |
| |
257 |
| |
258 irc_cmd_privmsg(irc, "msg", NULL, args); |
| |
259 return 1; |
| |
260 } |
| |
261 |
| |
262 static void irc_get_info(GaimConnection *gc, const char *who) |
| |
263 { |
| |
264 struct irc_conn *irc = gc->proto_data; |
| |
265 const char *args[1]; |
| |
266 args[0] = who; |
| |
267 irc_cmd_whois(irc, "whois", NULL, args); |
| |
268 } |
| |
269 |
| |
270 static void irc_set_away(GaimConnection *gc, const char *state, const char *msg) |
| |
271 { |
| |
272 struct irc_conn *irc = gc->proto_data; |
| |
273 const char *args[1]; |
| 2542 |
274 |
| 2543 if (gc->away) { |
275 if (gc->away) { |
| 2544 g_free(gc->away); |
276 g_free(gc->away); |
| 2545 gc->away = NULL; |
277 gc->away = NULL; |
| 2546 } |
278 } |
| 2547 |
279 |
| 2548 if (msg) { |
280 if (msg) |
| 2549 g_snprintf(buf, sizeof(buf), "AWAY :%s\r\n", msg); |
|
| 2550 gc->away = g_strdup(msg); |
281 gc->away = g_strdup(msg); |
| 2551 } else |
282 |
| 2552 g_snprintf(buf, sizeof(buf), "AWAY\r\n"); |
283 args[0] = msg; |
| 2553 |
284 irc_cmd_away(irc, "away", NULL, args); |
| 2554 irc_write(idata->fd, buf, strlen(buf)); |
285 } |
| 2555 } |
286 |
| 2556 |
287 static void irc_add_buddy(GaimConnection *gc, const char *who) |
| 2557 static const char * |
288 { |
| 2558 irc_list_icon(GaimAccount *a, struct buddy *b) |
289 struct irc_conn *irc = (struct irc_conn *)gc->proto_data; |
| 2559 { |
290 struct irc_buddy *ib = g_new0(struct irc_buddy, 1); |
| 2560 return "irc"; |
291 ib->name = g_strdup(who); |
| 2561 } |
292 g_hash_table_insert(irc->buddies, ib->name, ib); |
| 2562 |
293 } |
| 2563 static void irc_list_emblems(struct buddy *b, char **se, char **sw, char **nw, char **ne) |
294 |
| 2564 { |
295 static void irc_remove_buddy(GaimConnection *gc, const char *who, const char *group) |
| 2565 if (b->present == GAIM_BUDDY_OFFLINE) |
296 { |
| 2566 *se = "offline"; |
297 struct irc_conn *irc = (struct irc_conn *)gc->proto_data; |
| 2567 } |
298 g_hash_table_remove(irc->buddies, who); |
| 2568 |
299 } |
| 2569 static void |
300 |
| 2570 dcc_chat_connected(gpointer data, gint source, GdkInputCondition condition) |
301 |
| 2571 { |
302 static void irc_input_cb(gpointer data, gint source, GaimInputCondition cond) |
| 2572 struct dcc_chat *chat = data; |
303 { |
| 2573 GaimConversation *convo; |
304 GaimConnection *gc = data; |
| 2574 char buf[128]; |
305 struct irc_conn *irc = gc->proto_data; |
| 2575 struct sockaddr_in addr; |
306 char *cur, *end; |
| 2576 int addrlen = sizeof (addr); |
|
| 2577 addr.sin_family = AF_INET; |
|
| 2578 addr.sin_port = htons (chat->port); |
|
| 2579 addr.sin_addr.s_addr = INADDR_ANY; |
|
| 2580 chat->fd = accept (chat->fd, (struct sockaddr *) (&addr), &addrlen); |
|
| 2581 if (!chat->fd) { |
|
| 2582 dcc_chat_cancel (chat); |
|
| 2583 convo = gaim_conversation_new(GAIM_CONV_IM, chat->gc->account, |
|
| 2584 chat->nick); |
|
| 2585 g_snprintf (buf, sizeof buf, _("DCC Chat with %s closed"), |
|
| 2586 chat->nick); |
|
| 2587 gaim_conversation_write(convo, NULL, buf, -1, |
|
| 2588 WFLAG_SYSTEM, time(NULL)); |
|
| 2589 return; |
|
| 2590 } |
|
| 2591 chat->inpa = |
|
| 2592 gaim_input_add (chat->fd, GAIM_INPUT_READ, dcc_chat_in, chat); |
|
| 2593 convo = gaim_conversation_new(GAIM_CONV_IM, chat->gc->account, chat->nick); |
|
| 2594 g_snprintf (buf, sizeof buf, _("DCC Chat with %s established"), |
|
| 2595 chat->nick); |
|
| 2596 gaim_conversation_write(convo, NULL, buf, -1, WFLAG_SYSTEM, time(NULL)); |
|
| 2597 gaim_debug(GAIM_DEBUG_INFO, "irc", |
|
| 2598 "Chat with %s established\n", chat->nick); |
|
| 2599 dcc_chat_list = g_slist_append (dcc_chat_list, chat); |
|
| 2600 } |
|
| 2601 #if 0 |
|
| 2602 static void |
|
| 2603 irc_ask_send_file(GaimConnection *gc, char *destsn) { |
|
| 2604 struct irc_data *id = (struct irc_data *)gc->proto_data; |
|
| 2605 struct irc_file_transfer *ift = g_new0(struct irc_file_transfer, 1); |
|
| 2606 char *localip = (char *)malloc(12); |
|
| 2607 |
|
| 2608 if (getlocalip(localip) == -1) { |
|
| 2609 free(localip); |
|
| 2610 return; |
|
| 2611 } |
|
| 2612 |
|
| 2613 ift->type = IFT_SENDFILE_OUT; |
|
| 2614 ift->sn = g_strdup(destsn); |
|
| 2615 ift->gc = gc; |
|
| 2616 snprintf(ift->ip, sizeof(ift->ip), "%s", localip); |
|
| 2617 id->file_transfers = g_slist_append(id->file_transfers, ift); |
|
| 2618 |
|
| 2619 ift->xfer = transfer_out_add(gc, ift->sn); |
|
| 2620 } |
|
| 2621 |
|
| 2622 static struct |
|
| 2623 irc_file_transfer *find_ift_by_xfer(GaimConnection *gc, |
|
| 2624 struct file_transfer *xfer) { |
|
| 2625 |
|
| 2626 GSList *g = ((struct irc_data *)gc->proto_data)->file_transfers; |
|
| 2627 struct irc_file_transfer *f = NULL; |
|
| 2628 |
|
| 2629 while (g) { |
|
| 2630 f = (struct irc_file_transfer *)g->data; |
|
| 2631 if (f->xfer == xfer) |
|
| 2632 break; |
|
| 2633 g = g->next; |
|
| 2634 f = NULL; |
|
| 2635 } |
|
| 2636 |
|
| 2637 return f; |
|
| 2638 } |
|
| 2639 |
|
| 2640 static void |
|
| 2641 irc_file_transfer_data_chunk(GaimConnection *gc, struct file_transfer *xfer, const char *data, int len) { |
|
| 2642 struct irc_file_transfer *ift = find_ift_by_xfer(gc, xfer); |
|
| 2643 guint32 pos; |
|
| 2644 |
|
| 2645 ift->cur += len; |
|
| 2646 pos = htonl(ift->cur); |
|
| 2647 write(ift->fd, (char *)&pos, 4); |
|
| 2648 |
|
| 2649 // FIXME: You should check to verify that they received the data when |
|
| 2650 // you are sending a file ... |
|
| 2651 } |
|
| 2652 |
|
| 2653 static void |
|
| 2654 irc_file_transfer_cancel (GaimConnection *gc, struct file_transfer *xfer) { |
|
| 2655 struct irc_data *id = (struct irc_data *)gc->proto_data; |
|
| 2656 struct irc_file_transfer *ift = find_ift_by_xfer(gc, xfer); |
|
| 2657 |
|
| 2658 printf("Our shit got canceled, yo!\n"); |
|
| 2659 |
|
| 2660 /* Remove the FT from our list of transfers */ |
|
| 2661 id->file_transfers = g_slist_remove(id->file_transfers, ift); |
|
| 2662 |
|
| 2663 gaim_input_remove(ift->watcher); |
|
| 2664 |
|
| 2665 /* Close our FT because we're done */ |
|
| 2666 close(ift->fd); |
|
| 2667 |
|
| 2668 g_free(ift->sn); |
|
| 2669 g_free(ift->name); |
|
| 2670 |
|
| 2671 g_free(ift); |
|
| 2672 } |
|
| 2673 |
|
| 2674 static void |
|
| 2675 irc_file_transfer_done(GaimConnection *gc, struct file_transfer *xfer) { |
|
| 2676 struct irc_data *id = (struct irc_data *)gc->proto_data; |
|
| 2677 struct irc_file_transfer *ift = find_ift_by_xfer(gc, xfer); |
|
| 2678 |
|
| 2679 |
|
| 2680 printf("Our shit be done, yo.\n"); |
|
| 2681 |
|
| 2682 /* Remove the FT from our list of transfers */ |
|
| 2683 id->file_transfers = g_slist_remove(id->file_transfers, ift); |
|
| 2684 |
|
| 2685 gaim_input_remove(ift->watcher); |
|
| 2686 |
|
| 2687 /* Close our FT because we're done */ |
|
| 2688 close(ift->fd); |
|
| 2689 |
|
| 2690 g_free(ift->sn); |
|
| 2691 g_free(ift->name); |
|
| 2692 |
|
| 2693 g_free(ift); |
|
| 2694 } |
|
| 2695 |
|
| 2696 static void |
|
| 2697 irc_file_transfer_out (GaimConnection *gc, struct file_transfer *xfer, const char *name, int totfiles, int totsize) { |
|
| 2698 struct irc_file_transfer *ift = find_ift_by_xfer(gc, xfer); |
|
| 2699 struct sockaddr_in addr; |
|
| 2700 char buf[IRC_BUF_LEN]; |
|
| 2701 int len; |
307 int len; |
| 2702 |
308 |
| 2703 |
309 if (irc->inbuflen < irc->inbufused + IRC_INITIAL_BUFSIZE) { |
| 2704 ift->fd = socket (AF_INET, SOCK_STREAM, 0); |
310 irc->inbuflen += IRC_INITIAL_BUFSIZE; |
| 2705 addr.sin_family = AF_INET; |
311 irc->inbuf = g_realloc(irc->inbuf, irc->inbuflen); |
| 2706 addr.sin_port = 0; |
312 } |
| 2707 addr.sin_addr.s_addr = INADDR_ANY; |
313 |
| 2708 bind (ift->fd, (struct sockaddr *) &addr, sizeof(addr)); |
314 if ((len = read(irc->fd, irc->inbuf + irc->inbufused, IRC_INITIAL_BUFSIZE - 1)) < 0) { |
| 2709 listen(ift->fd, 1); |
315 gaim_connection_error(gc, "Read error"); |
| 2710 |
316 return; |
| 2711 len = sizeof(addr); |
317 } |
| 2712 getsockname (ift->fd, (struct sockaddr *) &addr, &len); |
318 irc->inbufused += len; |
| 2713 |
319 irc->inbuf[irc->inbufused] = '\0'; |
| 2714 ift->port = ntohs(addr.sin_port); |
320 |
| 2715 |
321 for (cur = irc->inbuf; cur < irc->inbuf + irc->inbufused && (end = strstr(cur, "\r\n")); cur = end + 2) { |
| 2716 ift->watcher = gaim_input_add (ift->fd, GAIM_INPUT_READ, dcc_send_callback, ift); |
322 *end = '\0'; |
| 2717 printf("watcher is %d\n", ift->watcher); |
323 irc_parse_msg(irc, cur); |
| 2718 |
324 } |
| 2719 snprintf(buf, sizeof(buf), "\001DCC SEND %s %s %d %d\001\n", name, ift->ip, ift->port, totsize); |
325 if (cur != irc->inbuf + irc->inbufused) { /* leftover */ |
| 2720 printf("Trying: %s\n", buf); |
326 irc->inbufused -= (cur - irc->inbuf); |
| 2721 irc_send_im (gc, ift->sn, buf, -1, 0); |
327 memmove(irc->inbuf, cur, irc->inbufused); |
| 2722 } |
328 } else { |
| 2723 |
329 irc->inbufused = 0; |
| 2724 static void |
330 } |
| 2725 irc_file_transfer_in(GaimConnection *gc, |
331 } |
| 2726 struct file_transfer *xfer, int offset) { |
332 |
| 2727 |
333 static void irc_chat_join (GaimConnection *gc, GHashTable *data) |
| 2728 struct irc_file_transfer *ift = find_ift_by_xfer(gc, xfer); |
334 { |
| 2729 |
335 struct irc_conn *irc = gc->proto_data; |
| 2730 ift->xfer = xfer; |
336 const char *args[2]; |
| 2731 gaim_proxy_connect(gc->account, ift->ip, ift->port, dcc_recv_callback, ift); |
337 |
| 2732 } |
338 args[0] = g_hash_table_lookup(data, "channel"); |
| 2733 #endif |
339 args[1] = g_hash_table_lookup(data, "password"); |
| 2734 |
340 irc_cmd_join(irc, "join", NULL, args); |
| 2735 static void |
341 } |
| 2736 irc_ctcp_clientinfo(GaimConnection *gc, const char *who) |
342 |
| 2737 { |
343 static void irc_chat_invite(GaimConnection *gc, int id, const char *message, const char *name) |
| 2738 char buf[IRC_BUF_LEN]; |
344 { |
| 2739 |
345 struct irc_conn *irc = gc->proto_data; |
| 2740 snprintf (buf, sizeof buf, "\001CLIENTINFO\001"); |
346 GaimConversation *convo = gaim_find_chat(gc, id); |
| 2741 irc_send_privmsg(gc, who, buf, FALSE); |
347 const char *args[2]; |
| 2742 } |
348 |
| 2743 |
349 if (!convo) { |
| 2744 static void |
350 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Got chat invite request for bogus chat\n"); |
| 2745 irc_ctcp_userinfo(GaimConnection *gc, const char *who) |
351 return; |
| 2746 { |
352 } |
| 2747 char buf[IRC_BUF_LEN]; |
353 args[0] = name; |
| 2748 |
354 args[1] = gaim_conversation_get_name(convo); |
| 2749 snprintf (buf, sizeof buf, "\001USERINFO\001"); |
355 irc_cmd_invite(irc, "invite", gaim_conversation_get_name(convo), args); |
| 2750 irc_send_privmsg(gc, who, buf, FALSE); |
356 } |
| 2751 } |
357 |
| 2752 |
358 |
| 2753 static void |
359 static void irc_chat_leave (GaimConnection *gc, int id) |
| 2754 irc_ctcp_version(GaimConnection *gc, const char *who) |
360 { |
| 2755 { |
361 struct irc_conn *irc = gc->proto_data; |
| 2756 char buf[IRC_BUF_LEN]; |
362 GaimConversation *convo = gaim_find_chat(gc, id); |
| 2757 |
363 const char *args[2]; |
| 2758 snprintf (buf, sizeof buf, "\001VERSION\001"); |
364 |
| 2759 irc_send_privmsg(gc, who, buf, FALSE); |
365 if (!convo) |
| 2760 } |
366 return; |
| 2761 |
367 |
| 2762 static void |
368 args[0] = gaim_conversation_get_name(convo); |
| 2763 irc_ctcp_ping(GaimConnection *gc, const char *who) |
369 args[1] = NULL; |
| 2764 { |
370 irc_cmd_part(irc, "part", gaim_conversation_get_name(convo), args); |
| 2765 char buf[IRC_BUF_LEN]; |
371 serv_got_chat_left(gc, id); |
| 2766 struct timeval now; |
372 } |
| 2767 |
373 |
| 2768 gettimeofday(&now, NULL); |
374 static int irc_chat_send(GaimConnection *gc, int id, const char *what) |
| 2769 g_snprintf (buf, sizeof(buf), "\001PING %lu.%.03lu\001", now.tv_sec, |
375 { |
| 2770 now.tv_usec/1000); |
376 struct irc_conn *irc = gc->proto_data; |
| 2771 irc_send_privmsg(gc, who, buf, FALSE); |
377 GaimConversation *convo = gaim_find_chat(gc, id); |
| 2772 } |
378 const char *args[2]; |
| 2773 |
379 |
| 2774 static void |
380 if (!convo) { |
| 2775 irc_send_notice(GaimConnection *gc, char *who, char *what) |
381 gaim_debug(GAIM_DEBUG_ERROR, "irc", "chat send on nonexistent chat\n"); |
| 2776 { |
382 return -EINVAL; |
| 2777 char buf[IRC_BUF_LEN], *intl; |
383 } |
| 2778 struct irc_data *id = gc->proto_data; |
384 |
| 2779 int len; |
385 if (*what == '/') { |
| 2780 |
386 return irc_parse_cmd(irc, convo->name, what + 1); |
| 2781 intl = irc_send_convert(gc, what, 501, &len); |
387 } |
| 2782 g_snprintf(buf, sizeof(buf), "NOTICE %s :%s\r\n", who, intl); |
388 |
| 2783 g_free(intl); |
389 args[0] = convo->name; |
| 2784 irc_write(id->fd, buf, strlen(buf)); |
390 args[1] = what; |
| 2785 } |
391 |
| 2786 |
392 irc_cmd_privmsg(irc, "msg", NULL, args); |
| 2787 /* Don't call this guy with fragment = 1 for anything but straight |
393 serv_got_chat_in(gc, id, gaim_connection_get_display_name(gc), 0, what, time(NULL)); |
| 2788 * up privmsgs. (no CTCP/whatever) It's still dangerous for CTCPs |
394 return 0; |
| 2789 * (it might not include the trailing \001), but I think this behavior |
395 } |
| 2790 * is generally better than not fragmenting at all on lots of our |
396 |
| 2791 * packets. */ |
397 static guint irc_nick_hash(const char *nick) |
| 2792 /* From RFC2812: |
398 { |
| 2793 * IRC messages are always lines of characters terminated with a CR-LF |
399 char *lc; |
| 2794 * (Carriage Return - Line Feed) pair, and these messages SHALL NOT |
400 guint bucket; |
| 2795 * exceed 512 characters in length, counting all characters including |
401 |
| 2796 * the trailing CR-LF. Thus, there are 510 characters maximum allowed |
402 lc = g_utf8_strdown(nick, -1); |
| 2797 * for the command and its parameters. */ |
403 bucket = g_str_hash(lc); |
| 2798 /* So apparently that includes all the inter-server crap, which is up |
404 g_free(lc); |
| 2799 * to NINETY-THREE chars on dancer, which seems to be a pretty liberal |
405 |
| 2800 * ircd. My rough calculation for now is ":<nick>!~<user>@<host> ", |
406 return bucket; |
| 2801 * where <host> is a max of an (uncalculated) 63 chars. Thanks to |
407 } |
| 2802 * trelane and #freenode for giving a hand here. */ |
408 |
| 2803 static void |
409 static gboolean irc_nick_equal(const char *nick1, const char *nick2) |
| 2804 irc_send_privmsg(GaimConnection *gc, const char *who, const char *what, gboolean fragment) |
410 { |
| 2805 { |
411 return (gaim_utf8_strcasecmp(nick1, nick2) == 0); |
| 2806 char buf[IRC_BUF_LEN], *intl; |
412 } |
| 2807 struct irc_data *id = gc->proto_data; |
413 |
| 2808 /* 512 - 12 (for PRIVMSG" "" :""\r\n") - namelen - nicklen - 68 */ |
414 static void irc_buddy_free(struct irc_buddy *ib) |
| 2809 int nicklen = (gc->account->alias && strlen(gc->account->alias)) ? strlen(gc->account->alias) : 4; |
415 { |
| 2810 int max = 444 - strlen(who) - strlen(g_get_user_name()) - nicklen; |
416 g_free(ib->name); |
| 2811 |
417 g_free(ib); |
| 2812 int len; |
|
| 2813 |
|
| 2814 do { |
|
| 2815 /* the \001 on CTCPs may cause a problem here for some |
|
| 2816 * charsets, but probably not ones people use for IRC. */ |
|
| 2817 intl = irc_send_convert(gc, what, max, &len); |
|
| 2818 g_snprintf(buf, sizeof(buf), "PRIVMSG %s :%s\r\n", who, intl); |
|
| 2819 g_free(intl); |
|
| 2820 irc_write(id->fd, buf, strlen(buf)); |
|
| 2821 what += len; |
|
| 2822 } while (fragment && strlen(what)); |
|
| 2823 } |
|
| 2824 |
|
| 2825 static void |
|
| 2826 irc_start_chat(GaimConnection *gc, const char *who) { |
|
| 2827 struct dcc_chat *chat; |
|
| 2828 int len; |
|
| 2829 struct sockaddr_in addr; |
|
| 2830 char buf[IRC_BUF_LEN]; |
|
| 2831 const char *ip; |
|
| 2832 |
|
| 2833 /* Create a socket */ |
|
| 2834 chat = g_new0 (struct dcc_chat, 1); |
|
| 2835 chat->fd = socket (AF_INET, SOCK_STREAM, 0); |
|
| 2836 chat->gc = gc; |
|
| 2837 g_snprintf (chat->nick, sizeof (chat->nick), "%s", who); |
|
| 2838 if (chat->fd < 0) { |
|
| 2839 dcc_chat_cancel (chat); |
|
| 2840 return; |
|
| 2841 } |
|
| 2842 addr.sin_family = AF_INET; |
|
| 2843 addr.sin_port = 0; |
|
| 2844 addr.sin_addr.s_addr = INADDR_ANY; |
|
| 2845 bind (chat->fd, (struct sockaddr *) &addr, sizeof (addr)); |
|
| 2846 listen (chat->fd, 1); |
|
| 2847 len = sizeof (addr); |
|
| 2848 getsockname (chat->fd, (struct sockaddr *) &addr, &len); |
|
| 2849 chat->port = ntohs (addr.sin_port); |
|
| 2850 |
|
| 2851 ip = gaim_xfers_get_ip_for_account(gaim_connection_get_account(gc)); |
|
| 2852 strncpy(chat->ip_address, ip, INET6_ADDRSTRLEN); |
|
| 2853 |
|
| 2854 chat->inpa = |
|
| 2855 gaim_input_add (chat->fd, GAIM_INPUT_READ, dcc_chat_connected, |
|
| 2856 chat); |
|
| 2857 g_snprintf (buf, sizeof buf, "\001DCC CHAT chat %s %d\001\n", |
|
| 2858 chat->ip_address, chat->port); |
|
| 2859 irc_send_im (gc, who, buf, -1, 0); |
|
| 2860 } |
|
| 2861 |
|
| 2862 static void |
|
| 2863 irc_get_info(GaimConnection *gc, const char *who) |
|
| 2864 { |
|
| 2865 struct irc_data *idata = gc->proto_data; |
|
| 2866 char buf[IRC_BUF_LEN]; |
|
| 2867 |
|
| 2868 if (*who == '@') |
|
| 2869 who++; |
|
| 2870 if (*who == '%') |
|
| 2871 who++; |
|
| 2872 if (*who == '+') |
|
| 2873 who++; |
|
| 2874 |
|
| 2875 g_snprintf(buf, sizeof(buf), "WHOIS %s\r\n", who); |
|
| 2876 irc_write(idata->fd, buf, strlen(buf)); |
|
| 2877 } |
|
| 2878 |
|
| 2879 static GList * |
|
| 2880 irc_buddy_menu(GaimConnection *gc, const char *who) |
|
| 2881 { |
|
| 2882 GList *m = NULL; |
|
| 2883 struct proto_buddy_menu *pbm; |
|
| 2884 |
|
| 2885 pbm = g_new0(struct proto_buddy_menu, 1); |
|
| 2886 pbm->label = _("DCC Chat"); |
|
| 2887 pbm->callback = irc_start_chat; |
|
| 2888 pbm->gc = gc; |
|
| 2889 m = g_list_append(m, pbm); |
|
| 2890 /* |
|
| 2891 pbm = g_new0(struct proto_buddy_menu, 1); |
|
| 2892 pbm->label = _("DCC Send"); |
|
| 2893 pbm->callback = irc_ask_send_file; |
|
| 2894 pbm->gc = gc; |
|
| 2895 m = g_list_append(m, pbm); |
|
| 2896 */ |
|
| 2897 |
|
| 2898 pbm = g_new0(struct proto_buddy_menu, 1); |
|
| 2899 pbm->label = _("CTCP ClientInfo"); |
|
| 2900 pbm->callback = irc_ctcp_clientinfo; |
|
| 2901 pbm->gc = gc; |
|
| 2902 m = g_list_append(m, pbm); |
|
| 2903 |
|
| 2904 pbm = g_new0(struct proto_buddy_menu, 1); |
|
| 2905 pbm->label = _("CTCP UserInfo"); |
|
| 2906 pbm->callback = irc_ctcp_userinfo; |
|
| 2907 pbm->gc = gc; |
|
| 2908 m = g_list_append(m, pbm); |
|
| 2909 |
|
| 2910 pbm = g_new0(struct proto_buddy_menu, 1); |
|
| 2911 pbm->label = _("CTCP Version"); |
|
| 2912 pbm->callback = irc_ctcp_version; |
|
| 2913 pbm->gc = gc; |
|
| 2914 m = g_list_append(m, pbm); |
|
| 2915 |
|
| 2916 pbm = g_new0(struct proto_buddy_menu, 1); |
|
| 2917 pbm->label = _("CTCP Ping"); |
|
| 2918 pbm->callback = irc_ctcp_ping; |
|
| 2919 pbm->gc = gc; |
|
| 2920 m = g_list_append(m, pbm); |
|
| 2921 |
|
| 2922 return m; |
|
| 2923 } |
418 } |
| 2924 |
419 |
| 2925 static GaimPluginProtocolInfo prpl_info = |
420 static GaimPluginProtocolInfo prpl_info = |
| 2926 { |
421 { |
| 2927 GAIM_PROTO_IRC, |
422 GAIM_PROTO_IRC, |
| 2928 OPT_PROTO_CHAT_TOPIC | OPT_PROTO_PASSWORD_OPTIONAL, |
423 OPT_PROTO_CHAT_TOPIC | OPT_PROTO_PASSWORD_OPTIONAL, |
| 2929 NULL, |
424 NULL, |
| 2930 NULL, |
425 NULL, |
| 2931 irc_list_icon, |
426 irc_blist_icon, |
| 2932 irc_list_emblems, |
427 irc_blist_emblems, |
| 2933 NULL, |
428 NULL, |
| 2934 NULL, |
429 NULL, |
| 2935 irc_away_states, |
430 irc_away_states, |
| 2936 NULL, |
431 NULL, |
| 2937 irc_buddy_menu, |
432 NULL, /*irc_buddy_menu,*/ |
| 2938 irc_chat_info, |
433 irc_chat_join_info, |
| 2939 irc_login, |
434 irc_login, |
| 2940 irc_close, |
435 irc_close, |
| 2941 irc_send_im, |
436 irc_im_send, |
| 2942 NULL, |
437 NULL, |
| 2943 NULL, |
438 NULL, |
| 2944 irc_get_info, |
439 irc_get_info, |
| 2945 irc_set_away, |
440 irc_set_away, |
| 2946 NULL, |
441 NULL, |