// tear.cpp : implements the TEAR console application // // This is a part of the Microsoft Foundation Classes C++ library. // Copyright (C) 1997-1998 Microsoft Corporation // All rights reserved. // // This source code is only intended as a supplement to the // Microsoft Foundation Classes Reference and related // electronic documentation provided with the library. // See these sources for detailed information regarding the // Microsoft Foundation Classes product. #include #include #include #include "tear.h" #include #include ///////////////////////////////////////////////////////////////////////////// // Globals LPCTSTR pszURL = NULL; BOOL bStripMode = FALSE; BOOL bProgressMode = FALSE; DWORD dwAccessType = PRE_CONFIG_INTERNET_ACCESS; DWORD dwHttpRequestFlags = INTERNET_FLAG_EXISTING_CONNECT | INTERNET_FLAG_NO_AUTO_REDIRECT; const TCHAR szHeaders[] = _T("Accept: text/*\r\nUser-Agent: MFC_Tear_Sample\r\n"); ///////////////////////////////////////////////////////////////////////////// // CTearSession object // TEAR wants to use its own derivative of the CInternetSession class // just so it can implement an OnStatusCallback() override. CTearSession::CTearSession(LPCTSTR pszAppName, int nMethod) : CInternetSession(pszAppName, 1, nMethod) { } void CTearSession::OnStatusCallback(DWORD /* dwContext */, DWORD dwInternetStatus, LPVOID /* lpvStatusInfomration */, DWORD /* dwStatusInformationLen */) { if (!bProgressMode) return; if (dwInternetStatus == INTERNET_STATUS_CONNECTED_TO_SERVER) cerr << _T("Connection made!") << endl; } ///////////////////////////////////////////////////////////////////////////// // CTearException -- used if something goes wrong for us // TEAR will throw its own exception type to handle problems it might // encounter while fulfilling the user's request. IMPLEMENT_DYNCREATE(CTearException, CException) CTearException::CTearException(int nCode) : m_nErrorCode(nCode) { } void ThrowTearException(int nCode) { CTearException* pEx = new CTearException(nCode); throw pEx; } ///////////////////////////////////////////////////////////////////////////// // Routines void ShowBanner() { cerr << _T("TEAR - Tear a Page Off the Internet!") << endl; cerr << _T("Version 4.2 - Copyright (C) Microsoft Corp 1998") << endl; cerr << endl; } void ShowUsage() { cerr << _T("Usage: TEAR [options] ") << endl << endl; cerr << _T("\t points at a HTTP resource") << endl; cerr << _T("\t[options] are any of:") << endl; cerr << _T("\t\t/F force reload of requested page") << endl; cerr << _T("\t\t/P show detailed progress information") << endl; cerr << _T("\t\t/S strip HTML tags from stream") << endl << endl; cerr << _T("\t\t/L use local Internet access") << endl; cerr << _T("\t\t/D use pre-configured Internet access (default)") << endl; cerr << endl; exit(1); } // ParseOptions() looks for options on the command line and sets global // flags so the rest of the program knows about them. ParseOptions() // also initializes pszURL to point at the URL the user wanted. BOOL ParseOptions(int argc, char* argv[]) { int nIndex; for (nIndex = 1; nIndex < argc; nIndex++) { // an option or a URL? if (*argv[nIndex] == '-' || *argv[nIndex] == '/') { if (argv[nIndex][1] == 'D' || argv[nIndex][1] == 'd') dwAccessType = PRE_CONFIG_INTERNET_ACCESS; else if (argv[nIndex][1] == 'L' || argv[nIndex][1] == 'l') dwAccessType = LOCAL_INTERNET_ACCESS; else if (argv[nIndex][1] == 'S' || argv[nIndex][1] == 's') bStripMode = TRUE; else if (argv[nIndex][1] == 'P' || argv[nIndex][1] == 'p') bProgressMode = TRUE; else if (argv[nIndex][1] == 'F' || argv[nIndex][1] == 'f') dwHttpRequestFlags |= INTERNET_FLAG_RELOAD; else { cerr << _T("Error: unrecognized option: ") << argv[nIndex] << endl; return FALSE; } } else { // can't have too many URLs if (pszURL != NULL) { cerr << _T("Error: can only specify one URL!") << endl; return FALSE; } else pszURL = argv[nIndex]; } } return TRUE; } // StripTags() rips through a buffer and removes HTML tags from it. // The function uses a static variable to remember its state in case // a HTML tag spans a buffer boundary. void StripTags(LPTSTR pszBuffer) { static BOOL bInTag = FALSE; LPTSTR pszSource = pszBuffer; LPTSTR pszDest = pszBuffer; while (*pszSource != '\0') { if (bInTag) { if (*pszSource == '>') bInTag = FALSE; pszSource++; } else { if (*pszSource == '<') bInTag = TRUE; else { *pszDest = *pszSource; pszDest++; } pszSource++; } } *pszDest = '\0'; } ///////////////////////////////////////////////////////////////////////////// // The main() Thang int main(int argc, char* argv[]) { ShowBanner(); if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) { cerr << _T("MFC Failed to initialize.\n"); return 1; } if (argc < 2 || !ParseOptions(argc, argv) || pszURL == NULL) ShowUsage(); int nRetCode = 0; CTearSession session(_T("TEAR - MFC Sample App"), dwAccessType); CHttpConnection* pServer = NULL; CHttpFile* pFile = NULL; try { // check to see if this is a reasonable URL CString strServerName; CString strObject; INTERNET_PORT nPort; DWORD dwServiceType; if (!AfxParseURL(pszURL, dwServiceType, strServerName, strObject, nPort) || dwServiceType != INTERNET_SERVICE_HTTP) { cerr << _T("Error: can only use URLs beginning with http://") << endl; ThrowTearException(1); } if (bProgressMode) { cerr << _T("Opening Internet..."); VERIFY(session.EnableStatusCallback(TRUE)); } pServer = session.GetHttpConnection(strServerName, nPort); pFile = pServer->OpenRequest(CHttpConnection::HTTP_VERB_GET, strObject, NULL, 1, NULL, NULL, dwHttpRequestFlags); pFile->AddRequestHeaders(szHeaders); pFile->SendRequest(); DWORD dwRet; pFile->QueryInfoStatusCode(dwRet); // if access was denied, prompt the user for the password if (dwRet == HTTP_STATUS_DENIED) { DWORD dwPrompt; dwPrompt = pFile->ErrorDlg(NULL, ERROR_INTERNET_INCORRECT_PASSWORD, FLAGS_ERROR_UI_FLAGS_GENERATE_DATA | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS, NULL); // if the user cancelled the dialog, bail out if (dwPrompt != ERROR_INTERNET_FORCE_RETRY) { cerr << _T("Access denied: Invalid password\n"); ThrowTearException(1); } pFile->SendRequest(); pFile->QueryInfoStatusCode(dwRet); } CString strNewLocation; pFile->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF, strNewLocation); // were we redirected? // these response status codes come from WININET.H if (dwRet == HTTP_STATUS_MOVED || dwRet == HTTP_STATUS_REDIRECT || dwRet == HTTP_STATUS_REDIRECT_METHOD) { CString strNewLocation; pFile->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF, strNewLocation); int nPlace = strNewLocation.Find(_T("Location: ")); if (nPlace == -1) { cerr << _T("Error: Site redirects with no new location") << endl; ThrowTearException(2); } strNewLocation = strNewLocation.Mid(nPlace + 10); nPlace = strNewLocation.Find('\n'); if (nPlace > 0) strNewLocation = strNewLocation.Left(nPlace); // close up the redirected site pFile->Close(); delete pFile; pServer->Close(); delete pServer; if (bProgressMode) { cerr << _T("Caution: redirected to "); cerr << (LPCTSTR) strNewLocation << endl; } // figure out what the old place was if (!AfxParseURL(strNewLocation, dwServiceType, strServerName, strObject, nPort)) { cerr << _T("Error: the redirected URL could not be parsed.") << endl; ThrowTearException(2); } if (dwServiceType != INTERNET_SERVICE_HTTP) { cerr << _T("Error: the redirected URL does not reference a HTTP resource.") << endl; ThrowTearException(2); } // try again at the new location pServer = session.GetHttpConnection(strServerName, nPort); pFile = pServer->OpenRequest(CHttpConnection::HTTP_VERB_GET, strObject, NULL, 1, NULL, NULL, dwHttpRequestFlags); pFile->AddRequestHeaders(szHeaders); pFile->SendRequest(); pFile->QueryInfoStatusCode(dwRet); if (dwRet != HTTP_STATUS_OK) { cerr << _T("Error: Got status code ") << dwRet << endl; ThrowTearException(2); } } cerr << _T("Status Code is ") << dwRet << endl; TCHAR sz[1024]; while (pFile->ReadString(sz, 1023)) { if (bStripMode) StripTags(sz); cout << sz; } // NOTE: Since HTTP servers normally spit back plain text, the // above code (which reads line by line) is just fine. However, // other data sources (eg, FTP servers) might provide binary data // which should be handled a buffer at a time, like this: #if 0 while (nRead > 0) { sz[nRead] = '\0'; if (bStripMode) StripTags(sz); cout << sz; nRead = pFile->Read(sz, 1023); } #endif pFile->Close(); pServer->Close(); } catch (CInternetException* pEx) { // catch errors from WinINet TCHAR szErr[1024]; pEx->GetErrorMessage(szErr, 1024); cerr << _T("Error: (") << pEx->m_dwError << _T(") "); cerr << szErr << endl; nRetCode = 2; pEx->Delete(); } catch (CTearException* pEx) { // catch things wrong with parameters, etc nRetCode = pEx->m_nErrorCode; TRACE1("Error: Exiting with CTearException(%d)\n", nRetCode); pEx->Delete(); } if (pFile != NULL) delete pFile; if (pServer != NULL) delete pServer; session.Close(); return nRetCode; }