📄 imapserverc.c
字号:
// Fix line ending if necessary// if ( terminate ) { if ( linebuf.EndsWith('\n') ) linebuf.CutEnd(1); linebuf += "\r\n"; }#ifdef HAVE_OPENSSL if ( TLSEnabled ) { void *buf = (char *) linebuf; int nbytes = SSL_write(ssl, buf, linebuf.length()); if (nbytes <= 0) { // FIXME: proper check // err = SSL_get_error(ssl, nbytes); if ( Query("Connection dropped. Retry?", *halApp, False) == QUERY_YES ) { ReConnect(); } return False; } else { return True; } }#endif fd_set writeSet; FD_ZERO(&writeSet); struct timeval timeout; while ( linebuf.length() > 0 ) {//// See if the socket is available// int rc; while (True) { FD_SET(sock, &writeSet); timeout.tv_sec = 5; timeout.tv_usec = 0; rc = select(sock+1, NULL, &writeSet, NULL, &timeout); // // a socket is available for writing // if ( rc > 0 ) break; // // error occurred // else if ( rc < 0) { if ( errno == EINTR ) { if (debuglev > 0) cout << "Select got SIGINTR, retrying" << endl; } else if ( errno == EBADF ) { ReConnect(); } else { int err = errno; errmsg = "Could not select socket "; errmsg += sock; errmsg += " for writing to the IMAP server "; errmsg += name; errmsg += ".\n" + SystemErrorMessage(err); halApp->PopupMessage(errmsg); return False; } } // // timeout // else if ( rc == 0 ) { errmsg = "Timeout trying to select the IMAP server\n"; errmsg += "\n\nRetry?"; if ( !RetryLogin(errmsg) ) { CloseSocket(); return False; } if ( !(connected = ReConnect()) ) { return False; } } } int writeCount; while ( True ) { writeCount = write(sock, (const char*) linebuf, linebuf.length()); if ( writeCount <= 0 ) { if ( errno == EPIPE) { errmsg = "Lost connection to the IMAP server\n"; errmsg += "\n\nTry to re-connect ?"; if ( !RetryLogin(errmsg) ) { CloseSocket(); return False; } if ( !(connected = ReConnect()) ) { return False; } } else { int err = errno; errmsg = "Could not write socket "; errmsg += sock; errmsg += " for IMAP server "; errmsg += name; errmsg += ".\n" + SystemErrorMessage(err); halApp->PopupMessage(errmsg); return False; } } else { break; // write succeeded } } linebuf.CutBeg(writeCount); } // End while all bytes not written return True;} // End PutLine/*------------------------------------------------------------------------ * Method to generate a unique tag */voidImapServerC::GenTag(StringC& tag){ tag = "ISH"; tag += tagCount++; tag += " ";}/*------------------------------------------------------------------------ * Method to display an error message about an unexpected reply */voidImapServerC::Unexpected(CharC str){ StringC errmsg = "Unexpected reply from IMAP server "; errmsg += name; errmsg += ":\n\""; errmsg += str; errmsg += "\"\n";#if 0 halApp->PopupMessage(errmsg);#endif // Rather than hang the program waiting for a user response, // just splat these messages to standard error. cerr << errmsg;}/*------------------------------------------------------------------------ * Function to run a command and wait for the response */BooleanImapServerC::RunCommand(CharC cmd, StringListC& output){ StringC tag; GenTag(tag); StringC cmdline(tag); cmdline += cmd; if ( !PutLine(cmdline) ) return False; if ( !GetLine(cmdline) ) return False; while ( !cmdline.StartsWith(tag) ) { output.add(cmdline); if ( !GetLine(cmdline) ) return False; } cmdline.CutBeg(tag.size()); output.add(cmdline); return True;} // End RunCommand/*------------------------------------------------------------------------ * Function to run a command and let the supplied hook function process * the response, including dealing with continuation requests from the * server. Any data can be passed to the callback function, if needed */ImapCommandReturnImapServerC::RunCommand2(CharC cmd, RunCommandHook hook, void *cdata){ Boolean ok = True; ImapCommandReturn ret; StringListC info, data; StringC tag; GenTag(tag); StringC cmdline(tag); cmdline += cmd; if ( !PutLine(cmdline) ) { return ImapCommandNoConnect; } while ( True ) { StringC response; ImapResponse type; if ( !GetLine(response) ) { return ImapCommandNoConnect; } if ( response.StartsWith(tag) ) { response.CutBeg(tag.size()); if ( response.StartsWith("OK", IGNORE_CASE) ) { type = ImapResponseOK; ret = ImapCommandOK; response.CutBeg(2); } else if (response.StartsWith("NO", IGNORE_CASE) ) { type = ImapResponseNO; ret = ImapCommandNO; response.CutBeg(2); } else if (response.StartsWith("BAD", IGNORE_CASE) ) { type = ImapResponseBAD; ret = ImapCommandBAD; response.CutBeg(3); } else { // Unknown response return ImapCommandFailed; } if ( hook && !hook(type, response, info, data, &cmdline, cdata) ) { ok = False; } if ( ok ) { return ret; } else { return ImapCommandFailed; } } else if ( response.StartsWith("* ") ) { response.CutBeg(2); info.add(response); // TODO: check for BYE here } else if ( response.StartsWith("+ ") ) { type = ImapResponseCont; response.CutBeg(2); if ( !hook || !hook(type, response, info, data, &cmdline, cdata) ) { ok = False; } if ( !PutLine(cmdline) ) { return ImapCommandNoConnect; } } else { data.add(response); } } // Should never reach this point return ImapCommandFailed;} // End RunCommand/*------------------------------------------------------------------------ * Function to send NOOP command and wait for response. */BooleanImapServerC::Noop(StringListC& output){ return RunCommand("NOOP", output);}// Anonymous loginstatic BooleanauthenticationAnonymousHook( ImapResponse type, CharC resp, StringListC info, StringListC data, StringC *reply, void *cdata){ if ( type == ImapResponseCont ) { // Ignore the server's challenge; it may be an empty string AuthData *ad = (AuthData *) cdata; reply->Clear(); // Respond with the password (which is the user's email, probably) if ( !TextToText64(ad->pass, reply, False, False) ) { return False; } return True; } else { // No special action on the command completion return True; }}// Base64-encoded plain text login; should be used as the last resortstatic BooleanauthenticationLoginHook( ImapResponse type, CharC resp, StringListC info, StringListC data, StringC *reply, void *cdata){ if ( type == ImapResponseCont ) { StringC buf; // Decode the response; it should be either "User Name" or "Password" if ( !Text64ToText(resp, &buf) ) { return False; } else { AuthData *ad = (AuthData *) cdata; reply->Clear(); // NB: below, StartsWith() is used instead of Equals() because the // base64 strings are explicitly 0-terminated if ( buf.StartsWith("User Name", IGNORE_CASE) ) { // Respond with the username if ( !TextToText64(ad->user, reply, False, False) ) { return False; } } else if ( buf.StartsWith("Password", IGNORE_CASE) ) { // Respond with the password if ( !TextToText64(ad->pass, reply, False, False) ) { return False; } } else { // Unexpected response return False; } return True; } } else { // No special action on the command completion return True; }}#ifdef HAVE_OPENSSL// Print data in hexadecimalsstatic char *hexpt(unsigned char *md){ int i; static char buf[2*MD5_DIGEST_LENGTH + 1]; for (i = 0; i < MD5_DIGEST_LENGTH; i++) { sprintf(&(buf[2*i]), "%02x", md[i]); } buf[2*MD5_DIGEST_LENGTH] = '\0'; return(buf);}/*------------------------------------------------------------------------ * CRAM-MD5 authentication scheme (RFC 2195) * Based on the keyed-hashing MD5 digest algorithm (aka HMAC) (RFC 2104) * All the hard work is done by the OpenSSL crypto library */static BooleanauthenticationHmacHook( ImapResponse type, CharC resp, StringListC info, StringListC data, StringC *reply, void *cdata){ if ( type == ImapResponseCont ) { StringC buf; // Decode the response; its a "challenge" if ( !Text64ToText(resp, &buf) ) { return False; } else { AuthData *ad = (AuthData *) cdata; reply->Clear(); // Produce the digest & print it in hexadecimals const char *d = buf; const char *p; p = hexpt(HMAC(EVP_md5(), ad->pass.Addr(), ad->pass.Length(), (const unsigned char *) d, buf.length(), NULL, NULL)); // Prepend username buf = ad->user; buf += " "; buf += p; // Base64 encode everything if ( !TextToText64(buf, reply, False, False) ) { return False; } return True; } } else { // No special action on the command completion return True; }}#endif/*------------------------------------------------------------------------ * Function to send LOGIN (for IMAP2) or AUTHENTICATE (IMAP4) command and * process response. */ImapCommandReturnImapServerC::Authenticate(CharC user, CharC pass){ ImapCommandReturn res; StringC cmd; RunCommandHook hook; Boolean secure; AuthData ad; ad.user = user; ad.pass = pass; if ( (authMethods & IMAP_AUTH_ANONYMOUS) && user.Equals("anonymous", IGNORE_CASE) ) { // Anonymous login; not really secure, but who cares? ;-) secure = True; cmd = "AUTHENTICATE ANONYMOUS"; hook = authenticationAnonymousHook; } else#ifdef HAVE_OPENSSL if ( authMethods & IMAP_AUTH_CRAM_MD5 ) { // MD5 digest secure = True; cmd = "AUTHENTICATE CRAM-MD5"; hook = authenticationHmacHook; } else#endif if ( authMethods & IMAP_AUTH_LOGIN ) { // using base64-encoded username & password secure = False; cmd = "AUTHENTICATE LOGIN"; hook = authenticationLoginHook; } else if ( authMethods & IMAP_AUTH_CLEAR_TEXT ) { // IMAP2 clear text login secure = False; cmd = "LOGIN "; cmd += user; cmd += " \""; cmd += pass; cmd += "\""; hook = NULL; } else { // should never happen return ImapCommandFailed; } // If the whole session is encrypted, don't worry about secure authentication if ( TLSEnabled ) { secure = True; } if ( !secure ) { // If the authentication method is insecure, ask user // whether (s)he agrees to use a clear text login StringC str; str = "No (supported) secure authentication mechanisms available."; str += "\n\nDo you want to use a clear text login?"; if ( Query(str, *halApp, False) != QUERY_YES ) { return ImapCommandCancelled; } } return RunCommand2(cmd, hook, (void *) &ad);}/*------------------------------------------------------------------------ * Function to send LOGOUT command and wait for response. */BooleanImapServerC::Logout(StringListC& output){ if ( !connected ) { return True; } else { return RunCommand("LOGOUT", output); }}/*------------------------------------------------------------------------ * Function to send CREATE command and wait for response. */BooleanImapServerC::Create(CharC mailbox, StringListC& output){ StringC cmd = "CREATE "; cmd += mailbox; return RunCommand(cmd, output);}/*------------------------------------------------------------------------ * Function to send DELETE command and wait for response. */BooleanImapServerC::Delete(CharC mailbox, StringListC& output){ // If the folder is currently open, close it first if ( folder == mailbox ) { StringListC output; if ( !Close(output) ) return False; } StringC cmd = "DELETE "; cmd += mailbox; return RunCommand(cmd, output);}/*------------------------------------------------------------------------ * Function to send RENAME command and wait for response. */BooleanImapServerC::Rename(CharC oldname, CharC newname, StringListC& output)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -