| 1 /* |
|
| 2 * purple |
|
| 3 * |
|
| 4 * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net> |
|
| 5 * |
|
| 6 * This program is free software; you can redistribute it and/or modify |
|
| 7 * it under the terms of the GNU General Public License as published by |
|
| 8 * the Free Software Foundation; either version 2 of the License, or |
|
| 9 * (at your option) any later version. |
|
| 10 * |
|
| 11 * This program is distributed in the hope that it will be useful, |
|
| 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 14 * GNU General Public License for more details. |
|
| 15 * |
|
| 16 * You should have received a copy of the GNU General Public License |
|
| 17 * along with this program; if not, write to the Free Software |
|
| 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA |
|
| 19 * |
|
| 20 */ |
|
| 21 #include "internal.h" |
|
| 22 |
|
| 23 #include "account.h" |
|
| 24 #include "accountopt.h" |
|
| 25 #include "conversation.h" |
|
| 26 #include "debug.h" |
|
| 27 #include "notify.h" |
|
| 28 #include "privacy.h" |
|
| 29 #include "proxy.h" |
|
| 30 #include "prpl.h" |
|
| 31 #include "request.h" |
|
| 32 #include "util.h" |
|
| 33 #include "version.h" |
|
| 34 |
|
| 35 static PurplePlugin *my_protocol = NULL; |
|
| 36 |
|
| 37 #define REVISION "penguin" |
|
| 38 |
|
| 39 #define TYPE_SIGNON 1 |
|
| 40 #define TYPE_DATA 2 |
|
| 41 #define TYPE_ERROR 3 |
|
| 42 #define TYPE_SIGNOFF 4 |
|
| 43 #define TYPE_KEEPALIVE 5 |
|
| 44 |
|
| 45 #define FLAPON "FLAPON\r\n\r\n" |
|
| 46 #define ROAST "Tic/Toc" |
|
| 47 |
|
| 48 #define TOC_HOST "toc.oscar.aol.com" |
|
| 49 #define TOC_PORT 9898 |
|
| 50 #define AUTH_HOST "login.oscar.aol.com" |
|
| 51 #define AUTH_PORT 5190 |
|
| 52 #define LANGUAGE "english" |
|
| 53 |
|
| 54 #define STATE_OFFLINE 0 |
|
| 55 #define STATE_FLAPON 1 |
|
| 56 #define STATE_SIGNON_REQUEST 2 |
|
| 57 #define STATE_ONLINE 3 |
|
| 58 #define STATE_PAUSE 4 |
|
| 59 |
|
| 60 #define VOICE_UID "09461341-4C7F-11D1-8222-444553540000" |
|
| 61 #define FILE_SEND_UID "09461343-4C7F-11D1-8222-444553540000" |
|
| 62 #define IMAGE_UID "09461345-4C7F-11D1-8222-444553540000" |
|
| 63 #define B_ICON_UID "09461346-4C7F-11D1-8222-444553540000" |
|
| 64 #define STOCKS_UID "09461347-4C7F-11D1-8222-444553540000" |
|
| 65 #define FILE_GET_UID "09461348-4C7F-11D1-8222-444553540000" |
|
| 66 #define GAMES_UID "0946134a-4C7F-11D1-8222-444553540000" |
|
| 67 |
|
| 68 #define UC_UNAVAILABLE 0x01 |
|
| 69 #define UC_AOL 0x02 |
|
| 70 #define UC_ADMIN 0x04 |
|
| 71 #define UC_UNCONFIRMED 0x08 |
|
| 72 #define UC_NORMAL 0x10 |
|
| 73 #define UC_WIRELESS 0x20 |
|
| 74 |
|
| 75 struct ft_request { |
|
| 76 PurpleConnection *gc; |
|
| 77 char *user; |
|
| 78 char UID[2048]; |
|
| 79 char *cookie; |
|
| 80 char *ip; |
|
| 81 int port; |
|
| 82 char *message; |
|
| 83 char *filename; |
|
| 84 int files; |
|
| 85 int size; |
|
| 86 }; |
|
| 87 |
|
| 88 struct buddy_icon { |
|
| 89 guint32 hash; |
|
| 90 guint32 len; |
|
| 91 time_t time; |
|
| 92 void *data; |
|
| 93 }; |
|
| 94 |
|
| 95 struct toc_data { |
|
| 96 int toc_fd; |
|
| 97 char toc_ip[20]; |
|
| 98 int seqno; |
|
| 99 int state; |
|
| 100 }; |
|
| 101 |
|
| 102 struct sflap_hdr { |
|
| 103 unsigned char ast; |
|
| 104 unsigned char type; |
|
| 105 unsigned short seqno; |
|
| 106 unsigned short len; |
|
| 107 }; |
|
| 108 |
|
| 109 struct signon { |
|
| 110 unsigned int ver; |
|
| 111 unsigned short tag; |
|
| 112 unsigned short namelen; |
|
| 113 char username[80]; |
|
| 114 }; |
|
| 115 |
|
| 116 /* constants to identify proto_opts */ |
|
| 117 #define USEROPT_AUTH 0 |
|
| 118 #define USEROPT_AUTHPORT 1 |
|
| 119 |
|
| 120 #define TOC_CONNECT_STEPS 3 |
|
| 121 |
|
| 122 static void toc_login_callback(gpointer, gint, const gchar *); |
|
| 123 static void toc_callback(gpointer, gint, PurpleInputCondition); |
|
| 124 |
|
| 125 /* ok. this function used to take username/password, and return 0 on success. |
|
| 126 * now, it takes username/password, and returns NULL on error or a new purple_connection |
|
| 127 * on success. */ |
|
| 128 static void toc_login(PurpleAccount *account) |
|
| 129 { |
|
| 130 PurpleConnection *gc; |
|
| 131 struct toc_data *tdt; |
|
| 132 char buf[80]; |
|
| 133 |
|
| 134 gc = purple_account_get_connection(account); |
|
| 135 gc->proto_data = tdt = g_new0(struct toc_data, 1); |
|
| 136 gc->flags |= PURPLE_CONNECTION_HTML; |
|
| 137 gc->flags |= PURPLE_CONNECTION_AUTO_RESP; |
|
| 138 |
|
| 139 g_snprintf(buf, sizeof buf, _("Looking up %s"), |
|
| 140 purple_account_get_string(account, "server", TOC_HOST)); |
|
| 141 purple_connection_update_progress(gc, buf, 0, TOC_CONNECT_STEPS); |
|
| 142 |
|
| 143 purple_debug(PURPLE_DEBUG_INFO, "toc", "Client connects to TOC\n"); |
|
| 144 if (purple_proxy_connect(gc, account, |
|
| 145 purple_account_get_string(account, "server", TOC_HOST), |
|
| 146 purple_account_get_int(account, "port", TOC_PORT), |
|
| 147 toc_login_callback, gc) != 0 || !account->gc) { |
|
| 148 g_snprintf(buf, sizeof(buf), _("Connect to %s failed"), |
|
| 149 purple_account_get_string(account, "server", TOC_HOST)); |
|
| 150 purple_connection_error(gc, buf); |
|
| 151 return; |
|
| 152 } |
|
| 153 } |
|
| 154 |
|
| 155 static void toc_login_callback(gpointer data, gint source, const gchar *error_message) |
|
| 156 { |
|
| 157 PurpleConnection *gc = data; |
|
| 158 struct toc_data *tdt; |
|
| 159 char buf[80]; |
|
| 160 struct sockaddr_in name; |
|
| 161 socklen_t namelen; |
|
| 162 |
|
| 163 if (!PURPLE_CONNECTION_IS_VALID(gc)) { |
|
| 164 if (source >= 0) |
|
| 165 close(source); |
|
| 166 return; |
|
| 167 } |
|
| 168 |
|
| 169 tdt = gc->proto_data; |
|
| 170 |
|
| 171 if (source == -1) { |
|
| 172 /* we didn't successfully connect. tdt->toc_fd is valid here */ |
|
| 173 purple_connection_error(gc, _("Unable to connect.")); |
|
| 174 return; |
|
| 175 } |
|
| 176 tdt->toc_fd = source; |
|
| 177 |
|
| 178 /* |
|
| 179 * Copy the IP that we're connected to. We need this because "GOTO_URL"'s |
|
| 180 * should open on the exact server we're connected to. toc.oscar.aol.com |
|
| 181 * doesn't work because that hostname resolves to multiple IP addresses. |
|
| 182 */ |
|
| 183 if (getpeername(tdt->toc_fd, (struct sockaddr *)&name, &namelen) == 0) |
|
| 184 strncpy(tdt->toc_ip, inet_ntoa(name.sin_addr), sizeof(tdt->toc_ip)); |
|
| 185 else |
|
| 186 strncpy(tdt->toc_ip, purple_account_get_string(gc->account, "server", TOC_HOST), sizeof(tdt->toc_ip)); |
|
| 187 |
|
| 188 purple_debug(PURPLE_DEBUG_INFO, "toc", |
|
| 189 "Client sends \"FLAPON\\r\\n\\r\\n\"\n"); |
|
| 190 if (write(tdt->toc_fd, FLAPON, strlen(FLAPON)) < 0) { |
|
| 191 purple_connection_error(gc, _("Disconnected.")); |
|
| 192 return; |
|
| 193 } |
|
| 194 tdt->state = STATE_FLAPON; |
|
| 195 |
|
| 196 /* i know a lot of people like to look at purple to see how TOC works. so i'll comment |
|
| 197 * on what this does. it's really simple. when there's data ready to be read from the |
|
| 198 * toc_fd file descriptor, toc_callback is called, with gc passed as its data arg. */ |
|
| 199 gc->inpa = purple_input_add(tdt->toc_fd, PURPLE_INPUT_READ, toc_callback, gc); |
|
| 200 |
|
| 201 g_snprintf(buf, sizeof(buf), _("Signon: %s"), purple_account_get_username(gc->account)); |
|
| 202 purple_connection_update_progress(gc, buf, 1, TOC_CONNECT_STEPS); |
|
| 203 } |
|
| 204 |
|
| 205 static void toc_close(PurpleConnection *gc) |
|
| 206 { |
|
| 207 if (gc->inpa > 0) |
|
| 208 purple_input_remove(gc->inpa); |
|
| 209 gc->inpa = 0; |
|
| 210 close(((struct toc_data *)gc->proto_data)->toc_fd); |
|
| 211 g_free(gc->proto_data); |
|
| 212 } |
|
| 213 |
|
| 214 static void toc_build_config(PurpleAccount *account, char *s, int len, gboolean show) |
|
| 215 { |
|
| 216 PurpleBlistNode *gnode, *cnode, *bnode; |
|
| 217 PurpleGroup *g; |
|
| 218 PurpleBuddy *b; |
|
| 219 GSList *plist = account->permit; |
|
| 220 GSList *dlist = account->deny; |
|
| 221 |
|
| 222 int pos = 0; |
|
| 223 |
|
| 224 if (!account->perm_deny) |
|
| 225 account->perm_deny = 1; |
|
| 226 |
|
| 227 pos += g_snprintf(&s[pos], len - pos, "m %d\n", account->perm_deny); |
|
| 228 for(gnode = purple_get_blist()->root; gnode && len > pos; gnode = gnode->next) { |
|
| 229 g = (PurpleGroup *)gnode; |
|
| 230 if(!PURPLE_BLIST_NODE_IS_GROUP(gnode)) |
|
| 231 continue; |
|
| 232 if(purple_group_on_account(g, account)) { |
|
| 233 pos += g_snprintf(&s[pos], len - pos, "g %s\n", g->name); |
|
| 234 for(cnode = gnode->child; cnode; cnode = cnode->next) { |
|
| 235 if(!PURPLE_BLIST_NODE_IS_CONTACT(cnode)) |
|
| 236 continue; |
|
| 237 for(bnode = gnode->child; bnode && len > pos; bnode = bnode->next) { |
|
| 238 b = (PurpleBuddy *)bnode; |
|
| 239 if(!PURPLE_BLIST_NODE_IS_BUDDY(bnode)) |
|
| 240 continue; |
|
| 241 if(b->account == account) { |
|
| 242 pos += g_snprintf(&s[pos], len - pos, "b %s%s%s\n", |
|
| 243 b->name, |
|
| 244 (show && b->alias) ? ":" : "", |
|
| 245 (show && b->alias) ? b->alias : ""); |
|
| 246 } |
|
| 247 } |
|
| 248 } |
|
| 249 } |
|
| 250 } |
|
| 251 |
|
| 252 while (len > pos && plist) { |
|
| 253 pos += g_snprintf(&s[pos], len - pos, "p %s\n", (char *)plist->data); |
|
| 254 plist = plist->next; |
|
| 255 } |
|
| 256 |
|
| 257 while (len > pos && dlist) { |
|
| 258 pos += g_snprintf(&s[pos], len - pos, "d %s\n", (char *)dlist->data); |
|
| 259 dlist = dlist->next; |
|
| 260 } |
|
| 261 } |
|
| 262 |
|
| 263 static char * |
|
| 264 escape_message(const char *msg) |
|
| 265 { |
|
| 266 char *ret; |
|
| 267 int i, j; |
|
| 268 |
|
| 269 if (!msg) |
|
| 270 return NULL; |
|
| 271 |
|
| 272 /* Calculate the length after escaping */ |
|
| 273 for (i=0, j=0; msg[i]; i++) |
|
| 274 switch (msg[i]) { |
|
| 275 case '$': |
|
| 276 case '[': |
|
| 277 case ']': |
|
| 278 case '(': |
|
| 279 case ')': |
|
| 280 j++; |
|
| 281 default: |
|
| 282 j++; |
|
| 283 } |
|
| 284 |
|
| 285 /* Allocate a string */ |
|
| 286 ret = (char *)g_malloc((j+1) * sizeof(char)); |
|
| 287 |
|
| 288 /* Copy the string */ |
|
| 289 for (i=0, j=0; msg[i]; i++) |
|
| 290 switch (msg[i]) { |
|
| 291 case '$': |
|
| 292 case '[': |
|
| 293 case ']': |
|
| 294 case '(': |
|
| 295 case ')': |
|
| 296 ret[j++] = '\\'; |
|
| 297 default: |
|
| 298 ret[j++] = msg[i]; |
|
| 299 } |
|
| 300 ret[j] = '\0'; |
|
| 301 |
|
| 302 return ret; |
|
| 303 } |
|
| 304 |
|
| 305 /* |
|
| 306 * Duplicates the input string, replacing each \n with a <BR>, and |
|
| 307 * escaping a few other characters. |
|
| 308 */ |
|
| 309 static char * |
|
| 310 escape_text(const char *msg) |
|
| 311 { |
|
| 312 char *ret; |
|
| 313 int i, j; |
|
| 314 |
|
| 315 if (!msg) |
|
| 316 return NULL; |
|
| 317 |
|
| 318 /* Calculate the length after escaping */ |
|
| 319 for (i=0, j=0; msg[i]; i++) |
|
| 320 switch (msg[i]) { |
|
| 321 case '\n': |
|
| 322 j += 4; |
|
| 323 break; |
|
| 324 case '{': |
|
| 325 case '}': |
|
| 326 case '\\': |
|
| 327 case '"': |
|
| 328 j += 1; |
|
| 329 default: |
|
| 330 j += 1; |
|
| 331 } |
|
| 332 |
|
| 333 /* Allocate a string */ |
|
| 334 ret = (char *)malloc((j+1) * sizeof(char)); |
|
| 335 |
|
| 336 /* Copy the string */ |
|
| 337 for (i=0, j=0; msg[i]; i++) |
|
| 338 switch (msg[i]) { |
|
| 339 case '\n': |
|
| 340 ret[j++] = '<'; |
|
| 341 ret[j++] = 'B'; |
|
| 342 ret[j++] = 'R'; |
|
| 343 ret[j++] = '>'; |
|
| 344 break; |
|
| 345 case '{': |
|
| 346 case '}': |
|
| 347 case '\\': |
|
| 348 case '"': |
|
| 349 ret[j++] = '\\'; |
|
| 350 default: |
|
| 351 ret[j++] = msg[i]; |
|
| 352 } |
|
| 353 ret[j] = '\0'; |
|
| 354 |
|
| 355 return ret; |
|
| 356 } |
|
| 357 |
|
| 358 static int sflap_send(PurpleConnection *gc, const char *buf, int olen, int type) |
|
| 359 { |
|
| 360 struct toc_data *tdt = (struct toc_data *)gc->proto_data; |
|
| 361 int len; |
|
| 362 int slen = 0; |
|
| 363 int ret; |
|
| 364 struct sflap_hdr hdr; |
|
| 365 char *escaped, *obuf; |
|
| 366 |
|
| 367 if (tdt->state == STATE_PAUSE) |
|
| 368 /* TOC has given us the PAUSE message; sending could cause a disconnect |
|
| 369 * so we just return here like everything went through fine */ |
|
| 370 return 0; |
|
| 371 |
|
| 372 if (olen < 0) { |
|
| 373 escaped = escape_message(buf); |
|
| 374 len = strlen(escaped); |
|
| 375 } else { |
|
| 376 escaped = g_memdup(buf, olen); |
|
| 377 len = olen; |
|
| 378 } |
|
| 379 |
|
| 380 /* |
|
| 381 * One _last_ 2048 check here! This shouldn't ever |
|
| 382 * get hit though, hopefully. If it gets hit on an IM |
|
| 383 * It'll lose the last " and the message won't go through, |
|
| 384 * but this'll stop a segfault. |
|
| 385 */ |
|
| 386 if (len > MSG_LEN) { |
|
| 387 purple_debug(PURPLE_DEBUG_WARNING, "toc", "message too long, truncating\n"); |
|
| 388 escaped[MSG_LEN - 1] = '\0'; |
|
| 389 len = MSG_LEN; |
|
| 390 } |
|
| 391 |
|
| 392 if (olen < 0) |
|
| 393 purple_debug(PURPLE_DEBUG_INFO, "toc", "C: %s\n", escaped); |
|
| 394 |
|
| 395 hdr.ast = '*'; |
|
| 396 hdr.type = type; |
|
| 397 hdr.seqno = htons(tdt->seqno++ & 0xffff); |
|
| 398 hdr.len = htons(len + (type == TYPE_SIGNON ? 0 : 1)); |
|
| 399 |
|
| 400 obuf = (char *)malloc((sizeof(hdr)+len+1) * sizeof(char)); |
|
| 401 memcpy(obuf, &hdr, sizeof(hdr)); |
|
| 402 slen += sizeof(hdr); |
|
| 403 |
|
| 404 memcpy(&obuf[slen], escaped, len); |
|
| 405 slen += len; |
|
| 406 |
|
| 407 if (type != TYPE_SIGNON) { |
|
| 408 obuf[slen] = '\0'; |
|
| 409 slen += 1; |
|
| 410 } |
|
| 411 |
|
| 412 ret = write(tdt->toc_fd, obuf, slen); |
|
| 413 free(obuf); |
|
| 414 g_free(escaped); |
|
| 415 |
|
| 416 return ret; |
|
| 417 } |
|
| 418 |
|
| 419 static int toc_send_raw(PurpleConnection *gc, const char *buf, int len) |
|
| 420 { |
|
| 421 return sflap_send(gc, buf, len, 2); |
|
| 422 } |
|
| 423 |
|
| 424 static int wait_reply(PurpleConnection *gc, char *buffer, size_t buflen) |
|
| 425 { |
|
| 426 struct toc_data *tdt = (struct toc_data *)gc->proto_data; |
|
| 427 struct sflap_hdr *hdr; |
|
| 428 int ret; |
|
| 429 |
|
| 430 if (read(tdt->toc_fd, buffer, sizeof(struct sflap_hdr)) < 0) { |
|
| 431 purple_debug(PURPLE_DEBUG_ERROR, "toc", "Couldn't read flap header\n"); |
|
| 432 return -1; |
|
| 433 } |
|
| 434 |
|
| 435 hdr = (struct sflap_hdr *)buffer; |
|
| 436 |
|
| 437 if (buflen < ntohs(hdr->len)) { |
|
| 438 /* fake like there's a read error */ |
|
| 439 purple_debug(PURPLE_DEBUG_ERROR, "toc", |
|
| 440 "buffer too small (have %" G_GSIZE_FORMAT ", need %d)\n", |
|
| 441 buflen, ntohs(hdr->len)); |
|
| 442 return -1; |
|
| 443 } |
|
| 444 |
|
| 445 if (ntohs(hdr->len) > 0) { |
|
| 446 int count = 0; |
|
| 447 ret = 0; |
|
| 448 do { |
|
| 449 count += ret; |
|
| 450 ret = read(tdt->toc_fd, |
|
| 451 buffer + sizeof(struct sflap_hdr) + count, ntohs(hdr->len) - count); |
|
| 452 } while (count + ret < ntohs(hdr->len) && ret > 0); |
|
| 453 buffer[sizeof(struct sflap_hdr) + count + ret] = '\0'; |
|
| 454 return ret; |
|
| 455 } else |
|
| 456 return 0; |
|
| 457 } |
|
| 458 |
|
| 459 static unsigned char *roast_password(const char *pass) |
|
| 460 { |
|
| 461 /* Trivial "encryption" */ |
|
| 462 static unsigned char rp[256]; |
|
| 463 static char *roast = ROAST; |
|
| 464 int pos = 2; |
|
| 465 int x; |
|
| 466 strcpy(rp, "0x"); |
|
| 467 for (x = 0; (x < 150) && pass[x]; x++) |
|
| 468 pos += sprintf(&rp[pos], "%02x", pass[x] ^ roast[x % strlen(roast)]); |
|
| 469 rp[pos] = '\0'; |
|
| 470 return rp; |
|
| 471 } |
|
| 472 |
|
| 473 static void toc_got_info(void *data, const char *url_text, size_t len) |
|
| 474 { |
|
| 475 if (!url_text) |
|
| 476 return; |
|
| 477 |
|
| 478 purple_notify_formatted(data, NULL, _("Buddy Information"), NULL, |
|
| 479 url_text, NULL, NULL); |
|
| 480 } |
|
| 481 |
|
| 482 static char *show_error_message() |
|
| 483 { |
|
| 484 int no = atoi(strtok(NULL, ":")); |
|
| 485 char *w = strtok(NULL, ":"); |
|
| 486 static char buf[256]; |
|
| 487 |
|
| 488 switch(no) { |
|
| 489 case 69: |
|
| 490 g_snprintf(buf, sizeof(buf), _("Unable to write file %s."), w); |
|
| 491 break; |
|
| 492 case 169: |
|
| 493 g_snprintf(buf, sizeof(buf), _("Unable to read file %s."), w); |
|
| 494 break; |
|
| 495 case 269: |
|
| 496 g_snprintf(buf, sizeof(buf), _("Message too long, last %s bytes truncated."), w); |
|
| 497 break; |
|
| 498 case 901: |
|
| 499 g_snprintf(buf, sizeof(buf), _("%s not currently logged in."), w); |
|
| 500 break; |
|
| 501 case 902: |
|
| 502 g_snprintf(buf, sizeof(buf), _("Warning of %s not allowed."), w); |
|
| 503 break; |
|
| 504 case 903: |
|
| 505 g_snprintf(buf, sizeof(buf), _("A message has been dropped, you are exceeding the server speed limit.")); |
|
| 506 break; |
|
| 507 case 950: |
|
| 508 g_snprintf(buf, sizeof(buf), _("Chat in %s is not available."), w); |
|
| 509 break; |
|
| 510 case 960: |
|
| 511 g_snprintf(buf, sizeof(buf), _("You are sending messages too fast to %s."), w); |
|
| 512 break; |
|
| 513 case 961: |
|
| 514 g_snprintf(buf, sizeof(buf), _("You missed an IM from %s because it was too big."), w); |
|
| 515 break; |
|
| 516 case 962: |
|
| 517 g_snprintf(buf, sizeof(buf), _("You missed an IM from %s because it was sent too fast."), w); |
|
| 518 break; |
|
| 519 case 970: |
|
| 520 g_snprintf(buf, sizeof(buf), _("Failure.")); |
|
| 521 break; |
|
| 522 case 971: |
|
| 523 g_snprintf(buf, sizeof(buf), _("Too many matches.")); |
|
| 524 break; |
|
| 525 case 972: |
|
| 526 g_snprintf(buf, sizeof(buf), _("Need more qualifiers.")); |
|
| 527 break; |
|
| 528 case 973: |
|
| 529 g_snprintf(buf, sizeof(buf), _("Dir service temporarily unavailable.")); |
|
| 530 break; |
|
| 531 case 974: |
|
| 532 g_snprintf(buf, sizeof(buf), _("Email lookup restricted.")); |
|
| 533 break; |
|
| 534 case 975: |
|
| 535 g_snprintf(buf, sizeof(buf), _("Keyword ignored.")); |
|
| 536 break; |
|
| 537 case 976: |
|
| 538 g_snprintf(buf, sizeof(buf), _("No keywords.")); |
|
| 539 break; |
|
| 540 case 977: |
|
| 541 g_snprintf(buf, sizeof(buf), _("User has no directory information.")); |
|
| 542 /* g_snprintf(buf, sizeof(buf), _("Language not supported.")); */ |
|
| 543 break; |
|
| 544 case 978: |
|
| 545 g_snprintf(buf, sizeof(buf), _("Country not supported.")); |
|
| 546 break; |
|
| 547 case 979: |
|
| 548 g_snprintf(buf, sizeof(buf), _("Failure unknown: %s."), w); |
|
| 549 break; |
|
| 550 case 980: |
|
| 551 g_snprintf(buf, sizeof(buf), _("Incorrect username or password.")); |
|
| 552 break; |
|
| 553 case 981: |
|
| 554 g_snprintf(buf, sizeof(buf), _("The service is temporarily unavailable.")); |
|
| 555 break; |
|
| 556 case 982: |
|
| 557 g_snprintf(buf, sizeof(buf), _("Your warning level is currently too high to log in.")); |
|
| 558 break; |
|
| 559 case 983: |
|
| 560 g_snprintf(buf, sizeof(buf), _("You have been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer.")); |
|
| 561 break; |
|
| 562 g_snprintf(buf, sizeof(buf), _("An unknown signon error has occurred: %s."), w); |
|
| 563 break; |
|
| 564 default: |
|
| 565 g_snprintf(buf, sizeof(buf), _("An unknown error, %d, has occurred. Info: %s"), no, w); |
|
| 566 } |
|
| 567 |
|
| 568 return buf; |
|
| 569 } |
|
| 570 |
|
| 571 static void |
|
| 572 parse_toc_buddy_list(PurpleAccount *account, char *config) |
|
| 573 { |
|
| 574 char *c; |
|
| 575 char current[256]; |
|
| 576 GList *buddies = NULL; |
|
| 577 |
|
| 578 if (config == NULL) |
|
| 579 return; |
|
| 580 |
|
| 581 /* skip "CONFIG:" (if it exists) */ |
|
| 582 c = strncmp(config + 6 /* sizeof(struct sflap_hdr) */ , "CONFIG:", strlen("CONFIG:")) ? |
|
| 583 strtok(config, "\n") : |
|
| 584 strtok(config + 6 /* sizeof(struct sflap_hdr) */ + strlen("CONFIG:"), "\n"); |
|
| 585 do { |
|
| 586 if (c == NULL) |
|
| 587 break; |
|
| 588 if (*c == 'g') { |
|
| 589 char *utf8 = NULL; |
|
| 590 utf8 = purple_utf8_try_convert(c + 2); |
|
| 591 if (utf8 == NULL) { |
|
| 592 g_strlcpy(current, _("Invalid Groupname"), sizeof(current)); |
|
| 593 } else { |
|
| 594 g_strlcpy(current, utf8, sizeof(current)); |
|
| 595 g_free(utf8); |
|
| 596 } |
|
| 597 if (!purple_find_group(current)) { |
|
| 598 PurpleGroup *g = purple_group_new(current); |
|
| 599 purple_blist_add_group(g, NULL); |
|
| 600 } |
|
| 601 } else if (*c == 'b') { /*&& !purple_find_buddy(user, c + 2)) {*/ |
|
| 602 char nm[80], sw[388], *a, *utf8 = NULL; |
|
| 603 |
|
| 604 if ((a = strchr(c + 2, ':')) != NULL) { |
|
| 605 *a++ = '\0'; /* nul the : */ |
|
| 606 } |
|
| 607 |
|
| 608 g_strlcpy(nm, c + 2, sizeof(nm)); |
|
| 609 if (a) { |
|
| 610 utf8 = purple_utf8_try_convert(a); |
|
| 611 if (utf8 == NULL) { |
|
| 612 purple_debug(PURPLE_DEBUG_ERROR, "toc blist", |
|
| 613 "Failed to convert alias for " |
|
| 614 "'%s' to UTF-8\n", nm); |
|
| 615 } |
|
| 616 } |
|
| 617 if (utf8 == NULL) { |
|
| 618 sw[0] = '\0'; |
|
| 619 } else { |
|
| 620 /* This can leave a partial sequence at the end, |
|
| 621 * but who cares? */ |
|
| 622 g_strlcpy(sw, utf8, sizeof(sw)); |
|
| 623 g_free(utf8); |
|
| 624 } |
|
| 625 |
|
| 626 if (!purple_find_buddy(account, nm)) { |
|
| 627 PurpleBuddy *b = purple_buddy_new(account, nm, sw); |
|
| 628 PurpleGroup *g = purple_find_group(current); |
|
| 629 purple_blist_add_buddy(b, NULL, g, NULL); |
|
| 630 buddies = g_list_append(buddies, b); |
|
| 631 } |
|
| 632 } else if (*c == 'p') { |
|
| 633 purple_privacy_permit_add(account, c + 2, TRUE); |
|
| 634 } else if (*c == 'd') { |
|
| 635 purple_privacy_deny_add(account, c + 2, TRUE); |
|
| 636 } else if (!strncmp("toc", c, 3)) { |
|
| 637 sscanf(c + strlen(c) - 1, "%d", &account->perm_deny); |
|
| 638 purple_debug(PURPLE_DEBUG_MISC, "toc blist", |
|
| 639 "permdeny: %d\n", account->perm_deny); |
|
| 640 if (account->perm_deny == 0) |
|
| 641 account->perm_deny = PURPLE_PRIVACY_ALLOW_ALL; |
|
| 642 } else if (*c == 'm') { |
|
| 643 sscanf(c + 2, "%d", &account->perm_deny); |
|
| 644 purple_debug(PURPLE_DEBUG_MISC, "toc blist", |
|
| 645 "permdeny: %d\n", account->perm_deny); |
|
| 646 if (account->perm_deny == 0) |
|
| 647 account->perm_deny = PURPLE_PRIVACY_ALLOW_ALL; |
|
| 648 } |
|
| 649 } while ((c = strtok(NULL, "\n"))); |
|
| 650 |
|
| 651 if (account->gc) { |
|
| 652 if (buddies != NULL) { |
|
| 653 purple_account_add_buddies(account, buddies); |
|
| 654 g_list_free(buddies); |
|
| 655 } |
|
| 656 serv_set_permit_deny(account->gc); |
|
| 657 } |
|
| 658 g_list_free(buddies); |
|
| 659 } |
|
| 660 |
|
| 661 static void toc_callback(gpointer data, gint source, PurpleInputCondition condition) |
|
| 662 { |
|
| 663 PurpleConnection *gc = (PurpleConnection *)data; |
|
| 664 PurpleAccount *account = purple_connection_get_account(gc); |
|
| 665 struct toc_data *tdt = (struct toc_data *)gc->proto_data; |
|
| 666 struct sflap_hdr *hdr; |
|
| 667 struct signon so; |
|
| 668 char buf[8 * 1024], *c; |
|
| 669 char snd[BUF_LEN * 2]; |
|
| 670 const char *username = purple_account_get_username(account); |
|
| 671 char *password; |
|
| 672 PurpleBuddy *buddy; |
|
| 673 |
|
| 674 /* there's data waiting to be read, so read it. */ |
|
| 675 if (wait_reply(gc, buf, 8 * 1024) <= 0) { |
|
| 676 purple_connection_error(gc, _("Connection Closed")); |
|
| 677 return; |
|
| 678 } |
|
| 679 |
|
| 680 if (tdt->state == STATE_FLAPON) { |
|
| 681 hdr = (struct sflap_hdr *)buf; |
|
| 682 if (hdr->type != TYPE_SIGNON) |
|
| 683 purple_debug(PURPLE_DEBUG_ERROR, "toc", "hdr->type != TYPE_SIGNON\n"); |
|
| 684 else |
|
| 685 purple_debug(PURPLE_DEBUG_INFO, "toc", |
|
| 686 "TOC sends Client FLAP SIGNON\n"); |
|
| 687 tdt->seqno = ntohs(hdr->seqno); |
|
| 688 tdt->state = STATE_SIGNON_REQUEST; |
|
| 689 |
|
| 690 purple_debug(PURPLE_DEBUG_INFO, "toc", "Client sends TOC FLAP SIGNON\n"); |
|
| 691 g_snprintf(so.username, sizeof(so.username), "%s", username); |
|
| 692 so.ver = htonl(1); |
|
| 693 so.tag = htons(1); |
|
| 694 so.namelen = htons(strlen(so.username)); |
|
| 695 if (sflap_send(gc, (char *)&so, ntohs(so.namelen) + 8, TYPE_SIGNON) < 0) { |
|
| 696 purple_connection_error(gc, _("Disconnected.")); |
|
| 697 return; |
|
| 698 } |
|
| 699 |
|
| 700 purple_debug(PURPLE_DEBUG_INFO, "toc", |
|
| 701 "Client sends TOC \"toc_signon\" message\n"); |
|
| 702 /* i hate icq. */ |
|
| 703 if (username[0] >= '0' && username[0] <= '9') |
|
| 704 password = g_strndup(purple_connection_get_password(gc), 8); |
|
| 705 else |
|
| 706 password = g_strdup(purple_connection_get_password(gc)); |
|
| 707 g_snprintf(snd, sizeof snd, "toc_signon %s %d %s %s %s \"%s\"", |
|
| 708 AUTH_HOST, AUTH_PORT, purple_normalize(account, username), |
|
| 709 roast_password(password), LANGUAGE, REVISION); |
|
| 710 g_free(password); |
|
| 711 if (sflap_send(gc, snd, -1, TYPE_DATA) < 0) { |
|
| 712 purple_connection_error(gc, _("Disconnected.")); |
|
| 713 return; |
|
| 714 } |
|
| 715 |
|
| 716 purple_connection_update_progress(gc, _("Waiting for reply..."), 2, TOC_CONNECT_STEPS); |
|
| 717 return; |
|
| 718 } |
|
| 719 |
|
| 720 if (tdt->state == STATE_SIGNON_REQUEST) { |
|
| 721 purple_debug(PURPLE_DEBUG_INFO, "toc", "TOC sends client SIGN_ON reply\n"); |
|
| 722 if (g_ascii_strncasecmp(buf + sizeof(struct sflap_hdr), "SIGN_ON", strlen("SIGN_ON"))) { |
|
| 723 purple_debug(PURPLE_DEBUG_ERROR, "toc", |
|
| 724 "Didn't get SIGN_ON! buf was: %s\n", |
|
| 725 buf + sizeof(struct sflap_hdr)); |
|
| 726 if (!g_ascii_strncasecmp(buf + sizeof(struct sflap_hdr), "ERROR", 5)) { |
|
| 727 strtok(buf + sizeof(struct sflap_hdr), ":"); |
|
| 728 purple_connection_error(gc, show_error_message()); |
|
| 729 } else |
|
| 730 purple_connection_error(gc, _("Authentication failed")); |
|
| 731 return; |
|
| 732 } |
|
| 733 /* we're supposed to check that it's really TOC v1 here but we know it is ;) */ |
|
| 734 purple_debug(PURPLE_DEBUG_INFO, "toc", |
|
| 735 "TOC version: %s\n", buf + sizeof(struct sflap_hdr) + 8); |
|
| 736 |
|
| 737 /* we used to check for the CONFIG here, but we'll wait until we've sent our |
|
| 738 * version of the config and then the toc_init_done message. we'll come back to |
|
| 739 * the callback in a better state if we get CONFIG anyway */ |
|
| 740 |
|
| 741 tdt->state = STATE_ONLINE; |
|
| 742 |
|
| 743 purple_connection_set_state(gc, PURPLE_CONNECTED); |
|
| 744 |
|
| 745 /* |
|
| 746 * Add me to my buddy list so that we know the time when |
|
| 747 * the server thinks I signed on. |
|
| 748 */ |
|
| 749 buddy = purple_buddy_new(account, username, NULL); |
|
| 750 /* XXX - Pick a group to add to */ |
|
| 751 /* purple_blist_add(buddy, NULL, g, NULL); */ |
|
| 752 purple_account_add_buddy(gc, buddy); |
|
| 753 |
|
| 754 /* Client sends TOC toc_init_done message */ |
|
| 755 purple_debug(PURPLE_DEBUG_INFO, "toc", |
|
| 756 "Client sends TOC toc_init_done message\n"); |
|
| 757 g_snprintf(snd, sizeof snd, "toc_init_done"); |
|
| 758 sflap_send(gc, snd, -1, TYPE_DATA); |
|
| 759 |
|
| 760 /* |
|
| 761 g_snprintf(snd, sizeof snd, "toc_set_caps %s %s %s", |
|
| 762 FILE_SEND_UID, FILE_GET_UID, B_ICON_UID); |
|
| 763 */ |
|
| 764 g_snprintf(snd, sizeof snd, "toc_set_caps %s %s", FILE_SEND_UID, FILE_GET_UID); |
|
| 765 sflap_send(gc, snd, -1, TYPE_DATA); |
|
| 766 |
|
| 767 return; |
|
| 768 } |
|
| 769 |
|
| 770 purple_debug(PURPLE_DEBUG_INFO, "toc", "S: %s\n", |
|
| 771 buf + sizeof(struct sflap_hdr)); |
|
| 772 |
|
| 773 c = strtok(buf + sizeof(struct sflap_hdr), ":"); /* Ditch the first part */ |
|
| 774 |
|
| 775 if (!g_ascii_strcasecmp(c, "SIGN_ON")) { |
|
| 776 /* we should only get here after a PAUSE */ |
|
| 777 if (tdt->state != STATE_PAUSE) |
|
| 778 purple_debug(PURPLE_DEBUG_ERROR, "toc", |
|
| 779 "got SIGN_ON but not PAUSE!\n"); |
|
| 780 else { |
|
| 781 tdt->state = STATE_ONLINE; |
|
| 782 g_snprintf(snd, sizeof snd, "toc_signon %s %d %s %s %s \"%s\"", |
|
| 783 AUTH_HOST, AUTH_PORT, |
|
| 784 purple_normalize(account, purple_account_get_username(account)), |
|
| 785 roast_password(purple_connection_get_password(gc)), |
|
| 786 LANGUAGE, REVISION); |
|
| 787 if (sflap_send(gc, snd, -1, TYPE_DATA) < 0) { |
|
| 788 purple_connection_error(gc, _("Disconnected.")); |
|
| 789 return; |
|
| 790 } |
|
| 791 g_snprintf(snd, sizeof snd, "toc_init_done"); |
|
| 792 sflap_send(gc, snd, -1, TYPE_DATA); |
|
| 793 purple_notify_info(gc, NULL, |
|
| 794 _("TOC has come back from its pause. You may " |
|
| 795 "now send messages again."), NULL); |
|
| 796 } |
|
| 797 } else if (!g_ascii_strcasecmp(c, "CONFIG")) { |
|
| 798 c = strtok(NULL, ":"); |
|
| 799 parse_toc_buddy_list(account, c); |
|
| 800 } else if (!g_ascii_strcasecmp(c, "NICK")) { |
|
| 801 /* ignore NICK so that things get imported/exported properly |
|
| 802 c = strtok(NULL, ":"); |
|
| 803 g_snprintf(gc->username, sizeof(gc->username), "%s", c); |
|
| 804 */ |
|
| 805 } else if (!g_ascii_strcasecmp(c, "IM_IN")) { |
|
| 806 char *away, *message; |
|
| 807 int a = 0; |
|
| 808 |
|
| 809 c = strtok(NULL, ":"); |
|
| 810 away = strtok(NULL, ":"); |
|
| 811 |
|
| 812 message = away; |
|
| 813 while (*message && (*message != ':')) |
|
| 814 message++; |
|
| 815 message++; |
|
| 816 |
|
| 817 a = (away && (*away == 'T')) ? PURPLE_MESSAGE_AUTO_RESP : 0; |
|
| 818 |
|
| 819 serv_got_im(gc, c, message, a, time(NULL)); |
|
| 820 } else if (!g_ascii_strcasecmp(c, "UPDATE_BUDDY")) { |
|
| 821 char *l, *uc, *tmp; |
|
| 822 gboolean logged_in; |
|
| 823 int evil, idle, type = 0; |
|
| 824 time_t signon, time_idle; |
|
| 825 |
|
| 826 c = strtok(NULL, ":"); /* name */ |
|
| 827 l = strtok(NULL, ":"); /* online */ |
|
| 828 sscanf(strtok(NULL, ":"), "%d", &evil); |
|
| 829 sscanf(strtok(NULL, ":"), "%ld", &signon); |
|
| 830 sscanf(strtok(NULL, ":"), "%d", &idle); |
|
| 831 uc = strtok(NULL, ":"); |
|
| 832 |
|
| 833 logged_in = (l && (*l == 'T')) ? TRUE : FALSE; |
|
| 834 |
|
| 835 if (uc[0] == 'A') |
|
| 836 type |= UC_AOL; |
|
| 837 switch (uc[1]) { |
|
| 838 case 'A': |
|
| 839 type |= UC_ADMIN; |
|
| 840 break; |
|
| 841 case 'U': |
|
| 842 type |= UC_UNCONFIRMED; |
|
| 843 break; |
|
| 844 case 'O': |
|
| 845 type |= UC_NORMAL; |
|
| 846 break; |
|
| 847 case 'C': |
|
| 848 type |= UC_WIRELESS; |
|
| 849 break; |
|
| 850 default: |
|
| 851 break; |
|
| 852 } |
|
| 853 if (uc[2] == 'U') |
|
| 854 type |= UC_UNAVAILABLE; |
|
| 855 |
|
| 856 if (idle) { |
|
| 857 time(&time_idle); |
|
| 858 time_idle -= idle * 60; |
|
| 859 } else |
|
| 860 time_idle = 0; |
|
| 861 |
|
| 862 /* |
|
| 863 * If we have info for ourselves then set our display name, warning |
|
| 864 * level and official time of login. |
|
| 865 */ |
|
| 866 tmp = g_strdup(purple_normalize(account, purple_account_get_username(gc->account))); |
|
| 867 if (!strcmp(tmp, purple_normalize(account, c))) { |
|
| 868 purple_connection_set_display_name(gc, c); |
|
| 869 /* XXX - What should the second parameter be here? */ |
|
| 870 /* purple_prpl_got_account_warning_level(account, NULL, evil);*/ |
|
| 871 purple_prpl_got_account_login_time(account, signon); |
|
| 872 } |
|
| 873 g_free(tmp); |
|
| 874 |
|
| 875 purple_prpl_got_user_status(account, c, (logged_in ? "online" : "offline"), NULL); |
|
| 876 purple_prpl_got_user_login_time(account, c, signon); |
|
| 877 if (time_idle > 0) |
|
| 878 purple_prpl_got_user_idle(account, c, TRUE, time_idle); |
|
| 879 else |
|
| 880 purple_prpl_got_user_idle(account, c, FALSE, 0); |
|
| 881 } else if (!g_ascii_strcasecmp(c, "ERROR")) { |
|
| 882 purple_notify_error(gc, NULL, show_error_message(), NULL); |
|
| 883 } else if (!g_ascii_strcasecmp(c, "EVILED")) { |
|
| 884 int lev; |
|
| 885 char *name; |
|
| 886 |
|
| 887 sscanf(strtok(NULL, ":"), "%d", &lev); |
|
| 888 name = strtok(NULL, ":"); |
|
| 889 |
|
| 890 /* purple_prpl_got_account_warning_level(account, name, lev); */ |
|
| 891 } else if (!g_ascii_strcasecmp(c, "CHAT_JOIN")) { |
|
| 892 char *name; |
|
| 893 int id; |
|
| 894 |
|
| 895 sscanf(strtok(NULL, ":"), "%d", &id); |
|
| 896 name = strtok(NULL, ":"); |
|
| 897 |
|
| 898 serv_got_joined_chat(gc, id, name); |
|
| 899 } else if (!g_ascii_strcasecmp(c, "CHAT_IN")) { |
|
| 900 int id; |
|
| 901 PurpleMessageFlags flags; |
|
| 902 char *m, *who, *whisper; |
|
| 903 |
|
| 904 sscanf(strtok(NULL, ":"), "%d", &id); |
|
| 905 who = strtok(NULL, ":"); |
|
| 906 whisper = strtok(NULL, ":"); |
|
| 907 m = whisper; |
|
| 908 while (*m && (*m != ':')) |
|
| 909 m++; |
|
| 910 m++; |
|
| 911 |
|
| 912 flags = (whisper && (*whisper == 'T')) ? PURPLE_MESSAGE_WHISPER : 0; |
|
| 913 |
|
| 914 serv_got_chat_in(gc, id, who, flags, m, time((time_t)NULL)); |
|
| 915 } else if (!g_ascii_strcasecmp(c, "CHAT_UPDATE_BUDDY")) { |
|
| 916 int id; |
|
| 917 char *in, *buddy; |
|
| 918 GSList *bcs = gc->buddy_chats; |
|
| 919 PurpleConversation *b = NULL; |
|
| 920 PurpleConvChat *chat; |
|
| 921 |
|
| 922 sscanf(strtok(NULL, ":"), "%d", &id); |
|
| 923 in = strtok(NULL, ":"); |
|
| 924 |
|
| 925 chat = PURPLE_CONV_CHAT(b); |
|
| 926 |
|
| 927 while (bcs) { |
|
| 928 b = (PurpleConversation *)bcs->data; |
|
| 929 if (id == purple_conv_chat_get_id(chat)) |
|
| 930 break; |
|
| 931 bcs = bcs->next; |
|
| 932 b = NULL; |
|
| 933 } |
|
| 934 |
|
| 935 if (!b) |
|
| 936 return; |
|
| 937 |
|
| 938 if (in && (*in == 'T')) |
|
| 939 while ((buddy = strtok(NULL, ":")) != NULL) |
|
| 940 purple_conv_chat_add_user(chat, buddy, NULL, PURPLE_CBFLAGS_NONE, TRUE); |
|
| 941 else |
|
| 942 while ((buddy = strtok(NULL, ":")) != NULL) |
|
| 943 purple_conv_chat_remove_user(chat, buddy, NULL); |
|
| 944 } else if (!g_ascii_strcasecmp(c, "CHAT_INVITE")) { |
|
| 945 char *name, *who, *message; |
|
| 946 int id; |
|
| 947 GHashTable *components = g_hash_table_new_full(g_str_hash, g_str_equal, |
|
| 948 g_free, g_free); |
|
| 949 |
|
| 950 name = strtok(NULL, ":"); |
|
| 951 sscanf(strtok(NULL, ":"), "%d", &id); |
|
| 952 who = strtok(NULL, ":"); |
|
| 953 message = strtok(NULL, ":"); |
|
| 954 |
|
| 955 g_hash_table_replace(components, g_strdup("id"), g_strdup_printf("%d", id)); |
|
| 956 |
|
| 957 serv_got_chat_invite(gc, name, who, message, components); |
|
| 958 } else if (!g_ascii_strcasecmp(c, "CHAT_LEFT")) { |
|
| 959 GSList *bcs = gc->buddy_chats; |
|
| 960 PurpleConversation *b = NULL; |
|
| 961 int id; |
|
| 962 |
|
| 963 sscanf(strtok(NULL, ":"), "%d", &id); |
|
| 964 |
|
| 965 while (bcs) { |
|
| 966 b = (PurpleConversation *)bcs->data; |
|
| 967 if (id == purple_conv_chat_get_id(PURPLE_CONV_CHAT(b))) |
|
| 968 break; |
|
| 969 b = NULL; |
|
| 970 bcs = bcs->next; |
|
| 971 } |
|
| 972 |
|
| 973 if (!b) |
|
| 974 return; |
|
| 975 |
|
| 976 if (b->window) { |
|
| 977 char error_buf[BUF_LONG]; |
|
| 978 purple_conversation_set_account(b, NULL); |
|
| 979 g_snprintf(error_buf, sizeof error_buf, _("You have been disconnected" |
|
| 980 " from chat room %s."), b->name); |
|
| 981 purple_notify_error(gc, NULL, error_buf, NULL); |
|
| 982 } else |
|
| 983 serv_got_chat_left(gc, id); |
|
| 984 } else if (!g_ascii_strcasecmp(c, "GOTO_URL")) { |
|
| 985 char *name, *url, tmp[256]; |
|
| 986 |
|
| 987 name = strtok(NULL, ":"); |
|
| 988 url = strtok(NULL, ":"); |
|
| 989 |
|
| 990 g_snprintf(tmp, sizeof(tmp), "http://%s:%d/%s", tdt->toc_ip, |
|
| 991 purple_account_get_int(gc->account, "port", TOC_PORT), |
|
| 992 url); |
|
| 993 purple_url_fetch(tmp, FALSE, NULL, FALSE, toc_got_info, gc); |
|
| 994 } else if (!g_ascii_strcasecmp(c, "DIR_STATUS")) { |
|
| 995 } else if (!g_ascii_strcasecmp(c, "ADMIN_NICK_STATUS")) { |
|
| 996 } else if (!g_ascii_strcasecmp(c, "ADMIN_PASSWD_STATUS")) { |
|
| 997 purple_notify_info(gc, NULL, _("Password Change Successful"), NULL); |
|
| 998 } else if (!g_ascii_strcasecmp(c, "PAUSE")) { |
|
| 999 tdt->state = STATE_PAUSE; |
|
| 1000 } else if (!g_ascii_strcasecmp(c, "RVOUS_PROPOSE")) { |
|
| 1001 #if 0 |
|
| 1002 char *user, *uuid, *cookie; |
|
| 1003 int seq; |
|
| 1004 char *rip, *pip, *vip, *trillian = NULL; |
|
| 1005 int port; |
|
| 1006 |
|
| 1007 user = strtok(NULL, ":"); |
|
| 1008 uuid = strtok(NULL, ":"); |
|
| 1009 cookie = strtok(NULL, ":"); |
|
| 1010 sscanf(strtok(NULL, ":"), "%d", &seq); |
|
| 1011 rip = strtok(NULL, ":"); |
|
| 1012 pip = strtok(NULL, ":"); |
|
| 1013 vip = strtok(NULL, ":"); |
|
| 1014 sscanf(strtok(NULL, ":"), "%d", &port); |
|
| 1015 |
|
| 1016 if (!strcmp(uuid, FILE_SEND_UID)) { |
|
| 1017 /* they want us to get a file */ |
|
| 1018 int unk[4], i; |
|
| 1019 char *messages[4], *tmp, *name; |
|
| 1020 int subtype, files, totalsize = 0; |
|
| 1021 struct ft_request *ft; |
|
| 1022 |
|
| 1023 for (i = 0; i < 4; i++) { |
|
| 1024 trillian = strtok(NULL, ":"); |
|
| 1025 sscanf(trillian, "%d", &unk[i]); |
|
| 1026 if (unk[i] == 10001) |
|
| 1027 break; |
|
| 1028 /* Trillian likes to send an empty token as a message, rather than |
|
| 1029 no message at all. */ |
|
| 1030 if (*(trillian + strlen(trillian) +1) != ':') |
|
| 1031 frombase64(strtok(NULL, ":"), &messages[i], NULL); |
|
| 1032 } |
|
| 1033 |
|
| 1034 frombase64(strtok(NULL, ":"), &tmp, NULL); |
|
| 1035 |
|
| 1036 subtype = tmp[1]; |
|
| 1037 files = tmp[3]; |
|
| 1038 |
|
| 1039 totalsize |= (tmp[4] << 24) & 0xff000000; |
|
| 1040 totalsize |= (tmp[5] << 16) & 0x00ff0000; |
|
| 1041 totalsize |= (tmp[6] << 8) & 0x0000ff00; |
|
| 1042 totalsize |= (tmp[7] << 0) & 0x000000ff; |
|
| 1043 |
|
| 1044 if (!totalsize) { |
|
| 1045 g_free(tmp); |
|
| 1046 for (i--; i >= 0; i--) |
|
| 1047 g_free(messages[i]); |
|
| 1048 return; |
|
| 1049 } |
|
| 1050 |
|
| 1051 name = tmp + 8; |
|
| 1052 |
|
| 1053 ft = g_new0(struct ft_request, 1); |
|
| 1054 ft->cookie = g_strdup(cookie); |
|
| 1055 ft->ip = g_strdup(pip); |
|
| 1056 ft->port = port; |
|
| 1057 if (i) |
|
| 1058 ft->message = g_strdup(messages[0]); |
|
| 1059 else |
|
| 1060 ft->message = NULL; |
|
| 1061 ft->filename = g_strdup(name); |
|
| 1062 ft->user = g_strdup(user); |
|
| 1063 ft->size = totalsize; |
|
| 1064 ft->files = files; |
|
| 1065 g_snprintf(ft->UID, sizeof(ft->UID), "%s", FILE_SEND_UID); |
|
| 1066 ft->gc = gc; |
|
| 1067 |
|
| 1068 g_free(tmp); |
|
| 1069 for (i--; i >= 0; i--) |
|
| 1070 g_free(messages[i]); |
|
| 1071 |
|
| 1072 purple_debug(PURPLE_DEBUG_MISC, "toc", |
|
| 1073 "English translation of RVOUS_PROPOSE: %s requests " |
|
| 1074 "Send File (i.e. send a file to you); %s:%d " |
|
| 1075 "(verified_ip:port), %d files at total size of " |
|
| 1076 "%d bytes.\n", user, vip, port, files, totalsize); |
|
| 1077 accept_file_dialog(ft); |
|
| 1078 } else if (!strcmp(uuid, FILE_GET_UID)) { |
|
| 1079 /* they want us to send a file */ |
|
| 1080 int unk[4], i; |
|
| 1081 char *messages[4], *tmp; |
|
| 1082 struct ft_request *ft; |
|
| 1083 |
|
| 1084 for (i = 0; i < 4; i++) { |
|
| 1085 sscanf(strtok(NULL, ":"), "%d", unk + i); |
|
| 1086 if (unk[i] == 10001) |
|
| 1087 break; |
|
| 1088 /* Trillian likes to send an empty token as a message, rather than |
|
| 1089 no message at all. */ |
|
| 1090 if (*(trillian + strlen(trillian) +1) != ':') |
|
| 1091 frombase64(strtok(NULL, ":"), &messages[i], NULL); |
|
| 1092 } |
|
| 1093 frombase64(strtok(NULL, ":"), &tmp, NULL); |
|
| 1094 |
|
| 1095 ft = g_new0(struct ft_request, 1); |
|
| 1096 ft->cookie = g_strdup(cookie); |
|
| 1097 ft->ip = g_strdup(pip); |
|
| 1098 ft->port = port; |
|
| 1099 if (i) |
|
| 1100 ft->message = g_strdup(messages[0]); |
|
| 1101 else |
|
| 1102 ft->message = NULL; |
|
| 1103 ft->user = g_strdup(user); |
|
| 1104 g_snprintf(ft->UID, sizeof(ft->UID), "%s", FILE_GET_UID); |
|
| 1105 ft->gc = gc; |
|
| 1106 |
|
| 1107 g_free(tmp); |
|
| 1108 for (i--; i >= 0; i--) |
|
| 1109 g_free(messages[i]); |
|
| 1110 |
|
| 1111 accept_file_dialog(ft); |
|
| 1112 } else if (!strcmp(uuid, VOICE_UID)) { |
|
| 1113 /* oh goody. voice over ip. fun stuff. */ |
|
| 1114 } else if (!strcmp(uuid, B_ICON_UID)) { |
|
| 1115 int unk[4], i; |
|
| 1116 char *messages[4]; |
|
| 1117 struct buddy_icon *icon; |
|
| 1118 |
|
| 1119 for (i = 0; i < 4; i++) { |
|
| 1120 sscanf(strtok(NULL, ":"), "%d", unk + i); |
|
| 1121 if (unk[i] == 10001) |
|
| 1122 break; |
|
| 1123 frombase64(strtok(NULL, ":"), &messages[i], NULL); |
|
| 1124 } |
|
| 1125 frombase64(strtok(NULL, ":"), (char **)&icon, NULL); |
|
| 1126 |
|
| 1127 purple_debug(PURPLE_DEBUG_MISC, "toc", |
|
| 1128 "received icon of length %d\n", icon->len); |
|
| 1129 g_free(icon); |
|
| 1130 for (i--; i >= 0; i--) |
|
| 1131 g_free(messages[i]); |
|
| 1132 } else if (!strcmp(uuid, IMAGE_UID)) { |
|
| 1133 /* aka Direct IM */ |
|
| 1134 } else { |
|
| 1135 purple_debug(PURPLE_DEBUG_ERROR, "toc", |
|
| 1136 "Don't know what to do with RVOUS UUID %s\n", uuid); |
|
| 1137 /* do we have to do anything here? i think it just times out */ |
|
| 1138 } |
|
| 1139 #endif |
|
| 1140 } else { |
|
| 1141 purple_debug(PURPLE_DEBUG_ERROR, "toc", |
|
| 1142 "don't know what to do with %s\n", c); |
|
| 1143 } |
|
| 1144 } |
|
| 1145 |
|
| 1146 static int toc_send_im(PurpleConnection *gc, const char *name, const char *message, PurpleMessageFlags flags) |
|
| 1147 { |
|
| 1148 char *buf1, *buf2; |
|
| 1149 |
|
| 1150 #if 1 |
|
| 1151 /* This is the old, non-i18n way */ |
|
| 1152 buf1 = escape_text(message); |
|
| 1153 if (strlen(buf1) + 52 > MSG_LEN) { |
|
| 1154 g_free(buf1); |
|
| 1155 return -E2BIG; |
|
| 1156 } |
|
| 1157 buf2 = g_strdup_printf("toc_send_im %s \"%s\"%s", purple_normalize(gc->account, name), buf1, |
|
| 1158 ((flags & PURPLE_MESSAGE_AUTO_RESP) ? " auto" : "")); |
|
| 1159 g_free(buf1); |
|
| 1160 #else |
|
| 1161 /* This doesn't work yet. See the comments below for details */ |
|
| 1162 buf1 = purple_strreplace(message, "\"", "\\\""); |
|
| 1163 |
|
| 1164 /* |
|
| 1165 * We still need to determine what encoding should be used and send the |
|
| 1166 * message in that encoding. This should be done the same as in |
|
| 1167 * oscar_encoding_check() in oscar.c. There is no encoding flag sent |
|
| 1168 * along with the message--the TOC to OSCAR proxy server must just |
|
| 1169 * use a lil' algorithm to determine what the actual encoding is. |
|
| 1170 * |
|
| 1171 * After that, you need to convert buf1 to that encoding, and keep track |
|
| 1172 * of the length of the resulting string. Then you need to make sure |
|
| 1173 * that length is passed to sflap_send(). |
|
| 1174 */ |
|
| 1175 |
|
| 1176 if (strlen(buf1) + 52 > MSG_LEN) { |
|
| 1177 g_free(buf1); |
|
| 1178 return -E2BIG; |
|
| 1179 } |
|
| 1180 |
|
| 1181 buf2 = g_strdup_printf("toc2_send_im_enc %s F U en \"%s\" %s", purple_normalize(gc->account, name), buf1, |
|
| 1182 ((flags & PURPLE_MESSAGE_AUTO_RESP) ? "auto" : "")); |
|
| 1183 g_free(buf1); |
|
| 1184 #endif |
|
| 1185 |
|
| 1186 sflap_send(gc, buf2, -1, TYPE_DATA); |
|
| 1187 g_free(buf2); |
|
| 1188 |
|
| 1189 return 1; |
|
| 1190 } |
|
| 1191 |
|
| 1192 static void toc_set_config(PurpleConnection *gc) |
|
| 1193 { |
|
| 1194 char *buf = g_malloc(MSG_LEN), snd[BUF_LEN * 2]; |
|
| 1195 toc_build_config(gc->account, buf, MSG_LEN - strlen("toc_set_config \\{\\}"), FALSE); |
|
| 1196 g_snprintf(snd, MSG_LEN, "toc_set_config {%s}", buf); |
|
| 1197 sflap_send(gc, snd, -1, TYPE_DATA); |
|
| 1198 g_free(buf); |
|
| 1199 } |
|
| 1200 |
|
| 1201 static void toc_get_info(PurpleConnection *gc, const char *name) |
|
| 1202 { |
|
| 1203 char buf[BUF_LEN * 2]; |
|
| 1204 g_snprintf(buf, MSG_LEN, "toc_get_info %s", purple_normalize(gc->account, name)); |
|
| 1205 sflap_send(gc, buf, -1, TYPE_DATA); |
|
| 1206 } |
|
| 1207 |
|
| 1208 /* Should be implemented as an Account Action? */ |
|
| 1209 static void toc_get_dir(PurpleBlistNode *node, gpointer data) |
|
| 1210 { |
|
| 1211 PurpleBuddy *buddy; |
|
| 1212 PurpleConnection *gc; |
|
| 1213 char buf[BUF_LEN * 2]; |
|
| 1214 |
|
| 1215 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); |
|
| 1216 |
|
| 1217 buddy = (PurpleBuddy *) node; |
|
| 1218 gc = purple_account_get_connection(buddy->account); |
|
| 1219 |
|
| 1220 g_snprintf(buf, MSG_LEN, "toc_get_dir %s", |
|
| 1221 purple_normalize(buddy->account, buddy->name)); |
|
| 1222 sflap_send(gc, buf, -1, TYPE_DATA); |
|
| 1223 } |
|
| 1224 |
|
| 1225 #if 0 |
|
| 1226 /* Should be implemented as an Account Action */ |
|
| 1227 static void toc_set_dir(PurpleConnection *g, const char *first, const char *middle, const char *last, |
|
| 1228 const char *maiden, const char *city, const char *state, const char *country, int web) |
|
| 1229 { |
|
| 1230 char *buf3, buf2[BUF_LEN * 4], buf[BUF_LEN]; |
|
| 1231 g_snprintf(buf2, sizeof(buf2), "%s:%s:%s:%s:%s:%s:%s:%s", first, |
|
| 1232 middle, last, maiden, city, state, country, (web == 1) ? "Y" : ""); |
|
| 1233 buf3 = escape_text(buf2); |
|
| 1234 g_snprintf(buf, sizeof(buf), "toc_set_dir %s", buf3); |
|
| 1235 g_free(buf3); |
|
| 1236 sflap_send(g, buf, -1, TYPE_DATA); |
|
| 1237 } |
|
| 1238 #endif |
|
| 1239 |
|
| 1240 #if 0 |
|
| 1241 /* Should be implemented as an Account Action */ |
|
| 1242 static void toc_dir_search(PurpleConnection *g, const char *first, const char *middle, const char *last, |
|
| 1243 const char *maiden, const char *city, const char *state, const char *country, const char *email) |
|
| 1244 { |
|
| 1245 char buf[BUF_LONG]; |
|
| 1246 g_snprintf(buf, sizeof(buf) / 2, "toc_dir_search %s:%s:%s:%s:%s:%s:%s:%s", first, middle, |
|
| 1247 last, maiden, city, state, country, email); |
|
| 1248 purple_debug(PURPLE_DEBUG_INFO, "toc", |
|
| 1249 "Searching for: %s,%s,%s,%s,%s,%s,%s\n", |
|
| 1250 first, middle, last, maiden, |
|
| 1251 city, state, country); |
|
| 1252 sflap_send(g, buf, -1, TYPE_DATA); |
|
| 1253 } |
|
| 1254 #endif |
|
| 1255 |
|
| 1256 static void toc_set_status(PurpleAccount *account, PurpleStatus *status) |
|
| 1257 { |
|
| 1258 #if 0 /* do we care about TOC any more? */ |
|
| 1259 char buf[BUF_LEN * 2]; |
|
| 1260 if (gc->away) { |
|
| 1261 g_free(gc->away); |
|
| 1262 gc->away = NULL; |
|
| 1263 } |
|
| 1264 if (message) { |
|
| 1265 char *tmp; |
|
| 1266 gc->away = g_strdup(message); |
|
| 1267 tmp = escape_text(message); |
|
| 1268 g_snprintf(buf, MSG_LEN, "toc_set_away \"%s\"", tmp); |
|
| 1269 g_free(tmp); |
|
| 1270 } else |
|
| 1271 g_snprintf(buf, MSG_LEN, "toc_set_away \"\""); |
|
| 1272 sflap_send(g, buf, -1, TYPE_DATA); |
|
| 1273 #endif |
|
| 1274 } |
|
| 1275 |
|
| 1276 static void toc_set_info(PurpleConnection *g, const char *info) |
|
| 1277 { |
|
| 1278 char buf[BUF_LEN * 2], *buf2; |
|
| 1279 buf2 = escape_text(info); |
|
| 1280 g_snprintf(buf, sizeof(buf), "toc_set_info \"%s\n\"", buf2); |
|
| 1281 g_free(buf2); |
|
| 1282 sflap_send(g, buf, -1, TYPE_DATA); |
|
| 1283 } |
|
| 1284 |
|
| 1285 static void toc_change_passwd(PurpleConnection *g, const char *orig, const char *new) |
|
| 1286 { |
|
| 1287 char buf[BUF_LEN * 2]; |
|
| 1288 g_snprintf(buf, BUF_LONG, "toc_change_passwd %s %s", orig, new); |
|
| 1289 sflap_send(g, buf, -1, TYPE_DATA); |
|
| 1290 } |
|
| 1291 |
|
| 1292 static void |
|
| 1293 toc_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) |
|
| 1294 { |
|
| 1295 char buf[BUF_LEN * 2]; |
|
| 1296 g_snprintf(buf, sizeof(buf), "toc_add_buddy %s", purple_normalize(gc->account, buddy->name)); |
|
| 1297 sflap_send(gc, buf, -1, TYPE_DATA); |
|
| 1298 toc_set_config(gc); |
|
| 1299 } |
|
| 1300 |
|
| 1301 static void toc_add_buddies(PurpleConnection *gc, GList *buddies, GList *groups) |
|
| 1302 { |
|
| 1303 char buf[BUF_LEN * 2]; |
|
| 1304 int n; |
|
| 1305 GList *cur; |
|
| 1306 |
|
| 1307 n = g_snprintf(buf, sizeof(buf), "toc_add_buddy"); |
|
| 1308 for (cur = buddies; cur != NULL; cur = cur->next) { |
|
| 1309 PurpleBuddy *buddy = cur->data; |
|
| 1310 |
|
| 1311 if (strlen(purple_normalize(gc->account, buddy->name)) + n + 32 > MSG_LEN) { |
|
| 1312 sflap_send(gc, buf, -1, TYPE_DATA); |
|
| 1313 n = g_snprintf(buf, sizeof(buf), "toc_add_buddy"); |
|
| 1314 } |
|
| 1315 n += g_snprintf(buf + n, sizeof(buf) - n, " %s", purple_normalize(gc->account, buddy->name)); |
|
| 1316 } |
|
| 1317 sflap_send(gc, buf, -1, TYPE_DATA); |
|
| 1318 } |
|
| 1319 |
|
| 1320 static void toc_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) |
|
| 1321 { |
|
| 1322 char buf[BUF_LEN * 2]; |
|
| 1323 g_snprintf(buf, sizeof(buf), "toc_remove_buddy %s", purple_normalize(gc->account, buddy->name)); |
|
| 1324 sflap_send(gc, buf, -1, TYPE_DATA); |
|
| 1325 toc_set_config(gc); |
|
| 1326 } |
|
| 1327 |
|
| 1328 static void toc_remove_buddies(PurpleConnection *gc, GList *buddies, GList *groups) |
|
| 1329 { |
|
| 1330 char buf[BUF_LEN * 2]; |
|
| 1331 int n; |
|
| 1332 GList *cur; |
|
| 1333 |
|
| 1334 n = g_snprintf(buf, sizeof(buf), "toc_remove_buddy"); |
|
| 1335 for (cur = buddies; cur != NULL; cur = cur->next) { |
|
| 1336 PurpleBuddy *buddy = cur->data; |
|
| 1337 |
|
| 1338 if (strlen(purple_normalize(gc->account, buddy->name)) + n + 32 > MSG_LEN) { |
|
| 1339 sflap_send(gc, buf, -1, TYPE_DATA); |
|
| 1340 n = g_snprintf(buf, sizeof(buf), "toc_remove_buddy"); |
|
| 1341 } |
|
| 1342 n += g_snprintf(buf + n, sizeof(buf) - n, " %s", purple_normalize(gc->account, buddy->name)); |
|
| 1343 } |
|
| 1344 sflap_send(gc, buf, -1, TYPE_DATA); |
|
| 1345 toc_set_config(gc); |
|
| 1346 } |
|
| 1347 |
|
| 1348 static void toc_set_idle(PurpleConnection *g, int time) |
|
| 1349 { |
|
| 1350 char buf[BUF_LEN * 2]; |
|
| 1351 g_snprintf(buf, sizeof(buf), "toc_set_idle %d", time); |
|
| 1352 sflap_send(g, buf, -1, TYPE_DATA); |
|
| 1353 } |
|
| 1354 |
|
| 1355 static void toc_warn(PurpleConnection *g, const char *name, int anon) |
|
| 1356 { |
|
| 1357 char send[BUF_LEN * 2]; |
|
| 1358 g_snprintf(send, 255, "toc_evil %s %s", name, ((anon) ? "anon" : "norm")); |
|
| 1359 sflap_send(g, send, -1, TYPE_DATA); |
|
| 1360 } |
|
| 1361 |
|
| 1362 static GList *toc_chat_info(PurpleConnection *gc) |
|
| 1363 { |
|
| 1364 GList *m = NULL; |
|
| 1365 struct proto_chat_entry *pce; |
|
| 1366 |
|
| 1367 pce = g_new0(struct proto_chat_entry, 1); |
|
| 1368 pce->label = _("_Group:"); |
|
| 1369 pce->identifier = "room"; |
|
| 1370 m = g_list_append(m, pce); |
|
| 1371 |
|
| 1372 pce = g_new0(struct proto_chat_entry, 1); |
|
| 1373 pce->label = _("_Exchange:"); |
|
| 1374 pce->identifier = "exchange"; |
|
| 1375 pce->is_int = TRUE; |
|
| 1376 pce->min = 4; |
|
| 1377 pce->max = 20; |
|
| 1378 m = g_list_append(m, pce); |
|
| 1379 |
|
| 1380 return m; |
|
| 1381 } |
|
| 1382 |
|
| 1383 GHashTable *toc_chat_info_defaults(PurpleConnection *gc, const char *chat_name) |
|
| 1384 { |
|
| 1385 GHashTable *defaults; |
|
| 1386 |
|
| 1387 defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free); |
|
| 1388 |
|
| 1389 if (chat_name != NULL) |
|
| 1390 g_hash_table_insert(defaults, "room", g_strdup(chat_name)); |
|
| 1391 |
|
| 1392 return defaults; |
|
| 1393 } |
|
| 1394 |
|
| 1395 static void toc_join_chat(PurpleConnection *g, GHashTable *data) |
|
| 1396 { |
|
| 1397 char buf[BUF_LONG]; |
|
| 1398 char *name, *exchange; |
|
| 1399 char *id; |
|
| 1400 |
|
| 1401 name = g_hash_table_lookup(data, "room"); |
|
| 1402 exchange = g_hash_table_lookup(data, "exchange"); |
|
| 1403 id = g_hash_table_lookup(data, "id"); |
|
| 1404 |
|
| 1405 if (id) { |
|
| 1406 g_snprintf(buf, 255, "toc_chat_accept %d", atoi(id)); |
|
| 1407 } else { |
|
| 1408 g_snprintf(buf, sizeof(buf) / 2, "toc_chat_join %d \"%s\"", atoi(exchange), name); |
|
| 1409 } |
|
| 1410 |
|
| 1411 sflap_send(g, buf, -1, TYPE_DATA); |
|
| 1412 } |
|
| 1413 |
|
| 1414 static void toc_chat_invite(PurpleConnection *gc, int id, const char *message, const char *name) |
|
| 1415 { |
|
| 1416 char buf[BUF_LONG]; |
|
| 1417 g_snprintf(buf, sizeof(buf) / 2, "toc_chat_invite %d \"%s\" %s", id, |
|
| 1418 message ? message : "", purple_normalize(gc->account, name)); |
|
| 1419 sflap_send(gc, buf, -1, TYPE_DATA); |
|
| 1420 } |
|
| 1421 |
|
| 1422 static void toc_chat_leave(PurpleConnection *g, int id) |
|
| 1423 { |
|
| 1424 GSList *bcs = g->buddy_chats; |
|
| 1425 PurpleConversation *b = NULL; |
|
| 1426 char buf[BUF_LEN * 2]; |
|
| 1427 |
|
| 1428 while (bcs) { |
|
| 1429 b = (PurpleConversation *)bcs->data; |
|
| 1430 if (id == purple_conv_chat_get_id(PURPLE_CONV_CHAT(b))) |
|
| 1431 break; |
|
| 1432 b = NULL; |
|
| 1433 bcs = bcs->next; |
|
| 1434 } |
|
| 1435 |
|
| 1436 if (!b) |
|
| 1437 return; /* can this happen? */ |
|
| 1438 |
|
| 1439 if (purple_conversation_get_account(b) == NULL) { |
|
| 1440 /* TOC already kicked us out of this room */ |
|
| 1441 serv_got_chat_left(g, id); |
|
| 1442 } |
|
| 1443 else { |
|
| 1444 g_snprintf(buf, 255, "toc_chat_leave %d", id); |
|
| 1445 sflap_send(g, buf, -1, TYPE_DATA); |
|
| 1446 } |
|
| 1447 } |
|
| 1448 |
|
| 1449 static void toc_chat_whisper(PurpleConnection *gc, int id, const char *who, const char *message) |
|
| 1450 { |
|
| 1451 char *buf1, *buf2; |
|
| 1452 buf1 = escape_text(message); |
|
| 1453 buf2 = g_strdup_printf("toc_chat_whisper %d %s \"%s\"", id, purple_normalize(gc->account, who), buf1); |
|
| 1454 g_free(buf1); |
|
| 1455 sflap_send(gc, buf2, -1, TYPE_DATA); |
|
| 1456 g_free(buf2); |
|
| 1457 } |
|
| 1458 |
|
| 1459 static int toc_chat_send(PurpleConnection *g, int id, const char *message, PurpleMessageFlags flags) |
|
| 1460 { |
|
| 1461 char *buf1, *buf2; |
|
| 1462 buf1 = escape_text(message); |
|
| 1463 if (strlen(buf1) > 2000) { |
|
| 1464 g_free(buf1); |
|
| 1465 return -E2BIG; |
|
| 1466 } |
|
| 1467 buf2 = g_strdup_printf("toc_chat_send %d \"%s\"", id, buf1); |
|
| 1468 g_free(buf1); |
|
| 1469 sflap_send(g, buf2, -1, TYPE_DATA); |
|
| 1470 g_free(buf2); |
|
| 1471 return 0; |
|
| 1472 } |
|
| 1473 |
|
| 1474 static void toc_keepalive(PurpleConnection *gc) |
|
| 1475 { |
|
| 1476 sflap_send(gc, "", 0, TYPE_KEEPALIVE); |
|
| 1477 } |
|
| 1478 |
|
| 1479 static const char * |
|
| 1480 toc_normalize(const PurpleAccount *account, const char *str) |
|
| 1481 { |
|
| 1482 static char buf[BUF_LEN]; |
|
| 1483 char *tmp1, *tmp2; |
|
| 1484 int i, j; |
|
| 1485 |
|
| 1486 g_return_val_if_fail(str != NULL, NULL); |
|
| 1487 |
|
| 1488 strncpy(buf, str, BUF_LEN); |
|
| 1489 for (i=0, j=0; buf[j]; i++, j++) |
|
| 1490 { |
|
| 1491 while (buf[j] == ' ') |
|
| 1492 j++; |
|
| 1493 buf[i] = buf[j]; |
|
| 1494 } |
|
| 1495 buf[i] = '\0'; |
|
| 1496 |
|
| 1497 tmp1 = g_utf8_strdown(buf, -1); |
|
| 1498 tmp2 = g_utf8_normalize(tmp1, -1, G_NORMALIZE_DEFAULT); |
|
| 1499 g_snprintf(buf, sizeof(buf), "%s", tmp2); |
|
| 1500 g_free(tmp2); |
|
| 1501 g_free(tmp1); |
|
| 1502 |
|
| 1503 return buf; |
|
| 1504 } |
|
| 1505 |
|
| 1506 static const char *toc_list_icon(PurpleAccount *a, PurpleBuddy *b) |
|
| 1507 { |
|
| 1508 if (!b || (b && b->name && b->name[0] == '+')) { |
|
| 1509 if (a != NULL && isdigit(*purple_account_get_username(a))) |
|
| 1510 return "icq"; |
|
| 1511 else |
|
| 1512 return "aim"; |
|
| 1513 } |
|
| 1514 |
|
| 1515 if (b && b->name && isdigit(b->name[0])) |
|
| 1516 return "icq"; |
|
| 1517 return "aim"; |
|
| 1518 } |
|
| 1519 |
|
| 1520 static const char* toc_list_emblem(PurpleBuddy *b) |
|
| 1521 { |
|
| 1522 if (b->uc & UC_AOL) |
|
| 1523 return "aol"; |
|
| 1524 if (b->uc & UC_ADMIN) |
|
| 1525 return "admin"; |
|
| 1526 if (b->uc & UC_WIRELESS) |
|
| 1527 return "mobile"; |
|
| 1528 return NULL |
|
| 1529 } |
|
| 1530 |
|
| 1531 static GList *toc_blist_node_menu(PurpleBlistNode *node) |
|
| 1532 { |
|
| 1533 GList *m = NULL; |
|
| 1534 PurpleMenuAction *act; |
|
| 1535 |
|
| 1536 if(PURPLE_BLIST_NODE_IS_BUDDY(node)) { |
|
| 1537 act = purple_menu_action_new(_("Get Dir Info"), |
|
| 1538 toc_get_dir, NULL, NULL); |
|
| 1539 m = g_list_append(m, act); |
|
| 1540 } |
|
| 1541 |
|
| 1542 return m; |
|
| 1543 } |
|
| 1544 |
|
| 1545 static void toc_add_permit(PurpleConnection *gc, const char *who) |
|
| 1546 { |
|
| 1547 char buf2[BUF_LEN * 2]; |
|
| 1548 if (gc->account->perm_deny != 3) |
|
| 1549 return; |
|
| 1550 g_snprintf(buf2, sizeof(buf2), "toc_add_permit %s", purple_normalize(gc->account, who)); |
|
| 1551 sflap_send(gc, buf2, -1, TYPE_DATA); |
|
| 1552 toc_set_config(gc); |
|
| 1553 } |
|
| 1554 |
|
| 1555 static void toc_add_deny(PurpleConnection *gc, const char *who) |
|
| 1556 { |
|
| 1557 char buf2[BUF_LEN * 2]; |
|
| 1558 if (gc->account->perm_deny != 4) |
|
| 1559 return; |
|
| 1560 g_snprintf(buf2, sizeof(buf2), "toc_add_deny %s", purple_normalize(gc->account, who)); |
|
| 1561 sflap_send(gc, buf2, -1, TYPE_DATA); |
|
| 1562 toc_set_config(gc); |
|
| 1563 } |
|
| 1564 |
|
| 1565 static void toc_set_permit_deny(PurpleConnection *gc) |
|
| 1566 { |
|
| 1567 char buf2[BUF_LEN * 2]; |
|
| 1568 GSList *list; |
|
| 1569 int at; |
|
| 1570 |
|
| 1571 switch (gc->account->perm_deny) { |
|
| 1572 case 1: |
|
| 1573 /* permit all, deny none. to get here reliably we need to have been in permit |
|
| 1574 * mode, and send an empty toc_add_deny message, which will switch us to deny none */ |
|
| 1575 g_snprintf(buf2, sizeof(buf2), "toc_add_permit "); |
|
| 1576 sflap_send(gc, buf2, -1, TYPE_DATA); |
|
| 1577 g_snprintf(buf2, sizeof(buf2), "toc_add_deny "); |
|
| 1578 sflap_send(gc, buf2, -1, TYPE_DATA); |
|
| 1579 break; |
|
| 1580 case 2: |
|
| 1581 /* deny all, permit none. to get here reliably we need to have been in deny |
|
| 1582 * mode, and send an empty toc_add_permit message, which will switch us to permit none */ |
|
| 1583 g_snprintf(buf2, sizeof(buf2), "toc_add_deny "); |
|
| 1584 sflap_send(gc, buf2, -1, TYPE_DATA); |
|
| 1585 g_snprintf(buf2, sizeof(buf2), "toc_add_permit "); |
|
| 1586 sflap_send(gc, buf2, -1, TYPE_DATA); |
|
| 1587 break; |
|
| 1588 case 3: |
|
| 1589 /* permit some. we want to switch to deny mode first, then send the toc_add_permit |
|
| 1590 * message, which will clear and set our permit list. toc sucks. */ |
|
| 1591 g_snprintf(buf2, sizeof(buf2), "toc_add_deny "); |
|
| 1592 sflap_send(gc, buf2, -1, TYPE_DATA); |
|
| 1593 |
|
| 1594 at = g_snprintf(buf2, sizeof(buf2), "toc_add_permit "); |
|
| 1595 list = gc->account->permit; |
|
| 1596 while (list) { |
|
| 1597 at += g_snprintf(buf2 + at, sizeof(buf2) - at, "%s ", purple_normalize(gc->account, list->data)); |
|
| 1598 if (at > MSG_LEN + 32) { /* from out my ass comes greatness */ |
|
| 1599 sflap_send(gc, buf2, -1, TYPE_DATA); |
|
| 1600 at = g_snprintf(buf2, sizeof(buf2), "toc_add_permit "); |
|
| 1601 } |
|
| 1602 list = list->next; |
|
| 1603 } |
|
| 1604 sflap_send(gc, buf2, -1, TYPE_DATA); |
|
| 1605 break; |
|
| 1606 case 4: |
|
| 1607 /* deny some. we want to switch to permit mode first, then send the toc_add_deny |
|
| 1608 * message, which will clear and set our deny list. toc sucks. */ |
|
| 1609 g_snprintf(buf2, sizeof(buf2), "toc_add_permit "); |
|
| 1610 sflap_send(gc, buf2, -1, TYPE_DATA); |
|
| 1611 |
|
| 1612 at = g_snprintf(buf2, sizeof(buf2), "toc_add_deny "); |
|
| 1613 list = gc->account->deny; |
|
| 1614 while (list) { |
|
| 1615 at += g_snprintf(buf2 + at, sizeof(buf2) - at, "%s ", purple_normalize(gc->account, list->data)); |
|
| 1616 if (at > MSG_LEN + 32) { /* from out my ass comes greatness */ |
|
| 1617 sflap_send(gc, buf2, -1, TYPE_DATA); |
|
| 1618 at = g_snprintf(buf2, sizeof(buf2), "toc_add_deny "); |
|
| 1619 } |
|
| 1620 list = list->next; |
|
| 1621 } |
|
| 1622 sflap_send(gc, buf2, -1, TYPE_DATA); |
|
| 1623 break; |
|
| 1624 default: |
|
| 1625 break; |
|
| 1626 } |
|
| 1627 toc_set_config(gc); |
|
| 1628 } |
|
| 1629 |
|
| 1630 static void toc_rem_permit(PurpleConnection *gc, const char *who) |
|
| 1631 { |
|
| 1632 if (gc->account->perm_deny != 3) |
|
| 1633 return; |
|
| 1634 toc_set_permit_deny(gc); |
|
| 1635 } |
|
| 1636 |
|
| 1637 static void toc_rem_deny(PurpleConnection *gc, const char *who) |
|
| 1638 { |
|
| 1639 if (gc->account->perm_deny != 4) |
|
| 1640 return; |
|
| 1641 toc_set_permit_deny(gc); |
|
| 1642 } |
|
| 1643 |
|
| 1644 static GList *toc_away_states(PurpleAccount *account) |
|
| 1645 { |
|
| 1646 #if 0 /* do we care about TOC any more? */ |
|
| 1647 return g_list_append(NULL, PURPLE_AWAY_CUSTOM); |
|
| 1648 #else |
|
| 1649 return NULL; |
|
| 1650 #endif |
|
| 1651 } |
|
| 1652 |
|
| 1653 static void |
|
| 1654 show_set_info(PurplePluginAction *action) |
|
| 1655 { |
|
| 1656 PurpleConnection *gc = (PurpleConnection *) action->context; |
|
| 1657 purple_account_request_change_user_info(purple_connection_get_account(gc)); |
|
| 1658 } |
|
| 1659 |
|
| 1660 static void |
|
| 1661 change_pass(PurplePluginAction *action) |
|
| 1662 { |
|
| 1663 PurpleConnection *gc = (PurpleConnection *) action->context; |
|
| 1664 purple_account_request_change_password(purple_connection_get_account(gc)); |
|
| 1665 } |
|
| 1666 |
|
| 1667 static GList *toc_actions(PurplePlugin *plugin, gpointer context) |
|
| 1668 { |
|
| 1669 GList *m = NULL; |
|
| 1670 PurplePluginAction *act; |
|
| 1671 |
|
| 1672 act = purple_plugin_action_new(_("Set User Info"), |
|
| 1673 show_set_info); |
|
| 1674 m = g_list_append(m, act); |
|
| 1675 |
|
| 1676 #if 0 |
|
| 1677 act = purple_plugin_action_new(_("Set Dir Info"), |
|
| 1678 show_set_dir); |
|
| 1679 m = g_list_append(m, act); |
|
| 1680 #endif |
|
| 1681 |
|
| 1682 act = purple_plugin_action_new(_("Change Password"), |
|
| 1683 change_pass); |
|
| 1684 m = g_list_append(m, act); |
|
| 1685 |
|
| 1686 return m; |
|
| 1687 } |
|
| 1688 |
|
| 1689 #if 0 |
|
| 1690 /********* |
|
| 1691 * RVOUS ACTIONS |
|
| 1692 ********/ |
|
| 1693 |
|
| 1694 struct file_header { |
|
| 1695 char magic[4]; /* 0 */ |
|
| 1696 short hdrlen; /* 4 */ |
|
| 1697 short hdrtype; /* 6 */ |
|
| 1698 char bcookie[8]; /* 8 */ |
|
| 1699 short encrypt; /* 16 */ |
|
| 1700 short compress; /* 18 */ |
|
| 1701 short totfiles; /* 20 */ |
|
| 1702 short filesleft; /* 22 */ |
|
| 1703 short totparts; /* 24 */ |
|
| 1704 short partsleft; /* 26 */ |
|
| 1705 long totsize; /* 28 */ |
|
| 1706 long size; /* 32 */ |
|
| 1707 long modtime; /* 36 */ |
|
| 1708 long checksum; /* 40 */ |
|
| 1709 long rfrcsum; /* 44 */ |
|
| 1710 long rfsize; /* 48 */ |
|
| 1711 long cretime; /* 52 */ |
|
| 1712 long rfcsum; /* 56 */ |
|
| 1713 long nrecvd; /* 60 */ |
|
| 1714 long recvcsum; /* 64 */ |
|
| 1715 char idstring[32]; /* 68 */ |
|
| 1716 char flags; /* 100 */ |
|
| 1717 char lnameoffset; /* 101 */ |
|
| 1718 char lsizeoffset; /* 102 */ |
|
| 1719 char dummy[69]; /* 103 */ |
|
| 1720 char macfileinfo[16]; /* 172 */ |
|
| 1721 short nencode; /* 188 */ |
|
| 1722 short nlanguage; /* 190 */ |
|
| 1723 char name[64]; /* 192 */ |
|
| 1724 /* 256 */ |
|
| 1725 }; |
|
| 1726 |
|
| 1727 struct file_transfer { |
|
| 1728 struct file_header hdr; |
|
| 1729 |
|
| 1730 PurpleConnection *gc; |
|
| 1731 |
|
| 1732 char *user; |
|
| 1733 char *cookie; |
|
| 1734 char *ip; |
|
| 1735 int port; |
|
| 1736 long size; |
|
| 1737 struct stat st; |
|
| 1738 |
|
| 1739 GtkWidget *window; |
|
| 1740 int files; |
|
| 1741 char *filename; |
|
| 1742 FILE *file; |
|
| 1743 int recvsize; |
|
| 1744 |
|
| 1745 gint inpa; |
|
| 1746 }; |
|
| 1747 |
|
| 1748 static void debug_header(struct file_transfer *ft) { |
|
| 1749 struct file_header *f = (struct file_header *)ft; |
|
| 1750 purple_debug(PURPLE_DEBUG_MISC, "toc", "FT HEADER:\n" |
|
| 1751 "\t%s %d 0x%04x\n" |
|
| 1752 "\t%s %d %d\n" |
|
| 1753 "\t%d %d %d %d %d %d\n" |
|
| 1754 "\t%d %d %d %d %d %d %d %d\n" |
|
| 1755 "\t%s\n" |
|
| 1756 "\t0x%02x, 0x%02x, 0x%02x\n" |
|
| 1757 "\t%s %s\n" |
|
| 1758 "\t%d %d\n" |
|
| 1759 "\t%s\n", |
|
| 1760 f->magic, ntohs(f->hdrlen), f->hdrtype, |
|
| 1761 f->bcookie, ntohs(f->encrypt), ntohs(f->compress), |
|
| 1762 ntohs(f->totfiles), ntohs(f->filesleft), ntohs(f->totparts), |
|
| 1763 ntohs(f->partsleft), ntohl(f->totsize), ntohl(f->size), |
|
| 1764 ntohl(f->modtime), ntohl(f->checksum), ntohl(f->rfrcsum), ntohl(f->rfsize), |
|
| 1765 ntohl(f->cretime), ntohl(f->rfcsum), ntohl(f->nrecvd), |
|
| 1766 ntohl(f->recvcsum), |
|
| 1767 f->idstring, |
|
| 1768 f->flags, f->lnameoffset, f->lsizeoffset, |
|
| 1769 f->dummy, f->macfileinfo, |
|
| 1770 ntohs(f->nencode), ntohs(f->nlanguage), |
|
| 1771 f->name); |
|
| 1772 } |
|
| 1773 |
|
| 1774 static void toc_send_file_callback(gpointer data, gint source, PurpleInputCondition cond) |
|
| 1775 { |
|
| 1776 char buf[BUF_LONG]; |
|
| 1777 int rt, i; |
|
| 1778 |
|
| 1779 struct file_transfer *ft = data; |
|
| 1780 |
|
| 1781 if (ft->hdr.hdrtype != 0x202) { |
|
| 1782 char *buf; |
|
| 1783 frombase64(ft->cookie, &buf, NULL); |
|
| 1784 |
|
| 1785 read(source, ft, 8); |
|
| 1786 read(source, &ft->hdr.bcookie, MIN(256 - 8, ntohs(ft->hdr.hdrlen) - 8)); |
|
| 1787 debug_header(ft); |
|
| 1788 |
|
| 1789 ft->hdr.hdrtype = 0x202; |
|
| 1790 memcpy(ft->hdr.bcookie, buf, 8); |
|
| 1791 g_free(buf); |
|
| 1792 ft->hdr.encrypt = 0; ft->hdr.compress = 0; |
|
| 1793 debug_header(ft); |
|
| 1794 write(source, ft, 256); |
|
| 1795 |
|
| 1796 if (ft->files == 1) { |
|
| 1797 ft->file = g_fopen(ft->filename, "w"); |
|
| 1798 if (!ft->file) { |
|
| 1799 buf = g_strdup_printf(_("Could not open %s for writing!"), ft->filename); |
|
| 1800 purple_notify_error(ft->gc, NULL, buf, g_strerror(errno)); |
|
| 1801 g_free(buf); |
|
| 1802 purple_input_remove(ft->inpa); |
|
| 1803 close(source); |
|
| 1804 g_free(ft->filename); |
|
| 1805 g_free(ft->user); |
|
| 1806 g_free(ft->ip); |
|
| 1807 g_free(ft->cookie); |
|
| 1808 g_free(ft); |
|
| 1809 } |
|
| 1810 } else { |
|
| 1811 buf = g_strdup_printf("%s/%s", ft->filename, ft->hdr.name); |
|
| 1812 ft->file = g_fopen(buf, "w"); |
|
| 1813 g_free(buf); |
|
| 1814 if (!ft->file) { |
|
| 1815 buf = g_strdup_printf("Could not open %s/%s for writing!", ft->filename, |
|
| 1816 ft->hdr.name); |
|
| 1817 purple_notify_error(ft->gc, NULL, buf, g_strerror(errno)); |
|
| 1818 g_free(buf); |
|
| 1819 purple_input_remove(ft->inpa); |
|
| 1820 close(source); |
|
| 1821 g_free(ft->filename); |
|
| 1822 g_free(ft->user); |
|
| 1823 g_free(ft->ip); |
|
| 1824 g_free(ft->cookie); |
|
| 1825 g_free(ft); |
|
| 1826 } |
|
| 1827 } |
|
| 1828 |
|
| 1829 return; |
|
| 1830 } |
|
| 1831 |
|
| 1832 rt = read(source, buf, MIN(ntohl(ft->hdr.size) - ft->recvsize, 1024)); |
|
| 1833 if (rt < 0) { |
|
| 1834 purple_notify_error(ft->gc, NULL, |
|
| 1835 _("File transfer failed; other side probably " |
|
| 1836 "canceled."), NULL); |
|
| 1837 purple_input_remove(ft->inpa); |
|
| 1838 close(source); |
|
| 1839 g_free(ft->user); |
|
| 1840 g_free(ft->ip); |
|
| 1841 g_free(ft->cookie); |
|
| 1842 if (ft->file) |
|
| 1843 fclose(ft->file); |
|
| 1844 g_free(ft); |
|
| 1845 return; |
|
| 1846 } |
|
| 1847 ft->recvsize += rt; |
|
| 1848 for (i = 0; i < rt; i++) |
|
| 1849 fprintf(ft->file, "%c", buf[i]); |
|
| 1850 |
|
| 1851 if (ft->recvsize == ntohl(ft->hdr.size)) { |
|
| 1852 ft->hdr.hdrtype = htons(0x0204); |
|
| 1853 ft->hdr.filesleft = htons(ntohs(ft->hdr.filesleft) - 1); |
|
| 1854 ft->hdr.partsleft = htons(ntohs(ft->hdr.partsleft) - 1); |
|
| 1855 ft->hdr.recvcsum = ft->hdr.checksum; /* uh... */ |
|
| 1856 ft->hdr.nrecvd = htons(ntohs(ft->hdr.nrecvd) + 1); |
|
| 1857 ft->hdr.flags = 0; |
|
| 1858 write(source, ft, 256); |
|
| 1859 debug_header(ft); |
|
| 1860 ft->recvsize = 0; |
|
| 1861 fclose(ft->file); |
|
| 1862 if (ft->hdr.filesleft == 0) { |
|
| 1863 purple_input_remove(ft->inpa); |
|
| 1864 close(source); |
|
| 1865 g_free(ft->filename); |
|
| 1866 g_free(ft->user); |
|
| 1867 g_free(ft->ip); |
|
| 1868 g_free(ft->cookie); |
|
| 1869 g_free(ft); |
|
| 1870 } |
|
| 1871 } |
|
| 1872 } |
|
| 1873 |
|
| 1874 static void toc_send_file_connect(gpointer data, gint src, PurpleInputCondition cond) |
|
| 1875 { |
|
| 1876 struct file_transfer *ft = data; |
|
| 1877 |
|
| 1878 if (src == -1) { |
|
| 1879 purple_notify_error(ft->gc, NULL, |
|
| 1880 _("Could not connect for transfer."), NULL); |
|
| 1881 g_free(ft->filename); |
|
| 1882 g_free(ft->cookie); |
|
| 1883 g_free(ft->user); |
|
| 1884 g_free(ft->ip); |
|
| 1885 g_free(ft); |
|
| 1886 return; |
|
| 1887 } |
|
| 1888 |
|
| 1889 ft->inpa = purple_input_add(src, PURPLE_INPUT_READ, toc_send_file_callback, ft); |
|
| 1890 } |
|
| 1891 |
|
| 1892 static void toc_send_file(gpointer a, struct file_transfer *old_ft) |
|
| 1893 { |
|
| 1894 struct file_transfer *ft; |
|
| 1895 const char *dirname = gtk_file_selection_get_filename(GTK_FILE_SELECTION(old_ft->window)); |
|
| 1896 PurpleAccount *account; |
|
| 1897 char buf[BUF_LEN * 2]; |
|
| 1898 |
|
| 1899 if (purple_gtk_check_if_dir(dirname, GTK_FILE_SELECTION(old_ft->window))) |
|
| 1900 return; |
|
| 1901 ft = g_new0(struct file_transfer, 1); |
|
| 1902 if (old_ft->files == 1) |
|
| 1903 ft->filename = g_strdup(dirname); |
|
| 1904 else |
|
| 1905 ft->filename = g_path_get_dirname(dirname); |
|
| 1906 ft->cookie = g_strdup(old_ft->cookie); |
|
| 1907 ft->user = g_strdup(old_ft->user); |
|
| 1908 ft->ip = g_strdup(old_ft->ip); |
|
| 1909 ft->files = old_ft->files; |
|
| 1910 ft->port = old_ft->port; |
|
| 1911 ft->gc = old_ft->gc; |
|
| 1912 account = ft->gc->account; |
|
| 1913 gtk_widget_destroy(old_ft->window); |
|
| 1914 |
|
| 1915 g_snprintf(buf, sizeof(buf), "toc_rvous_accept %s %s %s", ft->user, ft->cookie, FILE_SEND_UID); |
|
| 1916 sflap_send(ft->gc, buf, -1, TYPE_DATA); |
|
| 1917 |
|
| 1918 if (purple_proxy_connect(ft->gc, account, ft->ip, ft->port, toc_send_file_connect, ft) != 0) { |
|
| 1919 purple_notify_error(ft->gc, NULL, |
|
| 1920 _("Could not connect for transfer."), NULL); |
|
| 1921 g_free(ft->filename); |
|
| 1922 g_free(ft->cookie); |
|
| 1923 g_free(ft->user); |
|
| 1924 g_free(ft->ip); |
|
| 1925 g_free(ft); |
|
| 1926 return; |
|
| 1927 } |
|
| 1928 } |
|
| 1929 |
|
| 1930 static void toc_get_file_callback(gpointer data, gint source, PurpleInputCondition cond) |
|
| 1931 { |
|
| 1932 char buf[BUF_LONG]; |
|
| 1933 |
|
| 1934 struct file_transfer *ft = data; |
|
| 1935 |
|
| 1936 if (cond & PURPLE_INPUT_WRITE) { |
|
| 1937 int remain = MIN(ntohl(ft->hdr.totsize) - ft->recvsize, 1024); |
|
| 1938 int i; |
|
| 1939 for (i = 0; i < remain; i++) |
|
| 1940 fscanf(ft->file, "%c", &buf[i]); |
|
| 1941 write(source, buf, remain); |
|
| 1942 ft->recvsize += remain; |
|
| 1943 if (ft->recvsize == ntohl(ft->hdr.totsize)) { |
|
| 1944 purple_input_remove(ft->inpa); |
|
| 1945 ft->inpa = purple_input_add(source, PURPLE_INPUT_READ, |
|
| 1946 toc_get_file_callback, ft); |
|
| 1947 } |
|
| 1948 return; |
|
| 1949 } |
|
| 1950 |
|
| 1951 if (ft->hdr.hdrtype == htons(0x1108)) { |
|
| 1952 struct tm *fortime; |
|
| 1953 struct stat st; |
|
| 1954 char *basename; |
|
| 1955 |
|
| 1956 read(source, ft, 8); |
|
| 1957 read(source, &ft->hdr.bcookie, MIN(256 - 8, ntohs(ft->hdr.hdrlen) - 8)); |
|
| 1958 debug_header(ft); |
|
| 1959 |
|
| 1960 g_stat(ft->filename, &st); |
|
| 1961 fortime = localtime(&st.st_mtime); |
|
| 1962 basename = g_path_get_basename(ft->filename); |
|
| 1963 g_snprintf(buf, sizeof(buf), "%2d/%2d/%4d %2d:%2d %8ld %s\r\n", |
|
| 1964 fortime->tm_mon + 1, fortime->tm_mday, fortime->tm_year + 1900, |
|
| 1965 fortime->tm_hour + 1, fortime->tm_min + 1, (long)st.st_size, |
|
| 1966 basename); |
|
| 1967 write(source, buf, ntohl(ft->hdr.size)); |
|
| 1968 g_free(basename); |
|
| 1969 return; |
|
| 1970 } |
|
| 1971 |
|
| 1972 if (ft->hdr.hdrtype == htons(0x1209)) { |
|
| 1973 read(source, ft, 8); |
|
| 1974 read(source, &ft->hdr.bcookie, MIN(256 - 8, ntohs(ft->hdr.hdrlen) - 8)); |
|
| 1975 debug_header(ft); |
|
| 1976 return; |
|
| 1977 } |
|
| 1978 |
|
| 1979 if (ft->hdr.hdrtype == htons(0x120b)) { |
|
| 1980 read(source, ft, 8); |
|
| 1981 read(source, &ft->hdr.bcookie, MIN(256 - 8, ntohs(ft->hdr.hdrlen) - 8)); |
|
| 1982 debug_header(ft); |
|
| 1983 |
|
| 1984 if (ft->hdr.hdrtype != htons(0x120c)) { |
|
| 1985 g_snprintf(buf, sizeof(buf), "%s decided to cancel the transfer", ft->user); |
|
| 1986 purple_notify_error(ft->gc, NULL, buf, NULL); |
|
| 1987 purple_input_remove(ft->inpa); |
|
| 1988 close(source); |
|
| 1989 g_free(ft->filename); |
|
| 1990 g_free(ft->user); |
|
| 1991 g_free(ft->ip); |
|
| 1992 g_free(ft->cookie); |
|
| 1993 if (ft->file) |
|
| 1994 fclose(ft->file); |
|
| 1995 g_free(ft); |
|
| 1996 return; |
|
| 1997 } |
|
| 1998 |
|
| 1999 ft->hdr.hdrtype = 0x0101; |
|
| 2000 ft->hdr.totfiles = htons(1); ft->hdr.filesleft = htons(1); |
|
| 2001 ft->hdr.flags = 0x20; |
|
| 2002 write(source, ft, 256); |
|
| 2003 return; |
|
| 2004 } |
|
| 2005 |
|
| 2006 if (ft->hdr.hdrtype == 0x0101) { |
|
| 2007 read(source, ft, 8); |
|
| 2008 read(source, &ft->hdr.bcookie, MIN(256 - 8, ntohs(ft->hdr.hdrlen) - 8)); |
|
| 2009 debug_header(ft); |
|
| 2010 |
|
| 2011 purple_input_remove(ft->inpa); |
|
| 2012 ft->inpa = purple_input_add(source, PURPLE_INPUT_WRITE, |
|
| 2013 toc_get_file_callback, ft); |
|
| 2014 return; |
|
| 2015 } |
|
| 2016 |
|
| 2017 if (ft->hdr.hdrtype == 0x0202) { |
|
| 2018 read(source, ft, 8); |
|
| 2019 read(source, &ft->hdr.bcookie, MIN(256 - 8, ntohs(ft->hdr.hdrlen) - 8)); |
|
| 2020 debug_header(ft); |
|
| 2021 |
|
| 2022 purple_input_remove(ft->inpa); |
|
| 2023 close(source); |
|
| 2024 g_free(ft->filename); |
|
| 2025 g_free(ft->user); |
|
| 2026 g_free(ft->ip); |
|
| 2027 g_free(ft->cookie); |
|
| 2028 if (ft->file) |
|
| 2029 fclose(ft->file); |
|
| 2030 g_free(ft); |
|
| 2031 return; |
|
| 2032 } |
|
| 2033 } |
|
| 2034 |
|
| 2035 static void toc_get_file_connect(gpointer data, gint src, PurpleInputCondition cond) |
|
| 2036 { |
|
| 2037 struct file_transfer *ft = data; |
|
| 2038 struct file_header *hdr; |
|
| 2039 char *buf; |
|
| 2040 char *basename; |
|
| 2041 |
|
| 2042 if (src == -1) { |
|
| 2043 purple_notify_error(ft->gc, NULL, |
|
| 2044 _("Could not connect for transfer."), NULL); |
|
| 2045 fclose(ft->file); |
|
| 2046 g_free(ft->filename); |
|
| 2047 g_free(ft->cookie); |
|
| 2048 g_free(ft->user); |
|
| 2049 g_free(ft->ip); |
|
| 2050 g_free(ft); |
|
| 2051 return; |
|
| 2052 } |
|
| 2053 |
|
| 2054 hdr = (struct file_header *)ft; |
|
| 2055 hdr->magic[0] = 'O'; hdr->magic[1] = 'F'; hdr->magic[2] = 'T'; hdr->magic[3] = '2'; |
|
| 2056 hdr->hdrlen = htons(256); |
|
| 2057 hdr->hdrtype = htons(0x1108); |
|
| 2058 rombase64(ft->cookie, &buf, NULL); |
|
| 2059 g_snprintf(hdr->bcookie, 8, "%s", buf); |
|
| 2060 g_free(buf); |
|
| 2061 hdr->totfiles = htons(1); hdr->filesleft = htons(1); |
|
| 2062 hdr->totparts = htons(1); hdr->partsleft = htons(1); |
|
| 2063 hdr->totsize = htonl((long)ft->st.st_size); /* combined size of all files */ |
|
| 2064 /* size = strlen("mm/dd/yyyy hh:mm sizesize 'name'\r\n") */ |
|
| 2065 basename = g_path_get_basename(ft->filename); |
|
| 2066 hdr->size = htonl(28 + strlen(basename)); /* size of listing.txt */ |
|
| 2067 g_free(basename); |
|
| 2068 hdr->modtime = htonl(ft->st.st_mtime); |
|
| 2069 hdr->checksum = htonl(0x89f70000); /* uh... */ |
|
| 2070 g_snprintf(hdr->idstring, 32, "OFT_Windows ICBMFT V1.1 32"); |
|
| 2071 hdr->flags = 0x02; |
|
| 2072 hdr->lnameoffset = 0x1A; |
|
| 2073 hdr->lsizeoffset = 0x10; |
|
| 2074 g_snprintf(hdr->name, 64, "listing.txt"); |
|
| 2075 if (write(src, hdr, 256) < 0) { |
|
| 2076 purple_notify_error(ft->gc, NULL, |
|
| 2077 _("Could not write file header. The file will " |
|
| 2078 "not be transferred."), NULL); |
|
| 2079 fclose(ft->file); |
|
| 2080 g_free(ft->filename); |
|
| 2081 g_free(ft->cookie); |
|
| 2082 g_free(ft->user); |
|
| 2083 g_free(ft->ip); |
|
| 2084 g_free(ft); |
|
| 2085 return; |
|
| 2086 } |
|
| 2087 |
|
| 2088 ft->inpa = purple_input_add(src, PURPLE_INPUT_READ, toc_get_file_callback, ft); |
|
| 2089 } |
|
| 2090 |
|
| 2091 static void toc_get_file(gpointer a, struct file_transfer *old_ft) |
|
| 2092 { |
|
| 2093 struct file_transfer *ft; |
|
| 2094 const char *dirname = gtk_file_selection_get_filename(GTK_FILE_SELECTION(old_ft->window)); |
|
| 2095 PurpleAccount *account; |
|
| 2096 char *buf, buf2[BUF_LEN * 2]; |
|
| 2097 |
|
| 2098 if (purple_gtk_check_if_dir(dirname, GTK_FILE_SELECTION(old_ft->window))) |
|
| 2099 return; |
|
| 2100 ft = g_new0(struct file_transfer, 1); |
|
| 2101 ft->filename = g_strdup(dirname); |
|
| 2102 ft->file = g_fopen(ft->filename, "r"); |
|
| 2103 if (!ft->file) { |
|
| 2104 buf = g_strdup_printf("Unable to open %s for transfer.", ft->filename); |
|
| 2105 purple_notify_error(ft->gc, NULL, buf, NULL); |
|
| 2106 g_free(buf); |
|
| 2107 g_free(ft->filename); |
|
| 2108 g_free(ft); |
|
| 2109 return; |
|
| 2110 } |
|
| 2111 if (g_stat(dirname, &ft->st)) { |
|
| 2112 buf = g_strdup_printf("Unable to examine %s.", dirname); |
|
| 2113 purple_notify_error(ft->gc, NULL, buf, NULL); |
|
| 2114 g_free(buf); |
|
| 2115 g_free(ft->filename); |
|
| 2116 g_free(ft); |
|
| 2117 return; |
|
| 2118 } |
|
| 2119 ft->cookie = g_strdup(old_ft->cookie); |
|
| 2120 ft->user = g_strdup(old_ft->user); |
|
| 2121 ft->ip = g_strdup(old_ft->ip); |
|
| 2122 ft->port = old_ft->port; |
|
| 2123 ft->gc = old_ft->gc; |
|
| 2124 account = ft->gc->account; |
|
| 2125 gtk_widget_destroy(old_ft->window); |
|
| 2126 |
|
| 2127 g_snprintf(buf2, sizeof(buf2), "toc_rvous_accept %s %s %s", ft->user, ft->cookie, FILE_GET_UID); |
|
| 2128 sflap_send(ft->gc, buf2, -1, TYPE_DATA); |
|
| 2129 |
|
| 2130 if (purple_proxy_connect(ft->gc, account, ft->ip, ft->port, toc_get_file_connect, ft) < 0) { |
|
| 2131 purple_notify_error(ft->gc, NULL, |
|
| 2132 _("Could not connect for transfer."), NULL); |
|
| 2133 fclose(ft->file); |
|
| 2134 g_free(ft->filename); |
|
| 2135 g_free(ft->cookie); |
|
| 2136 g_free(ft->user); |
|
| 2137 g_free(ft->ip); |
|
| 2138 g_free(ft); |
|
| 2139 return; |
|
| 2140 } |
|
| 2141 } |
|
| 2142 |
|
| 2143 static void cancel_callback(gpointer a, struct file_transfer *ft) { |
|
| 2144 gtk_widget_destroy(ft->window); |
|
| 2145 if (a == ft->window) { |
|
| 2146 g_free(ft->cookie); |
|
| 2147 g_free(ft->user); |
|
| 2148 g_free(ft->ip); |
|
| 2149 g_free(ft); |
|
| 2150 } |
|
| 2151 } |
|
| 2152 |
|
| 2153 static void toc_reject_ft(struct ft_request *ft) { |
|
| 2154 g_free(ft->user); |
|
| 2155 g_free(ft->filename); |
|
| 2156 g_free(ft->ip); |
|
| 2157 g_free(ft->cookie); |
|
| 2158 if (ft->message) |
|
| 2159 g_free(ft->message); |
|
| 2160 g_free(ft); |
|
| 2161 } |
|
| 2162 |
|
| 2163 |
|
| 2164 static void toc_accept_ft(struct ft_request *fr) { |
|
| 2165 if(g_list_find(purple_connections_get_all(), fr->gc)) { |
|
| 2166 GtkWidget *window; |
|
| 2167 char buf[BUF_LEN]; |
|
| 2168 |
|
| 2169 struct file_transfer *ft = g_new0(struct file_transfer, 1); |
|
| 2170 ft->gc = fr->gc; |
|
| 2171 ft->user = g_strdup(fr->user); |
|
| 2172 ft->cookie = g_strdup(fr->cookie); |
|
| 2173 ft->ip = g_strdup(fr->ip); |
|
| 2174 ft->port = fr->port; |
|
| 2175 ft->files = fr->files; |
|
| 2176 |
|
| 2177 ft->window = window = gtk_file_selection_new(_("Save As...")); |
|
| 2178 g_snprintf(buf, sizeof(buf), "%s/%s", purple_home_dir(), fr->filename ? fr->filename : ""); |
|
| 2179 gtk_file_selection_set_filename(GTK_FILE_SELECTION(window), buf); |
|
| 2180 g_signal_connect(G_OBJECT(window), "destroy", |
|
| 2181 G_CALLBACK(cancel_callback), ft); |
|
| 2182 g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(ft->window)->cancel_button), |
|
| 2183 "clicked", G_CALLBACK(cancel_callback), ft); |
|
| 2184 |
|
| 2185 if (!strcmp(fr->UID, FILE_SEND_UID)) |
|
| 2186 g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(window)->ok_button), |
|
| 2187 "clicked", G_CALLBACK(toc_send_file), ft); |
|
| 2188 else |
|
| 2189 g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(window)->ok_button), |
|
| 2190 "clicked", G_CALLBACK(toc_get_file), ft); |
|
| 2191 |
|
| 2192 gtk_widget_show(window); |
|
| 2193 } |
|
| 2194 |
|
| 2195 toc_reject_ft(fr); |
|
| 2196 } |
|
| 2197 |
|
| 2198 static void accept_file_dialog(struct ft_request *ft) { |
|
| 2199 char buf[BUF_LONG]; |
|
| 2200 if (!strcmp(ft->UID, FILE_SEND_UID)) { |
|
| 2201 /* holy crap. who the fuck would transfer gigabytes through AIM?! */ |
|
| 2202 static char *sizes[4] = { "bytes", "KB", "MB", "GB" }; |
|
| 2203 float size = ft->size; |
|
| 2204 int index = 0; |
|
| 2205 while ((index < 4) && (size > 1024)) { |
|
| 2206 size /= 1024; |
|
| 2207 index++; |
|
| 2208 } |
|
| 2209 g_snprintf(buf, sizeof(buf), |
|
| 2210 dngettext(PACKAGE, |
|
| 2211 "%s requests %s to accept %d file: %s (%.2f %s)%s%s", |
|
| 2212 "%s requests %s to accept %d files: %s (%.2f %s)%s%s", |
|
| 2213 ft->files), |
|
| 2214 ft->user, purple_account_get_username(ft->gc->account), ft->files, |
|
| 2215 ft->filename, size, sizes[index], (ft->message) ? "\n" : "", |
|
| 2216 (ft->message) ? ft->message : ""); |
|
| 2217 } else { |
|
| 2218 g_snprintf(buf, sizeof(buf), _("%s requests you to send them a file"), ft->user); |
|
| 2219 } |
|
| 2220 |
|
| 2221 purple_request_accept_cancel(ft->gc, NULL, buf, NULL, |
|
| 2222 PURPLE_DEFAULT_ACTION_NONE, ft, |
|
| 2223 G_CALLBACK(toc_accept_ft), |
|
| 2224 G_CALLBACK(toc_reject_ft)); |
|
| 2225 } |
|
| 2226 #endif |
|
| 2227 |
|
| 2228 static PurplePluginProtocolInfo prpl_info = |
|
| 2229 { |
|
| 2230 0, |
|
| 2231 NULL, /* user_splits */ |
|
| 2232 NULL, /* protocol_options */ |
|
| 2233 NO_BUDDY_ICONS, /* icon_spec */ |
|
| 2234 toc_list_icon, /* list_icon */ |
|
| 2235 toc_list_emblem, /* list_emblems */ |
|
| 2236 NULL, /* status_text */ |
|
| 2237 NULL, /* tooltip_text */ |
|
| 2238 toc_away_states, /* away_states */ |
|
| 2239 toc_blist_node_menu, /* blist_node_menu */ |
|
| 2240 toc_chat_info, /* chat_info */ |
|
| 2241 toc_chat_info_defaults, /* chat_info_defaults */ |
|
| 2242 toc_login, /* login */ |
|
| 2243 toc_close, /* close */ |
|
| 2244 toc_send_im, /* send_im */ |
|
| 2245 toc_set_info, /* set_info */ |
|
| 2246 NULL, /* send_typing */ |
|
| 2247 toc_get_info, /* get_info */ |
|
| 2248 toc_set_status, /* set_away */ |
|
| 2249 toc_set_idle, /* set_idle */ |
|
| 2250 toc_change_passwd, /* change_passwd */ |
|
| 2251 toc_add_buddy, /* add_buddy */ |
|
| 2252 toc_add_buddies, /* add_buddies */ |
|
| 2253 toc_remove_buddy, /* remove_buddy */ |
|
| 2254 toc_remove_buddies, /* remove_buddies */ |
|
| 2255 toc_add_permit, /* add_permit */ |
|
| 2256 toc_add_deny, /* add_deny */ |
|
| 2257 toc_rem_permit, /* rem_permit */ |
|
| 2258 toc_rem_deny, /* rem_deny */ |
|
| 2259 toc_set_permit_deny, /* set_permit_deny */ |
|
| 2260 toc_join_chat, /* join_chat */ |
|
| 2261 NULL, /* reject_chat */ |
|
| 2262 NULL, /* get_chat_name */ |
|
| 2263 toc_chat_invite, /* chat_invite */ |
|
| 2264 toc_chat_leave, /* chat_leave */ |
|
| 2265 toc_chat_whisper, /* chat_whisper */ |
|
| 2266 toc_chat_send, /* chat_send */ |
|
| 2267 toc_keepalive, /* keepalive */ |
|
| 2268 NULL, /* register_user */ |
|
| 2269 NULL, /* get_cb_info */ |
|
| 2270 NULL, /* get_cb_away */ |
|
| 2271 NULL, /* alias_buddy */ |
|
| 2272 NULL, /* group_buddy */ |
|
| 2273 NULL, /* rename_group */ |
|
| 2274 NULL, /* buddy_free */ |
|
| 2275 NULL, /* convo_closed */ |
|
| 2276 toc_normalize, /* normalize */ |
|
| 2277 NULL, /* set_buddy_icon */ |
|
| 2278 NULL, /* remove_group */ |
|
| 2279 NULL, /* get_cb_real_name */ |
|
| 2280 NULL, /* set_chat_topic */ |
|
| 2281 NULL, /* find_blist_chat */ |
|
| 2282 NULL, /* roomlist_get_list */ |
|
| 2283 NULL, /* roomlist_cancel */ |
|
| 2284 NULL, /* roomlist_expand_category */ |
|
| 2285 NULL, /* can_receive_file */ |
|
| 2286 NULL, /* send_file */ |
|
| 2287 NULL, /* new_xfer */ |
|
| 2288 NULL, /* offline_message */ |
|
| 2289 NULL, /* whiteboard_prpl_ops */ |
|
| 2290 toc_send_raw, /* send_raw */ |
|
| 2291 }; |
|
| 2292 |
|
| 2293 static PurplePluginInfo info = |
|
| 2294 { |
|
| 2295 PURPLE_PLUGIN_MAGIC, |
|
| 2296 PURPLE_MAJOR_VERSION, |
|
| 2297 PURPLE_MINOR_VERSION, |
|
| 2298 PURPLE_PLUGIN_PROTOCOL, /**< type */ |
|
| 2299 NULL, /**< ui_requirement */ |
|
| 2300 0, /**< flags */ |
|
| 2301 NULL, /**< dependencies */ |
|
| 2302 PURPLE_PRIORITY_DEFAULT, /**< priority */ |
|
| 2303 |
|
| 2304 "prpl-toc", /**< id */ |
|
| 2305 "TOC", /**< name */ |
|
| 2306 DISPLAY_VERSION, /**< version */ |
|
| 2307 /** summary */ |
|
| 2308 N_("TOC Protocol Plugin"), |
|
| 2309 /** description */ |
|
| 2310 N_("TOC Protocol Plugin"), |
|
| 2311 NULL, /**< author */ |
|
| 2312 PURPLE_WEBSITE, /**< homepage */ |
|
| 2313 |
|
| 2314 NULL, /**< load */ |
|
| 2315 NULL, /**< unload */ |
|
| 2316 NULL, /**< destroy */ |
|
| 2317 |
|
| 2318 NULL, /**< ui_info */ |
|
| 2319 &prpl_info, /**< extra_info */ |
|
| 2320 NULL, |
|
| 2321 toc_actions |
|
| 2322 }; |
|
| 2323 |
|
| 2324 static void |
|
| 2325 init_plugin(PurplePlugin *plugin) |
|
| 2326 { |
|
| 2327 PurpleAccountOption *option; |
|
| 2328 |
|
| 2329 option = purple_account_option_string_new(_("Server"), "server", TOC_HOST); |
|
| 2330 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, |
|
| 2331 option); |
|
| 2332 |
|
| 2333 option = purple_account_option_int_new(_("Port"), "port", TOC_PORT); |
|
| 2334 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, |
|
| 2335 option); |
|
| 2336 |
|
| 2337 my_protocol = plugin; |
|
| 2338 } |
|
| 2339 |
|
| 2340 PURPLE_INIT_PLUGIN(toc, init_plugin, info); |
|