| 1 /* |
|
| 2 |
|
| 3 silcpurple_buddy.c |
|
| 4 |
|
| 5 Author: Pekka Riikonen <priikone@silcnet.org> |
|
| 6 |
|
| 7 Copyright (C) 2004 Pekka Riikonen |
|
| 8 |
|
| 9 This program is free software; you can redistribute it and/or modify |
|
| 10 it under the terms of the GNU General Public License as published by |
|
| 11 the Free Software Foundation; version 2 of the License. |
|
| 12 |
|
| 13 This program is distributed in the hope that it will be useful, |
|
| 14 but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 16 GNU General Public License for more details. |
|
| 17 |
|
| 18 */ |
|
| 19 |
|
| 20 #include "silcincludes.h" |
|
| 21 #include "silcclient.h" |
|
| 22 #include "silcpurple.h" |
|
| 23 #include "wb.h" |
|
| 24 |
|
| 25 /***************************** Key Agreement *********************************/ |
|
| 26 |
|
| 27 static void |
|
| 28 silcpurple_buddy_keyagr(PurpleBlistNode *node, gpointer data); |
|
| 29 |
|
| 30 static void |
|
| 31 silcpurple_buddy_keyagr_do(PurpleConnection *gc, const char *name, |
|
| 32 gboolean force_local); |
|
| 33 |
|
| 34 typedef struct { |
|
| 35 char *nick; |
|
| 36 PurpleConnection *gc; |
|
| 37 } *SilcPurpleResolve; |
|
| 38 |
|
| 39 static void |
|
| 40 silcpurple_buddy_keyagr_resolved(SilcClient client, |
|
| 41 SilcClientConnection conn, |
|
| 42 SilcClientEntry *clients, |
|
| 43 SilcUInt32 clients_count, |
|
| 44 void *context) |
|
| 45 { |
|
| 46 PurpleConnection *gc = client->application; |
|
| 47 SilcPurpleResolve r = context; |
|
| 48 char tmp[256]; |
|
| 49 |
|
| 50 if (!clients) { |
|
| 51 g_snprintf(tmp, sizeof(tmp), |
|
| 52 _("User %s is not present in the network"), r->nick); |
|
| 53 purple_notify_error(gc, _("Key Agreement"), |
|
| 54 _("Cannot perform the key agreement"), tmp); |
|
| 55 silc_free(r->nick); |
|
| 56 silc_free(r); |
|
| 57 return; |
|
| 58 } |
|
| 59 |
|
| 60 silcpurple_buddy_keyagr_do(gc, r->nick, FALSE); |
|
| 61 silc_free(r->nick); |
|
| 62 silc_free(r); |
|
| 63 } |
|
| 64 |
|
| 65 typedef struct { |
|
| 66 gboolean responder; |
|
| 67 } *SilcPurpleKeyAgr; |
|
| 68 |
|
| 69 static void |
|
| 70 silcpurple_buddy_keyagr_cb(SilcClient client, |
|
| 71 SilcClientConnection conn, |
|
| 72 SilcClientEntry client_entry, |
|
| 73 SilcKeyAgreementStatus status, |
|
| 74 SilcSKEKeyMaterial *key, |
|
| 75 void *context) |
|
| 76 { |
|
| 77 PurpleConnection *gc = client->application; |
|
| 78 SilcPurple sg = gc->proto_data; |
|
| 79 SilcPurpleKeyAgr a = context; |
|
| 80 |
|
| 81 if (!sg->conn) |
|
| 82 return; |
|
| 83 |
|
| 84 switch (status) { |
|
| 85 case SILC_KEY_AGREEMENT_OK: |
|
| 86 { |
|
| 87 PurpleConversation *convo; |
|
| 88 char tmp[128]; |
|
| 89 |
|
| 90 /* Set the private key for this client */ |
|
| 91 silc_client_del_private_message_key(client, conn, client_entry); |
|
| 92 silc_client_add_private_message_key_ske(client, conn, client_entry, |
|
| 93 NULL, NULL, key, a->responder); |
|
| 94 silc_ske_free_key_material(key); |
|
| 95 |
|
| 96 |
|
| 97 /* Open IM window */ |
|
| 98 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, |
|
| 99 client_entry->nickname, sg->account); |
|
| 100 if (convo) { |
|
| 101 /* we don't have windows in the core anymore...but we may want to |
|
| 102 * provide some method for asking the UI to show the window |
|
| 103 purple_conv_window_show(purple_conversation_get_window(convo)); |
|
| 104 */ |
|
| 105 } else { |
|
| 106 convo = purple_conversation_new(PURPLE_CONV_TYPE_IM, sg->account, |
|
| 107 client_entry->nickname); |
|
| 108 } |
|
| 109 g_snprintf(tmp, sizeof(tmp), "%s [private key]", client_entry->nickname); |
|
| 110 purple_conversation_set_title(convo, tmp); |
|
| 111 } |
|
| 112 break; |
|
| 113 |
|
| 114 case SILC_KEY_AGREEMENT_ERROR: |
|
| 115 purple_notify_error(gc, _("Key Agreement"), |
|
| 116 _("Error occurred during key agreement"), NULL); |
|
| 117 break; |
|
| 118 |
|
| 119 case SILC_KEY_AGREEMENT_FAILURE: |
|
| 120 purple_notify_error(gc, _("Key Agreement"), _("Key Agreement failed"), NULL); |
|
| 121 break; |
|
| 122 |
|
| 123 case SILC_KEY_AGREEMENT_TIMEOUT: |
|
| 124 purple_notify_error(gc, _("Key Agreement"), |
|
| 125 _("Timeout during key agreement"), NULL); |
|
| 126 break; |
|
| 127 |
|
| 128 case SILC_KEY_AGREEMENT_ABORTED: |
|
| 129 purple_notify_error(gc, _("Key Agreement"), |
|
| 130 _("Key agreement was aborted"), NULL); |
|
| 131 break; |
|
| 132 |
|
| 133 case SILC_KEY_AGREEMENT_ALREADY_STARTED: |
|
| 134 purple_notify_error(gc, _("Key Agreement"), |
|
| 135 _("Key agreement is already started"), NULL); |
|
| 136 break; |
|
| 137 |
|
| 138 case SILC_KEY_AGREEMENT_SELF_DENIED: |
|
| 139 purple_notify_error(gc, _("Key Agreement"), |
|
| 140 _("Key agreement cannot be started with yourself"), |
|
| 141 NULL); |
|
| 142 break; |
|
| 143 |
|
| 144 default: |
|
| 145 break; |
|
| 146 } |
|
| 147 |
|
| 148 silc_free(a); |
|
| 149 } |
|
| 150 |
|
| 151 static void |
|
| 152 silcpurple_buddy_keyagr_do(PurpleConnection *gc, const char *name, |
|
| 153 gboolean force_local) |
|
| 154 { |
|
| 155 SilcPurple sg = gc->proto_data; |
|
| 156 SilcClientEntry *clients; |
|
| 157 SilcUInt32 clients_count; |
|
| 158 char *local_ip = NULL, *remote_ip = NULL; |
|
| 159 gboolean local = TRUE; |
|
| 160 char *nickname; |
|
| 161 SilcPurpleKeyAgr a; |
|
| 162 |
|
| 163 if (!sg->conn || !name) |
|
| 164 return; |
|
| 165 |
|
| 166 if (!silc_parse_userfqdn(name, &nickname, NULL)) |
|
| 167 return; |
|
| 168 |
|
| 169 /* Find client entry */ |
|
| 170 clients = silc_client_get_clients_local(sg->client, sg->conn, nickname, name, |
|
| 171 &clients_count); |
|
| 172 if (!clients) { |
|
| 173 /* Resolve unknown user */ |
|
| 174 SilcPurpleResolve r = silc_calloc(1, sizeof(*r)); |
|
| 175 if (!r) |
|
| 176 return; |
|
| 177 r->nick = g_strdup(name); |
|
| 178 r->gc = gc; |
|
| 179 silc_client_get_clients(sg->client, sg->conn, nickname, NULL, |
|
| 180 silcpurple_buddy_keyagr_resolved, r); |
|
| 181 silc_free(nickname); |
|
| 182 return; |
|
| 183 } |
|
| 184 |
|
| 185 /* Resolve the local IP from the outgoing socket connection. We resolve |
|
| 186 it to check whether we have a private range IP address or public IP |
|
| 187 address. If we have public then we will assume that we are not behind |
|
| 188 NAT and will provide automatically the point of connection to the |
|
| 189 agreement. If we have private range address we assume that we are |
|
| 190 behind NAT and we let the responder provide the point of connection. |
|
| 191 |
|
| 192 The algorithm also checks the remote IP address of server connection. |
|
| 193 If it is private range address and we have private range address we |
|
| 194 assume that we are chatting in LAN and will provide the point of |
|
| 195 connection. |
|
| 196 |
|
| 197 Naturally this algorithm does not always get things right. */ |
|
| 198 |
|
| 199 if (silc_net_check_local_by_sock(sg->conn->sock->sock, NULL, &local_ip)) { |
|
| 200 /* Check if the IP is private */ |
|
| 201 if (!force_local && silcpurple_ip_is_private(local_ip)) { |
|
| 202 local = FALSE; |
|
| 203 |
|
| 204 /* Local IP is private, resolve the remote server IP to see whether |
|
| 205 we are talking to Internet or just on LAN. */ |
|
| 206 if (silc_net_check_host_by_sock(sg->conn->sock->sock, NULL, |
|
| 207 &remote_ip)) |
|
| 208 if (silcpurple_ip_is_private(remote_ip)) |
|
| 209 /* We assume we are in LAN. Let's provide |
|
| 210 the connection point. */ |
|
| 211 local = TRUE; |
|
| 212 } |
|
| 213 } |
|
| 214 |
|
| 215 if (force_local) |
|
| 216 local = TRUE; |
|
| 217 |
|
| 218 if (local && !local_ip) |
|
| 219 local_ip = silc_net_localip(); |
|
| 220 |
|
| 221 a = silc_calloc(1, sizeof(*a)); |
|
| 222 if (!a) |
|
| 223 return; |
|
| 224 a->responder = local; |
|
| 225 |
|
| 226 /* Send the key agreement request */ |
|
| 227 silc_client_send_key_agreement(sg->client, sg->conn, clients[0], |
|
| 228 local ? local_ip : NULL, NULL, 0, 60, |
|
| 229 silcpurple_buddy_keyagr_cb, a); |
|
| 230 |
|
| 231 silc_free(local_ip); |
|
| 232 silc_free(remote_ip); |
|
| 233 silc_free(clients); |
|
| 234 } |
|
| 235 |
|
| 236 typedef struct { |
|
| 237 SilcClient client; |
|
| 238 SilcClientConnection conn; |
|
| 239 SilcClientID client_id; |
|
| 240 char *hostname; |
|
| 241 SilcUInt16 port; |
|
| 242 } *SilcPurpleKeyAgrAsk; |
|
| 243 |
|
| 244 static void |
|
| 245 silcpurple_buddy_keyagr_request_cb(SilcPurpleKeyAgrAsk a, gint id) |
|
| 246 { |
|
| 247 SilcPurpleKeyAgr ai; |
|
| 248 SilcClientEntry client_entry; |
|
| 249 |
|
| 250 if (id != 1) |
|
| 251 goto out; |
|
| 252 |
|
| 253 /* Get the client entry. */ |
|
| 254 client_entry = silc_client_get_client_by_id(a->client, a->conn, |
|
| 255 &a->client_id); |
|
| 256 if (!client_entry) { |
|
| 257 purple_notify_error(a->client->application, _("Key Agreement"), |
|
| 258 _("The remote user is not present in the network any more"), |
|
| 259 NULL); |
|
| 260 goto out; |
|
| 261 } |
|
| 262 |
|
| 263 /* If the hostname was provided by the requestor perform the key agreement |
|
| 264 now. Otherwise, we will send him a request to connect to us. */ |
|
| 265 if (a->hostname) { |
|
| 266 ai = silc_calloc(1, sizeof(*ai)); |
|
| 267 if (!ai) |
|
| 268 goto out; |
|
| 269 ai->responder = FALSE; |
|
| 270 silc_client_perform_key_agreement(a->client, a->conn, client_entry, |
|
| 271 a->hostname, a->port, |
|
| 272 silcpurple_buddy_keyagr_cb, ai); |
|
| 273 } else { |
|
| 274 /* Send request. Force us as the point of connection since requestor |
|
| 275 did not provide the point of connection. */ |
|
| 276 silcpurple_buddy_keyagr_do(a->client->application, |
|
| 277 client_entry->nickname, TRUE); |
|
| 278 } |
|
| 279 |
|
| 280 out: |
|
| 281 silc_free(a->hostname); |
|
| 282 silc_free(a); |
|
| 283 } |
|
| 284 |
|
| 285 void silcpurple_buddy_keyagr_request(SilcClient client, |
|
| 286 SilcClientConnection conn, |
|
| 287 SilcClientEntry client_entry, |
|
| 288 const char *hostname, SilcUInt16 port) |
|
| 289 { |
|
| 290 char tmp[128], tmp2[128]; |
|
| 291 SilcPurpleKeyAgrAsk a; |
|
| 292 PurpleConnection *gc = client->application; |
|
| 293 |
|
| 294 g_snprintf(tmp, sizeof(tmp), |
|
| 295 _("Key agreement request received from %s. Would you like to " |
|
| 296 "perform the key agreement?"), client_entry->nickname); |
|
| 297 if (hostname) |
|
| 298 g_snprintf(tmp2, sizeof(tmp2), |
|
| 299 _("The remote user is waiting key agreement on:\n" |
|
| 300 "Remote host: %s\nRemote port: %d"), hostname, port); |
|
| 301 |
|
| 302 a = silc_calloc(1, sizeof(*a)); |
|
| 303 if (!a) |
|
| 304 return; |
|
| 305 a->client = client; |
|
| 306 a->conn = conn; |
|
| 307 a->client_id = *client_entry->id; |
|
| 308 if (hostname) |
|
| 309 a->hostname = strdup(hostname); |
|
| 310 a->port = port; |
|
| 311 |
|
| 312 purple_request_action(client->application, _("Key Agreement Request"), tmp, |
|
| 313 hostname ? tmp2 : NULL, 1, gc->account, client_entry->nickname, |
|
| 314 NULL, a, 2, _("Yes"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb), |
|
| 315 _("No"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb)); |
|
| 316 } |
|
| 317 |
|
| 318 static void |
|
| 319 silcpurple_buddy_keyagr(PurpleBlistNode *node, gpointer data) |
|
| 320 { |
|
| 321 PurpleBuddy *buddy; |
|
| 322 |
|
| 323 buddy = (PurpleBuddy *)node; |
|
| 324 silcpurple_buddy_keyagr_do(buddy->account->gc, buddy->name, FALSE); |
|
| 325 } |
|
| 326 |
|
| 327 |
|
| 328 /**************************** Static IM Key **********************************/ |
|
| 329 |
|
| 330 static void |
|
| 331 silcpurple_buddy_resetkey(PurpleBlistNode *node, gpointer data) |
|
| 332 { |
|
| 333 PurpleBuddy *b; |
|
| 334 PurpleConnection *gc; |
|
| 335 SilcPurple sg; |
|
| 336 char *nickname; |
|
| 337 SilcClientEntry *clients; |
|
| 338 SilcUInt32 clients_count; |
|
| 339 |
|
| 340 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); |
|
| 341 |
|
| 342 b = (PurpleBuddy *) node; |
|
| 343 gc = purple_account_get_connection(b->account); |
|
| 344 sg = gc->proto_data; |
|
| 345 |
|
| 346 if (!silc_parse_userfqdn(b->name, &nickname, NULL)) |
|
| 347 return; |
|
| 348 |
|
| 349 /* Find client entry */ |
|
| 350 clients = silc_client_get_clients_local(sg->client, sg->conn, |
|
| 351 nickname, b->name, |
|
| 352 &clients_count); |
|
| 353 if (!clients) { |
|
| 354 silc_free(nickname); |
|
| 355 return; |
|
| 356 } |
|
| 357 |
|
| 358 clients[0]->prv_resp = FALSE; |
|
| 359 silc_client_del_private_message_key(sg->client, sg->conn, |
|
| 360 clients[0]); |
|
| 361 silc_free(clients); |
|
| 362 silc_free(nickname); |
|
| 363 } |
|
| 364 |
|
| 365 typedef struct { |
|
| 366 SilcClient client; |
|
| 367 SilcClientConnection conn; |
|
| 368 SilcClientID client_id; |
|
| 369 } *SilcPurplePrivkey; |
|
| 370 |
|
| 371 static void |
|
| 372 silcpurple_buddy_privkey(PurpleConnection *gc, const char *name); |
|
| 373 |
|
| 374 static void |
|
| 375 silcpurple_buddy_privkey_cb(SilcPurplePrivkey p, const char *passphrase) |
|
| 376 { |
|
| 377 SilcClientEntry client_entry; |
|
| 378 |
|
| 379 if (!passphrase || !(*passphrase)) { |
|
| 380 silc_free(p); |
|
| 381 return; |
|
| 382 } |
|
| 383 |
|
| 384 /* Get the client entry. */ |
|
| 385 client_entry = silc_client_get_client_by_id(p->client, p->conn, |
|
| 386 &p->client_id); |
|
| 387 if (!client_entry) { |
|
| 388 purple_notify_error(p->client->application, _("IM With Password"), |
|
| 389 _("The remote user is not present in the network any more"), |
|
| 390 NULL); |
|
| 391 silc_free(p); |
|
| 392 return; |
|
| 393 } |
|
| 394 |
|
| 395 /* Set the private message key */ |
|
| 396 silc_client_del_private_message_key(p->client, p->conn, |
|
| 397 client_entry); |
|
| 398 silc_client_add_private_message_key(p->client, p->conn, |
|
| 399 client_entry, NULL, NULL, |
|
| 400 (unsigned char *)passphrase, |
|
| 401 strlen(passphrase), FALSE, |
|
| 402 client_entry->prv_resp); |
|
| 403 if (!client_entry->prv_resp) |
|
| 404 silc_client_send_private_message_key_request(p->client, |
|
| 405 p->conn, |
|
| 406 client_entry); |
|
| 407 silc_free(p); |
|
| 408 } |
|
| 409 |
|
| 410 static void |
|
| 411 silcpurple_buddy_privkey_resolved(SilcClient client, |
|
| 412 SilcClientConnection conn, |
|
| 413 SilcClientEntry *clients, |
|
| 414 SilcUInt32 clients_count, |
|
| 415 void *context) |
|
| 416 { |
|
| 417 char tmp[256]; |
|
| 418 |
|
| 419 if (!clients) { |
|
| 420 g_snprintf(tmp, sizeof(tmp), |
|
| 421 _("User %s is not present in the network"), |
|
| 422 (const char *)context); |
|
| 423 purple_notify_error(client->application, _("IM With Password"), |
|
| 424 _("Cannot set IM key"), tmp); |
|
| 425 g_free(context); |
|
| 426 return; |
|
| 427 } |
|
| 428 |
|
| 429 silcpurple_buddy_privkey(client->application, context); |
|
| 430 silc_free(context); |
|
| 431 } |
|
| 432 |
|
| 433 static void |
|
| 434 silcpurple_buddy_privkey(PurpleConnection *gc, const char *name) |
|
| 435 { |
|
| 436 SilcPurple sg = gc->proto_data; |
|
| 437 char *nickname; |
|
| 438 SilcPurplePrivkey p; |
|
| 439 SilcClientEntry *clients; |
|
| 440 SilcUInt32 clients_count; |
|
| 441 |
|
| 442 if (!name) |
|
| 443 return; |
|
| 444 if (!silc_parse_userfqdn(name, &nickname, NULL)) |
|
| 445 return; |
|
| 446 |
|
| 447 /* Find client entry */ |
|
| 448 clients = silc_client_get_clients_local(sg->client, sg->conn, |
|
| 449 nickname, name, |
|
| 450 &clients_count); |
|
| 451 if (!clients) { |
|
| 452 silc_client_get_clients(sg->client, sg->conn, nickname, NULL, |
|
| 453 silcpurple_buddy_privkey_resolved, |
|
| 454 g_strdup(name)); |
|
| 455 silc_free(nickname); |
|
| 456 return; |
|
| 457 } |
|
| 458 |
|
| 459 p = silc_calloc(1, sizeof(*p)); |
|
| 460 if (!p) |
|
| 461 return; |
|
| 462 p->client = sg->client; |
|
| 463 p->conn = sg->conn; |
|
| 464 p->client_id = *clients[0]->id; |
|
| 465 purple_request_input(gc, _("IM With Password"), NULL, |
|
| 466 _("Set IM Password"), NULL, FALSE, TRUE, NULL, |
|
| 467 _("OK"), G_CALLBACK(silcpurple_buddy_privkey_cb), |
|
| 468 _("Cancel"), G_CALLBACK(silcpurple_buddy_privkey_cb), |
|
| 469 gc->account, NULL, NULL, p); |
|
| 470 |
|
| 471 silc_free(clients); |
|
| 472 silc_free(nickname); |
|
| 473 } |
|
| 474 |
|
| 475 static void |
|
| 476 silcpurple_buddy_privkey_menu(PurpleBlistNode *node, gpointer data) |
|
| 477 { |
|
| 478 PurpleBuddy *buddy; |
|
| 479 PurpleConnection *gc; |
|
| 480 |
|
| 481 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); |
|
| 482 |
|
| 483 buddy = (PurpleBuddy *) node; |
|
| 484 gc = purple_account_get_connection(buddy->account); |
|
| 485 |
|
| 486 silcpurple_buddy_privkey(gc, buddy->name); |
|
| 487 } |
|
| 488 |
|
| 489 |
|
| 490 /**************************** Get Public Key *********************************/ |
|
| 491 |
|
| 492 typedef struct { |
|
| 493 SilcClient client; |
|
| 494 SilcClientConnection conn; |
|
| 495 SilcClientID client_id; |
|
| 496 } *SilcPurpleBuddyGetkey; |
|
| 497 |
|
| 498 static void |
|
| 499 silcpurple_buddy_getkey(PurpleConnection *gc, const char *name); |
|
| 500 |
|
| 501 static void |
|
| 502 silcpurple_buddy_getkey_cb(SilcPurpleBuddyGetkey g, |
|
| 503 SilcClientCommandReplyContext cmd) |
|
| 504 { |
|
| 505 SilcClientEntry client_entry; |
|
| 506 unsigned char *pk; |
|
| 507 SilcUInt32 pk_len; |
|
| 508 |
|
| 509 /* Get the client entry. */ |
|
| 510 client_entry = silc_client_get_client_by_id(g->client, g->conn, |
|
| 511 &g->client_id); |
|
| 512 if (!client_entry) { |
|
| 513 purple_notify_error(g->client->application, _("Get Public Key"), |
|
| 514 _("The remote user is not present in the network any more"), |
|
| 515 NULL); |
|
| 516 silc_free(g); |
|
| 517 return; |
|
| 518 } |
|
| 519 |
|
| 520 if (!client_entry->public_key) { |
|
| 521 silc_free(g); |
|
| 522 return; |
|
| 523 } |
|
| 524 |
|
| 525 /* Now verify the public key */ |
|
| 526 pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len); |
|
| 527 silcpurple_verify_public_key(g->client, g->conn, client_entry->nickname, |
|
| 528 SILC_SOCKET_TYPE_CLIENT, |
|
| 529 pk, pk_len, SILC_SKE_PK_TYPE_SILC, |
|
| 530 NULL, NULL); |
|
| 531 silc_free(pk); |
|
| 532 silc_free(g); |
|
| 533 } |
|
| 534 |
|
| 535 static void |
|
| 536 silcpurple_buddy_getkey_resolved(SilcClient client, |
|
| 537 SilcClientConnection conn, |
|
| 538 SilcClientEntry *clients, |
|
| 539 SilcUInt32 clients_count, |
|
| 540 void *context) |
|
| 541 { |
|
| 542 char tmp[256]; |
|
| 543 |
|
| 544 if (!clients) { |
|
| 545 g_snprintf(tmp, sizeof(tmp), |
|
| 546 _("User %s is not present in the network"), |
|
| 547 (const char *)context); |
|
| 548 purple_notify_error(client->application, _("Get Public Key"), |
|
| 549 _("Cannot fetch the public key"), tmp); |
|
| 550 g_free(context); |
|
| 551 return; |
|
| 552 } |
|
| 553 |
|
| 554 silcpurple_buddy_getkey(client->application, context); |
|
| 555 silc_free(context); |
|
| 556 } |
|
| 557 |
|
| 558 static void |
|
| 559 silcpurple_buddy_getkey(PurpleConnection *gc, const char *name) |
|
| 560 { |
|
| 561 SilcPurple sg = gc->proto_data; |
|
| 562 SilcClient client = sg->client; |
|
| 563 SilcClientConnection conn = sg->conn; |
|
| 564 SilcClientEntry *clients; |
|
| 565 SilcUInt32 clients_count; |
|
| 566 SilcPurpleBuddyGetkey g; |
|
| 567 char *nickname; |
|
| 568 |
|
| 569 if (!name) |
|
| 570 return; |
|
| 571 |
|
| 572 if (!silc_parse_userfqdn(name, &nickname, NULL)) |
|
| 573 return; |
|
| 574 |
|
| 575 /* Find client entry */ |
|
| 576 clients = silc_client_get_clients_local(client, conn, nickname, name, |
|
| 577 &clients_count); |
|
| 578 if (!clients) { |
|
| 579 silc_client_get_clients(client, conn, nickname, NULL, |
|
| 580 silcpurple_buddy_getkey_resolved, |
|
| 581 g_strdup(name)); |
|
| 582 silc_free(nickname); |
|
| 583 return; |
|
| 584 } |
|
| 585 |
|
| 586 /* Call GETKEY */ |
|
| 587 g = silc_calloc(1, sizeof(*g)); |
|
| 588 if (!g) |
|
| 589 return; |
|
| 590 g->client = client; |
|
| 591 g->conn = conn; |
|
| 592 g->client_id = *clients[0]->id; |
|
| 593 silc_client_command_call(client, conn, NULL, "GETKEY", |
|
| 594 clients[0]->nickname, NULL); |
|
| 595 silc_client_command_pending(conn, SILC_COMMAND_GETKEY, |
|
| 596 conn->cmd_ident, |
|
| 597 (SilcCommandCb)silcpurple_buddy_getkey_cb, g); |
|
| 598 silc_free(clients); |
|
| 599 silc_free(nickname); |
|
| 600 } |
|
| 601 |
|
| 602 static void |
|
| 603 silcpurple_buddy_getkey_menu(PurpleBlistNode *node, gpointer data) |
|
| 604 { |
|
| 605 PurpleBuddy *buddy; |
|
| 606 PurpleConnection *gc; |
|
| 607 |
|
| 608 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); |
|
| 609 |
|
| 610 buddy = (PurpleBuddy *) node; |
|
| 611 gc = purple_account_get_connection(buddy->account); |
|
| 612 |
|
| 613 silcpurple_buddy_getkey(gc, buddy->name); |
|
| 614 } |
|
| 615 |
|
| 616 static void |
|
| 617 silcpurple_buddy_showkey(PurpleBlistNode *node, gpointer data) |
|
| 618 { |
|
| 619 PurpleBuddy *b; |
|
| 620 PurpleConnection *gc; |
|
| 621 SilcPurple sg; |
|
| 622 SilcPublicKey public_key; |
|
| 623 const char *pkfile; |
|
| 624 |
|
| 625 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); |
|
| 626 |
|
| 627 b = (PurpleBuddy *) node; |
|
| 628 gc = purple_account_get_connection(b->account); |
|
| 629 sg = gc->proto_data; |
|
| 630 |
|
| 631 pkfile = purple_blist_node_get_string(node, "public-key"); |
|
| 632 if (!silc_pkcs_load_public_key(pkfile, &public_key, SILC_PKCS_FILE_PEM) && |
|
| 633 !silc_pkcs_load_public_key(pkfile, &public_key, SILC_PKCS_FILE_BIN)) { |
|
| 634 purple_notify_error(gc, |
|
| 635 _("Show Public Key"), |
|
| 636 _("Could not load public key"), NULL); |
|
| 637 return; |
|
| 638 } |
|
| 639 |
|
| 640 silcpurple_show_public_key(sg, b->name, public_key, NULL, NULL); |
|
| 641 silc_pkcs_public_key_free(public_key); |
|
| 642 } |
|
| 643 |
|
| 644 |
|
| 645 /**************************** Buddy routines *********************************/ |
|
| 646 |
|
| 647 /* The buddies are implemented by using the WHOIS and WATCH commands that |
|
| 648 can be used to search users by their public key. Since nicknames aren't |
|
| 649 unique in SILC we cannot trust the buddy list using their nickname. We |
|
| 650 associate public keys to buddies and use those to search and watch |
|
| 651 in the network. |
|
| 652 |
|
| 653 The problem is that Purple does not return PurpleBuddy contexts to the |
|
| 654 callbacks but the buddy names. Naturally, this is not going to work |
|
| 655 with SILC. But, for now, we have to do what we can... */ |
|
| 656 |
|
| 657 typedef struct { |
|
| 658 SilcClient client; |
|
| 659 SilcClientConnection conn; |
|
| 660 SilcClientID client_id; |
|
| 661 PurpleBuddy *b; |
|
| 662 unsigned char *offline_pk; |
|
| 663 SilcUInt32 offline_pk_len; |
|
| 664 unsigned int offline : 1; |
|
| 665 unsigned int pubkey_search : 1; |
|
| 666 unsigned int init : 1; |
|
| 667 } *SilcPurpleBuddyRes; |
|
| 668 |
|
| 669 static void |
|
| 670 silcpurple_add_buddy_ask_pk_cb(SilcPurpleBuddyRes r, gint id); |
|
| 671 static void |
|
| 672 silcpurple_add_buddy_resolved(SilcClient client, |
|
| 673 SilcClientConnection conn, |
|
| 674 SilcClientEntry *clients, |
|
| 675 SilcUInt32 clients_count, |
|
| 676 void *context); |
|
| 677 |
|
| 678 void silcpurple_get_info(PurpleConnection *gc, const char *who) |
|
| 679 { |
|
| 680 SilcPurple sg = gc->proto_data; |
|
| 681 SilcClient client = sg->client; |
|
| 682 SilcClientConnection conn = sg->conn; |
|
| 683 SilcClientEntry client_entry; |
|
| 684 PurpleBuddy *b; |
|
| 685 const char *filename, *nick = who; |
|
| 686 char tmp[256]; |
|
| 687 |
|
| 688 if (!who) |
|
| 689 return; |
|
| 690 if (strlen(who) > 1 && who[0] == '@') |
|
| 691 nick = who + 1; |
|
| 692 if (strlen(who) > 1 && who[0] == '*') |
|
| 693 nick = who + 1; |
|
| 694 if (strlen(who) > 2 && who[0] == '*' && who[1] == '@') |
|
| 695 nick = who + 2; |
|
| 696 |
|
| 697 b = purple_find_buddy(gc->account, nick); |
|
| 698 if (b) { |
|
| 699 /* See if we have this buddy's public key. If we do use that |
|
| 700 to search the details. */ |
|
| 701 filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key"); |
|
| 702 if (filename) { |
|
| 703 /* Call WHOIS. The user info is displayed in the WHOIS |
|
| 704 command reply. */ |
|
| 705 silc_client_command_call(client, conn, NULL, "WHOIS", |
|
| 706 "-details", "-pubkey", filename, NULL); |
|
| 707 return; |
|
| 708 } |
|
| 709 |
|
| 710 if (!b->proto_data) { |
|
| 711 g_snprintf(tmp, sizeof(tmp), |
|
| 712 _("User %s is not present in the network"), b->name); |
|
| 713 purple_notify_error(gc, _("User Information"), |
|
| 714 _("Cannot get user information"), tmp); |
|
| 715 return; |
|
| 716 } |
|
| 717 |
|
| 718 client_entry = silc_client_get_client_by_id(client, conn, b->proto_data); |
|
| 719 if (client_entry) { |
|
| 720 /* Call WHOIS. The user info is displayed in the WHOIS |
|
| 721 command reply. */ |
|
| 722 silc_client_command_call(client, conn, NULL, "WHOIS", |
|
| 723 client_entry->nickname, "-details", NULL); |
|
| 724 } |
|
| 725 } else { |
|
| 726 /* Call WHOIS just with nickname. */ |
|
| 727 silc_client_command_call(client, conn, NULL, "WHOIS", nick, NULL); |
|
| 728 } |
|
| 729 } |
|
| 730 |
|
| 731 static void |
|
| 732 silcpurple_add_buddy_pk_no(SilcPurpleBuddyRes r) |
|
| 733 { |
|
| 734 char tmp[512]; |
|
| 735 g_snprintf(tmp, sizeof(tmp), _("The %s buddy is not trusted"), |
|
| 736 r->b->name); |
|
| 737 purple_notify_error(r->client->application, _("Add Buddy"), tmp, |
|
| 738 _("You cannot receive buddy notifications until you " |
|
| 739 "import his/her public key. You can use the Get Public Key " |
|
| 740 "command to get the public key.")); |
|
| 741 purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_OFFLINE, NULL); |
|
| 742 } |
|
| 743 |
|
| 744 static void |
|
| 745 silcpurple_add_buddy_save(bool success, void *context) |
|
| 746 { |
|
| 747 SilcPurpleBuddyRes r = context; |
|
| 748 PurpleBuddy *b = r->b; |
|
| 749 SilcClient client = r->client; |
|
| 750 SilcClientEntry client_entry; |
|
| 751 SilcAttributePayload attr; |
|
| 752 SilcAttribute attribute; |
|
| 753 SilcVCardStruct vcard; |
|
| 754 SilcAttributeObjMime message, extension; |
|
| 755 #ifdef SILC_ATTRIBUTE_USER_ICON |
|
| 756 SilcAttributeObjMime usericon; |
|
| 757 #endif |
|
| 758 SilcAttributeObjPk serverpk, usersign, serversign; |
|
| 759 gboolean usign_success = TRUE, ssign_success = TRUE; |
|
| 760 char filename[512], filename2[512], *fingerprint = NULL, *tmp; |
|
| 761 SilcUInt32 len; |
|
| 762 int i; |
|
| 763 |
|
| 764 if (!success) { |
|
| 765 /* The user did not trust the public key. */ |
|
| 766 silcpurple_add_buddy_pk_no(r); |
|
| 767 silc_free(r); |
|
| 768 return; |
|
| 769 } |
|
| 770 |
|
| 771 if (r->offline) { |
|
| 772 /* User is offline. Associate the imported public key with |
|
| 773 this user. */ |
|
| 774 fingerprint = silc_hash_fingerprint(NULL, r->offline_pk, |
|
| 775 r->offline_pk_len); |
|
| 776 for (i = 0; i < strlen(fingerprint); i++) |
|
| 777 if (fingerprint[i] == ' ') |
|
| 778 fingerprint[i] = '_'; |
|
| 779 g_snprintf(filename, sizeof(filename) - 1, |
|
| 780 "%s" G_DIR_SEPARATOR_S "clientkeys" G_DIR_SEPARATOR_S "clientkey_%s.pub", |
|
| 781 silcpurple_silcdir(), fingerprint); |
|
| 782 purple_blist_node_set_string((PurpleBlistNode *)b, "public-key", filename); |
|
| 783 purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_OFFLINE, NULL); |
|
| 784 silc_free(fingerprint); |
|
| 785 silc_free(r->offline_pk); |
|
| 786 silc_free(r); |
|
| 787 return; |
|
| 788 } |
|
| 789 |
|
| 790 /* Get the client entry. */ |
|
| 791 client_entry = silc_client_get_client_by_id(r->client, r->conn, |
|
| 792 &r->client_id); |
|
| 793 if (!client_entry) { |
|
| 794 silc_free(r); |
|
| 795 return; |
|
| 796 } |
|
| 797 |
|
| 798 memset(&vcard, 0, sizeof(vcard)); |
|
| 799 memset(&message, 0, sizeof(message)); |
|
| 800 memset(&extension, 0, sizeof(extension)); |
|
| 801 #ifdef SILC_ATTRIBUTE_USER_ICON |
|
| 802 memset(&usericon, 0, sizeof(usericon)); |
|
| 803 #endif |
|
| 804 memset(&serverpk, 0, sizeof(serverpk)); |
|
| 805 memset(&usersign, 0, sizeof(usersign)); |
|
| 806 memset(&serversign, 0, sizeof(serversign)); |
|
| 807 |
|
| 808 /* Now that we have the public key and we trust it now we |
|
| 809 save the attributes of the buddy and update its status. */ |
|
| 810 |
|
| 811 if (client_entry->attrs) { |
|
| 812 silc_dlist_start(client_entry->attrs); |
|
| 813 while ((attr = silc_dlist_get(client_entry->attrs)) |
|
| 814 != SILC_LIST_END) { |
|
| 815 attribute = silc_attribute_get_attribute(attr); |
|
| 816 |
|
| 817 switch (attribute) { |
|
| 818 case SILC_ATTRIBUTE_USER_INFO: |
|
| 819 if (!silc_attribute_get_object(attr, (void *)&vcard, |
|
| 820 sizeof(vcard))) |
|
| 821 continue; |
|
| 822 break; |
|
| 823 |
|
| 824 case SILC_ATTRIBUTE_STATUS_MESSAGE: |
|
| 825 if (!silc_attribute_get_object(attr, (void *)&message, |
|
| 826 sizeof(message))) |
|
| 827 continue; |
|
| 828 break; |
|
| 829 |
|
| 830 case SILC_ATTRIBUTE_EXTENSION: |
|
| 831 if (!silc_attribute_get_object(attr, (void *)&extension, |
|
| 832 sizeof(extension))) |
|
| 833 continue; |
|
| 834 break; |
|
| 835 |
|
| 836 #ifdef SILC_ATTRIBUTE_USER_ICON |
|
| 837 case SILC_ATTRIBUTE_USER_ICON: |
|
| 838 if (!silc_attribute_get_object(attr, (void *)&usericon, |
|
| 839 sizeof(usericon))) |
|
| 840 continue; |
|
| 841 break; |
|
| 842 #endif |
|
| 843 |
|
| 844 case SILC_ATTRIBUTE_SERVER_PUBLIC_KEY: |
|
| 845 if (serverpk.type) |
|
| 846 continue; |
|
| 847 if (!silc_attribute_get_object(attr, (void *)&serverpk, |
|
| 848 sizeof(serverpk))) |
|
| 849 continue; |
|
| 850 break; |
|
| 851 |
|
| 852 case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE: |
|
| 853 if (usersign.data) |
|
| 854 continue; |
|
| 855 if (!silc_attribute_get_object(attr, (void *)&usersign, |
|
| 856 sizeof(usersign))) |
|
| 857 continue; |
|
| 858 break; |
|
| 859 |
|
| 860 case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE: |
|
| 861 if (serversign.data) |
|
| 862 continue; |
|
| 863 if (!silc_attribute_get_object(attr, (void *)&serversign, |
|
| 864 sizeof(serversign))) |
|
| 865 continue; |
|
| 866 break; |
|
| 867 |
|
| 868 default: |
|
| 869 break; |
|
| 870 } |
|
| 871 } |
|
| 872 } |
|
| 873 |
|
| 874 /* Verify the attribute signatures */ |
|
| 875 |
|
| 876 if (usersign.data) { |
|
| 877 SilcPKCS pkcs; |
|
| 878 unsigned char *verifyd; |
|
| 879 SilcUInt32 verify_len; |
|
| 880 |
|
| 881 silc_pkcs_alloc((unsigned char*)"rsa", &pkcs); |
|
| 882 verifyd = silc_attribute_get_verify_data(client_entry->attrs, |
|
| 883 FALSE, &verify_len); |
|
| 884 if (verifyd && silc_pkcs_public_key_set(pkcs, client_entry->public_key)){ |
|
| 885 if (!silc_pkcs_verify_with_hash(pkcs, client->sha1hash, |
|
| 886 usersign.data, |
|
| 887 usersign.data_len, |
|
| 888 verifyd, verify_len)) |
|
| 889 usign_success = FALSE; |
|
| 890 } |
|
| 891 silc_free(verifyd); |
|
| 892 } |
|
| 893 |
|
| 894 if (serversign.data && !strcmp(serverpk.type, "silc-rsa")) { |
|
| 895 SilcPublicKey public_key; |
|
| 896 SilcPKCS pkcs; |
|
| 897 unsigned char *verifyd; |
|
| 898 SilcUInt32 verify_len; |
|
| 899 |
|
| 900 if (silc_pkcs_public_key_decode(serverpk.data, serverpk.data_len, |
|
| 901 &public_key)) { |
|
| 902 silc_pkcs_alloc((unsigned char *)"rsa", &pkcs); |
|
| 903 verifyd = silc_attribute_get_verify_data(client_entry->attrs, |
|
| 904 TRUE, &verify_len); |
|
| 905 if (verifyd && silc_pkcs_public_key_set(pkcs, public_key)) { |
|
| 906 if (!silc_pkcs_verify_with_hash(pkcs, client->sha1hash, |
|
| 907 serversign.data, |
|
| 908 serversign.data_len, |
|
| 909 verifyd, verify_len)) |
|
| 910 ssign_success = FALSE; |
|
| 911 } |
|
| 912 silc_pkcs_public_key_free(public_key); |
|
| 913 silc_free(verifyd); |
|
| 914 } |
|
| 915 } |
|
| 916 |
|
| 917 fingerprint = silc_fingerprint(client_entry->fingerprint, |
|
| 918 client_entry->fingerprint_len); |
|
| 919 for (i = 0; i < strlen(fingerprint); i++) |
|
| 920 if (fingerprint[i] == ' ') |
|
| 921 fingerprint[i] = '_'; |
|
| 922 |
|
| 923 if (usign_success || ssign_success) { |
|
| 924 struct passwd *pw; |
|
| 925 struct stat st; |
|
| 926 |
|
| 927 memset(filename2, 0, sizeof(filename2)); |
|
| 928 |
|
| 929 /* Filename for dir */ |
|
| 930 tmp = fingerprint + strlen(fingerprint) - 9; |
|
| 931 g_snprintf(filename, sizeof(filename) - 1, |
|
| 932 "%s" G_DIR_SEPARATOR_S "friends" G_DIR_SEPARATOR_S "%s", |
|
| 933 silcpurple_silcdir(), tmp); |
|
| 934 |
|
| 935 pw = getpwuid(getuid()); |
|
| 936 if (!pw) |
|
| 937 return; |
|
| 938 |
|
| 939 /* Create dir if it doesn't exist */ |
|
| 940 if ((g_stat(filename, &st)) == -1) { |
|
| 941 if (errno == ENOENT) { |
|
| 942 if (pw->pw_uid == geteuid()) { |
|
| 943 int ret = g_mkdir(filename, 0755); |
|
| 944 if (ret < 0) |
|
| 945 return; |
|
| 946 } |
|
| 947 } |
|
| 948 } |
|
| 949 |
|
| 950 /* Save VCard */ |
|
| 951 g_snprintf(filename2, sizeof(filename2) - 1, |
|
| 952 "%s" G_DIR_SEPARATOR_S "vcard", filename); |
|
| 953 if (vcard.full_name) { |
|
| 954 tmp = (char *)silc_vcard_encode(&vcard, &len); |
|
| 955 silc_file_writefile(filename2, tmp, len); |
|
| 956 silc_free(tmp); |
|
| 957 } |
|
| 958 |
|
| 959 /* Save status message */ |
|
| 960 if (message.mime) { |
|
| 961 memset(filename2, 0, sizeof(filename2)); |
|
| 962 g_snprintf(filename2, sizeof(filename2) - 1, |
|
| 963 "%s" G_DIR_SEPARATOR_S "status_message.mime", |
|
| 964 filename); |
|
| 965 silc_file_writefile(filename2, (char *)message.mime, |
|
| 966 message.mime_len); |
|
| 967 } |
|
| 968 |
|
| 969 /* Save extension data */ |
|
| 970 if (extension.mime) { |
|
| 971 memset(filename2, 0, sizeof(filename2)); |
|
| 972 g_snprintf(filename2, sizeof(filename2) - 1, |
|
| 973 "%s" G_DIR_SEPARATOR_S "extension.mime", |
|
| 974 filename); |
|
| 975 silc_file_writefile(filename2, (char *)extension.mime, |
|
| 976 extension.mime_len); |
|
| 977 } |
|
| 978 |
|
| 979 #ifdef SILC_ATTRIBUTE_USER_ICON |
|
| 980 /* Save user icon */ |
|
| 981 if (usericon.mime) { |
|
| 982 SilcMime m = silc_mime_decode(usericon.mime, |
|
| 983 usericon.mime_len); |
|
| 984 if (m) { |
|
| 985 const char *type = silc_mime_get_field(m, "Content-Type"); |
|
| 986 if (!strcmp(type, "image/jpeg") || |
|
| 987 !strcmp(type, "image/gif") || |
|
| 988 !strcmp(type, "image/bmp") || |
|
| 989 !strcmp(type, "image/png")) { |
|
| 990 const unsigned char *data; |
|
| 991 SilcUInt32 data_len; |
|
| 992 data = silc_mime_get_data(m, &data_len); |
|
| 993 if (data) { |
|
| 994 /* TODO: Check if SILC gives us something to use as the checksum instead */ |
|
| 995 purple_buddy_icons_set_for_user(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), g_memdup(data, data_len), data_len, NULL); |
|
| 996 } |
|
| 997 } |
|
| 998 silc_mime_free(m); |
|
| 999 } |
|
| 1000 } |
|
| 1001 #endif |
|
| 1002 } |
|
| 1003 |
|
| 1004 /* Save the public key path to buddy properties, as it is used |
|
| 1005 to identify the buddy in the network (and not the nickname). */ |
|
| 1006 memset(filename, 0, sizeof(filename)); |
|
| 1007 g_snprintf(filename, sizeof(filename) - 1, |
|
| 1008 "%s" G_DIR_SEPARATOR_S "clientkeys" G_DIR_SEPARATOR_S "clientkey_%s.pub", |
|
| 1009 silcpurple_silcdir(), fingerprint); |
|
| 1010 purple_blist_node_set_string((PurpleBlistNode *)b, "public-key", filename); |
|
| 1011 |
|
| 1012 /* Update online status */ |
|
| 1013 purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_AVAILABLE, NULL); |
|
| 1014 |
|
| 1015 /* Finally, start watching this user so we receive its status |
|
| 1016 changes from the server */ |
|
| 1017 g_snprintf(filename2, sizeof(filename2) - 1, "+%s", filename); |
|
| 1018 silc_client_command_call(r->client, r->conn, NULL, "WATCH", "-pubkey", |
|
| 1019 filename2, NULL); |
|
| 1020 |
|
| 1021 silc_free(fingerprint); |
|
| 1022 silc_free(r); |
|
| 1023 } |
|
| 1024 |
|
| 1025 static void |
|
| 1026 silcpurple_add_buddy_ask_import(void *user_data, const char *name) |
|
| 1027 { |
|
| 1028 SilcPurpleBuddyRes r = (SilcPurpleBuddyRes)user_data; |
|
| 1029 SilcPublicKey public_key; |
|
| 1030 |
|
| 1031 /* Load the public key */ |
|
| 1032 if (!silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_PEM) && |
|
| 1033 !silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_BIN)) { |
|
| 1034 silcpurple_add_buddy_ask_pk_cb(r, 0); |
|
| 1035 purple_notify_error(r->client->application, |
|
| 1036 _("Add Buddy"), _("Could not load public key"), NULL); |
|
| 1037 return; |
|
| 1038 } |
|
| 1039 |
|
| 1040 /* Now verify the public key */ |
|
| 1041 r->offline_pk = silc_pkcs_public_key_encode(public_key, &r->offline_pk_len); |
|
| 1042 silcpurple_verify_public_key(r->client, r->conn, r->b->name, |
|
| 1043 SILC_SOCKET_TYPE_CLIENT, |
|
| 1044 r->offline_pk, r->offline_pk_len, |
|
| 1045 SILC_SKE_PK_TYPE_SILC, |
|
| 1046 silcpurple_add_buddy_save, r); |
|
| 1047 } |
|
| 1048 |
|
| 1049 static void |
|
| 1050 silcpurple_add_buddy_ask_pk_cancel(void *user_data, const char *name) |
|
| 1051 { |
|
| 1052 SilcPurpleBuddyRes r = (SilcPurpleBuddyRes)user_data; |
|
| 1053 |
|
| 1054 /* The user did not import public key. The buddy is unusable. */ |
|
| 1055 silcpurple_add_buddy_pk_no(r); |
|
| 1056 silc_free(r); |
|
| 1057 } |
|
| 1058 |
|
| 1059 static void |
|
| 1060 silcpurple_add_buddy_ask_pk_cb(SilcPurpleBuddyRes r, gint id) |
|
| 1061 { |
|
| 1062 if (id != 0) { |
|
| 1063 /* The user did not import public key. The buddy is unusable. */ |
|
| 1064 silcpurple_add_buddy_pk_no(r); |
|
| 1065 silc_free(r); |
|
| 1066 return; |
|
| 1067 } |
|
| 1068 |
|
| 1069 /* Open file selector to select the public key. */ |
|
| 1070 purple_request_file(r->client->application, _("Open..."), NULL, FALSE, |
|
| 1071 G_CALLBACK(silcpurple_add_buddy_ask_import), |
|
| 1072 G_CALLBACK(silcpurple_add_buddy_ask_pk_cancel), |
|
| 1073 purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r); |
|
| 1074 |
|
| 1075 } |
|
| 1076 |
|
| 1077 static void |
|
| 1078 silcpurple_add_buddy_ask_pk(SilcPurpleBuddyRes r) |
|
| 1079 { |
|
| 1080 char tmp[512]; |
|
| 1081 g_snprintf(tmp, sizeof(tmp), _("The %s buddy is not present in the network"), |
|
| 1082 r->b->name); |
|
| 1083 purple_request_action(r->client->application, _("Add Buddy"), tmp, |
|
| 1084 _("To add the buddy you must import his/her public key. " |
|
| 1085 "Press Import to import a public key."), 0, |
|
| 1086 purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r, 2, |
|
| 1087 _("Cancel"), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb), |
|
| 1088 _("_Import..."), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb)); |
|
| 1089 } |
|
| 1090 |
|
| 1091 static void |
|
| 1092 silcpurple_add_buddy_getkey_cb(SilcPurpleBuddyRes r, |
|
| 1093 SilcClientCommandReplyContext cmd) |
|
| 1094 { |
|
| 1095 SilcClientEntry client_entry; |
|
| 1096 unsigned char *pk; |
|
| 1097 SilcUInt32 pk_len; |
|
| 1098 |
|
| 1099 /* Get the client entry. */ |
|
| 1100 client_entry = silc_client_get_client_by_id(r->client, r->conn, |
|
| 1101 &r->client_id); |
|
| 1102 if (!client_entry || !client_entry->public_key) { |
|
| 1103 /* The buddy is offline/nonexistent. We will require user |
|
| 1104 to associate a public key with the buddy or the buddy |
|
| 1105 cannot be added. */ |
|
| 1106 r->offline = TRUE; |
|
| 1107 silcpurple_add_buddy_ask_pk(r); |
|
| 1108 return; |
|
| 1109 } |
|
| 1110 |
|
| 1111 /* Now verify the public key */ |
|
| 1112 pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len); |
|
| 1113 silcpurple_verify_public_key(r->client, r->conn, client_entry->nickname, |
|
| 1114 SILC_SOCKET_TYPE_CLIENT, |
|
| 1115 pk, pk_len, SILC_SKE_PK_TYPE_SILC, |
|
| 1116 silcpurple_add_buddy_save, r); |
|
| 1117 silc_free(pk); |
|
| 1118 } |
|
| 1119 |
|
| 1120 static void |
|
| 1121 silcpurple_add_buddy_select_cb(SilcPurpleBuddyRes r, PurpleRequestFields *fields) |
|
| 1122 { |
|
| 1123 PurpleRequestField *f; |
|
| 1124 GList *list; |
|
| 1125 SilcClientEntry client_entry; |
|
| 1126 |
|
| 1127 f = purple_request_fields_get_field(fields, "list"); |
|
| 1128 list = purple_request_field_list_get_selected(f); |
|
| 1129 if (!list) { |
|
| 1130 /* The user did not select any user. */ |
|
| 1131 silcpurple_add_buddy_pk_no(r); |
|
| 1132 silc_free(r); |
|
| 1133 return; |
|
| 1134 } |
|
| 1135 |
|
| 1136 client_entry = purple_request_field_list_get_data(f, list->data); |
|
| 1137 silcpurple_add_buddy_resolved(r->client, r->conn, &client_entry, 1, r); |
|
| 1138 } |
|
| 1139 |
|
| 1140 static void |
|
| 1141 silcpurple_add_buddy_select_cancel(SilcPurpleBuddyRes r, PurpleRequestFields *fields) |
|
| 1142 { |
|
| 1143 /* The user did not select any user. */ |
|
| 1144 silcpurple_add_buddy_pk_no(r); |
|
| 1145 silc_free(r); |
|
| 1146 } |
|
| 1147 |
|
| 1148 static void |
|
| 1149 silcpurple_add_buddy_select(SilcPurpleBuddyRes r, |
|
| 1150 SilcClientEntry *clients, |
|
| 1151 SilcUInt32 clients_count) |
|
| 1152 { |
|
| 1153 PurpleRequestFields *fields; |
|
| 1154 PurpleRequestFieldGroup *g; |
|
| 1155 PurpleRequestField *f; |
|
| 1156 char tmp[512], tmp2[128]; |
|
| 1157 int i; |
|
| 1158 char *fingerprint; |
|
| 1159 |
|
| 1160 fields = purple_request_fields_new(); |
|
| 1161 g = purple_request_field_group_new(NULL); |
|
| 1162 f = purple_request_field_list_new("list", NULL); |
|
| 1163 purple_request_field_group_add_field(g, f); |
|
| 1164 purple_request_field_list_set_multi_select(f, FALSE); |
|
| 1165 purple_request_fields_add_group(fields, g); |
|
| 1166 |
|
| 1167 for (i = 0; i < clients_count; i++) { |
|
| 1168 fingerprint = NULL; |
|
| 1169 if (clients[i]->fingerprint) { |
|
| 1170 fingerprint = silc_fingerprint(clients[i]->fingerprint, |
|
| 1171 clients[i]->fingerprint_len); |
|
| 1172 g_snprintf(tmp2, sizeof(tmp2), "\n%s", fingerprint); |
|
| 1173 } |
|
| 1174 g_snprintf(tmp, sizeof(tmp), "%s - %s (%s@%s)%s", |
|
| 1175 clients[i]->realname, clients[i]->nickname, |
|
| 1176 clients[i]->username, clients[i]->hostname ? |
|
| 1177 clients[i]->hostname : "", |
|
| 1178 fingerprint ? tmp2 : ""); |
|
| 1179 purple_request_field_list_add_icon(f, tmp, NULL, clients[i]); |
|
| 1180 silc_free(fingerprint); |
|
| 1181 } |
|
| 1182 |
|
| 1183 purple_request_fields(r->client->application, _("Add Buddy"), |
|
| 1184 _("Select correct user"), |
|
| 1185 r->pubkey_search |
|
| 1186 ? _("More than one user was found with the same public key. Select " |
|
| 1187 "the correct user from the list to add to the buddy list.") |
|
| 1188 : _("More than one user was found with the same name. Select " |
|
| 1189 "the correct user from the list to add to the buddy list."), |
|
| 1190 fields, |
|
| 1191 _("OK"), G_CALLBACK(silcpurple_add_buddy_select_cb), |
|
| 1192 _("Cancel"), G_CALLBACK(silcpurple_add_buddy_select_cancel), |
|
| 1193 purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r); |
|
| 1194 } |
|
| 1195 |
|
| 1196 static void |
|
| 1197 silcpurple_add_buddy_resolved(SilcClient client, |
|
| 1198 SilcClientConnection conn, |
|
| 1199 SilcClientEntry *clients, |
|
| 1200 SilcUInt32 clients_count, |
|
| 1201 void *context) |
|
| 1202 { |
|
| 1203 SilcPurpleBuddyRes r = context; |
|
| 1204 PurpleBuddy *b = r->b; |
|
| 1205 SilcAttributePayload pub; |
|
| 1206 SilcAttributeObjPk userpk; |
|
| 1207 unsigned char *pk; |
|
| 1208 SilcUInt32 pk_len; |
|
| 1209 const char *filename; |
|
| 1210 |
|
| 1211 filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key"); |
|
| 1212 |
|
| 1213 /* If the buddy is offline/nonexistent, we will require user |
|
| 1214 to associate a public key with the buddy or the buddy |
|
| 1215 cannot be added. */ |
|
| 1216 if (!clients_count) { |
|
| 1217 if (r->init) { |
|
| 1218 silc_free(r); |
|
| 1219 return; |
|
| 1220 } |
|
| 1221 |
|
| 1222 r->offline = TRUE; |
|
| 1223 /* If the user has already associated a public key, try loading it |
|
| 1224 * before prompting the user to load it again */ |
|
| 1225 if (filename != NULL) |
|
| 1226 silcpurple_add_buddy_ask_import(r, filename); |
|
| 1227 else |
|
| 1228 silcpurple_add_buddy_ask_pk(r); |
|
| 1229 return; |
|
| 1230 } |
|
| 1231 |
|
| 1232 /* If more than one client was found with nickname, we need to verify |
|
| 1233 from user which one is the correct. */ |
|
| 1234 if (clients_count > 1 && !r->pubkey_search) { |
|
| 1235 if (r->init) { |
|
| 1236 silc_free(r); |
|
| 1237 return; |
|
| 1238 } |
|
| 1239 |
|
| 1240 silcpurple_add_buddy_select(r, clients, clients_count); |
|
| 1241 return; |
|
| 1242 } |
|
| 1243 |
|
| 1244 /* If we searched using public keys and more than one entry was found |
|
| 1245 the same person is logged on multiple times. */ |
|
| 1246 if (clients_count > 1 && r->pubkey_search && b->name) { |
|
| 1247 if (r->init) { |
|
| 1248 /* Find the entry that closest matches to the |
|
| 1249 buddy nickname. */ |
|
| 1250 int i; |
|
| 1251 for (i = 0; i < clients_count; i++) { |
|
| 1252 if (!g_ascii_strncasecmp(b->name, clients[i]->nickname, |
|
| 1253 strlen(b->name))) { |
|
| 1254 clients[0] = clients[i]; |
|
| 1255 break; |
|
| 1256 } |
|
| 1257 } |
|
| 1258 } else { |
|
| 1259 /* Verify from user which one is correct */ |
|
| 1260 silcpurple_add_buddy_select(r, clients, clients_count); |
|
| 1261 return; |
|
| 1262 } |
|
| 1263 } |
|
| 1264 |
|
| 1265 /* The client was found. Now get its public key and verify |
|
| 1266 that before adding the buddy. */ |
|
| 1267 memset(&userpk, 0, sizeof(userpk)); |
|
| 1268 b->proto_data = silc_memdup(clients[0]->id, sizeof(*clients[0]->id)); |
|
| 1269 r->client_id = *clients[0]->id; |
|
| 1270 |
|
| 1271 /* Get the public key from attributes, if not present then |
|
| 1272 resolve it with GETKEY unless we have it cached already. */ |
|
| 1273 if (clients[0]->attrs && !clients[0]->public_key) { |
|
| 1274 pub = silcpurple_get_attr(clients[0]->attrs, |
|
| 1275 SILC_ATTRIBUTE_USER_PUBLIC_KEY); |
|
| 1276 if (!pub || !silc_attribute_get_object(pub, (void *)&userpk, |
|
| 1277 sizeof(userpk))) { |
|
| 1278 /* Get public key with GETKEY */ |
|
| 1279 silc_client_command_call(client, conn, NULL, |
|
| 1280 "GETKEY", clients[0]->nickname, NULL); |
|
| 1281 silc_client_command_pending(conn, SILC_COMMAND_GETKEY, |
|
| 1282 conn->cmd_ident, |
|
| 1283 (SilcCommandCb)silcpurple_add_buddy_getkey_cb, |
|
| 1284 r); |
|
| 1285 return; |
|
| 1286 } |
|
| 1287 if (!silc_pkcs_public_key_decode(userpk.data, userpk.data_len, |
|
| 1288 &clients[0]->public_key)) |
|
| 1289 return; |
|
| 1290 silc_free(userpk.data); |
|
| 1291 } else if (filename && !clients[0]->public_key) { |
|
| 1292 if (!silc_pkcs_load_public_key(filename, &clients[0]->public_key, |
|
| 1293 SILC_PKCS_FILE_PEM) && |
|
| 1294 !silc_pkcs_load_public_key(filename, &clients[0]->public_key, |
|
| 1295 SILC_PKCS_FILE_BIN)) { |
|
| 1296 /* Get public key with GETKEY */ |
|
| 1297 silc_client_command_call(client, conn, NULL, |
|
| 1298 "GETKEY", clients[0]->nickname, NULL); |
|
| 1299 silc_client_command_pending(conn, SILC_COMMAND_GETKEY, |
|
| 1300 conn->cmd_ident, |
|
| 1301 (SilcCommandCb)silcpurple_add_buddy_getkey_cb, |
|
| 1302 r); |
|
| 1303 return; |
|
| 1304 } |
|
| 1305 } else if (!clients[0]->public_key) { |
|
| 1306 /* Get public key with GETKEY */ |
|
| 1307 silc_client_command_call(client, conn, NULL, |
|
| 1308 "GETKEY", clients[0]->nickname, NULL); |
|
| 1309 silc_client_command_pending(conn, SILC_COMMAND_GETKEY, |
|
| 1310 conn->cmd_ident, |
|
| 1311 (SilcCommandCb)silcpurple_add_buddy_getkey_cb, |
|
| 1312 r); |
|
| 1313 return; |
|
| 1314 } |
|
| 1315 |
|
| 1316 /* We have the public key, verify it. */ |
|
| 1317 pk = silc_pkcs_public_key_encode(clients[0]->public_key, &pk_len); |
|
| 1318 silcpurple_verify_public_key(client, conn, clients[0]->nickname, |
|
| 1319 SILC_SOCKET_TYPE_CLIENT, |
|
| 1320 pk, pk_len, SILC_SKE_PK_TYPE_SILC, |
|
| 1321 silcpurple_add_buddy_save, r); |
|
| 1322 silc_free(pk); |
|
| 1323 } |
|
| 1324 |
|
| 1325 static void |
|
| 1326 silcpurple_add_buddy_i(PurpleConnection *gc, PurpleBuddy *b, gboolean init) |
|
| 1327 { |
|
| 1328 SilcPurple sg = gc->proto_data; |
|
| 1329 SilcClient client = sg->client; |
|
| 1330 SilcClientConnection conn = sg->conn; |
|
| 1331 SilcPurpleBuddyRes r; |
|
| 1332 SilcBuffer attrs; |
|
| 1333 const char *filename, *name = b->name; |
|
| 1334 |
|
| 1335 r = silc_calloc(1, sizeof(*r)); |
|
| 1336 if (!r) |
|
| 1337 return; |
|
| 1338 r->client = client; |
|
| 1339 r->conn = conn; |
|
| 1340 r->b = b; |
|
| 1341 r->init = init; |
|
| 1342 |
|
| 1343 /* See if we have this buddy's public key. If we do use that |
|
| 1344 to search the details. */ |
|
| 1345 filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key"); |
|
| 1346 if (filename) { |
|
| 1347 SilcPublicKey public_key; |
|
| 1348 SilcAttributeObjPk userpk; |
|
| 1349 |
|
| 1350 if (!silc_pkcs_load_public_key(filename, &public_key, |
|
| 1351 SILC_PKCS_FILE_PEM) && |
|
| 1352 !silc_pkcs_load_public_key(filename, &public_key, |
|
| 1353 SILC_PKCS_FILE_BIN)) |
|
| 1354 return; |
|
| 1355 |
|
| 1356 /* Get all attributes, and use the public key to search user */ |
|
| 1357 name = NULL; |
|
| 1358 attrs = silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO, |
|
| 1359 SILC_ATTRIBUTE_SERVICE, |
|
| 1360 SILC_ATTRIBUTE_STATUS_MOOD, |
|
| 1361 SILC_ATTRIBUTE_STATUS_FREETEXT, |
|
| 1362 SILC_ATTRIBUTE_STATUS_MESSAGE, |
|
| 1363 SILC_ATTRIBUTE_PREFERRED_LANGUAGE, |
|
| 1364 SILC_ATTRIBUTE_PREFERRED_CONTACT, |
|
| 1365 SILC_ATTRIBUTE_TIMEZONE, |
|
| 1366 SILC_ATTRIBUTE_GEOLOCATION, |
|
| 1367 #ifdef SILC_ATTRIBUTE_USER_ICON |
|
| 1368 SILC_ATTRIBUTE_USER_ICON, |
|
| 1369 #endif |
|
| 1370 SILC_ATTRIBUTE_DEVICE_INFO, 0); |
|
| 1371 userpk.type = "silc-rsa"; |
|
| 1372 userpk.data = silc_pkcs_public_key_encode(public_key, &userpk.data_len); |
|
| 1373 attrs = silc_attribute_payload_encode(attrs, |
|
| 1374 SILC_ATTRIBUTE_USER_PUBLIC_KEY, |
|
| 1375 SILC_ATTRIBUTE_FLAG_VALID, |
|
| 1376 &userpk, sizeof(userpk)); |
|
| 1377 silc_free(userpk.data); |
|
| 1378 silc_pkcs_public_key_free(public_key); |
|
| 1379 r->pubkey_search = TRUE; |
|
| 1380 } else { |
|
| 1381 /* Get all attributes */ |
|
| 1382 attrs = silc_client_attributes_request(0); |
|
| 1383 } |
|
| 1384 |
|
| 1385 /* Resolve */ |
|
| 1386 silc_client_get_clients_whois(client, conn, name, NULL, attrs, |
|
| 1387 silcpurple_add_buddy_resolved, r); |
|
| 1388 silc_buffer_free(attrs); |
|
| 1389 } |
|
| 1390 |
|
| 1391 void silcpurple_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) |
|
| 1392 { |
|
| 1393 /* Don't add if the buddy is already on the list. |
|
| 1394 * |
|
| 1395 * SILC doesn't have groups, so we don't need to do anything |
|
| 1396 * for a move. */ |
|
| 1397 if (purple_buddy_get_protocol_data(buddy) == NULL) |
|
| 1398 silcpurple_add_buddy_i(gc, buddy, FALSE); |
|
| 1399 } |
|
| 1400 |
|
| 1401 void silcpurple_send_buddylist(PurpleConnection *gc) |
|
| 1402 { |
|
| 1403 PurpleBuddyList *blist; |
|
| 1404 PurpleBlistNode *gnode, *cnode, *bnode; |
|
| 1405 PurpleBuddy *buddy; |
|
| 1406 PurpleAccount *account; |
|
| 1407 |
|
| 1408 account = purple_connection_get_account(gc); |
|
| 1409 |
|
| 1410 if ((blist = purple_get_blist()) != NULL) |
|
| 1411 { |
|
| 1412 for (gnode = blist->root; gnode != NULL; gnode = gnode->next) |
|
| 1413 { |
|
| 1414 if (!PURPLE_BLIST_NODE_IS_GROUP(gnode)) |
|
| 1415 continue; |
|
| 1416 for (cnode = gnode->child; cnode != NULL; cnode = cnode->next) |
|
| 1417 { |
|
| 1418 if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode)) |
|
| 1419 continue; |
|
| 1420 for (bnode = cnode->child; bnode != NULL; bnode = bnode->next) |
|
| 1421 { |
|
| 1422 if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode)) |
|
| 1423 continue; |
|
| 1424 buddy = (PurpleBuddy *)bnode; |
|
| 1425 if (purple_buddy_get_account(buddy) == account) |
|
| 1426 silcpurple_add_buddy_i(gc, buddy, TRUE); |
|
| 1427 } |
|
| 1428 } |
|
| 1429 } |
|
| 1430 } |
|
| 1431 } |
|
| 1432 |
|
| 1433 void silcpurple_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, |
|
| 1434 PurpleGroup *group) |
|
| 1435 { |
|
| 1436 silc_free(buddy->proto_data); |
|
| 1437 } |
|
| 1438 |
|
| 1439 void silcpurple_idle_set(PurpleConnection *gc, int idle) |
|
| 1440 |
|
| 1441 { |
|
| 1442 SilcPurple sg; |
|
| 1443 SilcClient client; |
|
| 1444 SilcClientConnection conn; |
|
| 1445 SilcAttributeObjService service; |
|
| 1446 const char *server; |
|
| 1447 int port; |
|
| 1448 |
|
| 1449 sg = gc->proto_data; |
|
| 1450 if (sg == NULL) |
|
| 1451 return; |
|
| 1452 |
|
| 1453 client = sg->client; |
|
| 1454 if (client == NULL) |
|
| 1455 return; |
|
| 1456 |
|
| 1457 conn = sg->conn; |
|
| 1458 if (conn == NULL) |
|
| 1459 return; |
|
| 1460 |
|
| 1461 server = purple_account_get_string(sg->account, "server", |
|
| 1462 "silc.silcnet.org"); |
|
| 1463 port = purple_account_get_int(sg->account, "port", 706), |
|
| 1464 |
|
| 1465 memset(&service, 0, sizeof(service)); |
|
| 1466 silc_client_attribute_del(client, conn, |
|
| 1467 SILC_ATTRIBUTE_SERVICE, NULL); |
|
| 1468 service.port = port; |
|
| 1469 g_snprintf(service.address, sizeof(service.address), "%s", server); |
|
| 1470 service.idle = idle; |
|
| 1471 silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_SERVICE, |
|
| 1472 &service, sizeof(service)); |
|
| 1473 } |
|
| 1474 |
|
| 1475 char *silcpurple_status_text(PurpleBuddy *b) |
|
| 1476 { |
|
| 1477 SilcPurple sg = b->account->gc->proto_data; |
|
| 1478 SilcClient client = sg->client; |
|
| 1479 SilcClientConnection conn = sg->conn; |
|
| 1480 SilcClientID *client_id = b->proto_data; |
|
| 1481 SilcClientEntry client_entry; |
|
| 1482 SilcAttributePayload attr; |
|
| 1483 SilcAttributeMood mood = 0; |
|
| 1484 |
|
| 1485 /* Get the client entry. */ |
|
| 1486 client_entry = silc_client_get_client_by_id(client, conn, client_id); |
|
| 1487 if (!client_entry) |
|
| 1488 return NULL; |
|
| 1489 |
|
| 1490 /* If user is online, we show the mood status, if available. |
|
| 1491 If user is offline or away that status is indicated. */ |
|
| 1492 |
|
| 1493 if (client_entry->mode & SILC_UMODE_DETACHED) |
|
| 1494 return g_strdup(_("Detached")); |
|
| 1495 if (client_entry->mode & SILC_UMODE_GONE) |
|
| 1496 return g_strdup(_("Away")); |
|
| 1497 if (client_entry->mode & SILC_UMODE_INDISPOSED) |
|
| 1498 return g_strdup(_("Indisposed")); |
|
| 1499 if (client_entry->mode & SILC_UMODE_BUSY) |
|
| 1500 return g_strdup(_("Busy")); |
|
| 1501 if (client_entry->mode & SILC_UMODE_PAGE) |
|
| 1502 return g_strdup(_("Wake Me Up")); |
|
| 1503 if (client_entry->mode & SILC_UMODE_HYPER) |
|
| 1504 return g_strdup(_("Hyper Active")); |
|
| 1505 if (client_entry->mode & SILC_UMODE_ROBOT) |
|
| 1506 return g_strdup(_("Robot")); |
|
| 1507 |
|
| 1508 attr = silcpurple_get_attr(client_entry->attrs, SILC_ATTRIBUTE_STATUS_MOOD); |
|
| 1509 if (attr && silc_attribute_get_object(attr, &mood, sizeof(mood))) { |
|
| 1510 /* The mood is a bit mask, so we could show multiple moods, |
|
| 1511 but let's show only one for now. */ |
|
| 1512 if (mood & SILC_ATTRIBUTE_MOOD_HAPPY) |
|
| 1513 return g_strdup(_("Happy")); |
|
| 1514 if (mood & SILC_ATTRIBUTE_MOOD_SAD) |
|
| 1515 return g_strdup(_("Sad")); |
|
| 1516 if (mood & SILC_ATTRIBUTE_MOOD_ANGRY) |
|
| 1517 return g_strdup(_("Angry")); |
|
| 1518 if (mood & SILC_ATTRIBUTE_MOOD_JEALOUS) |
|
| 1519 return g_strdup(_("Jealous")); |
|
| 1520 if (mood & SILC_ATTRIBUTE_MOOD_ASHAMED) |
|
| 1521 return g_strdup(_("Ashamed")); |
|
| 1522 if (mood & SILC_ATTRIBUTE_MOOD_INVINCIBLE) |
|
| 1523 return g_strdup(_("Invincible")); |
|
| 1524 if (mood & SILC_ATTRIBUTE_MOOD_INLOVE) |
|
| 1525 return g_strdup(_("In Love")); |
|
| 1526 if (mood & SILC_ATTRIBUTE_MOOD_SLEEPY) |
|
| 1527 return g_strdup(_("Sleepy")); |
|
| 1528 if (mood & SILC_ATTRIBUTE_MOOD_BORED) |
|
| 1529 return g_strdup(_("Bored")); |
|
| 1530 if (mood & SILC_ATTRIBUTE_MOOD_EXCITED) |
|
| 1531 return g_strdup(_("Excited")); |
|
| 1532 if (mood & SILC_ATTRIBUTE_MOOD_ANXIOUS) |
|
| 1533 return g_strdup(_("Anxious")); |
|
| 1534 } |
|
| 1535 |
|
| 1536 return NULL; |
|
| 1537 } |
|
| 1538 |
|
| 1539 void silcpurple_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full) |
|
| 1540 { |
|
| 1541 SilcPurple sg = b->account->gc->proto_data; |
|
| 1542 SilcClient client = sg->client; |
|
| 1543 SilcClientConnection conn = sg->conn; |
|
| 1544 SilcClientID *client_id = b->proto_data; |
|
| 1545 SilcClientEntry client_entry; |
|
| 1546 char *moodstr, *statusstr, *contactstr, *langstr, *devicestr, *tzstr, *geostr; |
|
| 1547 char tmp[256]; |
|
| 1548 |
|
| 1549 /* Get the client entry. */ |
|
| 1550 client_entry = silc_client_get_client_by_id(client, conn, client_id); |
|
| 1551 if (!client_entry) |
|
| 1552 return; |
|
| 1553 |
|
| 1554 if (client_entry->nickname) |
|
| 1555 purple_notify_user_info_add_pair(user_info, _("Nickname"), |
|
| 1556 client_entry->nickname); |
|
| 1557 if (client_entry->username && client_entry->hostname) { |
|
| 1558 g_snprintf(tmp, sizeof(tmp), "%s@%s", client_entry->username, client_entry->hostname); |
|
| 1559 purple_notify_user_info_add_pair(user_info, _("Username"), tmp); |
|
| 1560 } |
|
| 1561 if (client_entry->mode) { |
|
| 1562 memset(tmp, 0, sizeof(tmp)); |
|
| 1563 silcpurple_get_umode_string(client_entry->mode, |
|
| 1564 tmp, sizeof(tmp) - strlen(tmp)); |
|
| 1565 purple_notify_user_info_add_pair(user_info, _("User Modes"), tmp); |
|
| 1566 } |
|
| 1567 |
|
| 1568 silcpurple_parse_attrs(client_entry->attrs, &moodstr, &statusstr, &contactstr, &langstr, &devicestr, &tzstr, &geostr); |
|
| 1569 |
|
| 1570 if (statusstr) { |
|
| 1571 purple_notify_user_info_add_pair(user_info, _("Message"), statusstr); |
|
| 1572 g_free(statusstr); |
|
| 1573 } |
|
| 1574 |
|
| 1575 if (full) { |
|
| 1576 if (moodstr) { |
|
| 1577 purple_notify_user_info_add_pair(user_info, _("Mood"), moodstr); |
|
| 1578 g_free(moodstr); |
|
| 1579 } |
|
| 1580 |
|
| 1581 if (contactstr) { |
|
| 1582 purple_notify_user_info_add_pair(user_info, _("Preferred Contact"), contactstr); |
|
| 1583 g_free(contactstr); |
|
| 1584 } |
|
| 1585 |
|
| 1586 if (langstr) { |
|
| 1587 purple_notify_user_info_add_pair(user_info, _("Preferred Language"), langstr); |
|
| 1588 g_free(langstr); |
|
| 1589 } |
|
| 1590 |
|
| 1591 if (devicestr) { |
|
| 1592 purple_notify_user_info_add_pair(user_info, _("Device"), devicestr); |
|
| 1593 g_free(devicestr); |
|
| 1594 } |
|
| 1595 |
|
| 1596 if (tzstr) { |
|
| 1597 purple_notify_user_info_add_pair(user_info, _("Timezone"), tzstr); |
|
| 1598 g_free(tzstr); |
|
| 1599 } |
|
| 1600 |
|
| 1601 if (geostr) { |
|
| 1602 purple_notify_user_info_add_pair(user_info, _("Geolocation"), geostr); |
|
| 1603 g_free(geostr); |
|
| 1604 } |
|
| 1605 } |
|
| 1606 } |
|
| 1607 |
|
| 1608 static void |
|
| 1609 silcpurple_buddy_kill(PurpleBlistNode *node, gpointer data) |
|
| 1610 { |
|
| 1611 PurpleBuddy *b; |
|
| 1612 PurpleConnection *gc; |
|
| 1613 SilcPurple sg; |
|
| 1614 |
|
| 1615 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); |
|
| 1616 |
|
| 1617 b = (PurpleBuddy *) node; |
|
| 1618 gc = purple_account_get_connection(b->account); |
|
| 1619 sg = gc->proto_data; |
|
| 1620 |
|
| 1621 /* Call KILL */ |
|
| 1622 silc_client_command_call(sg->client, sg->conn, NULL, "KILL", |
|
| 1623 b->name, "Killed by operator", NULL); |
|
| 1624 } |
|
| 1625 |
|
| 1626 typedef struct { |
|
| 1627 SilcPurple sg; |
|
| 1628 SilcClientEntry client_entry; |
|
| 1629 } *SilcPurpleBuddyWb; |
|
| 1630 |
|
| 1631 static void |
|
| 1632 silcpurple_buddy_wb(PurpleBlistNode *node, gpointer data) |
|
| 1633 { |
|
| 1634 SilcPurpleBuddyWb wb = data; |
|
| 1635 silcpurple_wb_init(wb->sg, wb->client_entry); |
|
| 1636 silc_free(wb); |
|
| 1637 } |
|
| 1638 |
|
| 1639 GList *silcpurple_buddy_menu(PurpleBuddy *buddy) |
|
| 1640 { |
|
| 1641 PurpleConnection *gc = purple_account_get_connection(buddy->account); |
|
| 1642 SilcPurple sg = gc->proto_data; |
|
| 1643 SilcClientConnection conn = sg->conn; |
|
| 1644 const char *pkfile = NULL; |
|
| 1645 SilcClientEntry client_entry = NULL; |
|
| 1646 PurpleMenuAction *act; |
|
| 1647 GList *m = NULL; |
|
| 1648 SilcPurpleBuddyWb wb; |
|
| 1649 |
|
| 1650 pkfile = purple_blist_node_get_string((PurpleBlistNode *) buddy, "public-key"); |
|
| 1651 client_entry = silc_client_get_client_by_id(sg->client, |
|
| 1652 sg->conn, |
|
| 1653 buddy->proto_data); |
|
| 1654 |
|
| 1655 if (client_entry && client_entry->send_key) { |
|
| 1656 act = purple_menu_action_new(_("Reset IM Key"), |
|
| 1657 PURPLE_CALLBACK(silcpurple_buddy_resetkey), |
|
| 1658 NULL, NULL); |
|
| 1659 m = g_list_append(m, act); |
|
| 1660 |
|
| 1661 } else { |
|
| 1662 act = purple_menu_action_new(_("IM with Key Exchange"), |
|
| 1663 PURPLE_CALLBACK(silcpurple_buddy_keyagr), |
|
| 1664 NULL, NULL); |
|
| 1665 m = g_list_append(m, act); |
|
| 1666 |
|
| 1667 act = purple_menu_action_new(_("IM with Password"), |
|
| 1668 PURPLE_CALLBACK(silcpurple_buddy_privkey_menu), |
|
| 1669 NULL, NULL); |
|
| 1670 m = g_list_append(m, act); |
|
| 1671 } |
|
| 1672 |
|
| 1673 if (pkfile) { |
|
| 1674 act = purple_menu_action_new(_("Show Public Key"), |
|
| 1675 PURPLE_CALLBACK(silcpurple_buddy_showkey), |
|
| 1676 NULL, NULL); |
|
| 1677 m = g_list_append(m, act); |
|
| 1678 |
|
| 1679 } else { |
|
| 1680 act = purple_menu_action_new(_("Get Public Key..."), |
|
| 1681 PURPLE_CALLBACK(silcpurple_buddy_getkey_menu), |
|
| 1682 NULL, NULL); |
|
| 1683 m = g_list_append(m, act); |
|
| 1684 } |
|
| 1685 |
|
| 1686 if (conn && conn->local_entry->mode & SILC_UMODE_ROUTER_OPERATOR) { |
|
| 1687 act = purple_menu_action_new(_("Kill User"), |
|
| 1688 PURPLE_CALLBACK(silcpurple_buddy_kill), |
|
| 1689 NULL, NULL); |
|
| 1690 m = g_list_append(m, act); |
|
| 1691 } |
|
| 1692 |
|
| 1693 if (client_entry) { |
|
| 1694 wb = silc_calloc(1, sizeof(*wb)); |
|
| 1695 wb->sg = sg; |
|
| 1696 wb->client_entry = client_entry; |
|
| 1697 act = purple_menu_action_new(_("Draw On Whiteboard"), |
|
| 1698 PURPLE_CALLBACK(silcpurple_buddy_wb), |
|
| 1699 (void *)wb, NULL); |
|
| 1700 m = g_list_append(m, act); |
|
| 1701 } |
|
| 1702 return m; |
|
| 1703 } |
|
| 1704 |
|
| 1705 #ifdef SILC_ATTRIBUTE_USER_ICON |
|
| 1706 void silcpurple_buddy_set_icon(PurpleConnection *gc, PurpleStoredImage *img) |
|
| 1707 { |
|
| 1708 SilcPurple sg = gc->proto_data; |
|
| 1709 SilcClient client = sg->client; |
|
| 1710 SilcClientConnection conn = sg->conn; |
|
| 1711 SilcMime mime; |
|
| 1712 char type[32]; |
|
| 1713 unsigned char *icon; |
|
| 1714 const char *t; |
|
| 1715 SilcAttributeObjMime obj; |
|
| 1716 |
|
| 1717 /* Remove */ |
|
| 1718 if (!img) { |
|
| 1719 silc_client_attribute_del(client, conn, |
|
| 1720 SILC_ATTRIBUTE_USER_ICON, NULL); |
|
| 1721 return; |
|
| 1722 } |
|
| 1723 |
|
| 1724 /* Add */ |
|
| 1725 mime = silc_mime_alloc(); |
|
| 1726 if (!mime) |
|
| 1727 return; |
|
| 1728 |
|
| 1729 t = purple_imgstore_get_extension(img); |
|
| 1730 if (!t || !strcmp(t, "icon")) { |
|
| 1731 silc_mime_free(mime); |
|
| 1732 return; |
|
| 1733 } |
|
| 1734 if (!strcmp(t, "jpg")) |
|
| 1735 t = "jpeg"; |
|
| 1736 g_snprintf(type, sizeof(type), "image/%s", t); |
|
| 1737 silc_mime_add_field(mime, "Content-Type", type); |
|
| 1738 silc_mime_add_data(mime, purple_imgstore_get_data(img), purple_imgstore_get_size(img)); |
|
| 1739 |
|
| 1740 obj.mime = icon = silc_mime_encode(mime, &obj.mime_len); |
|
| 1741 if (obj.mime) |
|
| 1742 silc_client_attribute_add(client, conn, |
|
| 1743 SILC_ATTRIBUTE_USER_ICON, &obj, sizeof(obj)); |
|
| 1744 |
|
| 1745 silc_free(icon); |
|
| 1746 silc_mime_free(mime); |
|
| 1747 } |
|
| 1748 #endif |
|