Author: Pekka Riikonen <priikone@silcnet.org> Copyright (C) 2004 - 2007 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. extern SilcClientOperations ops; static PurplePlugin *silc_plugin = NULL; /* Error log message callback */ static SilcBool silcpurple_log_error(SilcLogType type, char *message, silc_say(NULL, NULL, SILC_CLIENT_MESSAGE_ERROR, message); silcpurple_list_icon(PurpleAccount *a, PurpleBuddy *b) return (const char *)"silc"; silcpurple_away_states(PurpleAccount *account) type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_AVAILABLE, NULL, TRUE, TRUE, FALSE); types = g_list_append(types, type); type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_HYPER, _("Hyper Active"), TRUE, TRUE, FALSE); types = g_list_append(types, type); type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_AWAY, NULL, TRUE, TRUE, FALSE); types = g_list_append(types, type); type = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, SILCPURPLE_STATUS_ID_BUSY, _("Busy"), TRUE, TRUE, FALSE); types = g_list_append(types, type); type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_INDISPOSED, _("Indisposed"), TRUE, TRUE, FALSE); types = g_list_append(types, type); type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_PAGE, _("Wake Me Up"), TRUE, TRUE, FALSE); types = g_list_append(types, type); type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, SILCPURPLE_STATUS_ID_OFFLINE, NULL, TRUE, TRUE, FALSE); types = g_list_append(types, type); silcpurple_set_status(PurpleAccount *account, PurpleStatus *status) PurpleConnection *gc = purple_account_get_connection(account); state = purple_status_get_id(status); if ((sg == NULL) || (sg->conn == NULL)) mode = sg->conn->local_entry->mode; mode &= ~(SILC_UMODE_GONE | if (!strcmp(state, "hyper")) mode |= SILC_UMODE_HYPER; else if (!strcmp(state, "away")) else if (!strcmp(state, "busy")) else if (!strcmp(state, "indisposed")) mode |= SILC_UMODE_INDISPOSED; else if (!strcmp(state, "page")) 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, silcpurple_command_reply, NULL, 2, 1, idp->data, silc_buffer_len(idp), /*************************** Connection Routines *****************************/ silcpurple_keepalive(PurpleConnection *gc) SilcPurple sg = gc->proto_data; silc_packet_send(sg->conn->stream, SILC_PACKET_HEARTBEAT, 0, #if __SILC_TOOLKIT_VERSION < SILC_VERSION(1,1,1) silcpurple_scheduler(gpointer *context) SilcClient client = (SilcClient)context; silc_client_run_one(client); /* A timeout occurred. Call SILC scheduler. */ silcpurple_scheduler_timeout(gpointer context) SilcPurpleTask task = (SilcPurpleTask)context; silc_client_run_one(task->sg->client); silc_dlist_del(task->sg->tasks, task); /* An fd task event occurred. Call SILC scheduler. */ silcpurple_scheduler_fd(gpointer data, gint fd, PurpleInputCondition cond) SilcClient client = (SilcClient)data; silc_client_run_one(client); /* SILC Scheduler notify callback. This is called whenever task is added to or deleted from SILC scheduler. It's also called when fd task events change. Here we add same tasks to glib's main loop. */ silcpurple_scheduler(SilcSchedule schedule, SilcBool added, SilcTask task, SilcBool fd_task, SilcUInt32 fd, long seconds, long useconds, SilcClient client = (SilcClient)context; PurpleConnection *gc = client->application; SilcPurple sg = gc->proto_data; SilcPurpleTask ptask = NULL; /* Add fd or change fd events */ PurpleInputCondition e = 0; silc_dlist_start(sg->tasks); while ((ptask = silc_dlist_get(sg->tasks))) purple_input_remove(ptask->tag); if (event & SILC_TASK_READ) if (event & SILC_TASK_WRITE) ptask = silc_calloc(1, sizeof(*ptask)); silc_dlist_add(sg->tasks, ptask); ptask->tag = purple_input_add(fd, e, silcpurple_scheduler_fd, silc_dlist_del(sg->tasks, ptask); ptask = silc_calloc(1, sizeof(*ptask)); ptask->tag = purple_timeout_add((seconds * 1000) + silcpurple_scheduler_timeout, silc_dlist_add(sg->tasks, ptask); silc_dlist_start(sg->tasks); while ((ptask = silc_dlist_get(sg->tasks))) purple_input_remove(ptask->tag); silc_dlist_del(sg->tasks, ptask); #endif /* __SILC_TOOLKIT_VERSION */ silcpurple_connect_cb(SilcClient client, SilcClientConnection conn, SilcClientConnectionStatus status, SilcStatus error, const char *message, void *context) PurpleConnection *gc = context; #ifdef HAVE_SYS_UTSNAME_H case SILC_CLIENT_CONN_SUCCESS: case SILC_CLIENT_CONN_SUCCESS_RESUME: /* Connection created successfully */ purple_connection_set_state(gc, PURPLE_CONNECTED); /* Send the server our buddy list */ silcpurple_send_buddylist(gc); g_unlink(silcpurple_session_file(purple_account_get_username(sg->account))); /* Send any UMODEs configured for account */ if (purple_account_get_bool(sg->account, "block-ims", FALSE)) { silc_client_command_call(sg->client, sg->conn, NULL, /* Set default attributes */ mask = SILC_ATTRIBUTE_MOOD_NORMAL; silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_STATUS_MOOD, mask = SILC_ATTRIBUTE_CONTACT_CHAT; silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_PREFERRED_CONTACT, #ifdef HAVE_SYS_UTSNAME_H SilcAttributeObjDevice dev; memset(&dev, 0, sizeof(dev)); dev.type = SILC_ATTRIBUTE_DEVICE_COMPUTER; silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_DEVICE_INFO, (void *)&dev, sizeof(dev)); silc_timezone(tz, sizeof(tz)); silc_client_attribute_add(client, conn, img = purple_buddy_icons_find_account_icon(sg->account); silcpurple_buddy_set_icon(gc, img); purple_imgstore_unref(img); case SILC_CLIENT_CONN_DISCONNECTED: if (sg->resuming && !sg->detaching) g_unlink(silcpurple_session_file(purple_account_get_username(sg->account))); /* Close the connection */ purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Disconnected by server")); /* TODO: Does this work correctly? Maybe we need to set wants_to_die? */ purple_account_disconnect(purple_connection_get_account(gc)); case SILC_CLIENT_CONN_ERROR: purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Error connecting to SILC Server")); g_unlink(silcpurple_session_file(purple_account_get_username(sg->account))); case SILC_CLIENT_CONN_ERROR_KE: purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR, _("Key Exchange failed")); case SILC_CLIENT_CONN_ERROR_AUTH: purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Authentication failed")); case SILC_CLIENT_CONN_ERROR_RESUME: purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("Resuming detached session failed. " "Press Reconnect to create new connection.")); g_unlink(silcpurple_session_file(purple_account_get_username(sg->account))); case SILC_CLIENT_CONN_ERROR_TIMEOUT: purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection timed out")); silcpurple_stream_created(SilcSocketStreamStatus status, SilcStream stream, PurpleConnection *gc = context; SilcClientConnectionParams params; if (status != SILC_SOCKET_OK) { purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, silc_pkcs_public_key_free(sg->public_key); silc_pkcs_private_key_free(sg->private_key); /* Get session detachment data, if available */ memset(¶ms, 0, sizeof(params)); dfile = silcpurple_session_file(purple_account_get_username(sg->account)); params.detach_data = (unsigned char *)silc_file_readfile(dfile, ¶ms.detach_data_len); params.detach_data[params.detach_data_len] = 0; params.ignore_requested_attributes = FALSE; params.pfs = purple_account_get_bool(sg->account, "pfs", FALSE); if (params.detach_data) { purple_connection_update_progress(gc, _("Resuming session"), 2, 5); purple_connection_update_progress(gc, _("Performing key exchange"), 2, 5); /* Perform SILC Key Exchange. */ silc_client_key_exchange(sg->client, ¶ms, sg->public_key, sg->private_key, stream, SILC_CONN_SERVER, silcpurple_connect_cb, gc); silc_free(params.detach_data); silcpurple_login_connected(gpointer data, gint source, const gchar *error_message) PurpleConnection *gc = data; g_return_if_fail(gc != NULL); purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, silc_pkcs_public_key_free(sg->public_key); silc_pkcs_private_key_free(sg->private_key); silc_hash_alloc((unsigned char *)"sha1", &sg->sha1hash); /* Wrap socket to TCP stream */ silc_socket_tcp_stream_create(source, TRUE, FALSE, silcpurple_stream_created, gc); static void silcpurple_continue_running(SilcPurple sg) PurpleConnection *gc = sg->gc; PurpleAccount *account = purple_connection_get_account(gc); /* Connect to the SILC server */ if (purple_proxy_connect(gc, account, purple_account_get_string(account, "server", purple_account_get_int(account, "port", 706), silcpurple_login_connected, gc) == NULL) purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, static void silcpurple_got_password_cb(PurpleConnection *gc, PurpleRequestFields *fields) SilcPurple sg = (SilcPurple)gc->proto_data; PurpleAccount *account = purple_connection_get_account(gc); /* The password prompt dialog doesn't get disposed if the account disconnects */ if (!PURPLE_CONNECTION_IS_VALID(gc)) password = purple_request_fields_get_string(fields, "password"); remember = purple_request_fields_get_bool(fields, "remember"); if (!password || !*password) purple_notify_error(gc, NULL, _("Password is required to sign on."), NULL); purple_account_set_remember_password(account, TRUE); purple_account_set_password(account, password); g_snprintf(pkd, sizeof(pkd), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir()); g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir()); if (!silc_load_key_pair((char *)purple_account_get_string(account, "public-key", pkd), (char *)purple_account_get_string(account, "private-key", prd), &sg->public_key, &sg->private_key)) { purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("Unable to load SILC key pair")); silcpurple_continue_running(sg); static void silcpurple_no_password_cb(PurpleConnection *gc, PurpleRequestFields *fields) /* The password prompt dialog doesn't get disposed if the account disconnects */ if (!PURPLE_CONNECTION_IS_VALID(gc)) purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("Unable to load SILC key pair")); static void silcpurple_running(SilcClient client, void *context) PurpleConnection *gc = sg->gc; PurpleAccount *account = purple_connection_get_account(gc); purple_connection_update_progress(gc, _("Connecting to SILC Server"), 1, 5); g_snprintf(pkd, sizeof(pkd), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir()); g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir()); if (!silc_load_key_pair((char *)purple_account_get_string(account, "public-key", pkd), (char *)purple_account_get_string(account, "private-key", prd), (gc->password == NULL) ? "" : gc->password, &sg->public_key, &sg->private_key)) { if (!purple_account_get_password(account)) { purple_account_request_password(account, G_CALLBACK(silcpurple_got_password_cb), G_CALLBACK(silcpurple_no_password_cb), gc); purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("Unable to load SILC key pair")); silcpurple_continue_running(sg); silcpurple_login(PurpleAccount *account) const char *cipher, *hmac; char *username, *hostname, *realname, **up; memset(¶ms, 0, sizeof(params)); strcat(params.nickname_format, "%n#a"); /* Allocate SILC client */ client = silc_client_alloc(&ops, ¶ms, gc, NULL); purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, /* Get username, real name and local hostname for SILC library */ if (!purple_account_get_username(account)) purple_account_set_username(account, silc_get_username()); username = (char *)purple_account_get_username(account); up = g_strsplit(username, "@", 2); username = g_strdup(up[0]); if (!purple_account_get_user_info(account)) { purple_account_set_user_info(account, silc_get_real_name()); if (!purple_account_get_user_info(account)) purple_account_set_user_info(account, realname = (char *)purple_account_get_user_info(account); hostname = silc_net_localhost(); purple_connection_set_display_name(gc, username); /* Register requested cipher and HMAC */ cipher = purple_account_get_string(account, "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])); hmac = purple_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])); sg = silc_calloc(1, sizeof(*sg)); if (!silc_client_init(client, username, hostname, realname, silcpurple_running, sg)) { purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("Unable to initialize SILC protocol")); /* Check the ~/.silc dir and create it, and new key pair if necessary. */ if (!silcpurple_check_silc_dir(gc)) { purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("Error loading SILC key pair")); #if __SILC_TOOLKIT_VERSION < SILC_VERSION(1,1,1) /* Schedule SILC using Glib's event loop */ sg->scheduler = purple_timeout_add(300, (GSourceFunc)silcpurple_scheduler, client); sg->tasks = silc_dlist_init(); silc_schedule_set_notify(client->schedule, silcpurple_scheduler, silc_client_run_one(client); #endif /* __SILC_TOOLKIT_VERSION */ silcpurple_close_final(gpointer *context) SilcPurple sg = (SilcPurple)context; purple_debug_info("silc", "Finalizing SilcPurple %p\n", sg); silc_client_stop(sg->client, NULL, NULL); silc_client_free(sg->client); silc_hash_free(sg->sha1hash); silc_mime_assembler_free(sg->mimeass); silcpurple_close(PurpleConnection *gc) SilcPurple sg = gc->proto_data; #if __SILC_TOOLKIT_VERSION >= SILC_VERSION(1,1,1) #endif /* __SILC_TOOLKIT_VERSION */ const char *ui_name = NULL, *ui_website = NULL; g_return_if_fail(sg != NULL); ui_info = purple_core_get_ui_info(); ui_name = g_hash_table_lookup(ui_info, "name"); ui_website = g_hash_table_lookup(ui_info, "website"); if(!ui_name || !ui_website) { ui_website = PURPLE_WEBSITE; quit_msg = g_strdup_printf(_("Download %s: %s"), silc_client_command_call(sg->client, sg->conn, NULL, silc_client_close_connection(sg->client, sg->conn); #if __SILC_TOOLKIT_VERSION >= SILC_VERSION(1,1,1) silc_client_run_one(sg->client); silc_schedule_set_notify(sg->client->schedule, NULL, NULL); silc_dlist_start(sg->tasks); while ((task = silc_dlist_get(sg->tasks))) { purple_input_remove(task->tag); silc_dlist_uninit(sg->tasks); #endif /* __SILC_TOOLKIT_VERSION */ purple_timeout_remove(sg->scheduler); purple_debug_info("silc", "Scheduling destruction of SilcPurple %p\n", sg); purple_timeout_add(1, (GSourceFunc)silcpurple_close_final, sg); /****************************** Protocol Actions *****************************/ silcpurple_attrs_cancel(PurpleConnection *gc, PurpleRequestFields *fields) silcpurple_attrs_cb(PurpleConnection *gc, PurpleRequestFields *fields) SilcPurple sg = gc->proto_data; SilcClient client = sg->client; SilcClientConnection conn = sg->conn; SilcUInt32 tmp_len, mask; SilcAttributeObjService service; SilcAttributeObjDevice dev; 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); f = purple_request_fields_get_field(fields, "mood_normal"); if (f && purple_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_MOOD_NORMAL; f = purple_request_fields_get_field(fields, "mood_happy"); if (f && purple_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_MOOD_HAPPY; f = purple_request_fields_get_field(fields, "mood_sad"); if (f && purple_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_MOOD_SAD; f = purple_request_fields_get_field(fields, "mood_angry"); if (f && purple_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_MOOD_ANGRY; f = purple_request_fields_get_field(fields, "mood_jealous"); if (f && purple_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_MOOD_JEALOUS; f = purple_request_fields_get_field(fields, "mood_ashamed"); if (f && purple_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_MOOD_ASHAMED; f = purple_request_fields_get_field(fields, "mood_invincible"); if (f && purple_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_MOOD_INVINCIBLE; f = purple_request_fields_get_field(fields, "mood_inlove"); if (f && purple_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_MOOD_INLOVE; f = purple_request_fields_get_field(fields, "mood_sleepy"); if (f && purple_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_MOOD_SLEEPY; f = purple_request_fields_get_field(fields, "mood_bored"); if (f && purple_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_MOOD_BORED; f = purple_request_fields_get_field(fields, "mood_excited"); if (f && purple_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_MOOD_EXCITED; f = purple_request_fields_get_field(fields, "mood_anxious"); if (f && purple_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_MOOD_ANXIOUS; silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_STATUS_MOOD, /* Set preferred contact */ f = purple_request_fields_get_field(fields, "contact_chat"); if (f && purple_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_CONTACT_CHAT; f = purple_request_fields_get_field(fields, "contact_email"); if (f && purple_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_CONTACT_EMAIL; f = purple_request_fields_get_field(fields, "contact_call"); if (f && purple_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_CONTACT_CALL; f = purple_request_fields_get_field(fields, "contact_sms"); if (f && purple_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_CONTACT_SMS; f = purple_request_fields_get_field(fields, "contact_mms"); if (f && purple_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_CONTACT_MMS; f = purple_request_fields_get_field(fields, "contact_video"); if (f && purple_request_field_bool_get_value(f)) mask |= SILC_ATTRIBUTE_CONTACT_VIDEO; silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_PREFERRED_CONTACT, f = purple_request_fields_get_field(fields, "status_text"); val = purple_request_field_string_get_value(f); silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_STATUS_FREETEXT, (void *)val, strlen(val)); f = purple_request_fields_get_field(fields, "vcard"); val = purple_request_field_string_get_value(f); purple_account_set_string(sg->account, "vcard", val); tmp = silc_file_readfile(val, &tmp_len); if (silc_vcard_decode((unsigned char *)tmp, tmp_len, &vcard)) silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_USER_INFO, purple_account_set_string(sg->account, "vcard", ""); #ifdef HAVE_SYS_UTSNAME_H f = purple_request_fields_get_field(fields, "device"); if (f && purple_request_field_bool_get_value(f)) { dev.type = SILC_ATTRIBUTE_DEVICE_COMPUTER; silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_DEVICE_INFO, (void *)&dev, sizeof(dev)); f = purple_request_fields_get_field(fields, "timezone"); val = purple_request_field_string_get_value(f); silc_client_attribute_add(client, conn, (void *)val, strlen(val)); silcpurple_attrs(PurplePluginAction *action) PurpleConnection *gc = (PurpleConnection *) action->context; SilcPurple sg = gc->proto_data; SilcClient client = sg->client; SilcClientConnection conn = sg->conn; PurpleRequestFields *fields; PurpleRequestFieldGroup *g; 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; char status[1024], tz[16]; memset(status, 0, sizeof(status)); attrs = silc_client_attributes_get(client, conn); if (silc_hash_table_find(attrs, SILC_32_TO_PTR(SILC_ATTRIBUTE_STATUS_MOOD), silc_attribute_get_object(attr, &mood, sizeof(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), 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), silc_attribute_get_object(attr, &status, sizeof(status)); if (!silc_hash_table_find(attrs, SILC_32_TO_PTR(SILC_ATTRIBUTE_DEVICE_INFO), fields = purple_request_fields_new(); g = purple_request_field_group_new(NULL); f = purple_request_field_label_new("l3", _("Your Current Mood")); purple_request_field_group_add_field(g, f); f = purple_request_field_bool_new("mood_normal", _("Normal"), mnormal); purple_request_field_group_add_field(g, f); f = purple_request_field_bool_new("mood_happy", _("Happy"), mhappy); purple_request_field_group_add_field(g, f); f = purple_request_field_bool_new("mood_sad", _("Sad"), msad); purple_request_field_group_add_field(g, f); f = purple_request_field_bool_new("mood_angry", _("Angry"), mangry); purple_request_field_group_add_field(g, f); f = purple_request_field_bool_new("mood_jealous", _("Jealous"), mjealous); purple_request_field_group_add_field(g, f); f = purple_request_field_bool_new("mood_ashamed", _("Ashamed"), mashamed); purple_request_field_group_add_field(g, f); f = purple_request_field_bool_new("mood_invincible", _("Invincible"), minvincible); purple_request_field_group_add_field(g, f); f = purple_request_field_bool_new("mood_inlove", _("In love"), minlove); purple_request_field_group_add_field(g, f); f = purple_request_field_bool_new("mood_sleepy", _("Sleepy"), msleepy); purple_request_field_group_add_field(g, f); f = purple_request_field_bool_new("mood_bored", _("Bored"), mbored); purple_request_field_group_add_field(g, f); f = purple_request_field_bool_new("mood_excited", _("Excited"), mexcited); purple_request_field_group_add_field(g, f); f = purple_request_field_bool_new("mood_anxious", _("Anxious"), manxious); purple_request_field_group_add_field(g, f); f = purple_request_field_label_new("l4", _("\nYour Preferred Contact Methods")); purple_request_field_group_add_field(g, f); f = purple_request_field_bool_new("contact_chat", _("Chat"), cchat); purple_request_field_group_add_field(g, f); f = purple_request_field_bool_new("contact_email", _("Email"), cemail); purple_request_field_group_add_field(g, f); f = purple_request_field_bool_new("contact_call", _("Phone"), ccall); purple_request_field_group_add_field(g, f); f = purple_request_field_bool_new("contact_sms", _("SMS"), csms); purple_request_field_group_add_field(g, f); f = purple_request_field_bool_new("contact_mms", _("MMS"), cmms); purple_request_field_group_add_field(g, f); f = purple_request_field_bool_new("contact_video", _("Video conferencing"), cvideo); purple_request_field_group_add_field(g, f); purple_request_fields_add_group(fields, g); g = purple_request_field_group_new(NULL); f = purple_request_field_string_new("status_text", _("Your Current Status"), status[0] ? status : NULL, TRUE); purple_request_field_group_add_field(g, f); purple_request_fields_add_group(fields, g); g = purple_request_field_group_new(NULL); f = purple_request_field_label_new("l2", _("Online Services")); purple_request_field_group_add_field(g, f); f = purple_request_field_bool_new("services", _("Let others see what services you are using"), purple_request_field_group_add_field(g, f); #ifdef HAVE_SYS_UTSNAME_H f = purple_request_field_bool_new("device", _("Let others see what computer you are using"), purple_request_field_group_add_field(g, f); purple_request_fields_add_group(fields, g); g = purple_request_field_group_new(NULL); f = purple_request_field_string_new("vcard", _("Your VCard File"), purple_account_get_string(sg->account, "vcard", ""), purple_request_field_group_add_field(g, f); silc_timezone(tz, sizeof(tz)); f = purple_request_field_string_new("timezone", _("Timezone (UTC)"), tz, FALSE); purple_request_field_group_add_field(g, f); purple_request_fields_add_group(fields, g); purple_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."), _("OK"), G_CALLBACK(silcpurple_attrs_cb), _("Cancel"), G_CALLBACK(silcpurple_attrs_cancel), gc->account, NULL, NULL, gc); silcpurple_detach(PurplePluginAction *action) PurpleConnection *gc = (PurpleConnection *) action->context; silc_client_command_call(sg->client, sg->conn, "DETACH"); silcpurple_view_motd(PurplePluginAction *action) PurpleConnection *gc = (PurpleConnection *) action->context; gc, _("Message of the Day"), _("No Message of the Day available"), _("There is no Message of the Day associated with this connection")); tmp = g_markup_escape_text(sg->motd, -1); purple_notify_formatted(gc, NULL, _("Message of the Day"), NULL, silcpurple_create_keypair_cancel(PurpleConnection *gc, PurpleRequestFields *fields) silcpurple_create_keypair_cb(PurpleConnection *gc, PurpleRequestFields *fields) 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; int keylen = SILCPURPLE_DEF_PKCS_LEN; SilcPublicKey public_key; f = purple_request_fields_get_field(fields, "pass1"); val = purple_request_field_string_get_value(f); f = purple_request_fields_get_field(fields, "pass2"); val = purple_request_field_string_get_value(f); if (strcmp(pass1, pass2)) { gc, _("Create New SILC Key Pair"), _("Passphrases do not match"), NULL); f = purple_request_fields_get_field(fields, "key"); val = purple_request_field_string_get_value(f); f = purple_request_fields_get_field(fields, "pkfile"); pkfile = purple_request_field_string_get_value(f); f = purple_request_fields_get_field(fields, "prfile"); prfile = purple_request_field_string_get_value(f); f = purple_request_fields_get_field(fields, "un"); un = purple_request_field_string_get_value(f); f = purple_request_fields_get_field(fields, "hn"); hn = purple_request_field_string_get_value(f); f = purple_request_fields_get_field(fields, "rn"); rn = purple_request_field_string_get_value(f); f = purple_request_fields_get_field(fields, "e"); e = purple_request_field_string_get_value(f); f = purple_request_fields_get_field(fields, "o"); o = purple_request_field_string_get_value(f); f = purple_request_fields_get_field(fields, "c"); c = purple_request_field_string_get_value(f); identifier = silc_pkcs_silc_encode_identifier((char *)un, (char *)hn, /* Create the key pair */ if (!silc_create_key_pair(SILCPURPLE_DEF_PKCS, keylen, pkfile, prfile, identifier, pass1, &public_key, NULL, gc, _("Create New SILC Key Pair"), _("Key Pair Generation failed"), NULL); silcpurple_show_public_key(sg, NULL, public_key, NULL, NULL); silc_pkcs_public_key_free(public_key); silcpurple_create_keypair(PurplePluginAction *action) PurpleConnection *gc = (PurpleConnection *) action->context; SilcPurple sg = gc->proto_data; PurpleRequestFields *fields; PurpleRequestFieldGroup *g; const char *username, *realname; char tmp[256], pkd[256], pkd2[256], prd[256], prd2[256]; username = purple_account_get_username(sg->account); u = g_strsplit(username, "@", 2); realname = purple_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", silcpurple_silcdir()); g_snprintf(prd2, sizeof(prd2), "%s" G_DIR_SEPARATOR_S"private_key.prv", silcpurple_silcdir()); g_snprintf(pkd, sizeof(pkd) - 1, "%s", purple_account_get_string(gc->account, "public-key", pkd2)); g_snprintf(prd, sizeof(prd) - 1, "%s", purple_account_get_string(gc->account, "private-key", prd2)); fields = purple_request_fields_new(); g = purple_request_field_group_new(NULL); f = purple_request_field_string_new("key", _("Key length"), "2048", FALSE); purple_request_field_group_add_field(g, f); f = purple_request_field_string_new("pkfile", _("Public key file"), pkd, FALSE); purple_request_field_group_add_field(g, f); f = purple_request_field_string_new("prfile", _("Private key file"), prd, FALSE); purple_request_field_group_add_field(g, f); purple_request_fields_add_group(fields, g); g = purple_request_field_group_new(NULL); f = purple_request_field_string_new("un", _("Username"), username ? username : "", FALSE); purple_request_field_group_add_field(g, f); f = purple_request_field_string_new("hn", _("Hostname"), hostname ? hostname : "", FALSE); purple_request_field_group_add_field(g, f); f = purple_request_field_string_new("rn", _("Real name"), realname ? realname : "", FALSE); purple_request_field_group_add_field(g, f); f = purple_request_field_string_new("e", _("Email"), tmp, FALSE); purple_request_field_group_add_field(g, f); f = purple_request_field_string_new("o", _("Organization"), "", FALSE); purple_request_field_group_add_field(g, f); f = purple_request_field_string_new("c", _("Country"), "", FALSE); purple_request_field_group_add_field(g, f); purple_request_fields_add_group(fields, g); g = purple_request_field_group_new(NULL); f = purple_request_field_string_new("pass1", _("Passphrase"), "", FALSE); purple_request_field_string_set_masked(f, TRUE); purple_request_field_group_add_field(g, f); f = purple_request_field_string_new("pass2", _("Passphrase (retype)"), "", FALSE); purple_request_field_string_set_masked(f, TRUE); purple_request_field_group_add_field(g, f); purple_request_fields_add_group(fields, g); purple_request_fields(gc, _("Create New SILC Key Pair"), _("Create New SILC Key Pair"), NULL, fields, _("Generate Key Pair"), G_CALLBACK(silcpurple_create_keypair_cb), _("Cancel"), G_CALLBACK(silcpurple_create_keypair_cancel), gc->account, NULL, NULL, gc); silcpurple_change_pass(PurplePluginAction *action) PurpleConnection *gc = (PurpleConnection *) action->context; purple_account_request_change_password(purple_connection_get_account(gc)); silcpurple_change_passwd(PurpleConnection *gc, const char *old, const char *new) g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.pub", silcpurple_silcdir()); silc_change_private_key_passphrase(purple_account_get_string(gc->account, prd), old ? old : "", new ? new : ""); silcpurple_show_set_info(PurplePluginAction *action) PurpleConnection *gc = (PurpleConnection *) action->context; purple_account_request_change_user_info(purple_connection_get_account(gc)); silcpurple_set_info(PurpleConnection *gc, const char *text) silcpurple_actions(PurplePlugin *plugin, gpointer context) act = purple_plugin_action_new(_("Online Status"), list = g_list_append(list, act); act = purple_plugin_action_new(_("Detach From Server"), list = g_list_append(list, act); act = purple_plugin_action_new(_("View Message of the Day"), list = g_list_append(list, act); act = purple_plugin_action_new(_("Create SILC Key Pair..."), silcpurple_create_keypair); list = g_list_append(list, act); act = purple_plugin_action_new(_("Change Password..."), list = g_list_append(list, act); act = purple_plugin_action_new(_("Set User Info..."), silcpurple_show_set_info); list = g_list_append(list, act); /******************************* IM Routines *********************************/ PurpleMessageFlags gflags; silcpurple_send_im_resolved(SilcClient client, SilcClientConnection conn, PurpleConnection *gc = client->application; SilcPurple sg = gc->proto_data; SilcPurpleIM im = context; PurpleConversation *convo; SilcClientEntry client_entry; gboolean free_list = FALSE; convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, im->nick, if (silc_dlist_count(clients) > 1) { /* 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, silc_dlist_start(clients); client_entry = silc_dlist_get(clients); if (im->gflags & PURPLE_MESSAGE_IMAGES) { list = silcpurple_image_message(im->message, &im->flags); /* Send one or more MIME message. If more than one, they are MIME fragments due to over large message */ while ((buf = silc_dlist_get(list)) != SILC_LIST_END) silc_client_send_private_message(client, conn, client_entry, im->flags, sg->sha1hash, silc_mime_partial_free(list); purple_conv_im_write(PURPLE_CONV_IM(convo), conn->local_entry->nickname, im->message, 0, time(NULL)); silc_client_send_private_message(client, conn, client_entry, im->flags, sg->sha1hash, (unsigned char *)im->message, im->message_len); purple_conv_im_write(PURPLE_CONV_IM(convo), conn->local_entry->nickname, im->message, 0, time(NULL)); g_snprintf(tmp, sizeof(tmp), _("User <I>%s</I> is not present in the network"), im->nick); purple_conversation_write(convo, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL)); silc_client_list_free(client, conn, clients); silcpurple_send_im(PurpleConnection *gc, const char *who, const char *message, PurpleMessageFlags flags) SilcPurple sg = gc->proto_data; SilcClient client = sg->client; SilcClientConnection conn = sg->conn; SilcClientEntry client_entry; gboolean sign = purple_account_get_bool(sg->account, "sign-verify", FALSE); mflags = SILC_MESSAGE_FLAG_UTF8; tmp = msg = purple_unescape_html(message); if (!g_ascii_strncasecmp(msg, "/me ", 4)) { mflags |= SILC_MESSAGE_FLAG_ACTION; } else if (strlen(msg) > 1 && msg[0] == '/') { if (!silc_client_command_call(client, conn, msg + 1)) purple_notify_error(gc, _("Call Command"), _("Cannot call command"), mflags |= SILC_MESSAGE_FLAG_SIGNED; clients = silc_client_get_clients_local(client, conn, who, FALSE); /* Resolve unknown user */ SilcPurpleIM im = silc_calloc(1, sizeof(*im)); im->nick = g_strdup(who); im->message = g_strdup(message); im->message_len = strlen(im->message); silc_client_get_clients(client, conn, who, NULL, silcpurple_send_im_resolved, im); silc_dlist_start(clients); client_entry = silc_dlist_get(clients); if (flags & PURPLE_MESSAGE_IMAGES) { list = silcpurple_image_message(message, &mflags); /* Send one or more MIME message. If more than one, they are MIME fragments due to over large message */ while ((buf = silc_dlist_get(list)) != SILC_LIST_END) silc_client_send_private_message(client, conn, client_entry, mflags, sg->sha1hash, silc_mime_partial_free(list); silc_client_list_free(client, conn, clients); /* Send private message directly */ ret = silc_client_send_private_message(client, conn, client_entry, silc_client_list_free(client, conn, clients); static GList *silcpurple_blist_node_menu(PurpleBlistNode *node) { /* split this single menu building function back into the two original: one for buddies and one for chats */ if(PURPLE_BLIST_NODE_IS_CHAT(node)) { return silcpurple_chat_menu((PurpleChat *) node); } else if(PURPLE_BLIST_NODE_IS_BUDDY(node)) { return silcpurple_buddy_menu((PurpleBuddy *) node); g_return_val_if_reached(NULL); /********************************* Commands **********************************/ static PurpleCmdRet silcpurple_cmd_chat_part(PurpleConversation *conv, const char *cmd, char **args, char **error, void *data) PurpleConversation *convo = conv; gc = purple_conversation_get_gc(conv); return PURPLE_CMD_RET_FAILED; convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[0], id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)); return PURPLE_CMD_RET_FAILED; silcpurple_chat_leave(gc, id); return PURPLE_CMD_RET_OK; static PurpleCmdRet silcpurple_cmd_chat_topic(PurpleConversation *conv, const char *cmd, char **args, char **error, void *data) gc = purple_conversation_get_gc(conv); id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)); if (gc == NULL || id == 0) return PURPLE_CMD_RET_FAILED; topic = purple_conv_chat_get_topic (PURPLE_CONV_CHAT(conv)); tmp = g_markup_escape_text(topic, -1); tmp2 = purple_markup_linkify(tmp); buf = g_strdup_printf(_("current topic is: %s"), tmp2); buf = g_strdup(_("No topic is set")); purple_conv_chat_write(PURPLE_CONV_CHAT(conv), gc->account->username, buf, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL)); if (args && args[0] && (strlen(args[0]) > 255)) { *error = g_strdup(_("Topic too long")); return PURPLE_CMD_RET_FAILED; silcpurple_chat_set_topic(gc, id, args ? args[0] : NULL); return PURPLE_CMD_RET_OK; static PurpleCmdRet silcpurple_cmd_chat_join(PurpleConversation *conv, const char *cmd, char **args, char **error, void *data) return PURPLE_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]); g_hash_table_replace(comp, "passphrase", args[1]); silcpurple_chat_join(purple_conversation_get_gc(conv), comp); g_hash_table_destroy(comp); return PURPLE_CMD_RET_OK; static PurpleCmdRet silcpurple_cmd_chat_list(PurpleConversation *conv, const char *cmd, char **args, char **error, void *data) gc = purple_conversation_get_gc(conv); purple_roomlist_show_with_account(purple_connection_get_account(gc)); return PURPLE_CMD_RET_OK; static PurpleCmdRet silcpurple_cmd_whois(PurpleConversation *conv, const char *cmd, char **args, char **error, void *data) gc = purple_conversation_get_gc(conv); return PURPLE_CMD_RET_FAILED; silcpurple_get_info(gc, args[0]); return PURPLE_CMD_RET_OK; static PurpleCmdRet silcpurple_cmd_msg(PurpleConversation *conv, const char *cmd, char **args, char **error, void *data) gc = purple_conversation_get_gc(conv); return PURPLE_CMD_RET_FAILED; ret = silcpurple_send_im(gc, args[0], args[1], PURPLE_MESSAGE_SEND); return PURPLE_CMD_RET_OK; return PURPLE_CMD_RET_FAILED; static PurpleCmdRet silcpurple_cmd_query(PurpleConversation *conv, const char *cmd, char **args, char **error, void *data) PurpleConversation *convo; *error = g_strdup(_("You must specify a nick")); return PURPLE_CMD_RET_FAILED; gc = purple_conversation_get_gc(conv); return PURPLE_CMD_RET_FAILED; account = purple_connection_get_account(gc); convo = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, args[0]); ret = silcpurple_send_im(gc, args[0], args[1], PURPLE_MESSAGE_SEND); purple_conv_im_write(PURPLE_CONV_IM(convo), purple_connection_get_display_name(gc), args[1], PURPLE_MESSAGE_SEND, time(NULL)); return PURPLE_CMD_RET_OK; return PURPLE_CMD_RET_FAILED; static PurpleCmdRet silcpurple_cmd_motd(PurpleConversation *conv, const char *cmd, char **args, char **error, void *data) gc = purple_conversation_get_gc(conv); return PURPLE_CMD_RET_FAILED; return PURPLE_CMD_RET_FAILED; *error = g_strdup(_("There is no Message of the Day associated with this connection")); return PURPLE_CMD_RET_FAILED; tmp = g_markup_escape_text(sg->motd, -1); purple_notify_formatted(gc, NULL, _("Message of the Day"), NULL, return PURPLE_CMD_RET_OK; static PurpleCmdRet silcpurple_cmd_detach(PurpleConversation *conv, const char *cmd, char **args, char **error, void *data) gc = purple_conversation_get_gc(conv); return PURPLE_CMD_RET_FAILED; return PURPLE_CMD_RET_FAILED; silc_client_command_call(sg->client, sg->conn, "DETACH"); return PURPLE_CMD_RET_OK; static PurpleCmdRet silcpurple_cmd_cmode(PurpleConversation *conv, const char *cmd, char **args, char **error, void *data) SilcChannelEntry channel; char *silccmd, *silcargs, *msg, tmp[256]; gc = purple_conversation_get_gc(conv); if (gc == NULL || !args || gc->proto_data == NULL) return PURPLE_CMD_RET_FAILED; chname = purple_conversation_get_name(conv); channel = silc_client_get_channel(sg->client, sg->conn, *error = g_strdup_printf(_("channel %s not found"), chname); return PURPLE_CMD_RET_FAILED; silcpurple_get_chmode_string(channel->mode, tmp, sizeof(tmp)); msg = g_strdup_printf(_("channel modes for %s: %s"), chname, tmp); msg = g_strdup_printf(_("no channel modes are set on %s"), chname); purple_conv_chat_write(PURPLE_CONV_CHAT(conv), "", msg, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL)); return PURPLE_CMD_RET_OK; silcargs = g_strjoinv(" ", args); silccmd = g_strconcat(cmd, " ", silcargs, NULL); if (!silc_client_command_call(sg->client, sg->conn, silccmd)) { *error = g_strdup_printf(_("Failed to set cmodes for %s"), args[0]); return PURPLE_CMD_RET_FAILED; return PURPLE_CMD_RET_OK; static PurpleCmdRet silcpurple_cmd_generic(PurpleConversation *conv, const char *cmd, char **args, char **error, void *data) char *silccmd, *silcargs; gc = purple_conversation_get_gc(conv); return PURPLE_CMD_RET_FAILED; return PURPLE_CMD_RET_FAILED; silcargs = g_strjoinv(" ", args); silccmd = g_strconcat(cmd, " ", args ? silcargs : NULL, NULL); if (!silc_client_command_call(sg->client, sg->conn, silccmd)) { *error = g_strdup_printf(_("Unknown command: %s, (may be a client bug)"), cmd); return PURPLE_CMD_RET_FAILED; return PURPLE_CMD_RET_OK; static PurpleCmdRet silcpurple_cmd_quit(PurpleConversation *conv, const char *cmd, char **args, char **error, void *data) const char *ui_name = NULL, *ui_website = NULL; gc = purple_conversation_get_gc(conv); return PURPLE_CMD_RET_FAILED; return PURPLE_CMD_RET_FAILED; ui_info = purple_core_get_ui_info(); ui_name = g_hash_table_lookup(ui_info, "name"); ui_website = g_hash_table_lookup(ui_info, "website"); if(!ui_name || !ui_website) { ui_website = PURPLE_WEBSITE; quit_msg = g_strdup_printf(_("Download %s: %s"), silc_client_command_call(sg->client, sg->conn, NULL, "QUIT", (args && args[0]) ? args[0] : quit_msg, NULL); return PURPLE_CMD_RET_OK; static PurpleCmdRet silcpurple_cmd_call(PurpleConversation *conv, const char *cmd, char **args, char **error, void *data) gc = purple_conversation_get_gc(conv); return PURPLE_CMD_RET_FAILED; return PURPLE_CMD_RET_FAILED; if (!silc_client_command_call(sg->client, sg->conn, args[0])) { *error = g_strdup_printf(_("Unknown command: %s"), args[0]); return PURPLE_CMD_RET_FAILED; return PURPLE_CMD_RET_OK; /************************** Plugin Initialization ****************************/ silcpurple_register_commands(void) purple_cmd_register("part", "w", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_chat_part, _("part [channel]: Leave the chat"), NULL); purple_cmd_register("leave", "w", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_chat_part, _("leave [channel]: Leave the chat"), NULL); purple_cmd_register("topic", "s", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_chat_topic, _("topic [<new topic>]: View or change the topic"), NULL); purple_cmd_register("join", "ws", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_chat_join, _("join <channel> [<password>]: Join a chat on this network"), NULL); purple_cmd_register("list", "", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_chat_list, _("list: List channels on this network"), NULL); purple_cmd_register("whois", "w", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, silcpurple_cmd_whois, _("whois <nick>: View nick's information"), NULL); purple_cmd_register("msg", "ws", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, "prpl-silc", silcpurple_cmd_msg, _("msg <nick> <message>: Send a private message to a user"), NULL); purple_cmd_register("query", "ws", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_query, _("query <nick> [<message>]: Send a private message to a user"), NULL); purple_cmd_register("motd", "", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_motd, _("motd: View the server's Message Of The Day"), NULL); purple_cmd_register("detach", "", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, "prpl-silc", silcpurple_cmd_detach, _("detach: Detach this session"), NULL); purple_cmd_register("quit", "s", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_quit, _("quit [message]: Disconnect from the server, with an optional message"), NULL); purple_cmd_register("call", "s", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, "prpl-silc", silcpurple_cmd_call, _("call <command>: Call any silc client command"), NULL); /* These below just get passed through for the silc client library to deal purple_cmd_register("kill", "ws", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic, _("kill <nick> [-pubkey|<reason>]: Kill nick"), NULL); purple_cmd_register("nick", "w", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, "prpl-silc", silcpurple_cmd_generic, _("nick <newnick>: Change your nickname"), NULL); purple_cmd_register("whowas", "ww", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic, _("whowas <nick>: View nick's information"), NULL); purple_cmd_register("cmode", "wws", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_cmode, _("cmode <channel> [+|-<modes>] [arguments]: Change or display channel modes"), NULL); purple_cmd_register("cumode", "wws", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic, _("cumode <channel> +|-<modes> <nick>: Change nick's modes on channel"), NULL); purple_cmd_register("umode", "w", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, "prpl-silc", silcpurple_cmd_generic, _("umode <usermodes>: Set your modes in the network"), NULL); purple_cmd_register("oper", "s", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, "prpl-silc", silcpurple_cmd_generic, _("oper <nick> [-pubkey]: Get server operator privileges"), NULL); purple_cmd_register("invite", "ws", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic, _("invite <channel> [-|+]<nick>: invite nick or add/remove from channel invite list"), NULL); purple_cmd_register("kick", "wws", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic, _("kick <channel> <nick> [comment]: Kick client from channel"), NULL); purple_cmd_register("info", "w", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic, _("info [server]: View server administrative details"), NULL); purple_cmd_register("ban", "ww", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic, _("ban [<channel> +|-<nick>]: Ban client from channel"), NULL); purple_cmd_register("getkey", "w", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, "prpl-silc", silcpurple_cmd_generic, _("getkey <nick|server>: Retrieve client's or server's public key"), NULL); purple_cmd_register("stats", "", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, "prpl-silc", silcpurple_cmd_generic, _("stats: View server and network statistics"), NULL); purple_cmd_register("ping", "", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, "prpl-silc", silcpurple_cmd_generic, _("ping: Send PING to the connected server"), NULL); #if 0 /* Purple doesn't handle these yet */ purple_cmd_register("users", "w", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, "prpl-silc", silcpurple_cmd_users, _("users <channel>: List users in channel")); purple_cmd_register("names", "ww", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_names, _("names [-count|-ops|-halfops|-voices|-normal] <channel(s)>: List specific users in channel(s)")); static PurpleWhiteboardPrplOps silcpurple_wb_ops = silcpurple_wb_get_dimensions, silcpurple_wb_set_dimensions, static PurplePluginProtocolInfo prpl_info = OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME | OPT_PROTO_PASSWORD_OPTIONAL | OPT_PROTO_IM_IMAGE | OPT_PROTO_SLASH_COMMANDS_NATIVE, NULL, /* protocol_options */ {"jpeg,gif,png,bmp", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */ silcpurple_list_icon, /* list_icon */ silcpurple_status_text, /* status_text */ silcpurple_tooltip_text, /* tooltip_text */ silcpurple_away_states, /* away_states */ silcpurple_blist_node_menu, /* blist_node_menu */ silcpurple_chat_info, /* chat_info */ silcpurple_chat_info_defaults, /* chat_info_defaults */ silcpurple_login, /* login */ silcpurple_close, /* close */ silcpurple_send_im, /* send_im */ silcpurple_set_info, /* set_info */ silcpurple_get_info, /* get_info */ silcpurple_set_status, /* set_status */ silcpurple_idle_set, /* set_idle */ silcpurple_change_passwd, /* change_passwd */ silcpurple_add_buddy, /* add_buddy */ silcpurple_remove_buddy, /* remove_buddy */ NULL, /* remove_buddies */ NULL, /* set_permit_deny */ silcpurple_chat_join, /* join_chat */ silcpurple_get_chat_name, /* get_chat_name */ silcpurple_chat_invite, /* chat_invite */ silcpurple_chat_leave, /* chat_leave */ silcpurple_chat_send, /* chat_send */ silcpurple_keepalive, /* keepalive */ NULL, /* register_user */ silcpurple_buddy_set_icon, /* set_buddy_icon */ NULL, /* get_cb_real_name */ silcpurple_chat_set_topic, /* set_chat_topic */ NULL, /* find_blist_chat */ silcpurple_roomlist_get_list, /* roomlist_get_list */ silcpurple_roomlist_cancel, /* roomlist_cancel */ NULL, /* roomlist_expand_category */ NULL, /* can_receive_file */ silcpurple_ftp_send_file, /* send_file */ silcpurple_ftp_new_xfer, /* new_xfer */ NULL, /* offline_message */ &silcpurple_wb_ops, /* whiteboard_prpl_ops */ NULL, /* roomlist_room_serialize */ NULL, /* unregister_user */ NULL, /* send_attention */ NULL, /* get_attention_types */ sizeof(PurplePluginProtocolInfo), /* struct_size */ NULL, /* get_account_text_table */ NULL, /* initiate_media */ NULL, /* get_media_caps */ NULL, /* set_public_alias */ NULL, /* get_public_alias */ NULL, /* add_buddy_with_invite */ NULL /* add_buddies_with_invite */ static PurplePluginInfo info = PURPLE_PLUGIN_PROTOCOL, /**< type */ NULL, /**< ui_requirement */ NULL, /**< dependencies */ PURPLE_PRIORITY_DEFAULT, /**< priority */ N_("SILC Protocol Plugin"), N_("Secure Internet Live Conferencing (SILC) Protocol"), "Pekka Riikonen", /**< author */ "http://silcnet.org/", /**< homepage */ &prpl_info, /**< extra_info */ static SilcBool silcpurple_debug_cb(char *file, char *function, int line, char *message, void *context) purple_debug_info("SILC", "%s:%d:%s - %s\n", file ? file : "(null)", line, function ? function : "(null)", message ? message : "(null)"); init_plugin(PurplePlugin *plugin) PurpleAccountOption *option; PurpleAccountUserSplit *split; split = purple_account_user_split_new(_("Network"), "silcnet.org", '@'); prpl_info.user_splits = g_list_append(prpl_info.user_splits, split); option = purple_account_option_string_new(_("Connect server"), prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); option = purple_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", silcpurple_silcdir()); option = purple_account_option_string_new(_("Public Key file"), 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", silcpurple_silcdir()); option = purple_account_option_string_new(_("Private Key file"), prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); for (i = 0; silc_default_ciphers[i].name; i++) { kvp = g_new0(PurpleKeyValuePair, 1); kvp->key = g_strdup(silc_default_ciphers[i].name); kvp->value = g_strdup(silc_default_ciphers[i].name); list = g_list_append(list, kvp); option = purple_account_option_list_new(_("Cipher"), "cipher", list); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); for (i = 0; silc_default_hmacs[i].name; i++) { kvp = g_new0(PurpleKeyValuePair, 1); kvp->key = g_strdup(silc_default_hmacs[i].name); kvp->value = g_strdup(silc_default_hmacs[i].name); list = g_list_append(list, kvp); option = purple_account_option_list_new(_("HMAC"), "hmac", list); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); option = purple_account_option_bool_new(_("Use Perfect Forward Secrecy"), prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); option = purple_account_option_bool_new(_("Public key authentication"), prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); option = purple_account_option_bool_new(_("Block IMs without Key Exchange"), prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); option = purple_account_option_bool_new(_("Block messages to whiteboard"), prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); option = purple_account_option_bool_new(_("Automatically open whiteboard"), prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); option = purple_account_option_bool_new(_("Digitally sign and verify all messages"), prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); purple_prefs_remove("/plugins/prpl/silc"); silc_log_set_callback(SILC_LOG_ERROR, silcpurple_log_error, NULL); silcpurple_register_commands(); silc_log_set_debug_string("*client*"); silc_log_set_debug_callbacks(silcpurple_debug_cb, NULL, NULL, NULL); PURPLE_INIT_PLUGIN(silc, init_plugin, info);