📄 mail.c
字号:
static gintread_select(gint fd, gchar *buf, size_t size, time_t timeout) { fd_set readfds; struct timeval tv; gint n = 0; gint s; do { FD_ZERO(&readfds); FD_SET(fd, &readfds); tv.tv_sec = timeout; tv.tv_usec = 0; if ((s = select(fd+1, &readfds, NULL, NULL, &tv)) > 0)#if defined(WIN32) n = recv(fd, buf, size, 0);#else n = read(fd, buf, size);#endif } while (s < 0 && errno == EINTR); return n; } /* Read \r\n terminated lines from a remote IMAP or POP3 mail server, */static voidtcp_getline(ConnInfo *conn, Mailbox *mbox) { gchar buf[256]; gint n; gchar *s; if (mbox->tcp_in) mbox->tcp_in = g_string_truncate(mbox->tcp_in, 0); else mbox->tcp_in = g_string_new(""); s = buf;; for (;;) {#ifdef HAVE_SSL if (conn->ssl) n = SSL_read(conn->ssl, s, 1); else#endif n = read_select(conn->fd, s, 1, TCP_TIMEOUT); if (n <= 0) break; *(s+1) = '\0'; if (*s++ == '\n') break; if (s >= buf + sizeof(buf) - 2) { g_string_append(mbox->tcp_in, buf); s = buf; } } if (s > buf) g_string_append(mbox->tcp_in, buf); if (_GK.debug_level & DEBUG_MAIL) { if (n < 0) perror("tcp_getline: "); format_remote_mbox_name(mbox, buf, sizeof(buf)); printf("server_response( %s )<%d>:%s\n", buf, (gint) mbox->tcp_in->len, mbox->tcp_in->str); } }static voidtcp_putline(ConnInfo *conn, gchar *line) {#ifdef HAVE_SSL if (conn->ssl) SSL_write(conn->ssl, line, strlen(line)); else#endif {#if defined(WIN32) send(conn->fd, line, strlen(line), 0);#else write(conn->fd, line, strlen (line));#endif } } /* Get a server response line and verify the beginning of the line | matches a string. */static gbooleanserver_response(ConnInfo *conn, Mailbox *mbox, gchar *match) { tcp_getline(conn, mbox); return (!strncmp(match, mbox->tcp_in->str, strlen(match)) ? TRUE : FALSE); } /* Get a imap server completion result response for a tagged command. | Skip over any untagged responses the server may send. */static gbooleanimap_completion_result(ConnInfo *conn, Mailbox *mbox, gchar *tag) { while (1) { tcp_getline(conn, mbox); if (*(mbox->tcp_in->str) == '*') /* untagged response */ continue; return (!strncmp(tag, mbox->tcp_in->str, strlen(tag)) ? TRUE : FALSE); } }static voidserver_command(ConnInfo *conn, Mailbox *mbox, gchar *line) { gchar buf[128]; tcp_putline(conn, line); if (_GK.debug_level & DEBUG_MAIL) { format_remote_mbox_name(mbox, buf, sizeof(buf)); printf("server_command( %s ):%s", buf, line); } }static gchar *tcp_error_message[] = { N_("Unable to connect."), N_("Bad response after connect."), N_("Bad response after username."), N_("Bad response after password."), N_("Bad response after STAT or STATUS."), N_("Bad response after UIDL."), N_("Bad APOP response after connect."), N_("Bad CRAM_MD5 response after connect."), };static voidtcp_close(ConnInfo *conn) {#ifdef HAVE_SSL#ifndef HAVE_GNUTLS SSL_SESSION *session;#endif#endif if (conn->fd != -1) {#ifdef WIN32 closesocket(conn->fd);#else close(conn->fd);#endif conn->fd = -1; }#ifdef HAVE_SSL if (conn->ssl) {#ifndef HAVE_GNUTLS session = SSL_get_session(conn->ssl); if (session) SSL_CTX_remove_session(conn->ssl_ctx, session);#endif SSL_free(conn->ssl); conn->ssl = NULL; } if (conn->ssl_ctx) { SSL_CTX_free(conn->ssl_ctx); conn->ssl_ctx = NULL; }#endif}static gbooleantcp_warn(Mailbox *mbox, gchar *message, gboolean warn) { gchar buf[128]; if (_GK.debug_level & DEBUG_MAIL) { format_remote_mbox_name(mbox, buf, sizeof(buf)); g_print(_("Mail TCP Error: %s - %s\n"), buf, _(message)); } if (warn && !mbox->warned) { g_free(mbox->warn_msg); format_remote_mbox_name(mbox, buf, sizeof(buf)); mbox->warn_msg = g_strdup_printf("%s\n%s\n%s\n", buf, _(message), mbox->tcp_in->str); } return FALSE; }static gbooleantcp_shutdown(ConnInfo *conn, Mailbox *mbox, gchar *message, gboolean warn) { tcp_close(conn); return tcp_warn(mbox, message, warn); }#ifdef HAVE_SSLstatic gbooleanssl_negotiate(ConnInfo *conn, Mailbox *mbox) { SSL_METHOD *ssl_method; SSLeay_add_ssl_algorithms(); SSL_load_error_strings(); if (mbox->account->use_ssl == SSL_TRANSPORT) ssl_method = SSLv23_client_method(); else ssl_method = TLSv1_client_method(); if (ssl_method == NULL) return tcp_shutdown(conn, mbox, N_("Cannot initialize SSL method."), FALSE); if ((conn->ssl_ctx = SSL_CTX_new(ssl_method)) == NULL) return tcp_shutdown(conn, mbox, N_("Cannot initialize SSL server certificate handler."), FALSE); SSL_CTX_set_options(conn->ssl_ctx, SSL_OP_ALL); SSL_CTX_set_verify(conn->ssl_ctx, SSL_VERIFY_NONE, NULL); if ((conn->ssl = SSL_new(conn->ssl_ctx)) == NULL) return tcp_shutdown(conn, mbox, N_("Cannot initialize SSL handler."), FALSE);#ifndef HAVE_GNUTLS SSL_clear(conn->ssl);#endif SSL_set_fd(conn->ssl, conn->fd); SSL_set_connect_state(conn->ssl); if (SSL_connect(conn->ssl) < 0) return tcp_shutdown(conn, mbox, tcp_error_message[0], FALSE); return TRUE; }#endifstatic gbooleantcp_connect(ConnInfo *conn, Mailbox *mbox) { MailAccount *account = mbox->account; gchar buf[128]; memset(conn, 0, sizeof(*conn)); if (_GK.debug_level & DEBUG_MAIL) { format_remote_mbox_name(mbox, buf, sizeof(buf)); printf("tcp_connect: connecting to %s\n", buf); } conn->fd = gkrellm_connect_to(account->server, account->port); if (conn->fd < 0) return tcp_warn(mbox, tcp_error_message[0], FALSE);#ifdef HAVE_SSL if (account->use_ssl == SSL_TRANSPORT && !ssl_negotiate(conn, mbox)) return FALSE;#endif return TRUE; }extern void to64frombits(unsigned char *, const unsigned char *, int);extern int from64tobits(char *, const char *, int);static voidhmac_md5(unsigned char *password, size_t pass_len, unsigned char *challenge, size_t chal_len, unsigned char *response, size_t resp_len) { int i; unsigned char ipad[64]; unsigned char opad[64]; unsigned char hash_passwd[16]; MD5_CTX ctx; if (resp_len != 16) return; if (pass_len > sizeof(ipad)) { MD5Init(&ctx); MD5Update(&ctx, password, pass_len); MD5Final(hash_passwd, &ctx); password = hash_passwd; pass_len = sizeof(hash_passwd); } memset(ipad, 0, sizeof(ipad)); memset(opad, 0, sizeof(opad)); memcpy(ipad, password, pass_len); memcpy(opad, password, pass_len); for (i = 0; i < 64; i++) { ipad[i] ^= 0x36; opad[i] ^= 0x5c; } MD5Init(&ctx); MD5Update(&ctx, ipad, sizeof(ipad)); MD5Update(&ctx, challenge, chal_len); MD5Final(response, &ctx); MD5Init(&ctx); MD5Update(&ctx, opad, sizeof(opad)); MD5Update(&ctx, response, resp_len); MD5Final(response, &ctx); }/* authenticate as per RFC2195 */static intdo_cram_md5(ConnInfo *conn, char *command, Mailbox *mbox, char *strip) { MailAccount *account = mbox->account; gint len; gchar buf1[1024]; gchar msg_id[768]; gchar reply[1024]; gchar *respdata; guchar response[16]; snprintf(buf1, sizeof(buf1), "%s CRAM-MD5\r\n", command); server_command(conn, mbox, buf1); /* From RFC2195: * The data encoded in the first ready response contains an * presumptively arbitrary string of random digits, a * timestamp, and the * fully-qualified primary host name of * the server. The syntax of the * unencoded form must * correspond to that of an RFC 822 'msg-id' * [RFC822] as * described in [POP3]. */ if (!server_response(conn, mbox, "+ ")) return FALSE; /* caller may specify a response prefix we should strip if present */ respdata = mbox->tcp_in->str; len = strlen(respdata); if (respdata[len - 1] == '\n') respdata[--len] = '\0'; if (respdata[len - 1] == '\r') respdata[--len] = '\0'; if (strip && strncmp(respdata, strip, strlen(strip)) == 0) respdata += strlen(strip); len = from64tobits(msg_id, respdata, sizeof(msg_id)); if (len < 0) { if (_GK.debug_level & DEBUG_MAIL) g_print(_("could not decode BASE64 challenge\n")); return FALSE; } else if (len < sizeof(msg_id)) msg_id[len] = 0; else msg_id[sizeof(msg_id) - 1] = 0; if (_GK.debug_level & DEBUG_MAIL) g_print(_("decoded as %s\n"), msg_id); /* The client makes note of the data and then responds with a string * consisting of the user name, a space, and a 'digest'. The latter is * computed by applying the keyed MD5 algorithm from [KEYED-MD5] where * the key is a shared secret and the digested text is the timestamp * (including angle-brackets). */ hmac_md5((guchar *) account->password, strlen(account->password), (guchar *) msg_id, strlen(msg_id), response, sizeof(response)); snprintf(reply, sizeof(reply), "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", account->username, response[0], response[1], response[2], response[3], response[4], response[5], response[6], response[7], response[8], response[9], response[10], response[11], response[12], response[13], response[14], response[15]); to64frombits((guchar *) buf1, (guchar *) reply, strlen(reply)); len = strlen(buf1); if (len + 3 > sizeof(buf1)) return FALSE; strcpy(buf1 + len, "\r\n"); server_command(conn, mbox, buf1); return TRUE; }/* NTLM authentication */static intdo_ntlm(ConnInfo *conn, char *command, Mailbox *mbox) { gint len; gchar msgbuf[2048]; tSmbNtlmAuthRequest request; tSmbNtlmAuthChallenge challenge; tSmbNtlmAuthResponse response; snprintf(msgbuf, sizeof(msgbuf), "%s NTLM\r\n", command); server_command(conn, mbox, msgbuf); if (!server_response(conn, mbox, "+ ")) return FALSE; buildSmbNtlmAuthRequest(&request, mbox->account->username, NULL); if (_GK.debug_level & DEBUG_MAIL) dumpSmbNtlmAuthRequest(stdout, &request); memset(msgbuf, 0, sizeof(msgbuf)); to64frombits((guchar *) msgbuf, (guchar *) &request, SmbLength(&request)); len = strlen(msgbuf); if (len + 3 > sizeof(msgbuf)) return FALSE; strcpy(msgbuf + len, "\r\n"); server_command(conn, mbox, msgbuf); if (!server_response(conn, mbox, "+ ")) return FALSE; len = from64tobits((char *)&challenge, mbox->tcp_in->str, sizeof(challenge)); if (len < 0) { if (_GK.debug_level & DEBUG_MAIL) g_print(_("could not decode BASE64 challenge\n")); return FALSE; } if (_GK.debug_level & DEBUG_MAIL) dumpSmbNtlmAuthChallenge(stdout, &challenge); buildSmbNtlmAuthResponse(&challenge, &response, mbox->account->username, mbox->account->password); if (_GK.debug_level & DEBUG_MAIL) dumpSmbNtlmAuthResponse(stdout, &response); memset(msgbuf, 0, sizeof msgbuf); to64frombits((guchar *)msgbuf, (guchar *) &response, SmbLength(&response)); len = strlen(msgbuf); if (len + 3 > sizeof(msgbuf)) return FALSE; strcpy(msgbuf + len, "\r\n"); server_command(conn, mbox, msgbuf); return TRUE; }static gbooleancheck_pop3(Mailbox *mbox) { MailAccount *account = mbox->account; ConnInfo conn; gchar line[128], buf[128]; gchar *challenge = NULL; if (!tcp_connect(&conn, mbox)) return FALSE; /* Is the machine we are connected to really a POP3 server? */ if (! server_response(&conn, mbox, "+OK")) return tcp_shutdown(&conn, mbox, tcp_error_message[1], FALSE); if (account->authmech == AUTH_APOP && (strlen(mbox->tcp_in->str) < 3 || (challenge = g_strdup(mbox->tcp_in->str + 3)) == NULL)) return tcp_shutdown(&conn, mbox, tcp_error_message[1], FALSE);#ifdef HAVE_SSL if (account->use_ssl == SSL_STARTTLS) { server_command(&conn, mbox, "STLS\r\n"); if (!server_response(&conn, mbox, "+OK")) { if (challenge) g_free(challenge); return tcp_shutdown(&conn, mbox, N_("Bad response after STLS."), FALSE); } if (!ssl_negotiate(&conn, mbox)) { if (challenge) g_free(challenge); return FALSE; } }#endif if (account->authmech == AUTH_APOP) { static const gchar hex[] = "0123456789abcdef"; MD5_CTX ctx; gint i; gchar *key, *p; guchar digest[16]; gchar ascii_digest[33]; if ((key = strchr(challenge, '<')) == NULL) { g_free(challenge); return tcp_shutdown(&conn, mbox, tcp_error_message[6], TRUE); } if ((p = strchr(key, '>')) == NULL) { g_free(challenge); return tcp_shutdown(&conn, mbox, tcp_error_message[6],
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -