diff options
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/dcc.c | 9 | ||||
-rw-r--r-- | src/common/dcc.h | 1 | ||||
-rw-r--r-- | src/common/fe.h | 3 | ||||
-rw-r--r-- | src/common/hexchat.h | 10 | ||||
-rw-r--r-- | src/common/inbound.c | 114 | ||||
-rw-r--r-- | src/common/inbound.h | 2 | ||||
-rw-r--r-- | src/common/modes.c | 2 | ||||
-rw-r--r-- | src/common/notify.c | 64 | ||||
-rw-r--r-- | src/common/notify.h | 5 | ||||
-rw-r--r-- | src/common/outbound.c | 72 | ||||
-rw-r--r-- | src/common/outbound.h | 1 | ||||
-rw-r--r-- | src/common/plugin.c | 42 | ||||
-rw-r--r-- | src/common/plugin.h | 1 | ||||
-rw-r--r-- | src/common/proto-irc.c | 43 | ||||
-rw-r--r-- | src/common/server.c | 14 | ||||
-rw-r--r-- | src/common/servlist.c | 12 | ||||
-rw-r--r-- | src/common/servlist.h | 1 | ||||
-rw-r--r-- | src/common/text.c | 8 | ||||
-rw-r--r-- | src/common/textevents.in | 16 | ||||
-rw-r--r-- | src/common/url.c | 238 | ||||
-rw-r--r-- | src/common/util.c | 239 | ||||
-rw-r--r-- | src/common/util.h | 4 |
22 files changed, 735 insertions, 166 deletions
diff --git a/src/common/dcc.c b/src/common/dcc.c index 1137c444..5f4b1190 100644 --- a/src/common/dcc.c +++ b/src/common/dcc.c @@ -227,6 +227,15 @@ is_dcc (struct DCC *dcc) return FALSE; } +gboolean +is_dcc_completed (struct DCC *dcc) +{ + if (dcc != NULL) + return (dcc->dccstat == STAT_FAILED || dcc->dccstat == STAT_DONE || dcc->dccstat == STAT_ABORTED); + + return FALSE; +} + /* this is called from hexchat.c:hexchat_misc_checks() every 1 second. */ void diff --git a/src/common/dcc.h b/src/common/dcc.h index e3163c8a..acb87f34 100644 --- a/src/common/dcc.h +++ b/src/common/dcc.h @@ -117,6 +117,7 @@ struct dccstat_info extern struct dccstat_info dccstat[]; gboolean is_dcc (struct DCC *dcc); +gboolean is_dcc_completed (struct DCC *dcc); void dcc_abort (session *sess, struct DCC *dcc); void dcc_get (struct DCC *dcc); int dcc_resume (struct DCC *dcc); diff --git a/src/common/fe.h b/src/common/fe.h index 22db38df..91bf0a5b 100644 --- a/src/common/fe.h +++ b/src/common/fe.h @@ -125,11 +125,12 @@ void fe_get_str (char *prompt, char *def, void *callback, void *ud); void fe_get_int (char *prompt, int def, void *callback, void *ud); #define FRF_WRITE 1 /* save file */ #define FRF_MULTIPLE 2 /* multi-select */ -#define FRF_ADDFOLDER 4 /* add ~/.config/hexchat to favourites */ +#define FRF_RECENTLYUSED 4 /* let gtk decide start dir instead of our config */ #define FRF_CHOOSEFOLDER 8 /* choosing a folder only */ #define FRF_FILTERISINITIAL 16 /* filter is initial directory */ #define FRF_NOASKOVERWRITE 32 /* don't ask to overwrite existing files */ #define FRF_EXTENSIONS 64 /* specify file extensions to be displayed */ +#define FRF_MIMETYPES 128 /* specify file mimetypes to be displayed */ void fe_get_file (const char *title, char *initial, void (*callback) (void *userdata, char *file), void *userdata, int flags); diff --git a/src/common/hexchat.h b/src/common/hexchat.h index 76d6c3c3..68da144d 100644 --- a/src/common/hexchat.h +++ b/src/common/hexchat.h @@ -461,6 +461,12 @@ struct msproxy_state_t unsigned char seq_sent; /* seq number of last packet sent. */ }; +/* SASL Mechanisms */ +#define MECH_PLAIN 0 +#define MECH_BLOWFISH 1 +#define MECH_AES 2 +#define MECH_EXTERNAL 3 + typedef struct server { /* server control operations (in server*.c) */ @@ -598,9 +604,13 @@ typedef struct server unsigned int have_sasl:1; /* SASL capability */ unsigned int have_except:1; /* ban exemptions +e */ unsigned int have_invite:1; /* invite exemptions +I */ + unsigned int have_cert:1; /* have loaded a cert */ unsigned int using_cp1255:1; /* encoding is CP1255/WINDOWS-1255? */ unsigned int using_irc:1; /* encoding is "IRC" (CP1252/UTF-8 hybrid)? */ unsigned int use_who:1; /* whether to use WHO command to get dcc_ip */ + unsigned int sasl_mech; /* mechanism for sasl auth */ + unsigned int sent_saslauth:1; /* have sent AUTHENICATE yet */ + unsigned int sent_capend:1; /* have sent CAP END yet */ #ifdef USE_OPENSSL unsigned int use_ssl:1; /* is server SSL capable? */ unsigned int accept_invalid_cert:1;/* ignore result of server's cert. verify */ diff --git a/src/common/inbound.c b/src/common/inbound.c index f57207d1..fca5f071 100644 --- a/src/common/inbound.c +++ b/src/common/inbound.c @@ -392,6 +392,8 @@ inbound_action (session *sess, char *chan, char *from, char *ip, char *text, user->lasttalk = time (0); if (user->account) id = TRUE; + if (user->me) + fromme = TRUE; } inbound_make_idtext (serv, idtext, sizeof (idtext), id); @@ -459,6 +461,8 @@ inbound_chanmsg (server *serv, session *sess, char *chan, char *from, id = TRUE; nickchar[0] = user->prefix[0]; user->lasttalk = time (0); + if (user->me) + fromme = TRUE; } if (fromme) @@ -1450,7 +1454,12 @@ nowindow: static int inbound_exec_eom_cmd (char *str, void *sess) { - handle_command (sess, (str[0] == '/') ? str + 1 : str, TRUE); + char *cmd; + + cmd = command_insert_vars ((session*)sess, (str[0] == '/') ? str + 1 : str); + handle_command ((session*)sess, cmd, TRUE); + g_free (cmd); + return 1; } @@ -1557,8 +1566,6 @@ void inbound_cap_ack (server *serv, char *nick, char *extensions, const message_tags_data *tags_data) { - char *pass; /* buffer for SASL password */ - EMIT_SIGNAL_TIMESTAMP (XP_TE_CAPACK, serv->server_session, nick, extensions, NULL, NULL, 0, tags_data->timestamp); @@ -1594,20 +1601,25 @@ inbound_cap_ack (server *serv, char *nick, char *extensions, if (strstr (extensions, "sasl") != NULL) { - char *user; - serv->have_sasl = TRUE; + serv->sent_saslauth = FALSE; - user = (((ircnet *)serv->network)->user) - ? (((ircnet *)serv->network)->user) : prefs.hex_irc_user_name; - - EMIT_SIGNAL_TIMESTAMP (XP_TE_SASLAUTH, serv->server_session, user, NULL, - NULL, NULL, 0, tags_data->timestamp); +#ifdef USE_OPENSSL + if (serv->loginmethod == LOGIN_SASLEXTERNAL) + { + serv->sasl_mech = MECH_EXTERNAL; + tcp_send_len (serv, "AUTHENTICATE EXTERNAL\r\n", 23); + } + else + { + /* default to most secure, it will fallback if not supported */ + serv->sasl_mech = MECH_AES; + tcp_send_len (serv, "AUTHENTICATE DH-AES\r\n", 21); + } +#else + serv->sasl_mech = MECH_PLAIN; tcp_send_len (serv, "AUTHENTICATE PLAIN\r\n", 20); - - pass = encode_sasl_pass (user, serv->password); - tcp_sendf (serv, "AUTHENTICATE %s\r\n", pass); - free (pass); +#endif } } @@ -1678,9 +1690,9 @@ inbound_cap_ls (server *serv, char *nick, char *extensions_str, } /* if the SASL password is set AND auth mode is set to SASL, request SASL auth */ - if (serv->loginmethod == LOGIN_SASL - && strcmp (extension, "sasl") == 0 - && strlen (serv->password) != 0) + if (!strcmp (extension, "sasl") + && ((serv->loginmethod == LOGIN_SASL && strlen (serv->password) != 0) + || (serv->loginmethod == LOGIN_SASLEXTERNAL && serv->have_cert))) { strcat (buffer, "sasl "); want_cap = 1; @@ -1701,6 +1713,7 @@ inbound_cap_ls (server *serv, char *nick, char *extensions_str, if (!want_sasl) { /* if we use SASL, CAP END is dealt via raw numerics */ + serv->sent_capend = TRUE; tcp_send_len (serv, "CAP END\r\n", 9); } } @@ -1708,6 +1721,7 @@ inbound_cap_ls (server *serv, char *nick, char *extensions_str, void inbound_cap_nak (server *serv, const message_tags_data *tags_data) { + serv->sent_capend = TRUE; tcp_send_len (serv, "CAP END\r\n", 9); } @@ -1718,3 +1732,69 @@ inbound_cap_list (server *serv, char *nick, char *extensions, EMIT_SIGNAL_TIMESTAMP (XP_TE_CAPACK, serv->server_session, nick, extensions, NULL, NULL, 0, tags_data->timestamp); } + +static const char *sasl_mechanisms[] = +{ + "PLAIN", + "DH-BLOWFISH", + "DH-AES", + "EXTERNAL" +}; + +void +inbound_sasl_authenticate (server *serv, char *data) +{ + char *user, *pass = NULL; + const char *mech = sasl_mechanisms[serv->sasl_mech]; + + user = (((ircnet*)serv->network)->user) + ? (((ircnet*)serv->network)->user) : prefs.hex_irc_user_name; + + switch (serv->sasl_mech) + { + case MECH_PLAIN: + pass = encode_sasl_pass_plain (user, serv->password); + break; +#ifdef USE_OPENSSL + case MECH_BLOWFISH: + pass = encode_sasl_pass_blowfish (user, serv->password, data); + break; + case MECH_AES: + pass = encode_sasl_pass_aes (user, serv->password, data); + break; + case MECH_EXTERNAL: + pass = g_strdup ("+"); + break; +#endif + } + + if (pass == NULL) + { + /* something went wrong abort */ + serv->sent_saslauth = TRUE; /* prevent trying PLAIN */ + tcp_sendf (serv, "AUTHENTICATE *\r\n"); + return; + } + + serv->sent_saslauth = TRUE; + tcp_sendf (serv, "AUTHENTICATE %s\r\n", pass); + g_free (pass); + + + EMIT_SIGNAL_TIMESTAMP (XP_TE_SASLAUTH, serv->server_session, user, (char*)mech, + NULL, NULL, 0, 0); +} + +int +inbound_sasl_error (server *serv) +{ + /* If server sent 904 before we sent password, + * mech not support so fallback to next mech */ + if (!serv->sent_saslauth && serv->sasl_mech != MECH_EXTERNAL && serv->sasl_mech != MECH_PLAIN) + { + serv->sasl_mech -= 1; + tcp_sendf (serv, "AUTHENTICATE %s\r\n", sasl_mechanisms[serv->sasl_mech]); + return 1; + } + return 0; +} diff --git a/src/common/inbound.h b/src/common/inbound.h index cbb04890..40eeb8f6 100644 --- a/src/common/inbound.h +++ b/src/common/inbound.h @@ -95,6 +95,8 @@ void inbound_cap_ls (server *serv, char *nick, char *extensions, void inbound_cap_nak (server *serv, const message_tags_data *tags_data); void inbound_cap_list (server *serv, char *nick, char *extensions, const message_tags_data *tags_data); +void inbound_sasl_authenticate (server *serv, char *data); +int inbound_sasl_error (server *serv); void do_dns (session *sess, char *nick, char *host, const message_tags_data *tags_data); gboolean alert_match_word (char *word, char *masks); diff --git a/src/common/modes.c b/src/common/modes.c index 420f3e8c..416805c3 100644 --- a/src/common/modes.c +++ b/src/common/modes.c @@ -848,7 +848,7 @@ inbound_005 (server * serv, char *word[], const message_tags_data *tags_data) serv->p_cmp = (void *)g_ascii_strcasecmp; } else if (strncmp (word[w], "CHARSET=", 8) == 0) { - if (g_ascii_strcasecmp (word[w] + 8, "UTF-8") == 0) + if (g_ascii_strncasecmp (word[w] + 8, "UTF-8", 5) == 0) { server_set_encoding (serv, "UTF-8"); } diff --git a/src/common/notify.c b/src/common/notify.c index 944d826c..f1439140 100644 --- a/src/common/notify.c +++ b/src/common/notify.c @@ -284,6 +284,70 @@ notify_set_online (server * serv, char *nick, notify_announce_online (serv, servnot, nick, tags_data); } +/* monitor can send lists for numeric 730/731 */ + +void +notify_set_offline_list (server * serv, char *users, int quiet, + const message_tags_data *tags_data) +{ + struct notify_per_server *servnot; + char nick[NICKLEN]; + char *token, *chr; + int pos; + + token = strtok (users, ","); + while (token != NULL) + { + chr = strchr (token, '!'); + if (!chr) + goto end; + + pos = chr - token; + if (pos + 1 >= sizeof(nick)) + goto end; + + memset (nick, 0, sizeof(nick)); + strncpy (nick, token, pos); + + servnot = notify_find (serv, nick); + if (servnot) + notify_announce_offline (serv, servnot, nick, quiet, tags_data); +end: + token = strtok (NULL, ","); + } +} + +void +notify_set_online_list (server * serv, char *users, + const message_tags_data *tags_data) +{ + struct notify_per_server *servnot; + char nick[NICKLEN]; + char *token, *chr; + int pos; + + token = strtok (users, ","); + while (token != NULL) + { + chr = strchr (token, '!'); + if (!chr) + goto end; + + pos = chr - token; + if (pos + 1 >= sizeof(nick)) + goto end; + + memset (nick, 0, sizeof(nick)); + strncpy (nick, token, pos); + + servnot = notify_find (serv, nick); + if (servnot) + notify_announce_online (serv, servnot, nick, tags_data); +end: + token = strtok (NULL, ","); + } +} + static void notify_watch (server * serv, char *nick, int add) { diff --git a/src/common/notify.h b/src/common/notify.h index 4a6ffb35..5bf43410 100644 --- a/src/common/notify.h +++ b/src/common/notify.h @@ -47,6 +47,11 @@ void notify_set_online (server * serv, char *nick, const message_tags_data *tags_data); void notify_set_offline (server * serv, char *nick, int quiet, const message_tags_data *tags_data); +/* the MONITOR stuff */ +void notify_set_online_list (server * serv, char *users, + const message_tags_data *tags_data); +void notify_set_offline_list (server * serv, char *users, int quiet, + const message_tags_data *tags_data); void notify_send_watches (server * serv); /* the general stuff */ diff --git a/src/common/outbound.c b/src/common/outbound.c index 9ddacb75..a35ba429 100644 --- a/src/common/outbound.c +++ b/src/common/outbound.c @@ -487,19 +487,19 @@ create_mask (session * sess, char *mask, char *mode, char *typestr, int deop) switch (type) { case 0: - snprintf (buf, sizeof (buf), "%s%s *!*@%s.*", mode, p2, domain); + snprintf (buf, sizeof (buf), "%s %s *!*@%s.*", mode, p2, domain); break; case 1: - snprintf (buf, sizeof (buf), "%s%s *!*@%s", mode, p2, fullhost); + snprintf (buf, sizeof (buf), "%s %s *!*@%s", mode, p2, fullhost); break; case 2: - snprintf (buf, sizeof (buf), "%s%s *!%s@%s.*", mode, p2, username, domain); + snprintf (buf, sizeof (buf), "%s %s *!%s@%s.*", mode, p2, username, domain); break; case 3: - snprintf (buf, sizeof (buf), "%s%s *!%s@%s", mode, p2, username, fullhost); + snprintf (buf, sizeof (buf), "%s %s *!%s@%s", mode, p2, username, fullhost); break; } } else @@ -507,19 +507,19 @@ create_mask (session * sess, char *mask, char *mode, char *typestr, int deop) switch (type) { case 0: - snprintf (buf, sizeof (buf), "%s%s *!*@*%s", mode, p2, domain); + snprintf (buf, sizeof (buf), "%s %s *!*@*%s", mode, p2, domain); break; case 1: - snprintf (buf, sizeof (buf), "%s%s *!*@%s", mode, p2, fullhost); + snprintf (buf, sizeof (buf), "%s %s *!*@%s", mode, p2, fullhost); break; case 2: - snprintf (buf, sizeof (buf), "%s%s *!%s@*%s", mode, p2, username, domain); + snprintf (buf, sizeof (buf), "%s %s *!%s@*%s", mode, p2, username, domain); break; case 3: - snprintf (buf, sizeof (buf), "%s%s *!%s@%s", mode, p2, username, fullhost); + snprintf (buf, sizeof (buf), "%s %s *!%s@%s", mode, p2, username, fullhost); break; } } @@ -3498,6 +3498,39 @@ cmd_unload (struct session *sess, char *tbuf, char *word[], char *word_eol[]) return FALSE; } +static int +cmd_reload (struct session *sess, char *tbuf, char *word[], char *word_eol[]) +{ +#ifdef USE_PLUGIN + int len, by_file = FALSE; + + len = strlen (word[2]); +#ifdef WIN32 + if (len > 4 && g_ascii_strcasecmp (word[2] + len - 4, ".dll") == 0) +#else +#if defined(__hpux) + if (len > 3 && g_ascii_strcasecmp (word[2] + len - 3, ".sl") == 0) +#else + if (len > 3 && g_ascii_strcasecmp (word[2] + len - 3, ".so") == 0) +#endif +#endif + by_file = TRUE; + + switch (plugin_reload (sess, word[2], by_file)) + { + case 0: /* error */ + PrintText (sess, _("No such plugin found.\n")); + break; + case 1: /* success */ + return TRUE; + case 2: /* fake plugin, we know it exists but scripts should handle it. */ + return TRUE; + } +#endif + + return FALSE; +} + static server * find_server_from_hostname (char *hostname) { @@ -3782,8 +3815,8 @@ const struct commands xc_cmds[] = { N_("BAN <mask> [<bantype>], bans everyone matching the mask from the current channel. If they are already on the channel this doesn't kick them (needs chanop)")}, {"CHANOPT", cmd_chanopt, 0, 0, 1, N_("CHANOPT [-quiet] <variable> [<value>]")}, {"CHARSET", cmd_charset, 0, 0, 1, N_("CHARSET [<encoding>], get or set the encoding used for the current connection")}, - {"CLEAR", cmd_clear, 0, 0, 1, N_("CLEAR [ALL|HISTORY], Clears the current text window or command history")}, - {"CLOSE", cmd_close, 0, 0, 1, N_("CLOSE, Closes the current window/tab")}, + {"CLEAR", cmd_clear, 0, 0, 1, N_("CLEAR [ALL|HISTORY|[-]<amount>], Clears the current text window or command history")}, + {"CLOSE", cmd_close, 0, 0, 1, N_("CLOSE [-m], Closes the current window/tab or all queries")}, {"COUNTRY", cmd_country, 0, 0, 1, N_("COUNTRY [-s] <code|wildcard>, finds a country code, eg: au = australia")}, @@ -3883,7 +3916,7 @@ const struct commands xc_cmds[] = { {"MODE", cmd_mode, 1, 0, 1, 0}, {"MOP", cmd_mop, 1, 1, 1, N_("MOP, Mass op's all users in the current channel (needs chanop)")}, - {"MSG", cmd_msg, 0, 0, 1, N_("MSG <nick> <message>, sends a private message")}, + {"MSG", cmd_msg, 0, 0, 1, N_("MSG <nick> <message>, sends a private message, message \".\" to send to last nick or prefix with \"=\" for dcc chat")}, {"NAMES", cmd_names, 1, 0, 1, N_("NAMES, Lists the nicks on the current channel")}, @@ -3918,7 +3951,7 @@ const struct commands xc_cmds[] = { N_("RECONNECT [<host>] [<port>] [<password>], Can be called just as /RECONNECT to reconnect to the current server or with /RECONNECT ALL to reconnect to all the open servers")}, #endif {"RECV", cmd_recv, 1, 0, 1, N_("RECV <text>, send raw data to HexChat, as if it was received from the IRC server")}, - + {"RELOAD", cmd_reload, 0, 0, 1, N_("RELOAD <name>, reloads a plugin or script")}, {"SAY", cmd_say, 0, 0, 1, N_("SAY <text>, sends the text to the object in the current window")}, {"SEND", cmd_send, 0, 0, 1, N_("SEND <nick> [<file>]")}, @@ -4551,7 +4584,6 @@ handle_command (session *sess, char *cmd, int check_spch) char tbuf_static[TBUFSIZE]; char *pdibuf; char *tbuf; - char *cmd_vars; int len; int ret = TRUE; @@ -4563,9 +4595,7 @@ handle_command (session *sess, char *cmd, int check_spch) command_level++; /* anything below MUST DEC command_level before returning */ - cmd_vars = command_insert_vars (sess, cmd); - - len = strlen (cmd_vars); + len = strlen (cmd); if (len >= sizeof (pdibuf_static)) { pdibuf = malloc (len + 1); @@ -4585,7 +4615,7 @@ handle_command (session *sess, char *cmd, int check_spch) } /* split the text into words and word_eol */ - process_data_init (pdibuf, cmd_vars, word, word_eol, TRUE, TRUE); + process_data_init (pdibuf, cmd, word, word_eol, TRUE, TRUE); /* ensure an empty string at index 32 for cmd_deop etc */ /* (internal use only, plugins can still only read 1-31). */ @@ -4596,12 +4626,12 @@ handle_command (session *sess, char *cmd, int check_spch) /* redo it without quotes processing, for some commands like /JOIN */ if (int_cmd && !int_cmd->handle_quotes) { - process_data_init (pdibuf, cmd_vars, word, word_eol, FALSE, FALSE); + process_data_init (pdibuf, cmd, word, word_eol, FALSE, FALSE); } if (check_spch && prefs.hex_input_perc_color) { - check_special_chars (cmd_vars, prefs.hex_input_perc_ascii); + check_special_chars (cmd, prefs.hex_input_perc_ascii); } if (plugin_emit_command (sess, word[1], word, word_eol)) @@ -4668,7 +4698,7 @@ handle_command (session *sess, char *cmd, int check_spch) } else { - sess->server->p_raw (sess->server, cmd_vars); + sess->server->p_raw (sess->server, cmd); } } @@ -4685,8 +4715,6 @@ xit: free (tbuf); } - g_free (cmd_vars); - return ret; } diff --git a/src/common/outbound.h b/src/common/outbound.h index 7dc1ba97..81a98666 100644 --- a/src/common/outbound.h +++ b/src/common/outbound.h @@ -25,6 +25,7 @@ extern GSList *menu_list; int auto_insert (char *dest, int destlen, unsigned char *src, char *word[], char *word_eol[], char *a, char *c, char *d, char *e, char *h, char *n, char *s, char *u); +char *command_insert_vars (session *sess, char *cmd); int handle_command (session *sess, char *cmd, int check_spch); void process_data_init (char *buf, char *cmd, char *word[], char *word_eol[], gboolean handle_quotes, gboolean allow_escape_quotes); void handle_multiline (session *sess, char *cmd, int history, int nocommand); diff --git a/src/common/plugin.c b/src/common/plugin.c index 50157ea1..d186679a 100644 --- a/src/common/plugin.c +++ b/src/common/plugin.c @@ -512,6 +512,44 @@ plugin_auto_load (session *sess) g_free (sub_dir); } +int +plugin_reload (session *sess, char *name, int by_filename) +{ + GSList *list; + char *filename; + char *ret; + hexchat_plugin *pl; + + list = plugin_list; + while (list) + { + pl = list->data; + /* static-plugins (plugin-timer.c) have a NULL filename */ + if ((by_filename && pl->filename && g_ascii_strcasecmp (name, pl->filename) == 0) || + (by_filename && pl->filename && g_ascii_strcasecmp (name, file_part (pl->filename)) == 0) || + (!by_filename && g_ascii_strcasecmp (name, pl->name) == 0)) + { + /* statically linked plugins have a NULL filename */ + if (pl->filename != NULL && !pl->fake) + { + filename = g_strdup (pl->filename); + plugin_free (pl, TRUE, FALSE); + ret = plugin_load (sess, filename, NULL); + g_free (filename); + if (ret == NULL) + return 1; + else + return 0; + } + else + return 2; + } + list = list->next; + } + + return 0; +} + #endif static GSList * @@ -1343,7 +1381,7 @@ hexchat_list_fields (hexchat_plugin *ph, const char *name) }; static const char * const channels_fields[] = { - "schannel", "schantypes", "pcontext", "iflags", "iid", "ilag", "imaxmodes", + "schannel", "schannelkey", "schantypes", "pcontext", "iflags", "iid", "ilag", "imaxmodes", "snetwork", "snickmodes", "snickprefixes", "iqueue", "sserver", "itype", "iusers", NULL }; @@ -1438,6 +1476,8 @@ hexchat_list_str (hexchat_plugin *ph, hexchat_list *xlist, const char *name) { case 0x2c0b7d03: /* channel */ return ((session *)data)->channel; + case 0x8cea5e7c: /* channelkey */ + return ((session *)data)->channelkey; case 0x577e0867: /* chantypes */ return ((session *)data)->server->chantypes; case 0x38b735af: /* context */ diff --git a/src/common/plugin.h b/src/common/plugin.h index f75639e9..ee9da8c1 100644 --- a/src/common/plugin.h +++ b/src/common/plugin.h @@ -164,6 +164,7 @@ struct _hexchat_plugin #endif char *plugin_load (session *sess, char *filename, char *arg); +int plugin_reload (session *sess, char *name, int by_filename); void plugin_add (session *sess, char *filename, void *handle, void *init_func, void *deinit_func, char *arg, int fake); int plugin_kill (char *name, int by_filename); void plugin_kill_all (void); diff --git a/src/common/proto-irc.c b/src/common/proto-irc.c index 128c0c85..250a2937 100644 --- a/src/common/proto-irc.c +++ b/src/common/proto-irc.c @@ -45,11 +45,11 @@ #include "url.h" #include "servlist.h" - static void irc_login (server *serv, char *user, char *realname) { tcp_sendf (serv, "CAP LS\r\n"); /* start with CAP LS as Charybdis sasl.txt suggests */ + serv->sent_capend = FALSE; /* track if we have finished */ if (serv->password[0] && serv->loginmethod == LOGIN_PASS) { @@ -465,8 +465,6 @@ process_numeric (session * sess, int n, server *serv = sess->server; /* show whois is the server tab */ session *whois_sess = serv->server_session; - - char *ex; /* unless this setting is on */ if (prefs.hex_irc_whois_front) @@ -678,6 +676,15 @@ process_numeric (session * sess, int n, handle_mode (serv, word, word_eol, "", TRUE, tags_data); break; + case 328: /* channel url */ + sess = find_channel (serv, word[4]); + if (sess) + { + EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANURL, sess, word[4], word[5] + 1, + NULL, NULL, 0, tags_data->timestamp); + } + break; + case 329: sess = find_channel (serv, word[4]); if (sess) @@ -933,17 +940,11 @@ process_numeric (session * sess, int n, break; case 730: /* RPL_MONONLINE */ - ex = strchr (word[4], '!'); /* only send the nick */ - if (ex) - ex[0] = 0; - notify_set_online (serv, word[4] + 1, tags_data); + notify_set_online_list (serv, word[4] + 1, tags_data); break; case 731: /* RPL_MONOFFLINE */ - ex = strchr (word[4], '!'); /* only send the nick */ - if (ex) - ex[0] = 0; - notify_set_offline (serv, word[4] + 1, FALSE, tags_data); + notify_set_offline_list (serv, word[4] + 1, FALSE, tags_data); break; case 900: /* successful SASL 'logged in as ' */ @@ -952,14 +953,20 @@ process_numeric (session * sess, int n, tags_data->timestamp); break; case 903: /* successful SASL auth */ - case 904: /* aborted SASL auth */ + case 904: /* failed SASL auth */ + if (inbound_sasl_error (serv)) + break; /* might retry */ case 905: /* failed SASL auth */ - case 906: /* registration completes before SASL auth */ + case 906: /* aborted */ case 907: /* attempting to re-auth after a successful auth */ EMIT_SIGNAL_TIMESTAMP (XP_TE_SASLRESPONSE, serv->server_session, word[1], word[2], word[3], ++word_eol[4], 0, tags_data->timestamp); - tcp_send_len (serv, "CAP END\r\n", 9); + if (!serv->sent_capend) + { + serv->sent_capend = TRUE; + tcp_send_len (serv, "CAP END\r\n", 9); + } break; default: @@ -1144,7 +1151,6 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[], case WORDL('N','O','T','I'): { int id = FALSE; /* identified */ - char *response; text = word_eol[4]; if (*text == ':') @@ -1152,9 +1158,10 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[], text++; } +#ifdef USE_OPENSSL if (!strncmp (text, "CHALLENGE ", 10)) /* QuakeNet CHALLENGE upon our request */ { - response = challengeauth_response (((ircnet *)serv->network)->user ? ((ircnet *)serv->network)->user : prefs.hex_irc_user_name, serv->password, word[5]); + char *response = challengeauth_response (((ircnet *)serv->network)->user ? ((ircnet *)serv->network)->user : prefs.hex_irc_user_name, serv->password, word[5]); tcp_sendf (serv, "PRIVMSG %s :CHALLENGEAUTH %s %s %s\r\n", CHALLENGEAUTH_NICK, @@ -1165,6 +1172,7 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[], g_free (response); return; /* omit the CHALLENGE <hash> ALGOS message */ } +#endif if (serv->have_idmsg) { @@ -1320,8 +1328,9 @@ process_named_servermsg (session *sess, char *buf, char *rawname, char *word_eol tags_data->timestamp); return; } - if (!strncmp (buf, "AUTHENTICATE +", 14)) /* omit SASL "empty" responses */ + if (!strncmp (buf, "AUTHENTICATE", 12)) { + inbound_sasl_authenticate (sess->server, word_eol[2]); return; } diff --git a/src/common/server.c b/src/common/server.c index e59a7ee3..eea7ce08 100644 --- a/src/common/server.c +++ b/src/common/server.c @@ -1049,7 +1049,8 @@ server_cleanup (server * serv) #ifdef USE_OPENSSL if (serv->ssl) { - _SSL_close (serv->ssl); + SSL_shutdown (serv->ssl); + SSL_free (serv->ssl); serv->ssl = NULL; } #endif @@ -1705,18 +1706,25 @@ server_connect (server *serv, char *hostname, int port, int no_login) if (serv->use_ssl) { char *cert_file; + serv->have_cert = FALSE; /* first try network specific cert/key */ cert_file = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "certs" G_DIR_SEPARATOR_S "%s.pem", get_xdir (), server_get_network (serv, TRUE)); if (SSL_CTX_use_certificate_file (ctx, cert_file, SSL_FILETYPE_PEM) == 1) - SSL_CTX_use_PrivateKey_file (ctx, cert_file, SSL_FILETYPE_PEM); + { + if (SSL_CTX_use_PrivateKey_file (ctx, cert_file, SSL_FILETYPE_PEM) == 1) + serv->have_cert = TRUE; + } else { /* if that doesn't exist, try <config>/certs/client.pem */ cert_file = g_build_filename (get_xdir (), "certs", "client.pem", NULL); if (SSL_CTX_use_certificate_file (ctx, cert_file, SSL_FILETYPE_PEM) == 1) - SSL_CTX_use_PrivateKey_file (ctx, cert_file, SSL_FILETYPE_PEM); + { + if (SSL_CTX_use_PrivateKey_file (ctx, cert_file, SSL_FILETYPE_PEM) == 1) + serv->have_cert = TRUE; + } } g_free (cert_file); } diff --git a/src/common/servlist.c b/src/common/servlist.c index ceee3455..3ccd81a6 100644 --- a/src/common/servlist.c +++ b/src/common/servlist.c @@ -251,11 +251,13 @@ static const struct defaultserver def[] = {0, "irc.ggn.net"}, {0, "irc.vendetta.com"}, - {"freenode", 0, "#hexchat", 0, LOGIN_SASL}, + {"freenode", 0, 0, 0, LOGIN_SASL}, #ifdef USE_OPENSSL {0, "chat.freenode.net/+6697"}, #endif {0, "chat.freenode.net"}, + /* irc. points to chat. but many users and urls still reference it */ + {0, "irc.freenode.net"}, /* {"Freeworld", 0}, {0, "kabel.freeworld.nu"}, @@ -418,6 +420,9 @@ static const struct defaultserver def[] = /* {"NullusNet", 0}, {0, "irc.nullus.net"},*/ + + {"ObsidianIRC", 0}, + {0, "irc.obsidianirc.net"}, {"Oceanius", 0}, {0, "irc.oceanius.com"}, @@ -535,7 +540,7 @@ static const struct defaultserver def[] = {0, "eu.spidernet.org"}, {0, "irc.spidernet.org"},*/ - {"SpotChat", 0}, + {"SpotChat", 0, 0, 0, LOGIN_SASL}, #ifdef USE_OPENSSL {0, "irc.spotchat.org/+6697"}, #endif @@ -565,6 +570,9 @@ static const struct defaultserver def[] = #endif {0, "irc.swiftirc.net/6667"}, + {"TinyCrab", 0}, + {0, "irc.tinycrab.net"}, + /* {"TNI3", 0}, {0, "irc.tni3.com"},*/ diff --git a/src/common/servlist.h b/src/common/servlist.h index 45b6dad6..6d6f1bd3 100644 --- a/src/common/servlist.h +++ b/src/common/servlist.h @@ -79,6 +79,7 @@ extern GSList *network_list; #define LOGIN_PASS 7 #define LOGIN_CHALLENGEAUTH 8 #define LOGIN_CUSTOM 9 +#define LOGIN_SASLEXTERNAL 10 #define CHALLENGEAUTH_ALGO "HMAC-SHA-256" #define CHALLENGEAUTH_NICK "Q@CServe.quakenet.org" diff --git a/src/common/text.c b/src/common/text.c index b825faba..e0cdb5ee 100644 --- a/src/common/text.c +++ b/src/common/text.c @@ -1297,7 +1297,8 @@ static char * const pevt_generic_channel_help[] = { }; static char * const pevt_saslauth_help[] = { - N_("Username") + N_("Username"), + N_("Mechanism") }; static char * const pevt_saslresponse_help[] = { @@ -1359,6 +1360,11 @@ static char * const pevt_chanmodes_help[] = { N_("Modes string"), }; +static char * const pevt_chanurl_help[] = { + N_("Channel Name"), + N_("URL"), +}; + static char * const pevt_rawmodes_help[] = { N_("Nickname"), N_("Modes string"), diff --git a/src/common/textevents.in b/src/common/textevents.in index 1f86d90b..3631e363 100644 --- a/src/common/textevents.in +++ b/src/common/textevents.in @@ -202,6 +202,12 @@ pevt_chanunquiet_help %C22*%O$t%C26$1%O removes quiet on %C18$2%O 2 +Channel Url +XP_TE_CHANURL +pevt_chanurl_help +%C22*%O$tChannel %C22$1%O url: %C24$2 +2 + Channel Voice XP_TE_CHANVOICE pevt_chanvoice_help @@ -487,7 +493,7 @@ pevt_invited_help Join XP_TE_JOIN pevt_join_help -%C23*$t$1 ($3) has joined +%C23*$t$1 ($3%C23) has joined 3 Keyword @@ -619,13 +625,13 @@ n0 Part XP_TE_PART pevt_part_help -%C24*$t$1 ($2) has left +%C24*$t$1 ($2%C24) has left 3 Part with Reason XP_TE_PARTREASON pevt_partreason_help -%C24*$t$1 ($2) has left ($4) +%C24*$t$1 ($2%C24) has left ($4) 4 Ping Reply @@ -697,8 +703,8 @@ pevt_resolvinguser_help SASL Authenticating XP_TE_SASLAUTH pevt_saslauth_help -%C23*%O$tAuthenticating via SASL as %C18$1%O -1 +%C23*%O$tAuthenticating via SASL as %C18$1%O (%C24$2%O) +2 SASL Response XP_TE_SASLRESPONSE diff --git a/src/common/url.c b/src/common/url.c index 892441c8..57d8d88b 100644 --- a/src/common/url.c +++ b/src/common/url.c @@ -32,15 +32,22 @@ void *url_tree = NULL; GTree *url_btree = NULL; -static int do_an_re (const char *word, int *start, int *end, int *type); -static GRegex *re_url (void); -static GRegex *re_host (void); -static GRegex *re_host6 (void); -static GRegex *re_email (void); -static GRegex *re_nick (void); -static GRegex *re_channel (void); -static GRegex *re_path (void); - +static gboolean regex_match (const GRegex *re, const char *word, + int *start, int *end); +static const GRegex *re_url (void); +static const GRegex *re_host (void); +static const GRegex *re_host6 (void); +static const GRegex *re_email (void); +static const GRegex *re_nick (void); +static const GRegex *re_channel (void); +static const GRegex *re_path (void); +static gboolean match_nick (const char *word, int *start, int *end); +static gboolean match_channel (const char *word, int *start, int *end); +static gboolean match_email (const char *word, int *start, int *end); +static gboolean match_url (const char *word, int *start, int *end); +static gboolean match_host (const char *word, int *start, int *end); +static gboolean match_host6 (const char *word, int *start, int *end); +static gboolean match_path (const char *word, int *start, int *end); static int url_free (char *url, void *data) @@ -189,50 +196,111 @@ static int laststart = 0; static int lastend = 0; static int lasttype = 0; -static int -strchrs (char c, char *s) -{ - while (*s) - if (c == *s++) - return TRUE; - return FALSE; -} +#define NICKPRE "~+!@%&" +#define CHANPRE "#&!+" -#define NICKPRE "~+!@%%&" int url_check_word (const char *word) { + struct { + gboolean (*match) (const char *word, int *start, int *end); + int type; + } m[] = { + { match_url, WORD_URL }, + { match_email, WORD_EMAIL }, + { match_nick, WORD_NICK }, + { match_channel, WORD_CHANNEL }, + { match_host6, WORD_HOST6 }, + { match_host, WORD_HOST }, + { match_path, WORD_PATH }, + { NULL, 0} + }; + int i; + laststart = lastend = lasttype = 0; - if (do_an_re (word, &laststart, &lastend, &lasttype)) - { - switch (lasttype) + + for (i = 0; m[i].match; i++) + if (m[i].match (word, &laststart, &lastend)) { - char *str; - - case WORD_NICK: - if (strchrs (word[laststart], NICKPRE)) - laststart++; - str = g_strndup (&word[laststart], lastend - laststart); - if (!userlist_find (current_sess, str)) - lasttype = 0; - g_free (str); - return lasttype; - case WORD_EMAIL: - if (!isalnum (word[laststart])) - laststart++; - /* Fall through */ - case WORD_URL: - case WORD_HOST: - case WORD_HOST6: - case WORD_CHANNEL: - case WORD_PATH: - return lasttype; - default: - return 0; /* Should not occur */ + lasttype = m[i].type; + return lasttype; } + + return 0; +} + +static gboolean +match_nick (const char *word, int *start, int *end) +{ + const server *serv = current_sess->server; + const char *nick_prefixes = serv ? serv->nick_prefixes : NICKPRE; + char *str; + + if (!regex_match (re_nick (), word, start, end)) + return FALSE; + + /* ignore matches with prefixes that the server doesn't use */ + if (strchr (NICKPRE, word[*start]) + && !strchr (nick_prefixes, word[*start])) + return FALSE; + + /* nick prefix is not part of the matched word */ + if (strchr (nick_prefixes, word[*start])) + (*start)++; + + str = g_strndup (&word[*start], *end - *start); + + if (!userlist_find (current_sess, str)) + { + g_free (str); + return FALSE; } - else - return 0; + + g_free (str); + + return TRUE; +} + +static gboolean +match_channel (const char *word, int *start, int *end) +{ + const server *serv = current_sess->server; + const char *chan_prefixes = serv ? serv->chantypes : CHANPRE; + + if (!regex_match (re_channel (), word, start, end)) + return FALSE; + + return strchr (chan_prefixes, word[*start]) != NULL; +} + +static gboolean +match_email (const char *word, int *start, int *end) +{ + return regex_match (re_email (), word, start, end); +} + +static gboolean +match_url (const char *word, int *start, int *end) +{ + return regex_match (re_url (), word, start, end); +} + +static gboolean +match_host (const char *word, int *start, int *end) +{ + return regex_match (re_host (), word, start, end); +} + +static gboolean +match_host6 (const char *word, int *start, int *end) +{ + return regex_match (re_host6 (), word, start, end); +} + +static gboolean +match_path (const char *word, int *start, int *end) +{ + return regex_match (re_path (), word, start, end); } /* List of IRC commands for which contents (and thus possible URLs) @@ -307,46 +375,28 @@ url_last (int *lstart, int *lend) return lasttype; } -static int -do_an_re(const char *word, int *start, int *end, int *type) +static gboolean +regex_match (const GRegex *re, const char *word, int *start, int *end) { - typedef struct func_s { - GRegex *(*fn)(void); - int type; - } func_t; - func_t funcs[] = - { - { re_url, WORD_URL }, - { re_email, WORD_EMAIL }, - { re_channel, WORD_CHANNEL }, - { re_host6, WORD_HOST6 }, - { re_host, WORD_HOST }, - { re_path, WORD_PATH }, - { re_nick, WORD_NICK } - }; - GMatchInfo *gmi; - int k; - for (k = 0; k < sizeof funcs / sizeof (func_t); k++) + g_regex_match (re, word, 0, &gmi); + + if (!g_match_info_matches (gmi)) { - g_regex_match (funcs[k].fn(), word, 0, &gmi); - if (!g_match_info_matches (gmi)) - { - g_match_info_free (gmi); - continue; - } - while (g_match_info_matches (gmi)) - { - g_match_info_fetch_pos (gmi, 0, start, end); - g_match_info_next (gmi, NULL); - } g_match_info_free (gmi); - *type = funcs[k].type; - return TRUE; + return FALSE; } - - return FALSE; + + while (g_match_info_matches (gmi)) + { + g_match_info_fetch_pos (gmi, 0, start, end); + g_match_info_next (gmi, NULL); + } + + g_match_info_free (gmi); + + return TRUE; } /* Miscellaneous description --- */ @@ -376,7 +426,7 @@ make_re (char *grist) /* HOST description --- */ /* (see miscellaneous above) */ -static GRegex * +static const GRegex * re_host (void) { static GRegex *host_ret; @@ -384,7 +434,7 @@ re_host (void) if (host_ret) return host_ret; - grist = g_strdup_printf ( + grist = g_strdup ( "(" "(" HOST_URL PORT ")|(" HOST ")" ")" @@ -393,7 +443,7 @@ re_host (void) return host_ret; } -static GRegex * +static const GRegex * re_host6 (void) { static GRegex *host6_ret; @@ -401,7 +451,7 @@ re_host6 (void) if (host6_ret) return host6_ret; - grist = g_strdup_printf ( + grist = g_strdup ( "(" "(" IPV6ADDR ")|(" "\\[" IPV6ADDR "\\]" PORT ")" ")" @@ -489,7 +539,7 @@ struct { NULL, "", 0} }; -static GRegex * +static const GRegex * re_url (void) { static GRegex *url_ret = NULL; @@ -546,7 +596,7 @@ re_url (void) /* EMAIL description --- */ #define EMAIL "[a-z][-_a-z0-9]+@" "(" HOST_URL ")" -static GRegex * +static const GRegex * re_email (void) { static GRegex *email_ret; @@ -554,7 +604,7 @@ re_email (void) if (email_ret) return email_ret; - grist = g_strdup_printf ( + grist = g_strdup ( "(" EMAIL ")" @@ -577,12 +627,12 @@ re_email (void) /* Rationale is that do_an_re() above will anyway look up what */ /* we find, and that WORD_NICK is the last item in the array */ /* that do_an_re() runs through. */ -#define NICK0 "[" NICKPRE "]?[" NICKLET NICKDIG NICKSPE "]" +#define NICK0 "^[" NICKPRE "]?[" NICKLET NICKDIG NICKSPE "]" #endif #define NICK1 "[" NICKHYP NICKLET NICKDIG NICKSPE "]*" #define NICK NICK0 NICK1 -static GRegex * +static const GRegex * re_nick (void) { static GRegex *nick_ret; @@ -590,7 +640,7 @@ re_nick (void) if (nick_ret) return nick_ret; - grist = g_strdup_printf ( + grist = g_strdup ( "(" NICK ")" @@ -600,9 +650,9 @@ re_nick (void) } /* CHANNEL description --- */ -#define CHANNEL "#[^ \t\a,:]+" +#define CHANNEL "[" CHANPRE "][^ \t\a,]+(?:,[" CHANPRE "][^ \t\a,]+)*" -static GRegex * +static const GRegex * re_channel (void) { static GRegex *channel_ret; @@ -610,7 +660,7 @@ re_channel (void) if (channel_ret) return channel_ret; - grist = g_strdup_printf ( + grist = g_strdup ( "(" CHANNEL ")" @@ -628,7 +678,7 @@ re_channel (void) #define FS_PATH "^(/|\\./|\\.\\./).*" #endif -static GRegex * +static const GRegex * re_path (void) { static GRegex *path_ret; @@ -636,7 +686,7 @@ re_path (void) if (path_ret) return path_ret; - grist = g_strdup_printf ( + grist = g_strdup ( "(" FS_PATH ")" diff --git a/src/common/util.c b/src/common/util.c index 52464621..9771b1f6 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -58,6 +58,17 @@ #include <socks.h> #endif +/* SASL mechanisms */ +#ifdef USE_OPENSSL +#include <openssl/bn.h> +#include <openssl/rand.h> +#include <openssl/blowfish.h> +#include <openssl/aes.h> +#ifndef WIN32 +#include <netinet/in.h> +#endif +#endif + #ifndef HAVE_SNPRINTF #define snprintf g_snprintf #endif @@ -1929,7 +1940,7 @@ get_subdirs (const char *path) } char * -encode_sasl_pass (char *user, char *pass) +encode_sasl_pass_plain (char *user, char *pass) { int authlen; char *buffer; @@ -1944,6 +1955,230 @@ encode_sasl_pass (char *user, char *pass) return encoded; } +#ifdef USE_OPENSSL +/* Adapted from ZNC's SASL module */ + +static int +parse_dh (char *str, DH **dh_out, unsigned char **secret_out, int *keysize_out) +{ + DH *dh; + guchar *data, *decoded_data; + guchar *secret; + gsize data_len; + guint size; + guint16 size16; + BIGNUM *pubkey; + gint key_size; + + dh = DH_new(); + data = decoded_data = g_base64_decode (str, &data_len); + if (data_len < 2) + goto fail; + + /* prime number */ + memcpy (&size16, data, sizeof(size16)); + size = ntohs (size16); + data += 2; + data_len -= 2; + + if (size > data_len) + goto fail; + + dh->p = BN_bin2bn (data, size, NULL); + data += size; + + /* Generator */ + if (data_len < 2) + goto fail; + + memcpy (&size16, data, sizeof(size16)); + size = ntohs (size16); + data += 2; + data_len -= 2; + + if (size > data_len) + goto fail; + + dh->g = BN_bin2bn (data, size, NULL); + data += size; + + /* pub key */ + if (data_len < 2) + goto fail; + + memcpy (&size16, data, sizeof(size16)); + size = ntohs(size16); + data += 2; + data_len -= 2; + + pubkey = BN_bin2bn (data, size, NULL); + if (!(DH_generate_key (dh))) + goto fail; + + secret = (unsigned char*)malloc (DH_size(dh)); + key_size = DH_compute_key (secret, pubkey, dh); + if (key_size == -1) + goto fail; + + g_free (decoded_data); + + *dh_out = dh; + *secret_out = secret; + *keysize_out = key_size; + return 1; + +fail: + if (decoded_data) + g_free (decoded_data); + return 0; +} + +char * +encode_sasl_pass_blowfish (char *user, char *pass, char *data) +{ + DH *dh; + char *response, *ret; + unsigned char *secret; + unsigned char *encrypted_pass; + char *plain_pass; + BF_KEY key; + int key_size, length; + int pass_len = strlen (pass) + (8 - (strlen (pass) % 8)); + int user_len = strlen (user); + guint16 size16; + char *in_ptr, *out_ptr; + + if (!parse_dh (data, &dh, &secret, &key_size)) + return NULL; + BF_set_key (&key, key_size, secret); + + encrypted_pass = (guchar*)malloc (pass_len); + memset (encrypted_pass, 0, pass_len); + plain_pass = (char*)malloc (pass_len); + memset (plain_pass, 0, pass_len); + memcpy (plain_pass, pass, pass_len); + out_ptr = (char*)encrypted_pass; + in_ptr = (char*)plain_pass; + + for (length = pass_len; length; length -= 8, in_ptr += 8, out_ptr += 8) + BF_ecb_encrypt ((unsigned char*)in_ptr, (unsigned char*)out_ptr, &key, BF_ENCRYPT); + + /* Create response */ + length = 2 + BN_num_bytes (dh->pub_key) + pass_len + user_len + 1; + response = (char*)malloc (length); + out_ptr = response; + + /* our key */ + size16 = htons ((guint16)BN_num_bytes (dh->pub_key)); + memcpy (out_ptr, &size16, sizeof(size16)); + out_ptr += 2; + BN_bn2bin (dh->pub_key, (guchar*)out_ptr); + out_ptr += BN_num_bytes (dh->pub_key); + + /* username */ + memcpy (out_ptr, user, user_len + 1); + out_ptr += user_len + 1; + + /* pass */ + memcpy (out_ptr, encrypted_pass, pass_len); + + ret = g_base64_encode ((const guchar*)response, length); + + DH_free (dh); + free (plain_pass); + free (encrypted_pass); + free (secret); + free (response); + + return ret; +} + +char * +encode_sasl_pass_aes (char *user, char *pass, char *data) +{ + DH *dh; + AES_KEY key; + char *response = NULL; + char *out_ptr, *ret = NULL; + unsigned char *secret, *ptr; + unsigned char *encrypted_userpass, *plain_userpass; + int key_size, length; + guint16 size16; + unsigned char iv[16], iv_copy[16]; + int user_len = strlen (user) + 1; + int pass_len = strlen (pass) + 1; + int len = user_len + pass_len; + int padlen = 16 - (len % 16); + int userpass_len = len + padlen; + + if (!parse_dh (data, &dh, &secret, &key_size)) + return NULL; + + encrypted_userpass = (guchar*)malloc (userpass_len); + memset (encrypted_userpass, 0, userpass_len); + plain_userpass = (guchar*)malloc (userpass_len); + memset (plain_userpass, 0, userpass_len); + + /* create message */ + /* format of: <username>\0<password>\0<padding> */ + ptr = plain_userpass; + memcpy (ptr, user, user_len); + ptr += user_len; + memcpy (ptr, pass, pass_len); + ptr += pass_len; + if (padlen) + { + /* Padding */ + unsigned char randbytes[16]; + if (!RAND_bytes (randbytes, padlen)) + goto end; + + memcpy (ptr, randbytes, padlen); + } + + if (!RAND_bytes (iv, sizeof (iv))) + goto end; + + memcpy (iv_copy, iv, sizeof(iv)); + + /* Encrypt */ + AES_set_encrypt_key (secret, key_size * 8, &key); + AES_cbc_encrypt(plain_userpass, encrypted_userpass, userpass_len, &key, iv_copy, AES_ENCRYPT); + + /* Create response */ + /* format of: <size pubkey><pubkey><iv (always 16 bytes)><ciphertext> */ + length = 2 + key_size + sizeof(iv) + userpass_len; + response = (char*)malloc (length); + out_ptr = response; + + /* our key */ + size16 = htons ((guint16)key_size); + memcpy (out_ptr, &size16, sizeof(size16)); + out_ptr += 2; + BN_bn2bin (dh->pub_key, (guchar*)out_ptr); + out_ptr += key_size; + + /* iv */ + memcpy (out_ptr, iv, sizeof(iv)); + out_ptr += sizeof(iv); + + /* userpass */ + memcpy (out_ptr, encrypted_userpass, userpass_len); + + ret = g_base64_encode ((const guchar*)response, length); + +end: + DH_free (dh); + free (plain_userpass); + free (encrypted_userpass); + free (secret); + if (response) + free (response); + + return ret; +} +#endif + #ifdef WIN32 int find_font (const char *fontname) @@ -1975,6 +2210,7 @@ find_font (const char *fontname) } #endif +#ifdef USE_OPENSSL static char * str_sha256hash (char *string) { @@ -2050,3 +2286,4 @@ challengeauth_response (char *username, char *password, char *challenge) return (char *) digest; } +#endif diff --git a/src/common/util.h b/src/common/util.h index 0ebd89d4..6b8d359c 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -79,7 +79,9 @@ void canonalize_key (char *key); int portable_mode (); int unity_mode (); GSList *get_subdirs (const char *path); -char *encode_sasl_pass (char *user, char *pass); +char *encode_sasl_pass_plain (char *user, char *pass); +char *encode_sasl_pass_blowfish (char *user, char *pass, char *data); +char *encode_sasl_pass_aes (char *user, char *pass, char *data); char *challengeauth_response (char *username, char *password, char *challenge); #endif |