/*------------------------------------------------------------------------------*\ FetchOwnFQDN() - fetches hostname and domainname from network settings and build FQDN from that. \*------------------------------------------------------------------------------*/ void BmRoster::FetchOwnFQDN() { BmString buffer; Regexx rx; #ifdef BEAM_FOR_BONE FetchFile( "/etc/hostname", mOwnFQDN); mOwnFQDN.RemoveSet( " \n\r\t"); if (!mOwnFQDN.Length()) mOwnFQDN = "bepc"; FetchFile( "/etc/resolv.conf", buffer); if (rx.exec( buffer, "DOMAIN\\s*(\\S*)", Regexx::nocase) && rx.match[0].atom[0].Length()) mOwnFQDN << "." << rx.match[0].atom[0]; else mOwnFQDN << "." << time( NULL) << ".fake"; #else BPath path; if (find_directory( B_COMMON_SETTINGS_DIRECTORY, &path) == B_OK) { FetchFile( BmString(path.Path())<<"/network", buffer); if (rx.exec( buffer, "HOSTNAME\\s*=[ \\t]*(\\S*)", Regexx::nocase)) { mOwnFQDN = rx.match[0].atom[0]; if (!mOwnFQDN.Length()) mOwnFQDN = "bepc"; if (rx.exec( buffer, "DNS_DOMAIN\\s*=[ \\t]*(\\S*)", Regexx::nocase) && rx.match[0].atom[0].Length()) mOwnFQDN << "." << rx.match[0].atom[0]; else mOwnFQDN << "." << time( NULL) << ".fake"; } } #endif if (!mOwnFQDN.Length()) mOwnFQDN << "bepc." << time( NULL) << ".fake"; mOwnFQDN.RemoveSet( "\r\n"); }
/*------------------------------------------------------------------------------*\ StateConnect() - Initiates network-connection to POP-server \*------------------------------------------------------------------------------*/ void BmPopper::StateConnect() { BNetAddress addr; if (addr.SetTo( mPopAccount->Server().String(), mPopAccount->PortNr()) != B_OK) { BmString s = BmString("Could not determine address of POP-Server ") << mPopAccount->Server(); throw BM_network_error( s); } if (!Connect( &addr)) { BmString s = BmString("Could not connect to POP-Server ") << mPopAccount->Server() << "\n\bError:\n\t" << mErrorString; throw BM_network_error( s); } BmString encryptionType = mPopAccount->EncryptionType(); if (TheNetEndpointRoster->SupportsEncryption() && (encryptionType.ICompare(BmPopAccount::ENCR_TLS) == 0 || encryptionType.ICompare(BmPopAccount::ENCR_SSL) == 0)) { // straight TLS or SSL, we start the encryption layer: if (!StartEncryption(encryptionType.String())) return; } // accept server greeting (either encrypted or unencrypted): CheckForPositiveAnswer(); Regexx rx; if (rx.exec( StatusText(), "(<.+?>)\\s*$", Regexx::newline)) { mServerTimestamp = rx.match[0]; } }
std::vector<std::string> regexx::splitex(const std::string& _regex, const std::string& _str) { std::vector<std::string> v; Regexx rxx; rxx.expr(_regex); rxx.str(_str); v.reserve(rxx.exec()); std::vector<RegexxMatch>::const_iterator i; std::string::size_type lastpos = 0; for(i = rxx.match.begin(); i != rxx.match.end(); i++) { v.push_back(_str.substr(lastpos,i->start()-lastpos)); lastpos = i->start()+i->length(); } v.push_back(_str.substr(lastpos,i->start())); return v; }
/*------------------------------------------------------------------------------*\ Mail( mail) - announces the given mail to the server \*------------------------------------------------------------------------------*/ void BmSmtp::Mail( BmMail* mail) { BmString sender = mail->Header()->DetermineSender(); Regexx rx; if (!rx.exec( sender, "@\\w+")) { // no domain part within sender-address, we add our current domain: if (sender.FindFirst("@") == B_ERROR) sender << "@"; BmString fqdn = mSmtpAccount->DomainToAnnounce(); sender << OwnDomain( fqdn); } BmString cmd = BmString("MAIL from:<") << sender <<">"; if (mServerMayHaveSizeLimit) { int32 mailSize = mail->RawText().Length(); cmd << " SIZE=" << mailSize; } SendCommand( cmd); CheckForPositiveAnswer(); }
/*------------------------------------------------------------------------------*\ StateHelo() - Sends greeting to server and checks result - EHLO is tried first, aiming to find out more about server capabilities; if that fails, HELO is used - if EHLO succeeds, info about the server-capabilities are extracted from the answer \*------------------------------------------------------------------------------*/ void BmSmtp::StateHelo() { BmString domain = mSmtpAccount->DomainToAnnounce(); if (!domain.Length()) domain = BeamRoster->OwnFQDN(); BmString cmd = BmString("EHLO ") << domain; SendCommand( cmd); try { CheckForPositiveAnswer(); Regexx rx; if (rx.exec( StatusText(), "^\\d\\d\\d.SIZE\\b", Regexx::newline)) { mServerMayHaveSizeLimit = true; } if (rx.exec( StatusText(), "^\\d\\d\\d.DSN\\b", Regexx::newline)) { mServerSupportsDSN = true; } if (rx.exec( StatusText(), "^\\d\\d\\d.AUTH\\s+(.*?)$", Regexx::newline )) { mSupportedAuthTypes = rx.match[0].atom[0]; } else if (rx.exec( StatusText(), "^\\d\\d\\d.AUTH\\s*=\\s*(.*?)$", Regexx::newline )) { mSupportedAuthTypes = rx.match[0].atom[0]; } if (rx.exec( StatusText(), "^\\d\\d\\d.STARTTLS\\b", Regexx::newline)) { mServerSupportsTLS = true; } } catch(...) { cmd = BmString("HELO ") << domain; SendCommand( cmd); CheckForPositiveAnswer(); } }
/*------------------------------------------------------------------------------*\ StateCapa() - asks server for its capabilities \*------------------------------------------------------------------------------*/ void BmPopper::StateCapa() { BmString cmd("CAPA"); SendCommand( cmd); try { CheckForPositiveAnswer( 16384, true); // we expect a multiline string as answer Regexx rx; if (rx.exec( mAnswerText, "^\\s*SASL\\s+(.*?)$", Regexx::newline )) { mSupportedAuthTypes = rx.match[0].atom[0]; } else if (rx.exec( mAnswerText, "^\\s*AUTH\\s+(.*?)$", Regexx::newline )) { mSupportedAuthTypes = rx.match[0].atom[0]; } if (rx.exec( mAnswerText, "^\\s*STLS\\b", Regexx::newline)) mServerSupportsTLS = true; else mServerSupportsTLS = false; } catch(...) { } }
int main() { Regexx rxx; try { // 1 - Replacing strings with atom substitution: std::cout << "1: "; std::cout << rxx.replace("I love expensive software!","expensive (soft[a-z]*)", "free %0"); std::cout << std::endl; // 2 - Atom retrieving: std::cout << "2: "; rxx.exec("http://distro.conectiva.com/projetos/32/","http://([-1-9a-zA-Z\\.]*)/"); if(rxx.match.size() > 0 && rxx.match[0].atom.size() > 0) std::cout << "Regexx's host is " << rxx.match[0].atom[0] << "." << std::endl; else std::cout << "Oops, no hosts found!" << std::endl; // 3 - Patern matching: std::cout << "3: "; if(rxx.exec("*****@*****.**",".+@gnu\\.org$")) std::cout << "Yeah! It's from gnu.org!" << std::endl; else std::cout << "No, it's not from gnu.org." << std::endl; // 4 - Counting occurrences: std::cout << "4: "; std::cout << "There are " << rxx.exec("There are n 'a's in this phrase.", "a",Regexx::global|Regexx::nomatch) << " 'a's in this phrase." << std::endl; // 5 - Removing HTML tags: std::cout << "5: "; std::cout << rxx.replace("<B>Please, <STRIKE>no</STRIKE> tags.</B>", "</?[a-z]+( [^>]*)*>", "", Regexx::global|Regexx::nocase|Regexx::study); std::cout << std::endl; // 6 - Customizing replaces std::cout << "6: "; std::cout << rxx.replacef("Turn your free software into expensive software.", "free|expensive", invert, Regexx::global); std::cout << std::endl; // 7 - One-line regular expressions using constructors std::cout << "7: "; if(Regexx("Using constructor!","constructor")) std::cout << "I've found the 'constructor' word!" << std::endl; else std::cout << "I haven't found the 'constructor' word!" << std::endl; } catch(Regexx::CompileException &e) { std::cerr << e.message() << std::endl; } return 0; }
/*------------------------------------------------------------------------------*\ StateCheck() - looks for new mail \*------------------------------------------------------------------------------*/ void BmPopper::StateCheck() { uint32 msgNum = 0; BmString cmd("STAT"); SendCommand( cmd); if (!CheckForPositiveAnswer()) return; Regexx rx; if (!rx.exec( StatusText(), "^\\+OK\\s+(\\d+)", Regexx::nocase)) throw BM_network_error( "answer to STAT has unknown format"); BmString numStr = rx.match[0].atom[0]; mMsgCount = atoi(numStr.String()); if (mMsgCount == 0) { UpdateMailStatus( 0, NULL, 0); // we remove all local UIDs, since none are listed on the server: BmString removedUids = mPopAccount->AdjustToCurrentServerUids( mMsgUIDs); BM_LOG2( BM_LogRecv, removedUids); return; // no messages found, nothing more to do } // we try to fetch a list of unique message IDs from server: cmd = BmString("UIDL"); SendCommand( cmd); try { // The UIDL-command may not be implemented by this server, so we // do not require a positive answer, we just hope for it: if (!CheckForPositiveAnswer( 16384, true)) return; // interrupted, we give up // ok, we've got the UIDL-listing, so we fetch it, // fetch UIDLs one per line and store them in array: int numLines = rx.exec( mAnswerText, "\\s*(\\d+)\\s+(.+?)\\s*$", Regexx::newline | Regexx::global); if (numLines < mMsgCount) throw BM_network_error( BmString("answer to UIDL has unknown format" ", too few lines matching")); for( int32 i=0; i<mMsgCount; ++i) mMsgUIDs.push_back( rx.match[i].atom[1]); // we remove local UIDs that are not listed on the server anymore: BmString removedUids = mPopAccount->AdjustToCurrentServerUids( mMsgUIDs); BM_LOG( BM_LogRecv, removedUids); } catch( BM_network_error& err) { // no UIDL-listing from server, we will have to get by without... } // compute total size of messages that are new to us: mNewMsgTotalSize = 0; mNewMsgCount = 0; mCleanupMsgs.clear(); cmd = "LIST"; SendCommand( cmd); if (!CheckForPositiveAnswer( 16384, true)) return; vector<BmString> listAnswerVect; split( "\r\n", mAnswerText, listAnswerVect); uint32 count = mMsgCount; if (count != listAnswerVect.size()) { BM_LOG( BM_LogRecv, BmString("Strange: server indicated ")<< mMsgCount << " mails, but LIST received " << listAnswerVect.size() << " lines!"); if (count > listAnswerVect.size()) count = listAnswerVect.size(); } for( uint32 i=0; i<count; i++) { int32 msgSize; if (!mPopAccount->IsUIDDownloaded( mMsgUIDs[i])) { // msg is new (according to unknown UID) // fetch msgsize for message... Regexx rx; if (!rx.exec( listAnswerVect[i], "^\\s*(\\d+)\\s+(\\d+)\\s*$")) throw BM_network_error( BmString("answer to LIST has unknown format, msg ") << i+1 ); BmString msgNumStr = rx.match[0].atom[0]; msgNum = atoi(msgNumStr.String()); if (msgNum != i+1) throw BM_network_error( BmString("answer to LIST has unexpected msg-nr. ") << msgNum << " in line " << i+1 ); BmString msgSizeStr = rx.match[0].atom[1]; msgSize = atoi(msgSizeStr.String()); // add msg-size to total: mNewMsgTotalSize += msgSize; mNewMsgSizes.push_back( msgSize); mNewMsgCount++; } else { // msg is old (according to known UID), we may have to remove it now: BmString log; bool shouldBeRemoved = mPopAccount->ShouldUIDBeDeletedFromServer(mMsgUIDs[i], log); BM_LOG2( BM_LogRecv, log); if (shouldBeRemoved) { // store msg-index-number for cleanup state mCleanupMsgs.push_back(i + 1); } } } if (mNewMsgCount == 0) UpdateMailStatus( 0, NULL, 0); }
/*------------------------------------------------------------------------------*\ StateSendMails() - sends the queued mails to the server (invokes MAIL, RCPT and DATA-commands) - depending on the handling of Bcc-recipients, each mail is only sent to the server once (SpecialHeaderForEachBcc=false) or each mail is being sent once for the standard recipients (To, Cc) plus a personalized version for each Bcc-recipient (SpecialHeaderForEachBcc=true) \*------------------------------------------------------------------------------*/ void BmSmtp::StateSendMails() { mMailCount = mQueuedRefVect.size(); mMsgTotalSize = 0; vector<BmRef<BmMailRef> > mailRefs; BmRef<BmMail> mail; for( int32 i=0; i<mMailCount; ++i) { mailRefs.push_back(BmMailRef::CreateInstance( mQueuedRefVect[i])); if (mailRefs[i] && mailRefs[i]->IsValid()) mMsgTotalSize += int32(mailRefs[i]->Size()); } Regexx rx; mCurrMailNr = 1; for( int32 i=0; i<mMailCount; ++i, ++mCurrMailNr) { if (!mailRefs[i] || !mailRefs[i]->IsValid()) { BM_LOGERR( BmString("SendMails(): mail no. ") << i+1 << " can't be found, skipping it."); continue; } BmRef<BmMail> mail = BmMail::CreateInstance( mailRefs[i].Get()); if (mail) { if (mail->InitCheck() != B_OK) mail->StartJobInThisThread( BmMail::BM_READ_MAIL_JOB); if (mail->InitCheck() != B_OK) { BM_LOGERR( BmString("SendMails(): mail no. ") << i+1 << " can't be read, skipping it."); continue; } } mCurrMailSize = mail->RawText().Length(); BmString headerText = mail->HeaderText(); if (!mail->Header()->IsFieldEmpty(BM_FIELD_RESENT_BCC)) { // remove RESENT-BCC-header from mailtext... headerText = rx.replace( headerText, "^Resent-Bcc:\\s*.+?\\r\\n(\\s+.*?\\r\\n)*", "", Regexx::newline ); } if (!mail->Header()->IsFieldEmpty(BM_FIELD_BCC)) { // remove BCC-header from mailtext... headerText = rx.replace( headerText, "^Bcc:\\s*.+?\\r\\n(\\s+.*?\\r\\n)*", "", Regexx::newline ); } try { BmRcptSet rcptSet; if (ThePrefs->GetBool("SpecialHeaderForEachBcc")) { if (HasStdRcpts( mail.Get(), rcptSet)) { Mail( mail.Get()); Rcpt( rcptSet); Data( mail.Get(), headerText); } BccRcpt( mail.Get(), true, headerText); } else { Mail( mail.Get()); if (HasStdRcpts( mail.Get(), rcptSet)) Rcpt( rcptSet); BccRcpt( mail.Get(), false, headerText); Data( mail.Get(), headerText); } if (ShouldContinue()) { mail->MarkAs( BM_MAIL_STATUS_SENT); mail->ApplyOutboundFilters(); // give filters a chance that check for 'Sent'-status... } } catch( BM_runtime_error &err) { // a problem occurred, we tell the user: BM_LOGERR( BmString("SendMails(): mail no. ") << i+1 << " couldn't be sent.\n\nError:\n" << err.what()); mail->MarkAs( BM_MAIL_STATUS_ERROR); // mark mail as ERROR since it couldn't be sent SendCommand("RSET"); // reset SMTP-state in order to start afresh with next mail } } mCurrMailSize = 0; mCurrMailNr = 0; }