📄 imapserverc.c
字号:
/* * $Id: ImapServerC.C,v 1.38 2001/07/22 18:07:30 evgeny Exp $ * * Copyright (c) 1994 HAL Computer Systems International, Ltd. * * HAL COMPUTER SYSTEMS INTERNATIONAL, LTD. * 1315 Dell Avenue * Campbell, CA 95008 * * Author: Greg Hilton * Contributors: Tom Lang, Frank Bieser, and others * * 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; either version 2 * of the License, or (at your option) any later version. * * 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. * * http://www.gnu.org/copyleft/gpl.html * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#include <config.h>#include "IshAppC.h"#include "MainWinC.h"#include "Misc.h"#include "FolderPrefC.h"#include "ImapServerC.h"#include "ImapFolderC.h"#include "MsgStatus.h"#include "LoginWinC.h"#include "Query.h"#include "Base64.h"#include <hgl/HalAppC.h>#include <hgl/StringC.h>#include <hgl/SysErr.h>#include <hgl/PtrListC.h>#include <hgl/StringListC.h>#include <hgl/CharC.h>#include <hgl/WArgList.h>#include <hgl/WXmString.h>#include <hgl/IntListC.h>#include <Xm/MessageB.h>#include <Xm/Protocols.h>#include <Xm/AtomMgr.h>#include <X11/Intrinsic.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#ifdef TIME_WITH_SYS_TIME# include <sys/time.h># include <time.h>#else# ifdef HAVE_SYS_TIME_H# include <sys/time.h># else# include <time.h># endif#endif#include <unistd.h>#include <errno.h>#include <string.h>#ifdef HAVE_SYS_SELECT_H# include <sys/select.h>#endif#include <netdb.h>#ifdef HAVE_OPENSSL // A hack to find out OPENSSLDIR# define HEADER_CRYPTLIB_H# include <openssl/opensslconf.h># undef HEADER_CRYPTLIB_H# ifndef OPENSSLDIR# define OPENSSLDIR "/usr/local/ssl"# endif# include <openssl/hmac.h>#endifextern int debuglev;static PtrListC *serverList = NULL;#ifdef HAVE_OPENSSLstatic Boolean sslInited = False;#endif/*------------------------------------------------------------------------ * Constructor to open a connection to an imap server */ImapServerC::ImapServerC(const char *hostname, const char *username, long portnum, Boolean imaps_flag){ sock = 0; connected = False; TLSEnabled = False; authenticated = False; bugs = 0L; // let's assume the implementation is perfect haveStartTLS = False; dataPending = False; // Should be no data pending at this point//// save hostname, port # etc in server object// name = hostname; port = portnum; if ( username ) { user = username; } else { user = ishApp->userId; }//// Init buffers// lineList = new StringListC; lineList->AllowDuplicates(TRUE); lastBuf[0] = 0; #ifndef HAVE_OPENSSL if ( imaps_flag ) { char *errmsg = "Support for secure connections is not compiled in"; halApp->PopupMessage(errmsg); return; }#else imaps = imaps_flag;#endif if ( !EstablishSession() ) return;//// Add this server to the list// if ( !serverList ) serverList = new PtrListC; void *tmp = (void*)this; serverList->add(tmp);} // End constructor/*------------------------------------------------------------------------ * Destructor */ImapServerC::~ImapServerC(){ if ( connected ) { StringListC output; Logout(output); } delete lineList;//// Remove this server from the list// if ( serverList ) { void *tmp = (void*)this; serverList->remove(tmp); } } // End destructor/*------------------------------------------------------------------------ * Method to establish an authenticated session with the IMAP server * - this is called from the constructor, and also to re-connect if * the connection drops for some reason. */BooleanImapServerC::EstablishSession(){//// Connect to the server// connected = Connect(); if ( !connected ) return False;//// Determine capabilities of the server// if ( !Capability() ) { CloseSocket(); return False; } #ifdef HAVE_OPENSSL if ( !imaps && haveStartTLS && !(bugs & IMAP_BUG_STARTTLS_BROKEN) ) { // We MUST re-check capabilities once switched to TLS if ( StartTLS() && Capability() ) { ; // OK } else { StringC errmsg = "Can't start TLS; falling back to unencrypted session"; halApp->PopupMessage(errmsg); // Re-try connecting, this time without TLS return EstablishSession(); } }#endif//// Log in if needed// authenticated = DoLogin(); if ( !authenticated ) { CloseSocket(); return False; } return True;}/*------------------------------------------------------------------------ * Method to connect a socket to the named IMAP server */BooleanImapServerC::Connect(){ StringC errmsg; authenticated = False; dataPending = False; // Should be no data pending at this point CloseSocket();//// Get host name// host = gethostbyname(name); if ( !host ) { int err = errno; errmsg = "Could not find entry for host: "; errmsg += name; errmsg += ".\n" + SystemErrorMessage(err); halApp->PopupMessage(errmsg); return False; }//// Create socket// sock = socket(AF_INET, SOCK_STREAM, 0); if ( sock <= 0 ) { int err = errno; errmsg = "Could not create socket.\n"; errmsg += SystemErrorMessage(err); halApp->PopupMessage(errmsg); return False; }//// Open connection// struct sockaddr_in sin; memset((char*)&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = (u_short)htons(port); memcpy((char*)&sin.sin_addr, host->h_addr, host->h_length); if ( connect(sock, (struct sockaddr*)&sin, sizeof(sin)) < 0 ) { int err = errno; errmsg = "Could not connect to host: "; errmsg += name; errmsg += ".\n" + SystemErrorMessage(err); halApp->PopupMessage(errmsg); CloseSocket(); return False; } // Start right in the TLS mode if told so if ( imaps ) { if ( !EnableTLS() ) { CloseSocket(); return False; } }//// Wait for the server's response to see if the user must log in// StringC line; if ( !GetLine(line) ) { CloseSocket(); return False; } if ( line.StartsWith("* OK ") ) { // OK; will need to log in later on return True; } else if ( line.StartsWith("* BYE") ) { // Not accepted line.CutBeg(5); line.Trim(); errmsg = "The IMAP server on host "; errmsg += name; errmsg += "\nwould not accept a connection:\n\n"; errmsg += line; halApp->PopupMessage(errmsg); CloseSocket(); return False; } else if ( line.StartsWith("* PREAUTH") ) { // Connection has already been authenticated by external means authenticated = True; return True; } else { Unexpected(line); CloseSocket(); return False; } return True;}/*------------------------------------------------------------------------ * Method to close socket (if it has been opened) */voidImapServerC::CloseSocket(){ if ( sock > 0 ) { close(sock); } if ( TLSEnabled ) { DisableTLS(); } sock = 0; connected = False; authenticated = False;}/*------------------------------------------------------------------------ * Method to start TLS session */BooleanImapServerC::StartTLS(){#ifdef HAVE_OPENSSL if ( !haveStartTLS || TLSEnabled ) { return False; } StringListC output; if ( !RunCommand("STARTTLS", output) ) return False; return EnableTLS(TLS_VERSION_TLSV1);#else return False;#endif} #ifdef HAVE_OPENSSLstatic int verify_cert(int ok, X509_STORE_CTX *ctx){ X509 *serverCert; char *certSubjectName, *certIssuerName; // Get server's certificate serverCert = X509_STORE_CTX_get_current_cert(ctx); if ( !serverCert ) { halApp->PopupMessage("No server certificate presented!", XmDIALOG_ERROR); return False; } certSubjectName = X509_NAME_oneline(X509_get_subject_name(serverCert), 0, 0); certIssuerName = X509_NAME_oneline(X509_get_issuer_name(serverCert), 0, 0); if ( debuglev ) { cout << "Server certificate:" << "\n\t subject: " << certSubjectName << "\n\t issuer: " << certIssuerName << endl; } if ( !ok ) { StringC str = "Could not verify the following certificate:"; str += "\n Subject: "; str += certSubjectName; str += "\n Issued by: "; str += certIssuerName; str.Replace("/", "\n "); str += "\n\nReason: "; str += X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx)); str += ".\n\nAccept the certificate?"; if ( Query(str, *halApp, False) == QUERY_YES ) { ok = True; // Certificate path (TODO: configurable) StringC certFile = ishApp->home + "/.ishmail.pem"; FILE *fp = fopen(certFile, "wa"); if (fp) { PEM_write_X509(fp, serverCert); } } } return ok;}#endif/*------------------------------------------------------------------------ * Method to enable TLS negotiation supporting protocols of vers */BooleanImapServerC::EnableTLS(unsigned long vers){#ifdef HAVE_OPENSSL if ( !sock || !(haveStartTLS || imaps) || TLSEnabled ) { return False; } // Initialization sslCtx = NULL; ssl = NULL; int err; SSL_METHOD *meth; if ( !sslInited ) { if ( debuglev ) { cout << "Initializing OpenSSL" << endl; } // For error messages SSL_load_error_strings(); // Setup all the global SSL stuff SSL_library_init(); sslInited = True; } Boolean sslv2_ok = vers & TLS_VERSION_SSLV2; Boolean sslv3_ok = vers & TLS_VERSION_SSLV3; Boolean tlsv1_ok = vers & TLS_VERSION_TLSV1; if ( sslv2_ok && !sslv3_ok && !tlsv1_ok ) { meth = SSLv2_client_method(); } else if ( !sslv2_ok && sslv3_ok && tlsv1_ok ) { meth = SSLv3_client_method(); } else if ( !sslv2_ok && !sslv3_ok && tlsv1_ok ) { meth = TLSv1_client_method(); } else { meth = SSLv23_client_method(); } sslCtx = SSL_CTX_new(meth); if ( !sslCtx ) { return False; } // set up CAs to look up // Certificate path (TODO: configurable) StringC certFile = ishApp->home + "/.ishmail.pem"; StringC certPath = OPENSSLDIR; certPath += "/certs"; if (!SSL_CTX_load_verify_locations(sslCtx, certFile, certPath)) { SSL_CTX_set_default_verify_paths(sslCtx); } SSL_CTX_set_verify(sslCtx, SSL_VERIFY_PEER, verify_cert); SSL_CTX_set_verify_depth(sslCtx, 1); // Start SSL negotiation ssl = SSL_new(sslCtx); if ( !ssl ) { DisableTLS(); return False; } SSL_set_connect_state(ssl); SSL_set_fd(ssl, sock); int res = SSL_connect(ssl); if ( res <= 0 ) { err = SSL_get_error(ssl, res); if ( err == SSL_ERROR_SYSCALL ) { halApp->PopupMessage(SystemErrorMessage(errno)); } // Broken STARTTLS implementation? bugs |= IMAP_BUG_STARTTLS_BROKEN; DisableTLS(); return False; } // Get the cipher if ( debuglev ) { cout << "SSL connection using " << SSL_get_cipher(ssl) << endl; } TLSEnabled = True; return True;#else return False;#endif}/*------------------------------------------------------------------------ * Method to end TLS negotiation and free respective structures */BooleanImapServerC::DisableTLS(){#ifdef HAVE_OPENSSL if ( !haveStartTLS ) { return False; } if ( ssl ) { SSL_shutdown(ssl); SSL_free(ssl); } if ( sslCtx ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -