Mercurial > pidgin
view src/protocols/silc/silc.c @ 12645:fc28451f5d96
[gaim-migrate @ 14983]
SF Patch #1314512 from Sadrul (who has a patch for everything)
"This patch introduces a flag for protocol plugins that
support offline messages (like Y!M and ICQ). This was
encouraged by the following conversation:
<sadrul> should offline buddies be listed/enabled in
the send-to menu?
<rekkanoryo> i would think only for protocols that
support offline messaging, if it's indicated that the
buddy is offline
-- <snip> --
<Bleeter> sadrul: personally, I'd like to see a
'supports offline' flag of some description
<Bleeter> one could then redirect (via plugins) through
email or alternative methods
<Bleeter> just a thought
<Paco-Paco> yeah, that sounds like a reasonble thing to have
This patch uses this flag to disable the buddies in the
send-to menu who are offline and the protocol doesn't
support offline messages."
I made this make the label insensitive instead of the whole menuitem. This
should address SimGuy's concerns about inconsistency (i.e. you could create a
conversation with someone via the buddy list that you couldn't create via the
Send To menu). I also hacked up some voodoo to show the label as sensitive when
moused-over, as that looks better (given the label-insensitive thing is itself a
hack). I think this works quite well.
BUG NOTE:
This makes more obvious an existing bug. The Send To menu isn't updated when
buddies sign on or off or change status (at least under some circumstances).
We need to fix that anyway, so I'm not going to let it hold up this commit.
Switching tabs will clear it up. I'm thinking we just might want to build the
contents of that menu when it is selected. That would save us a mess of
inefficient signal callbacks that update the Send To menus in open windows all
the time.
AIM NOTE:
This assumes that AIM can't offline message. That's not strictly true. You can
message invisible users on AIM. However, by design, we can't tell when a user
is invisible without resorting to dirty hackery. In practice, this isn't a
problem, as you can still select the AIM user from the menu. And really, how
often will you be choosing the Invisible contact, rather than the user going
Invisible in the middle of a conversation or IMing you while they're Invisible?
JABBER NOTE:
This assumes that Jabber can always offline message. This isn't strictly true.
Sadrul said:
I have updated Jabber according to this link which seems to
talk about how to determine the existence offline-message
support in a server:
http://www.jabber.org/jeps/jep-0013.html#discover
However, jabber.org doesn't seem to send the required
info. So I am not sure about it.
He later said:
I talked to Nathan and he said offline message support is
mostly assumed for most jabber servers. GTalk doesn't yet
support it, but they are working on it. So I have made
jabber to always return TRUE.
If there is truly no way to detect offline messaging capability, then this is
an acceptable solution. We could special case Google Talk because of its
popularity, and remove that later. It's probably not worth it though.
MSN NOTE:
This assumes that MSN can never offline message. That's effectively true, but
to be technically correct, MSN can offline message if there's already a
switchboard conversation open with a user. We could write an offline_message
function in the MSN prpl to detect that, but it'd be of limited usefulness,
especially given that under most circumstances (where this might matter), the
switchboard connection will be closed almost immediately.
CVS NOTE:
I'm writing to share a tragic little story.
I have a PC that I use for Gaim development. One day, I was writing a commit
message on it, when all of a suddent it went berserk. The screen started
flashing, and the whole commit message just disappeared. All of it. And it was
a good commit message! I had to cram and rewrite it really quickly. Needless to
say, my rushed commit message wasn't nearly as good, and I blame the PC for that.
Seriously, though, what kind of version control system loses your commit
message on a broken connection to the server? Stupid!
committer: Tailor Script <tailor@pidgin.im>
author | Richard Laager <rlaager@wiktel.com> |
---|---|
date | Fri, 23 Dec 2005 19:26:04 +0000 |
parents | e856f985a0b9 |
children | a0fd3ebcd6fa |
line wrap: on
line source
/* silcgaim.c Author: Pekka Riikonen <priikone@silcnet.org> Copyright (C) 2004 - 2005 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ #include "silcincludes.h" #include "silcclient.h" #include "silcgaim.h" #include "version.h" #include "wb.h" extern SilcClientOperations ops; static GaimPlugin *silc_plugin = NULL; static const char * silcgaim_list_icon(GaimAccount *a, GaimBuddy *b) { return (const char *)"silc"; } static void silcgaim_list_emblems(GaimBuddy *b, const char **se, const char **sw, const char **nw, const char **ne) { } static GList * silcgaim_away_states(GaimAccount *account) { GaimStatusType *type; GList *types = NULL; type = gaim_status_type_new_full(GAIM_STATUS_OFFLINE, SILCGAIM_STATUS_ID_OFFLINE, NULL, FALSE, TRUE, FALSE); types = g_list_append(types, type); type = gaim_status_type_new_full(GAIM_STATUS_AVAILABLE, SILCGAIM_STATUS_ID_AVAILABLE, NULL, FALSE, TRUE, FALSE); types = g_list_append(types, type); type = gaim_status_type_new_full(GAIM_STATUS_AVAILABLE, SILCGAIM_STATUS_ID_HYPER, _("Hyper Active"), FALSE, TRUE, FALSE); types = g_list_append(types, type); type = gaim_status_type_new_full(GAIM_STATUS_AWAY, SILCGAIM_STATUS_ID_AWAY, NULL, FALSE, TRUE, FALSE); types = g_list_append(types, type); type = gaim_status_type_new_full(GAIM_STATUS_UNAVAILABLE, SILCGAIM_STATUS_ID_BUSY, _("Busy"), FALSE, TRUE, FALSE); types = g_list_append(types, type); type = gaim_status_type_new_full(GAIM_STATUS_AWAY, SILCGAIM_STATUS_ID_INDISPOSED, _("Indisposed"), FALSE, TRUE, FALSE); types = g_list_append(types, type); type = gaim_status_type_new_full(GAIM_STATUS_AWAY, SILCGAIM_STATUS_ID_PAGE, _("Wake Me Up"), FALSE, TRUE, FALSE); types = g_list_append(types, type); return types; } static void silcgaim_set_status(GaimAccount *account, GaimStatus *status) { GaimConnection *gc = gaim_account_get_connection(account); SilcGaim sg = NULL; SilcUInt32 mode; SilcBuffer idp; unsigned char mb[4]; const char *state; if (gc != NULL) sg = gc->proto_data; if (status == NULL) return; state = gaim_status_get_id(status); if (state == NULL) return; if ((sg == NULL) || (sg->conn == NULL)) return; mode = sg->conn->local_entry->mode; mode &= ~(SILC_UMODE_GONE | SILC_UMODE_HYPER | SILC_UMODE_BUSY | SILC_UMODE_INDISPOSED | SILC_UMODE_PAGE); if (!strcmp(state, "hyper")) mode |= SILC_UMODE_HYPER; else if (!strcmp(state, "away")) mode |= SILC_UMODE_GONE; else if (!strcmp(state, "busy")) mode |= SILC_UMODE_BUSY; else if (!strcmp(state, "indisposed")) mode |= SILC_UMODE_INDISPOSED; else if (!strcmp(state, "page")) mode |= SILC_UMODE_PAGE; /* Send UMODE */ idp = silc_id_payload_encode(sg->conn->local_id, SILC_ID_CLIENT); SILC_PUT32_MSB(mode, mb); silc_client_command_send(sg->client, sg->conn, SILC_COMMAND_UMODE, ++sg->conn->cmd_ident, 2, 1, idp->data, idp->len, 2, mb, sizeof(mb)); silc_buffer_free(idp); } /*************************** Connection Routines *****************************/ static void silcgaim_keepalive(GaimConnection *gc) { SilcGaim sg = gc->proto_data; silc_client_send_packet(sg->client, sg->conn, SILC_PACKET_HEARTBEAT, NULL, 0); } static int silcgaim_scheduler(gpointer *context) { SilcGaim sg = (SilcGaim)context; silc_client_run_one(sg->client); return 1; } static void silcgaim_nickname_parse(const char *nickname, char **ret_nickname) { silc_parse_userfqdn(nickname, ret_nickname, NULL); } static void silcgaim_login_connected(gpointer data, gint source, GaimInputCondition cond) { GaimConnection *gc = data; SilcGaim sg = gc->proto_data; SilcClient client; SilcClientConnection conn; GaimAccount *account = sg->account; SilcClientConnectionParams params; const char *dfile; if (source < 0) { gaim_connection_error(gc, _("Connection failed")); return; } if (sg == NULL) return; client = sg->client; if (!g_list_find(gaim_connections_get_all(), gc)) { close(source); g_source_remove(sg->scheduler); silc_client_stop(sg->client); silc_client_free(sg->client); silc_free(sg); return; } /* Get session detachment data, if available */ memset(¶ms, 0, sizeof(params)); dfile = silcgaim_session_file(gaim_account_get_username(sg->account)); params.detach_data = (unsigned char *)silc_file_readfile(dfile, ¶ms.detach_data_len); if (params.detach_data) params.detach_data[params.detach_data_len] = 0; /* Add connection to SILC client library */ conn = silc_client_add_connection( sg->client, ¶ms, (char *)gaim_account_get_string(account, "server", "silc.silcnet.org"), gaim_account_get_int(account, "port", 706), sg); if (!conn) { gaim_connection_error(gc, _("Cannot initialize SILC Client connection")); gc->proto_data = NULL; return; } sg->conn = conn; /* Progress */ if (params.detach_data) { gaim_connection_update_progress(gc, _("Resuming session"), 2, 5); sg->resuming = TRUE; } else { gaim_connection_update_progress(gc, _("Performing key exchange"), 2, 5); } /* Perform SILC Key Exchange. The "silc_connected" will be called eventually. */ silc_client_start_key_exchange(sg->client, sg->conn, source); /* Set default attributes */ if (!gaim_account_get_bool(account, "reject-attrs", FALSE)) { SilcUInt32 mask; const char *tmp; #ifdef HAVE_SYS_UTSNAME_H struct utsname u; #endif mask = SILC_ATTRIBUTE_MOOD_NORMAL; silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_STATUS_MOOD, SILC_32_TO_PTR(mask), sizeof(SilcUInt32)); mask = SILC_ATTRIBUTE_CONTACT_CHAT; silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_PREFERRED_CONTACT, SILC_32_TO_PTR(mask), sizeof(SilcUInt32)); #ifdef HAVE_SYS_UTSNAME_H if (!uname(&u)) { SilcAttributeObjDevice dev; memset(&dev, 0, sizeof(dev)); dev.type = SILC_ATTRIBUTE_DEVICE_COMPUTER; dev.version = u.release; dev.model = u.sysname; silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_DEVICE_INFO, (void *)&dev, sizeof(dev)); } #endif #ifdef _WIN32 tmp = _tzname[0]; #else tmp = tzname[0]; #endif silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_TIMEZONE, (void *)tmp, strlen(tmp)); } silc_free(params.detach_data); } static void silcgaim_login(GaimAccount *account) { SilcGaim sg; SilcClient client; SilcClientParams params; GaimConnection *gc; char pkd[256], prd[256]; const char *cipher, *hmac; int i; gc = account->gc; if (!gc) return; gc->proto_data = NULL; memset(¶ms, 0, sizeof(params)); strcat(params.nickname_format, "%n@%h%a"); params.nickname_parse = silcgaim_nickname_parse; params.ignore_requested_attributes = gaim_account_get_bool(account, "reject-attrs", FALSE); /* Allocate SILC client */ client = silc_client_alloc(&ops, ¶ms, gc, NULL); if (!client) { gaim_connection_error(gc, _("Out of memory")); return; } /* Get username, real name and local hostname for SILC library */ if (gaim_account_get_username(account)) { const char *u = gaim_account_get_username(account); char **up = g_strsplit(u, "@", 2); client->username = strdup(up[0]); g_strfreev(up); } else { client->username = silc_get_username(); gaim_account_set_username(account, client->username); } if (gaim_account_get_user_info(account)) { client->realname = strdup(gaim_account_get_user_info(account)); } else { client->realname = silc_get_real_name(); gaim_account_set_user_info(account, client->realname); } client->hostname = silc_net_localhost(); gaim_connection_set_display_name(gc, client->username); /* Register requested cipher and HMAC */ cipher = gaim_account_get_string(account, "cipher", SILC_DEFAULT_CIPHER); for (i = 0; silc_default_ciphers[i].name; i++) if (!strcmp(silc_default_ciphers[i].name, cipher)) { silc_cipher_register(&(silc_default_ciphers[i])); break; } hmac = gaim_account_get_string(account, "hmac", SILC_DEFAULT_HMAC); for (i = 0; silc_default_hmacs[i].name; i++) if (!strcmp(silc_default_hmacs[i].name, hmac)) { silc_hmac_register(&(silc_default_hmacs[i])); break; } /* Init SILC client */ if (!silc_client_init(client)) { gc->wants_to_die = TRUE; gaim_connection_error(gc, ("Cannot initialize SILC protocol")); return; } /* Check the ~/.silc dir and create it, and new key pair if necessary. */ if (!silcgaim_check_silc_dir(gc)) { gc->wants_to_die = TRUE; gaim_connection_error(gc, ("Cannot find/access ~/.silc directory")); return; } /* Progress */ gaim_connection_update_progress(gc, _("Connecting to SILC Server"), 1, 5); /* Load SILC key pair */ g_snprintf(pkd, sizeof(pkd), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcgaim_silcdir()); g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcgaim_silcdir()); if (!silc_load_key_pair((char *)gaim_account_get_string(account, "public-key", pkd), (char *)gaim_account_get_string(account, "private-key", prd), (gc->password == NULL) ? "" : gc->password, &client->pkcs, &client->public_key, &client->private_key)) { g_snprintf(pkd, sizeof(pkd), ("Could not load SILC key pair: %s"), strerror(errno)); gaim_connection_error(gc, pkd); return; } sg = silc_calloc(1, sizeof(*sg)); if (!sg) return; memset(sg, 0, sizeof(*sg)); sg->client = client; sg->gc = gc; sg->account = account; gc->proto_data = sg; /* Connect to the SILC server */ if (gaim_proxy_connect(account, gaim_account_get_string(account, "server", "silc.silcnet.org"), gaim_account_get_int(account, "port", 706), silcgaim_login_connected, gc)) { gaim_connection_error(gc, ("Unable to create connection")); return; } /* Schedule SILC using Glib's event loop */ #ifndef _WIN32 sg->scheduler = g_timeout_add(5, (GSourceFunc)silcgaim_scheduler, sg); #else sg->scheduler = g_timeout_add(300, (GSourceFunc)silcgaim_scheduler, sg); #endif } static int silcgaim_close_final(gpointer *context) { SilcGaim sg = (SilcGaim)context; silc_client_stop(sg->client); silc_client_free(sg->client); #ifdef HAVE_SILCMIME_H if (sg->mimeass) silc_mime_assembler_free(sg->mimeass); #endif silc_free(sg); return 0; } static void silcgaim_close(GaimConnection *gc) { SilcGaim sg = gc->proto_data; g_return_if_fail(sg != NULL); /* Send QUIT */ silc_client_command_call(sg->client, sg->conn, NULL, "QUIT", "Download Gaim: " GAIM_WEBSITE, NULL); if (sg->conn) silc_client_close_connection(sg->client, sg->conn); g_source_remove(sg->scheduler); g_timeout_add(1, (GSourceFunc)silcgaim_close_final, sg); } /****************************** Protocol Actions *****************************/ static void silcgaim_attrs_cancel(GaimConnection *gc, GaimRequestFields *fields) { /* Nothing */ } static void silcgaim_attrs_cb(GaimConnection *gc, GaimRequestFields *fields) { SilcGaim sg = gc->proto_data; SilcClient client = sg->client; SilcClientConnection conn = sg->conn; GaimRequestField *f; char *tmp; SilcUInt32 tmp_len, mask; SilcAttributeObjService service; SilcAttributeObjDevice dev; SilcVCardStruct vcard; const char *val; sg = gc->proto_data; if (!sg) return; memset(&service, 0, sizeof(service)); memset(&dev, 0, sizeof(dev)); memset(&vcard, 0, sizeof(vcard)); silc_client_attribute_del(client, conn, SILC_ATTRIBUTE_USER_INFO, NULL); silc_client_attribute_del(client, conn, SILC_ATTRIBUTE_SERVICE, NULL); silc_client_attribute_del(client, conn, SILC_ATTRIBUTE_STATUS_MOOD, NULL); silc_client_attribute_del(client, conn, SILC_ATTRIBUTE_STATUS_FREETEXT, NULL); silc_client_attribute_del(client, conn, SILC_ATTRIBUTE_STATUS_MESSAGE, NULL); silc_client_attribute_del(client, conn, SILC_ATTRIBUTE_PREFERRED_LANGUAGE, NULL); silc_client_attribute_del(client, conn, SILC_ATTRIBUTE_PREFERRED_CONTACT, NULL); silc_client_attribute_del(client, conn, SILC_ATTRIBUTE_TIMEZONE, NULL); silc_client_attribute_del(client, conn, SILC_ATTRIBUTE_GEOLOCATION, NULL); silc_client_attribute_del(client, conn, SILC_ATTRIBUTE_DEVICE_INFO, NULL); /* Set mood */ mask = 0; f = gaim_request_fields_get_field(fields, "mood_normal"); if (f && gaim_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_MOOD_NORMAL; f = gaim_request_fields_get_field(fields, "mood_happy"); if (f && gaim_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_MOOD_HAPPY; f = gaim_request_fields_get_field(fields, "mood_sad"); if (f && gaim_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_MOOD_SAD; f = gaim_request_fields_get_field(fields, "mood_angry"); if (f && gaim_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_MOOD_ANGRY; f = gaim_request_fields_get_field(fields, "mood_jealous"); if (f && gaim_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_MOOD_JEALOUS; f = gaim_request_fields_get_field(fields, "mood_ashamed"); if (f && gaim_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_MOOD_ASHAMED; f = gaim_request_fields_get_field(fields, "mood_invincible"); if (f && gaim_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_MOOD_INVINCIBLE; f = gaim_request_fields_get_field(fields, "mood_inlove"); if (f && gaim_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_MOOD_INLOVE; f = gaim_request_fields_get_field(fields, "mood_sleepy"); if (f && gaim_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_MOOD_SLEEPY; f = gaim_request_fields_get_field(fields, "mood_bored"); if (f && gaim_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_MOOD_BORED; f = gaim_request_fields_get_field(fields, "mood_excited"); if (f && gaim_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_MOOD_EXCITED; f = gaim_request_fields_get_field(fields, "mood_anxious"); if (f && gaim_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_MOOD_ANXIOUS; silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_STATUS_MOOD, SILC_32_TO_PTR(mask), sizeof(SilcUInt32)); /* Set preferred contact */ mask = 0; f = gaim_request_fields_get_field(fields, "contact_chat"); if (f && gaim_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_CONTACT_CHAT; f = gaim_request_fields_get_field(fields, "contact_email"); if (f && gaim_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_CONTACT_EMAIL; f = gaim_request_fields_get_field(fields, "contact_call"); if (f && gaim_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_CONTACT_CALL; f = gaim_request_fields_get_field(fields, "contact_sms"); if (f && gaim_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_CONTACT_SMS; f = gaim_request_fields_get_field(fields, "contact_mms"); if (f && gaim_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_CONTACT_MMS; f = gaim_request_fields_get_field(fields, "contact_video"); if (f && gaim_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_CONTACT_VIDEO; if (mask) silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_PREFERRED_CONTACT, SILC_32_TO_PTR(mask), sizeof(SilcUInt32)); /* Set status text */ val = NULL; f = gaim_request_fields_get_field(fields, "status_text"); if (f) val = gaim_request_field_string_get_value(f); if (val && *val) silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_STATUS_FREETEXT, (void *)val, strlen(val)); /* Set vcard */ val = NULL; f = gaim_request_fields_get_field(fields, "vcard"); if (f) val = gaim_request_field_string_get_value(f); if (val && *val) { gaim_account_set_string(sg->account, "vcard", val); tmp = silc_file_readfile(val, &tmp_len); if (tmp) { tmp[tmp_len] = 0; if (silc_vcard_decode((unsigned char *)tmp, tmp_len, &vcard)) silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_USER_INFO, (void *)&vcard, sizeof(vcard)); } silc_vcard_free(&vcard); silc_free(tmp); } else { gaim_account_set_string(sg->account, "vcard", ""); } #ifdef HAVE_SYS_UTSNAME_H /* Set device info */ f = gaim_request_fields_get_field(fields, "device"); if (f && gaim_request_field_bool_get_value(f)) { struct utsname u; if (!uname(&u)) { dev.type = SILC_ATTRIBUTE_DEVICE_COMPUTER; dev.version = u.release; dev.model = u.sysname; silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_DEVICE_INFO, (void *)&dev, sizeof(dev)); } } #endif /* Set timezone */ val = NULL; f = gaim_request_fields_get_field(fields, "timezone"); if (f) val = gaim_request_field_string_get_value(f); if (val && *val) silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_TIMEZONE, (void *)val, strlen(val)); } static void silcgaim_attrs(GaimPluginAction *action) { GaimConnection *gc = (GaimConnection *) action->context; SilcGaim sg = gc->proto_data; SilcClient client = sg->client; SilcClientConnection conn = sg->conn; GaimRequestFields *fields; GaimRequestFieldGroup *g; GaimRequestField *f; SilcHashTable attrs; SilcAttributePayload attr; gboolean mnormal = TRUE, mhappy = FALSE, msad = FALSE, mangry = FALSE, mjealous = FALSE, mashamed = FALSE, minvincible = FALSE, minlove = FALSE, msleepy = FALSE, mbored = FALSE, mexcited = FALSE, manxious = FALSE; gboolean cemail = FALSE, ccall = FALSE, csms = FALSE, cmms = FALSE, cchat = TRUE, cvideo = FALSE; gboolean device = TRUE; char status[1024]; sg = gc->proto_data; if (!sg) return; memset(status, 0, sizeof(status)); attrs = silc_client_attributes_get(client, conn); if (attrs) { if (silc_hash_table_find(attrs, SILC_32_TO_PTR(SILC_ATTRIBUTE_STATUS_MOOD), NULL, (void *)&attr)) { SilcUInt32 mood = 0; silc_attribute_get_object(attr, &mood, sizeof(mood)); mnormal = !mood; mhappy = (mood & SILC_ATTRIBUTE_MOOD_HAPPY); msad = (mood & SILC_ATTRIBUTE_MOOD_SAD); mangry = (mood & SILC_ATTRIBUTE_MOOD_ANGRY); mjealous = (mood & SILC_ATTRIBUTE_MOOD_JEALOUS); mashamed = (mood & SILC_ATTRIBUTE_MOOD_ASHAMED); minvincible = (mood & SILC_ATTRIBUTE_MOOD_INVINCIBLE); minlove = (mood & SILC_ATTRIBUTE_MOOD_INLOVE); msleepy = (mood & SILC_ATTRIBUTE_MOOD_SLEEPY); mbored = (mood & SILC_ATTRIBUTE_MOOD_BORED); mexcited = (mood & SILC_ATTRIBUTE_MOOD_EXCITED); manxious = (mood & SILC_ATTRIBUTE_MOOD_ANXIOUS); } if (silc_hash_table_find(attrs, SILC_32_TO_PTR(SILC_ATTRIBUTE_PREFERRED_CONTACT), NULL, (void *)&attr)) { SilcUInt32 contact = 0; silc_attribute_get_object(attr, &contact, sizeof(contact)); cemail = (contact & SILC_ATTRIBUTE_CONTACT_EMAIL); ccall = (contact & SILC_ATTRIBUTE_CONTACT_CALL); csms = (contact & SILC_ATTRIBUTE_CONTACT_SMS); cmms = (contact & SILC_ATTRIBUTE_CONTACT_MMS); cchat = (contact & SILC_ATTRIBUTE_CONTACT_CHAT); cvideo = (contact & SILC_ATTRIBUTE_CONTACT_VIDEO); } if (silc_hash_table_find(attrs, SILC_32_TO_PTR(SILC_ATTRIBUTE_STATUS_FREETEXT), NULL, (void *)&attr)) silc_attribute_get_object(attr, &status, sizeof(status)); if (!silc_hash_table_find(attrs, SILC_32_TO_PTR(SILC_ATTRIBUTE_DEVICE_INFO), NULL, (void *)&attr)) device = FALSE; } fields = gaim_request_fields_new(); g = gaim_request_field_group_new(NULL); f = gaim_request_field_label_new("l3", _("Your Current Mood")); gaim_request_field_group_add_field(g, f); f = gaim_request_field_bool_new("mood_normal", _("Normal"), mnormal); gaim_request_field_group_add_field(g, f); f = gaim_request_field_bool_new("mood_happy", _("Happy"), mhappy); gaim_request_field_group_add_field(g, f); f = gaim_request_field_bool_new("mood_sad", _("Sad"), msad); gaim_request_field_group_add_field(g, f); f = gaim_request_field_bool_new("mood_angry", _("Angry"), mangry); gaim_request_field_group_add_field(g, f); f = gaim_request_field_bool_new("mood_jealous", _("Jealous"), mjealous); gaim_request_field_group_add_field(g, f); f = gaim_request_field_bool_new("mood_ashamed", _("Ashamed"), mashamed); gaim_request_field_group_add_field(g, f); f = gaim_request_field_bool_new("mood_invincible", _("Invincible"), minvincible); gaim_request_field_group_add_field(g, f); f = gaim_request_field_bool_new("mood_inlove", _("In Love"), minlove); gaim_request_field_group_add_field(g, f); f = gaim_request_field_bool_new("mood_sleepy", _("Sleepy"), msleepy); gaim_request_field_group_add_field(g, f); f = gaim_request_field_bool_new("mood_bored", _("Bored"), mbored); gaim_request_field_group_add_field(g, f); f = gaim_request_field_bool_new("mood_excited", _("Excited"), mexcited); gaim_request_field_group_add_field(g, f); f = gaim_request_field_bool_new("mood_anxious", _("Anxious"), manxious); gaim_request_field_group_add_field(g, f); f = gaim_request_field_label_new("l4", _("\nYour Preferred Contact Methods")); gaim_request_field_group_add_field(g, f); f = gaim_request_field_bool_new("contact_chat", _("Chat"), cchat); gaim_request_field_group_add_field(g, f); f = gaim_request_field_bool_new("contact_email", _("Email"), cemail); gaim_request_field_group_add_field(g, f); f = gaim_request_field_bool_new("contact_call", _("Phone"), ccall); gaim_request_field_group_add_field(g, f); f = gaim_request_field_bool_new("contact_sms", _("SMS"), csms); gaim_request_field_group_add_field(g, f); f = gaim_request_field_bool_new("contact_mms", _("MMS"), cmms); gaim_request_field_group_add_field(g, f); f = gaim_request_field_bool_new("contact_video", _("Video Conferencing"), cvideo); gaim_request_field_group_add_field(g, f); gaim_request_fields_add_group(fields, g); g = gaim_request_field_group_new(NULL); f = gaim_request_field_string_new("status_text", _("Your Current Status"), status[0] ? status : NULL, TRUE); gaim_request_field_group_add_field(g, f); gaim_request_fields_add_group(fields, g); g = gaim_request_field_group_new(NULL); #if 0 f = gaim_request_field_label_new("l2", _("Online Services")); gaim_request_field_group_add_field(g, f); f = gaim_request_field_bool_new("services", _("Let others see what services you are using"), TRUE); gaim_request_field_group_add_field(g, f); #endif #ifdef HAVE_SYS_UTSNAME_H f = gaim_request_field_bool_new("device", _("Let others see what computer you are using"), device); gaim_request_field_group_add_field(g, f); #endif gaim_request_fields_add_group(fields, g); g = gaim_request_field_group_new(NULL); f = gaim_request_field_string_new("vcard", _("Your VCard File"), gaim_account_get_string(sg->account, "vcard", ""), FALSE); gaim_request_field_group_add_field(g, f); #ifdef _WIN32 f = gaim_request_field_string_new("timezone", _("Timezone"), _tzname[0], FALSE); #else f = gaim_request_field_string_new("timezone", _("Timezone"), tzname[0], FALSE); #endif gaim_request_field_group_add_field(g, f); gaim_request_fields_add_group(fields, g); gaim_request_fields(gc, _("User Online Status Attributes"), _("User Online Status Attributes"), _("You can let other users see your online status information " "and your personal information. Please fill the information " "you would like other users to see about yourself."), fields, _("OK"), G_CALLBACK(silcgaim_attrs_cb), _("Cancel"), G_CALLBACK(silcgaim_attrs_cancel), gc); } static void silcgaim_detach(GaimPluginAction *action) { GaimConnection *gc = (GaimConnection *) action->context; SilcGaim sg; if (!gc) return; sg = gc->proto_data; if (!sg) return; /* Call DETACH */ silc_client_command_call(sg->client, sg->conn, "DETACH"); sg->detaching = TRUE; } static void silcgaim_view_motd(GaimPluginAction *action) { GaimConnection *gc = (GaimConnection *) action->context; SilcGaim sg; char *tmp; if (!gc) return; sg = gc->proto_data; if (!sg) return; if (!sg->motd) { gaim_notify_error( gc, _("Message of the Day"), _("No Message of the Day available"), _("There is no Message of the Day associated with this connection")); return; } tmp = g_markup_escape_text(sg->motd, -1); gaim_notify_formatted(gc, NULL, _("Message of the Day"), NULL, tmp, NULL, NULL); g_free(tmp); } static void silcgaim_create_keypair_cancel(GaimConnection *gc, GaimRequestFields *fields) { /* Nothing */ } static void silcgaim_create_keypair_cb(GaimConnection *gc, GaimRequestFields *fields) { SilcGaim sg = gc->proto_data; GaimRequestField *f; const char *val, *pkfile = NULL, *prfile = NULL; const char *pass1 = NULL, *pass2 = NULL, *un = NULL, *hn = NULL; const char *rn = NULL, *e = NULL, *o = NULL, *c = NULL; char *identifier; int keylen = SILCGAIM_DEF_PKCS_LEN; SilcPublicKey public_key; sg = gc->proto_data; if (!sg) return; val = NULL; f = gaim_request_fields_get_field(fields, "pass1"); if (f) val = gaim_request_field_string_get_value(f); if (val && *val) pass1 = val; else pass1 = ""; val = NULL; f = gaim_request_fields_get_field(fields, "pass2"); if (f) val = gaim_request_field_string_get_value(f); if (val && *val) pass2 = val; else pass2 = ""; if (strcmp(pass1, pass2)) { gaim_notify_error( gc, _("Create New SILC Key Pair"), _("Passphrases do not match"), NULL); return; } val = NULL; f = gaim_request_fields_get_field(fields, "key"); if (f) val = gaim_request_field_string_get_value(f); if (val && *val) keylen = atoi(val); f = gaim_request_fields_get_field(fields, "pkfile"); if (f) pkfile = gaim_request_field_string_get_value(f); f = gaim_request_fields_get_field(fields, "prfile"); if (f) prfile = gaim_request_field_string_get_value(f); f = gaim_request_fields_get_field(fields, "un"); if (f) un = gaim_request_field_string_get_value(f); f = gaim_request_fields_get_field(fields, "hn"); if (f) hn = gaim_request_field_string_get_value(f); f = gaim_request_fields_get_field(fields, "rn"); if (f) rn = gaim_request_field_string_get_value(f); f = gaim_request_fields_get_field(fields, "e"); if (f) e = gaim_request_field_string_get_value(f); f = gaim_request_fields_get_field(fields, "o"); if (f) o = gaim_request_field_string_get_value(f); f = gaim_request_fields_get_field(fields, "c"); if (f) c = gaim_request_field_string_get_value(f); identifier = silc_pkcs_encode_identifier((char *)un, (char *)hn, (char *)rn, (char *)e, (char *)o, (char *)c); /* Create the key pair */ if (!silc_create_key_pair(SILCGAIM_DEF_PKCS, keylen, pkfile, prfile, identifier, pass1, NULL, &public_key, NULL, FALSE)) { gaim_notify_error( gc, _("Create New SILC Key Pair"), _("Key Pair Generation failed"), NULL); return; } silcgaim_show_public_key(sg, NULL, public_key, NULL, NULL); silc_pkcs_public_key_free(public_key); silc_free(identifier); } static void silcgaim_create_keypair(GaimPluginAction *action) { GaimConnection *gc = (GaimConnection *) action->context; SilcGaim sg = gc->proto_data; GaimRequestFields *fields; GaimRequestFieldGroup *g; GaimRequestField *f; const char *username, *realname; char *hostname, **u; char tmp[256], pkd[256], pkd2[256], prd[256], prd2[256]; username = gaim_account_get_username(sg->account); u = g_strsplit(username, "@", 2); username = u[0]; realname = gaim_account_get_user_info(sg->account); hostname = silc_net_localhost(); g_snprintf(tmp, sizeof(tmp), "%s@%s", username, hostname); g_snprintf(pkd2, sizeof(pkd2), "%s" G_DIR_SEPARATOR_S"public_key.pub", silcgaim_silcdir()); g_snprintf(prd2, sizeof(prd2), "%s" G_DIR_SEPARATOR_S"private_key.prv", silcgaim_silcdir()); g_snprintf(pkd, sizeof(pkd) - 1, "%s", gaim_account_get_string(gc->account, "public-key", pkd2)); g_snprintf(prd, sizeof(prd) - 1, "%s", gaim_account_get_string(gc->account, "private-key", prd2)); fields = gaim_request_fields_new(); g = gaim_request_field_group_new(NULL); f = gaim_request_field_string_new("key", _("Key Length"), "2048", FALSE); gaim_request_field_group_add_field(g, f); f = gaim_request_field_string_new("pkfile", _("Public Key File"), pkd, FALSE); gaim_request_field_group_add_field(g, f); f = gaim_request_field_string_new("prfile", _("Private Key File"), prd, FALSE); gaim_request_field_group_add_field(g, f); gaim_request_fields_add_group(fields, g); g = gaim_request_field_group_new(NULL); f = gaim_request_field_string_new("un", _("Username"), username ? username : "", FALSE); gaim_request_field_group_add_field(g, f); f = gaim_request_field_string_new("hn", _("Hostname"), hostname ? hostname : "", FALSE); gaim_request_field_group_add_field(g, f); f = gaim_request_field_string_new("rn", _("Real Name"), realname ? realname : "", FALSE); gaim_request_field_group_add_field(g, f); f = gaim_request_field_string_new("e", _("Email"), tmp, FALSE); gaim_request_field_group_add_field(g, f); f = gaim_request_field_string_new("o", _("Organization"), "", FALSE); gaim_request_field_group_add_field(g, f); f = gaim_request_field_string_new("c", _("Country"), "", FALSE); gaim_request_field_group_add_field(g, f); gaim_request_fields_add_group(fields, g); g = gaim_request_field_group_new(NULL); f = gaim_request_field_string_new("pass1", _("Passphrase"), "", FALSE); gaim_request_field_string_set_masked(f, TRUE); gaim_request_field_group_add_field(g, f); f = gaim_request_field_string_new("pass2", _("Re-type Passphrase"), "", FALSE); gaim_request_field_string_set_masked(f, TRUE); gaim_request_field_group_add_field(g, f); gaim_request_fields_add_group(fields, g); gaim_request_fields(gc, _("Create New SILC Key Pair"), _("Create New SILC Key Pair"), NULL, fields, _("Generate Key Pair"), G_CALLBACK(silcgaim_create_keypair_cb), _("Cancel"), G_CALLBACK(silcgaim_create_keypair_cancel), gc); g_strfreev(u); silc_free(hostname); } static void silcgaim_change_pass(GaimPluginAction *action) { GaimConnection *gc = (GaimConnection *) action->context; gaim_account_request_change_password(gaim_connection_get_account(gc)); } static void silcgaim_change_passwd(GaimConnection *gc, const char *old, const char *new) { char prd[256]; g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.pub", silcgaim_silcdir()); silc_change_private_key_passphrase(gaim_account_get_string(gc->account, "private-key", prd), old, new); } static void silcgaim_show_set_info(GaimPluginAction *action) { GaimConnection *gc = (GaimConnection *) action->context; gaim_account_request_change_user_info(gaim_connection_get_account(gc)); } static void silcgaim_set_info(GaimConnection *gc, const char *text) { } static GList * silcgaim_actions(GaimPlugin *plugin, gpointer context) { GaimConnection *gc = context; GList *list = NULL; GaimPluginAction *act; if (!gaim_account_get_bool(gc->account, "reject-attrs", FALSE)) { act = gaim_plugin_action_new(_("Online Status"), silcgaim_attrs); list = g_list_append(list, act); } act = gaim_plugin_action_new(_("Detach From Server"), silcgaim_detach); list = g_list_append(list, act); act = gaim_plugin_action_new(_("View Message of the Day"), silcgaim_view_motd); list = g_list_append(list, act); act = gaim_plugin_action_new(_("Create SILC Key Pair..."), silcgaim_create_keypair); list = g_list_append(list, act); act = gaim_plugin_action_new(_("Change Password..."), silcgaim_change_pass); list = g_list_append(list, act); act = gaim_plugin_action_new(_("Set User Info..."), silcgaim_show_set_info); list = g_list_append(list, act); return list; } /******************************* IM Routines *********************************/ typedef struct { char *nick; char *message; SilcUInt32 message_len; SilcMessageFlags flags; GaimMessageFlags gflags; } *SilcGaimIM; static void silcgaim_send_im_resolved(SilcClient client, SilcClientConnection conn, SilcClientEntry *clients, SilcUInt32 clients_count, void *context) { GaimConnection *gc = client->application; SilcGaim sg = gc->proto_data; SilcGaimIM im = context; GaimConversation *convo; char tmp[256], *nickname = NULL; SilcClientEntry client_entry; #ifdef HAVE_SILCMIME_H SilcDList list; #endif convo = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, im->nick, sg->account); if (!convo) return; if (!clients) goto err; if (clients_count > 1) { silc_parse_userfqdn(im->nick, &nickname, NULL); /* Find the correct one. The im->nick might be a formatted nick so this will find the correct one. */ clients = silc_client_get_clients_local(client, conn, nickname, im->nick, &clients_count); if (!clients) goto err; client_entry = clients[0]; silc_free(clients); } else { client_entry = clients[0]; } #ifdef HAVE_SILCMIME_H /* Check for images */ if (im->gflags & GAIM_MESSAGE_IMAGES) { list = silcgaim_image_message(im->message, (SilcUInt32 *)&im->flags); if (list) { /* Send one or more MIME message. If more than one, they are MIME fragments due to over large message */ SilcBuffer buf; silc_dlist_start(list); while ((buf = silc_dlist_get(list)) != SILC_LIST_END) silc_client_send_private_message(client, conn, client_entry, im->flags, buf->data, buf->len, TRUE); silc_mime_partial_free(list); gaim_conv_im_write(GAIM_CONV_IM(convo), conn->local_entry->nickname, im->message, 0, time(NULL)); goto out; } } #endif /* Send the message */ silc_client_send_private_message(client, conn, client_entry, im->flags, (unsigned char *)im->message, im->message_len, TRUE); gaim_conv_im_write(GAIM_CONV_IM(convo), conn->local_entry->nickname, im->message, 0, time(NULL)); goto out; err: g_snprintf(tmp, sizeof(tmp), _("User <I>%s</I> is not present in the network"), im->nick); gaim_conversation_write(convo, NULL, tmp, GAIM_MESSAGE_SYSTEM, time(NULL)); out: g_free(im->nick); g_free(im->message); silc_free(im); silc_free(nickname); } static int silcgaim_send_im(GaimConnection *gc, const char *who, const char *message, GaimMessageFlags flags) { SilcGaim sg = gc->proto_data; SilcClient client = sg->client; SilcClientConnection conn = sg->conn; SilcClientEntry *clients; SilcUInt32 clients_count, mflags; char *nickname, *msg, *tmp; int ret = 0; gboolean sign = gaim_account_get_bool(sg->account, "sign-verify", FALSE); #ifdef HAVE_SILCMIME_H SilcDList list; #endif if (!who || !message) return 0; mflags = SILC_MESSAGE_FLAG_UTF8; tmp = msg = gaim_unescape_html(message); if (!g_ascii_strncasecmp(msg, "/me ", 4)) { msg += 4; if (!*msg) { g_free(tmp); return 0; } mflags |= SILC_MESSAGE_FLAG_ACTION; } else if (strlen(msg) > 1 && msg[0] == '/') { if (!silc_client_command_call(client, conn, msg + 1)) gaim_notify_error(gc, ("Call Command"), _("Cannot call command"), _("Unknown command")); g_free(tmp); return 0; } if (!silc_parse_userfqdn(who, &nickname, NULL)) { g_free(tmp); return 0; } if (sign) mflags |= SILC_MESSAGE_FLAG_SIGNED; /* Find client entry */ clients = silc_client_get_clients_local(client, conn, nickname, who, &clients_count); if (!clients) { /* Resolve unknown user */ SilcGaimIM im = silc_calloc(1, sizeof(*im)); if (!im) { g_free(tmp); return 0; } im->nick = g_strdup(who); im->message = g_strdup(message); im->message_len = strlen(im->message); im->flags = mflags; im->gflags = flags; silc_client_get_clients(client, conn, nickname, NULL, silcgaim_send_im_resolved, im); silc_free(nickname); g_free(tmp); return 0; } #ifdef HAVE_SILCMIME_H /* Check for images */ if (flags & GAIM_MESSAGE_IMAGES) { list = silcgaim_image_message(message, &mflags); if (list) { /* Send one or more MIME message. If more than one, they are MIME fragments due to over large message */ SilcBuffer buf; silc_dlist_start(list); while ((buf = silc_dlist_get(list)) != SILC_LIST_END) ret = silc_client_send_private_message(client, conn, clients[0], mflags, buf->data, buf->len, TRUE); silc_mime_partial_free(list); g_free(tmp); silc_free(nickname); silc_free(clients); return ret; } } #endif /* Send private message directly */ ret = silc_client_send_private_message(client, conn, clients[0], mflags, (unsigned char *)msg, strlen(msg), TRUE); g_free(tmp); silc_free(nickname); silc_free(clients); return ret; } static GList *silcgaim_blist_node_menu(GaimBlistNode *node) { /* split this single menu building function back into the two original: one for buddies and one for chats */ if(GAIM_BLIST_NODE_IS_CHAT(node)) { return silcgaim_chat_menu((GaimChat *) node); } else if(GAIM_BLIST_NODE_IS_BUDDY(node)) { return silcgaim_buddy_menu((GaimBuddy *) node); } else { g_return_val_if_reached(NULL); } } /********************************* Commands **********************************/ static GaimCmdRet silcgaim_cmd_chat_part(GaimConversation *conv, const char *cmd, char **args, char **error, void *data) { GaimConnection *gc; GaimConversation *convo; int id = 0; gc = gaim_conversation_get_gc(conv); if (gc == NULL) return GAIM_CMD_RET_FAILED; if(args && args[0]) { convo = gaim_find_conversation_with_account(GAIM_CONV_TYPE_CHAT, args[0], gc->account); } else convo = conv; id = gaim_conv_chat_get_id(GAIM_CONV_CHAT(convo)); if (id == 0) return GAIM_CMD_RET_FAILED; silcgaim_chat_leave(gc, id); return GAIM_CMD_RET_OK; } static GaimCmdRet silcgaim_cmd_chat_topic(GaimConversation *conv, const char *cmd, char **args, char **error, void *data) { GaimConnection *gc; int id = 0; char *buf, *tmp, *tmp2; const char *topic; gc = gaim_conversation_get_gc(conv); id = gaim_conv_chat_get_id(GAIM_CONV_CHAT(conv)); if (gc == NULL || id == 0) return GAIM_CMD_RET_FAILED; if (!args || !args[0]) { topic = gaim_conv_chat_get_topic (GAIM_CONV_CHAT(conv)); if (topic) { tmp = g_markup_escape_text(topic, -1); tmp2 = gaim_markup_linkify(tmp); buf = g_strdup_printf(_("current topic is: %s"), tmp2); g_free(tmp); g_free(tmp2); } else buf = g_strdup(_("No topic is set")); gaim_conv_chat_write(GAIM_CONV_CHAT(conv), gc->account->username, buf, GAIM_MESSAGE_SYSTEM|GAIM_MESSAGE_NO_LOG, time(NULL)); g_free(buf); } if (args && args[0] && (strlen(args[0]) > 255)) { *error = g_strdup(_("Topic too long")); return GAIM_CMD_RET_FAILED; } silcgaim_chat_set_topic(gc, id, args ? args[0] : NULL); return GAIM_CMD_RET_OK; } static GaimCmdRet silcgaim_cmd_chat_join(GaimConversation *conv, const char *cmd, char **args, char **error, void *data) { GHashTable *comp; if(!args || !args[0]) return GAIM_CMD_RET_FAILED; comp = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); g_hash_table_replace(comp, "channel", args[0]); if(args[1]) g_hash_table_replace(comp, "passphrase", args[1]); silcgaim_chat_join(gaim_conversation_get_gc(conv), comp); g_hash_table_destroy(comp); return GAIM_CMD_RET_OK; } static GaimCmdRet silcgaim_cmd_chat_list(GaimConversation *conv, const char *cmd, char **args, char **error, void *data) { GaimConnection *gc; gc = gaim_conversation_get_gc(conv); gaim_roomlist_show_with_account(gaim_connection_get_account(gc)); return GAIM_CMD_RET_OK; } static GaimCmdRet silcgaim_cmd_whois(GaimConversation *conv, const char *cmd, char **args, char **error, void *data) { GaimConnection *gc; gc = gaim_conversation_get_gc(conv); if (gc == NULL) return GAIM_CMD_RET_FAILED; silcgaim_get_info(gc, args[0]); return GAIM_CMD_RET_OK; } static GaimCmdRet silcgaim_cmd_msg(GaimConversation *conv, const char *cmd, char **args, char **error, void *data) { int ret; GaimConnection *gc; gc = gaim_conversation_get_gc(conv); if (gc == NULL) return GAIM_CMD_RET_FAILED; ret = silcgaim_send_im(gc, args[0], args[1], GAIM_MESSAGE_SEND); if (ret) return GAIM_CMD_RET_OK; else return GAIM_CMD_RET_FAILED; } static GaimCmdRet silcgaim_cmd_query(GaimConversation *conv, const char *cmd, char **args, char **error, void *data) { int ret = 1; GaimConversation *convo; GaimConnection *gc; GaimAccount *account; if (!args || !args[0]) { *error = g_strdup(_("You must specify a nick")); return GAIM_CMD_RET_FAILED; } gc = gaim_conversation_get_gc(conv); if (gc == NULL) return GAIM_CMD_RET_FAILED; account = gaim_connection_get_account(gc); convo = gaim_conversation_new(GAIM_CONV_TYPE_IM, account, args[0]); if (args[1]) { ret = silcgaim_send_im(gc, args[0], args[1], GAIM_MESSAGE_SEND); gaim_conv_im_write(GAIM_CONV_IM(convo), gaim_connection_get_display_name(gc), args[1], GAIM_MESSAGE_SEND, time(NULL)); } if (ret) return GAIM_CMD_RET_OK; else return GAIM_CMD_RET_FAILED; } static GaimCmdRet silcgaim_cmd_motd(GaimConversation *conv, const char *cmd, char **args, char **error, void *data) { GaimConnection *gc; SilcGaim sg; char *tmp; gc = gaim_conversation_get_gc(conv); if (gc == NULL) return GAIM_CMD_RET_FAILED; sg = gc->proto_data; if (sg == NULL) return GAIM_CMD_RET_FAILED; if (!sg->motd) { *error = g_strdup(_("There is no Message of the Day associated with this connection")); return GAIM_CMD_RET_FAILED; } tmp = g_markup_escape_text(sg->motd, -1); gaim_notify_formatted(gc, NULL, _("Message of the Day"), NULL, tmp, NULL, NULL); g_free(tmp); return GAIM_CMD_RET_OK; } static GaimCmdRet silcgaim_cmd_detach(GaimConversation *conv, const char *cmd, char **args, char **error, void *data) { GaimConnection *gc; SilcGaim sg; gc = gaim_conversation_get_gc(conv); if (gc == NULL) return GAIM_CMD_RET_FAILED; sg = gc->proto_data; if (sg == NULL) return GAIM_CMD_RET_FAILED; silc_client_command_call(sg->client, sg->conn, "DETACH"); sg->detaching = TRUE; return GAIM_CMD_RET_OK; } static GaimCmdRet silcgaim_cmd_cmode(GaimConversation *conv, const char *cmd, char **args, char **error, void *data) { GaimConnection *gc; SilcGaim sg; SilcChannelEntry channel; char *silccmd, *silcargs, *msg, tmp[256]; const char *chname; gc = gaim_conversation_get_gc(conv); if (gc == NULL || !args || gc->proto_data == NULL) return GAIM_CMD_RET_FAILED; sg = gc->proto_data; if (args[0]) chname = args[0]; else chname = gaim_conversation_get_name(conv); if (!args[1]) { channel = silc_client_get_channel(sg->client, sg->conn, (char *)chname); if (!channel) { *error = g_strdup_printf(_("channel %s not found"), chname); return GAIM_CMD_RET_FAILED; } if (channel->mode) { silcgaim_get_chmode_string(channel->mode, tmp, sizeof(tmp)); msg = g_strdup_printf(_("channel modes for %s: %s"), chname, tmp); } else { msg = g_strdup_printf(_("no channel modes are set on %s"), chname); } gaim_conv_chat_write(GAIM_CONV_CHAT(conv), "", msg, GAIM_MESSAGE_SYSTEM|GAIM_MESSAGE_NO_LOG, time(NULL)); g_free(msg); return GAIM_CMD_RET_OK; } silcargs = g_strjoinv(" ", args); silccmd = g_strconcat(cmd, " ", args ? silcargs : NULL, NULL); g_free(silcargs); if (!silc_client_command_call(sg->client, sg->conn, silccmd)) { g_free(silccmd); *error = g_strdup_printf(_("Failed to set cmodes for %s"), args[0]); return GAIM_CMD_RET_FAILED; } g_free(silccmd); return GAIM_CMD_RET_OK; } static GaimCmdRet silcgaim_cmd_generic(GaimConversation *conv, const char *cmd, char **args, char **error, void *data) { GaimConnection *gc; SilcGaim sg; char *silccmd, *silcargs; gc = gaim_conversation_get_gc(conv); if (gc == NULL) return GAIM_CMD_RET_FAILED; sg = gc->proto_data; if (sg == NULL) return GAIM_CMD_RET_FAILED; silcargs = g_strjoinv(" ", args); silccmd = g_strconcat(cmd, " ", args ? silcargs : NULL, NULL); g_free(silcargs); if (!silc_client_command_call(sg->client, sg->conn, silccmd)) { g_free(silccmd); *error = g_strdup_printf(_("Unknown command: %s, (may be a Gaim bug)"), cmd); return GAIM_CMD_RET_FAILED; } g_free(silccmd); return GAIM_CMD_RET_OK; } static GaimCmdRet silcgaim_cmd_quit(GaimConversation *conv, const char *cmd, char **args, char **error, void *data) { GaimConnection *gc; SilcGaim sg; gc = gaim_conversation_get_gc(conv); if (gc == NULL) return GAIM_CMD_RET_FAILED; sg = gc->proto_data; if (sg == NULL) return GAIM_CMD_RET_FAILED; silc_client_command_call(sg->client, sg->conn, NULL, "QUIT", (args && args[0]) ? args[0] : "Download Gaim: " GAIM_WEBSITE, NULL); return GAIM_CMD_RET_OK; } static GaimCmdRet silcgaim_cmd_call(GaimConversation *conv, const char *cmd, char **args, char **error, void *data) { GaimConnection *gc; SilcGaim sg; gc = gaim_conversation_get_gc(conv); if (gc == NULL) return GAIM_CMD_RET_FAILED; sg = gc->proto_data; if (sg == NULL) return GAIM_CMD_RET_FAILED; if (!silc_client_command_call(sg->client, sg->conn, args[0])) { *error = g_strdup_printf(_("Unknown command: %s"), args[0]); return GAIM_CMD_RET_FAILED; } return GAIM_CMD_RET_OK; } /************************** Plugin Initialization ****************************/ static void silcgaim_register_commands(void) { gaim_cmd_register("part", "w", GAIM_CMD_P_PRPL, GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY | GAIM_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcgaim_cmd_chat_part, _("part [channel]: Leave the chat"), NULL); gaim_cmd_register("leave", "w", GAIM_CMD_P_PRPL, GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY | GAIM_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcgaim_cmd_chat_part, _("leave [channel]: Leave the chat"), NULL); gaim_cmd_register("topic", "s", GAIM_CMD_P_PRPL, GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY | GAIM_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcgaim_cmd_chat_topic, _("topic [<new topic>]: View or change the topic"), NULL); gaim_cmd_register("join", "ws", GAIM_CMD_P_PRPL, GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY | GAIM_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcgaim_cmd_chat_join, _("join <channel> [<password>]: Join a chat on this network"), NULL); gaim_cmd_register("list", "", GAIM_CMD_P_PRPL, GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY | GAIM_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcgaim_cmd_chat_list, _("list: List channels on this network"), NULL); gaim_cmd_register("whois", "w", GAIM_CMD_P_PRPL, GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY, "prpl-silc", silcgaim_cmd_whois, _("whois <nick>: View nick's information"), NULL); gaim_cmd_register("msg", "ws", GAIM_CMD_P_PRPL, GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY, "prpl-silc", silcgaim_cmd_msg, _("msg <nick> <message>: Send a private message to a user"), NULL); gaim_cmd_register("query", "ws", GAIM_CMD_P_PRPL, GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY | GAIM_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcgaim_cmd_query, _("query <nick> [<message>]: Send a private message to a user"), NULL); gaim_cmd_register("motd", "", GAIM_CMD_P_PRPL, GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY | GAIM_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcgaim_cmd_motd, _("motd: View the server's Message Of The Day"), NULL); gaim_cmd_register("detach", "", GAIM_CMD_P_PRPL, GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY, "prpl-silc", silcgaim_cmd_detach, _("detach: Detach this session"), NULL); gaim_cmd_register("quit", "s", GAIM_CMD_P_PRPL, GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY | GAIM_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcgaim_cmd_quit, _("quit [message]: Disconnect from the server, with an optional message"), NULL); gaim_cmd_register("call", "s", GAIM_CMD_P_PRPL, GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY, "prpl-silc", silcgaim_cmd_call, _("call <command>: Call any silc client command"), NULL); /* These below just get passed through for the silc client library to deal * with */ gaim_cmd_register("kill", "ws", GAIM_CMD_P_PRPL, GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY | GAIM_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcgaim_cmd_generic, _("kill <nick> [-pubkey|<reason>]: Kill nick"), NULL); gaim_cmd_register("nick", "w", GAIM_CMD_P_PRPL, GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY, "prpl-silc", silcgaim_cmd_generic, _("nick <newnick>: Change your nickname"), NULL); gaim_cmd_register("whowas", "ww", GAIM_CMD_P_PRPL, GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY | GAIM_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcgaim_cmd_generic, _("whowas <nick>: View nick's information"), NULL); gaim_cmd_register("cmode", "wws", GAIM_CMD_P_PRPL, GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY | GAIM_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcgaim_cmd_cmode, _("cmode <channel> [+|-<modes>] [arguments]: Change or display channel modes"), NULL); gaim_cmd_register("cumode", "wws", GAIM_CMD_P_PRPL, GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY | GAIM_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcgaim_cmd_generic, _("cumode <channel> +|-<modes> <nick>: Change nick's modes on channel"), NULL); gaim_cmd_register("umode", "w", GAIM_CMD_P_PRPL, GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY, "prpl-silc", silcgaim_cmd_generic, _("umode <usermodes>: Set your modes in the network"), NULL); gaim_cmd_register("oper", "s", GAIM_CMD_P_PRPL, GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY, "prpl-silc", silcgaim_cmd_generic, _("oper <nick> [-pubkey]: Get server operator privileges"), NULL); gaim_cmd_register("invite", "ws", GAIM_CMD_P_PRPL, GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY | GAIM_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcgaim_cmd_generic, _("invite <channel> [-|+]<nick>: invite nick or add/remove from channel invite list"), NULL); gaim_cmd_register("kick", "wws", GAIM_CMD_P_PRPL, GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY | GAIM_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcgaim_cmd_generic, _("kick <channel> <nick> [comment]: Kick client from channel"), NULL); gaim_cmd_register("info", "w", GAIM_CMD_P_PRPL, GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY | GAIM_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcgaim_cmd_generic, _("info [server]: View server administrative details"), NULL); gaim_cmd_register("ban", "ww", GAIM_CMD_P_PRPL, GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY | GAIM_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcgaim_cmd_generic, _("ban [<channel> +|-<nick>]: Ban client from channel"), NULL); gaim_cmd_register("getkey", "w", GAIM_CMD_P_PRPL, GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY, "prpl-silc", silcgaim_cmd_generic, _("getkey <nick|server>: Retrieve client's or server's public key"), NULL); gaim_cmd_register("stats", "", GAIM_CMD_P_PRPL, GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY, "prpl-silc", silcgaim_cmd_generic, _("stats: View server and network statistics"), NULL); gaim_cmd_register("ping", "", GAIM_CMD_P_PRPL, GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY, "prpl-silc", silcgaim_cmd_generic, _("ping: Send PING to the connected server"), NULL); #if 0 /* Gaim doesn't handle these yet */ gaim_cmd_register("users", "w", GAIM_CMD_P_PRPL, GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY, "prpl-silc", silcgaim_cmd_users, _("users <channel>: List users in channel")); gaim_cmd_register("names", "ww", GAIM_CMD_P_PRPL, GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY | GAIM_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcgaim_cmd_names, _("names [-count|-ops|-halfops|-voices|-normal] <channel(s)>: List specific users in channel(s)")); #endif } static GaimWhiteboardPrplOps silcgaim_wb_ops = { silcgaim_wb_start, silcgaim_wb_end, silcgaim_wb_get_dimensions, silcgaim_wb_set_dimensions, silcgaim_wb_get_brush, silcgaim_wb_set_brush, silcgaim_wb_send, silcgaim_wb_clear, }; static GaimPluginProtocolInfo prpl_info = { #ifdef HAVE_SILCMIME_H OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME | OPT_PROTO_PASSWORD_OPTIONAL | OPT_PROTO_IM_IMAGE, #else OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME | OPT_PROTO_PASSWORD_OPTIONAL, #endif NULL, /* user_splits */ NULL, /* protocol_options */ NO_BUDDY_ICONS, /* icon_spec */ silcgaim_list_icon, /* list_icon */ silcgaim_list_emblems, /* list_emblems */ silcgaim_status_text, /* status_text */ silcgaim_tooltip_text, /* tooltip_text */ silcgaim_away_states, /* away_states */ silcgaim_blist_node_menu, /* blist_node_menu */ silcgaim_chat_info, /* chat_info */ silcgaim_chat_info_defaults,/* chat_info_defaults */ silcgaim_login, /* login */ silcgaim_close, /* close */ silcgaim_send_im, /* send_im */ silcgaim_set_info, /* set_info */ NULL, /* send_typing */ silcgaim_get_info, /* get_info */ silcgaim_set_status, /* set_status */ silcgaim_idle_set, /* set_idle */ silcgaim_change_passwd, /* change_passwd */ silcgaim_add_buddy, /* add_buddy */ NULL, /* add_buddies */ silcgaim_remove_buddy, /* remove_buddy */ NULL, /* remove_buddies */ NULL, /* add_permit */ NULL, /* add_deny */ NULL, /* rem_permit */ NULL, /* rem_deny */ NULL, /* set_permit_deny */ silcgaim_chat_join, /* join_chat */ NULL, /* reject_chat */ silcgaim_get_chat_name, /* get_chat_name */ silcgaim_chat_invite, /* chat_invite */ silcgaim_chat_leave, /* chat_leave */ NULL, /* chat_whisper */ silcgaim_chat_send, /* chat_send */ silcgaim_keepalive, /* keepalive */ NULL, /* register_user */ NULL, /* get_cb_info */ NULL, /* get_cb_away */ NULL, /* alias_buddy */ NULL, /* group_buddy */ NULL, /* rename_group */ NULL, /* buddy_free */ NULL, /* convo_closed */ NULL, /* normalize */ NULL, /* set_buddy_icon */ NULL, /* remove_group */ NULL, /* get_cb_real_name */ silcgaim_chat_set_topic, /* set_chat_topic */ NULL, /* find_blist_chat */ silcgaim_roomlist_get_list, /* roomlist_get_list */ silcgaim_roomlist_cancel, /* roomlist_cancel */ NULL, /* roomlist_expand_category */ NULL, /* can_receive_file */ silcgaim_ftp_send_file, /* send_file */ silcgaim_ftp_new_xfer, /* new_xfer */ NULL, /* offline_message */ &silcgaim_wb_ops, /* whiteboard_prpl_ops */ NULL, /* media_prpl_ops */ }; static GaimPluginInfo info = { GAIM_PLUGIN_MAGIC, GAIM_MAJOR_VERSION, GAIM_MINOR_VERSION, GAIM_PLUGIN_PROTOCOL, /**< type */ NULL, /**< ui_requirement */ 0, /**< flags */ NULL, /**< dependencies */ GAIM_PRIORITY_DEFAULT, /**< priority */ "prpl-silc", /**< id */ "SILC", /**< name */ "1.0", /**< version */ /** summary */ N_("SILC Protocol Plugin"), /** description */ N_("Secure Internet Live Conferencing (SILC) Protocol"), "Pekka Riikonen", /**< author */ "http://silcnet.org/", /**< homepage */ NULL, /**< load */ NULL, /**< unload */ NULL, /**< destroy */ NULL, /**< ui_info */ &prpl_info, /**< extra_info */ NULL, /**< prefs_info */ silcgaim_actions }; static void init_plugin(GaimPlugin *plugin) { GaimAccountOption *option; GaimAccountUserSplit *split; char tmp[256]; int i; GaimKeyValuePair *kvp; GList *list = NULL; silc_plugin = plugin; split = gaim_account_user_split_new(_("Network"), "silcnet.org", '@'); prpl_info.user_splits = g_list_append(prpl_info.user_splits, split); /* Account options */ option = gaim_account_option_string_new(_("Connect server"), "server", "silc.silcnet.org"); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); option = gaim_account_option_int_new(_("Port"), "port", 706); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); g_snprintf(tmp, sizeof(tmp), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcgaim_silcdir()); option = gaim_account_option_string_new(_("Public Key file"), "public-key", tmp); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); g_snprintf(tmp, sizeof(tmp), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcgaim_silcdir()); option = gaim_account_option_string_new(_("Private Key file"), "private-key", tmp); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); for (i = 0; silc_default_ciphers[i].name; i++) { kvp = silc_calloc(1, sizeof(*kvp)); kvp->key = strdup(silc_default_ciphers[i].name); kvp->value = strdup(silc_default_ciphers[i].name); list = g_list_append(list, kvp); } option = gaim_account_option_list_new(_("Cipher"), "cipher", list); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); list = NULL; for (i = 0; silc_default_hmacs[i].name; i++) { kvp = silc_calloc(1, sizeof(*kvp)); kvp->key = strdup(silc_default_hmacs[i].name); kvp->value = strdup(silc_default_hmacs[i].name); list = g_list_append(list, kvp); } option = gaim_account_option_list_new(_("HMAC"), "hmac", list); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); option = gaim_account_option_bool_new(_("Public key authentication"), "pubkey-auth", FALSE); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); option = gaim_account_option_bool_new(_("Reject watching by other users"), "reject-watch", FALSE); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); option = gaim_account_option_bool_new(_("Block invites"), "block-invites", FALSE); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); option = gaim_account_option_bool_new(_("Block IMs without Key Exchange"), "block-ims", FALSE); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); option = gaim_account_option_bool_new(_("Reject online status attribute requests"), "reject-attrs", FALSE); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); option = gaim_account_option_bool_new(_("Block messages to whiteboard"), "block-wb", FALSE); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); option = gaim_account_option_bool_new(_("Automatically open whiteboard"), "open-wb", FALSE); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); option = gaim_account_option_bool_new(_("Digitally sign and verify all messages"), "sign-verify", FALSE); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); gaim_prefs_remove("/plugins/prpl/silc"); silcgaim_register_commands(); #ifdef _WIN32 silc_net_win32_init(); #endif } GAIM_INIT_PLUGIN(silc, init_plugin, info);