<commit_before>/**
 * Appcelerator Titanium - licensed under the Apache Public License 2
 * see LICENSE in the root folder for details on the license.
 * Copyright (c) 2009 Appcelerator, Inc. All Rights Reserved.
 */

#include <windows.h>
#include <objbase.h>
#include <Wininet.h>
#include <vector>
#include <iostream>
#include <fstream>
#include <io.h>
#include <stdlib.h>
#include <stdio.h>
#include "Progress.h"
#include "Resource.h"

#define alert(msg) MessageBoxA(GetDesktopWindow(), msg, "Message", MB_OK);
#define walert(msg) MessageBoxW(GetDesktopWindow(), msg, L"Message", MB_OK);

#define DISTRIBUTION_UUID	L"7F7FA377-E695-4280-9F1F-96126F3D2C2A"
#define RUNTIME_UUID		L"A2AC5CB5-8C52-456C-9525-601A5B0725DA"
#define MODULE_UUID			L"1ACE5D3A-2B52-43FB-A136-007BD166CFD0"

std::wstring ParseQueryParam(std::wstring uri, std::wstring name)
{
	std::wstring key = name;
	key+=L"=";
	size_t pos = uri.find(key);
	if (pos!=std::wstring::npos)
	{
		std::wstring p = uri.substr(pos + key.length());
		pos = p.find(L"&");
		if (pos!=std::wstring::npos)
		{
			p = p.substr(0,pos);
		}

		// decode
		WCHAR szOut[INTERNET_MAX_URL_LENGTH];
		DWORD cchDecodedUrl = INTERNET_MAX_URL_LENGTH;
		CoInternetParseUrl(p.c_str(), PARSE_UNESCAPE, 0, szOut, INTERNET_MAX_URL_LENGTH, &cchDecodedUrl, 0);
		p.assign(szOut);

		return p;
	}
	return L"";
}

std::wstring ProgressString(DWORD size, DWORD total)
{
	char str[1024];

#define KB 1024
#define MB KB * 1024

	if (total < KB) {
		sprintf(str, "%0.2f of %0.2f bytes", size, total);
	}

	else if (size < MB) {
		double totalKB = total/1024.0;
		double sizeKB = size/1024.0;
		sprintf(str, "%0.2f of %0.2f KB", sizeKB, totalKB);
	}

	else {
		// hopefully we shouldn't ever need to count more than 1023 in a single file!
		double totalMB = total/1024.0/1024.0;
		double sizeMB = size/1024.0/1024.0;
		sprintf(str, "%0.2f of %0.2f MB", size, total);
	}

	wchar_t wstr[1024];
	mbtowc(wstr, str, strlen(str));
	return std::wstring(wstr);
}

bool DownloadURL(Progress *p, HINTERNET hINet, std::wstring url, std::wstring outFilename)
{
	WCHAR szDecodedUrl[INTERNET_MAX_URL_LENGTH];
	DWORD cchDecodedUrl = INTERNET_MAX_URL_LENGTH;
	WCHAR szDomainName[INTERNET_MAX_URL_LENGTH];

	// parse the URL
	HRESULT hr = CoInternetParseUrl(url.c_str(), PARSE_DECODE, URL_ENCODING_NONE, szDecodedUrl,
                    INTERNET_MAX_URL_LENGTH, &cchDecodedUrl, 0);
	if (hr != S_OK)
	{
		return false;
	}

	// figure out the domain/hostname
	hr = CoInternetParseUrl(szDecodedUrl, PARSE_DOMAIN, 0, szDomainName, INTERNET_MAX_URL_LENGTH, &cchDecodedUrl, 0);
	if (hr != S_OK)
	{
		return false;
	}

	// TODO - how to cancel download if user presses the Cancel button

	// start the HTTP fetch
	HINTERNET hConnection = InternetConnectW( hINet, szDomainName, 80, L" ", L" ", INTERNET_SERVICE_HTTP, 0, 0 );
	if ( !hConnection )
	{
		return false;
	}

	std::wstring wurl(szDecodedUrl);
	std::wstring path = wurl.substr(wurl.find(szDomainName)+wcslen(szDomainName));
	//std::wstring queryString = url.substr(url.rfind("?")+1);
	//astd::wstring object = path + "?" + queryString;

	HINTERNET hRequest = HttpOpenRequestW( hConnection, L"GET", path.c_str(), NULL, NULL, NULL, INTERNET_FLAG_RELOAD|INTERNET_FLAG_NO_CACHE_WRITE|INTERNET_FLAG_NO_COOKIES|INTERNET_FLAG_NO_UI|INTERNET_FLAG_IGNORE_CERT_CN_INVALID|INTERNET_FLAG_IGNORE_CERT_DATE_INVALID, 0 );

	if ( !hRequest )
	{
		InternetCloseHandle(hConnection);
		return false;
	}

	// now stream the resulting HTTP into a file
	std::ofstream ostr;
	ostr.open(outFilename.c_str(), std::ios_base::binary | std::ios_base::trunc);

	bool failed = false;

	CHAR buffer[2048];
	DWORD dwRead;
	DWORD total = 0;
	wchar_t msg[255];

	HttpSendRequest( hRequest, NULL, 0, NULL, 0);
	while( InternetReadFile( hRequest, buffer, 2047, &dwRead ) )
	{
		if ( dwRead == 0)
		{
			break;
		}
		if (p->IsCancelled())
		{
			failed = true;
			break;
		}
		buffer[dwRead] = '\0';
		total+=dwRead;
		ostr.write(buffer, dwRead);
		wsprintfW(msg,L"Downloaded %d KB",total/1024);
		p->SetLineText(2,msg,true);
	}
	ostr.close();
	InternetCloseHandle(hConnection);
	InternetCloseHandle(hRequest);

	return ! failed;
}

bool DownloadURL2(Progress *p, HINTERNET hINet, std::wstring url, std::wstring outFilename)
{
	// TODO - how to cancel download if user presses the Cancel button

	//url = L"http://www.google.com";
	// start the HTTP fetch
	HINTERNET urlConn = InternetOpenUrlW(hINet, url.c_str(), NULL, 0, 0, 0);
	if ( !urlConn )
	{
		MessageBoxW(GetDesktopWindow(), L"didn't get a good url connection?", L"Download no good", MB_OK);
		return false;
	}

	bool failed = false;

	// now stream the resulting HTTP into a file
	std::ofstream ostr;
	ostr.open(outFilename.c_str(), std::ios_base::binary | std::ios_base::trunc);

	CHAR buffer[2048];
	DWORD dwRead;
	DWORD total = 0;
	wchar_t msg[255];
	while( InternetReadFile(urlConn, buffer, 2047, &dwRead ) )
	{
		if ( dwRead == 0)
		{
			break;
		}
		if (p->IsCancelled())
		{
			failed = true;
			break;
		}
		buffer[dwRead] = 0;
		total+=dwRead;
		ostr << buffer;
		wsprintfW(msg,L"Downloaded %d bytes",total);
		p->SetLineText(2,msg,true);
	}
	ostr.close();
	InternetCloseHandle(urlConn);

	MessageBoxW(GetDesktopWindow(), url.c_str(), L"URL Downloaded", MB_OK);
	MessageBoxW(GetDesktopWindow(), outFilename.c_str(), L"File Downloaded", MB_OK);

	return ! failed;
}

BOOL DirectoryExists(std::wstring dirName)
{
	DWORD attribs = ::GetFileAttributesW(dirName.c_str());

	if (attribs == INVALID_FILE_ATTRIBUTES) {
		return false;
	}

	return (attribs & FILE_ATTRIBUTE_DIRECTORY);
}

void CreateDirectoryTree(std::wstring dir)
{
	if(! DirectoryExists(dir))
	{
		// ensure parent dir exits
		int lastSlash = dir.find_last_of(L"\\");
		if(lastSlash != std::wstring::npos)
		{
			std::wstring parent = dir.substr(0, lastSlash);

			CreateDirectoryTree(parent);
		}

		// by this point, all parent dirs are created
		CreateDirectoryW(dir.c_str(), NULL);
	}
}

bool UnzipFile(std::wstring unzipper, std::wstring zipFile, std::wstring destdir)
{
	// ensure destdir exists
	CreateDirectoryTree(destdir);

	// now we're going to invoke back into the boot to unzip our file and install
	STARTUPINFOW si;
	PROCESS_INFORMATION pi;
	ZeroMemory( &si, sizeof(si) );
	ZeroMemory( &pi, sizeof(pi) );
	si.cb = sizeof(si);

	std::wstring cmdline = L"\"";
	cmdline+=unzipper;
	cmdline+=L"\" --tiunzip \"";
	cmdline+=zipFile;
	cmdline+=L"\" \"";
	cmdline+=destdir;
	cmdline+=L"\"";

	// in win32, we just invoke back the same process and let him unzip
	if (!CreateProcessW(NULL,(LPWSTR)cmdline.c_str(),NULL,NULL,FALSE,NULL,NULL,NULL,&si,&pi))
	{
		return false;
	}

	// wait for the process to finish unzipping
	WaitForSingleObject(pi.hProcess,INFINITE);

	return true;
}
int WINAPI WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nCmdShow
)
{
	//TODO: set the icon for this app and dialogs
//	HICON icon = LoadIcon(hInstance,MAKEINTRESOURCE(IDR_MAINFRAME));
//	SendMessage(NULL, WM_SETICON, (WPARAM)true, (LPARAM)icon);

	// get the command line
	LPWSTR cmdline = GetCommandLineW();
    int argcount = 0;
	LPWSTR *args = CommandLineToArgvW(cmdline,&argcount);

	// we must have at least the mandatory args + 1 URL
	if (argcount < 7)
	{
		MessageBoxW(GetDesktopWindow(),L"Invalid arguments passed to Installer",L"Application Error",MB_OK|MB_SYSTEMMODAL|MB_ICONEXCLAMATION);
		return 1;
	}

	std::wstring appname = args[1];
	std::wstring title = args[2];
	std::wstring message = args[3];
	std::wstring appTitle = appname + L" Installer";
	std::wstring tempdir = args[4];
	std::wstring installdir = args[5];
	std::wstring unzipper = args[6];

	// verify the installation
	if (IDOK != MessageBoxW(GetDesktopWindow(),message.c_str(),title.c_str(),MB_ICONINFORMATION|MB_OKCANCEL))
	{
		MessageBoxW(GetDesktopWindow(),L"Installation Aborted. To install later, re-run the application again.", title.c_str(), MB_OK|MB_ICONWARNING);
		return 1;
	}

	int count = argcount - 7;
	int startAt = 7;

	CoInitialize(NULL);

	// create our progress indicator class
	Progress *p = new Progress;
	p->SetTitle(appTitle.c_str());
	p->SetCancelMessage(L"Cancelling, one moment...");

	wchar_t buf[255];
	wsprintfW(buf,L"Preparing to download %d file%s", count, (count > 1 ? L"s" : L""));

	p->SetLineText(1,std::wstring(buf),true);
	p->Show();
	p->Update(0,count);

	// initialize interent DLL
	HINTERNET hINet = InternetOpenW(L"Mozilla/5.0 (compatible; Titanium_Downloader/0.1; Win32)", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0 );

	// for each URL, fetch the URL and then unzip it
	bool failed = false;
	DWORD x = 0;
	for (int c=startAt;c<argcount;c++)
	{
		p->Update(x++,count);
		std::wstring url = args[c];

		std::wstring uuid = ParseQueryParam(url,L"uuid");
		std::wstring name = ParseQueryParam(url,L"name");
		std::wstring version = ParseQueryParam(url,L"version");
		std::wstring filename = name;
		filename+=L"-";
		filename+=version;
		filename+=L".zip";

		// figure out the path and destination
		std::wstring path = tempdir + L"\\" + filename;
		std::wstring destdir;

		if (RUNTIME_UUID == uuid)
        {
			destdir = installdir + L"\\runtime\\win32\\" + version;
        }
        else if (MODULE_UUID == uuid)
        {
			destdir = installdir + L"\\modules\\win32\\" + name + L"\\" + version;
        }
        else
        {
			continue;
        }

		bool downloaded = DownloadURL(p, hINet, url, path);

		if(downloaded)
		{
			wchar_t msg[255];
			wsprintfW(msg, L"Installing %s/%s ...",name.c_str(),version.c_str());
			p->SetLineText(2,msg,true);
			UnzipFile(unzipper, path, destdir);
		}
	}

	// done with iNet - so close it
	InternetCloseHandle(hINet);

	if (p->IsCancelled())
	{
		failed = true;
	}

	// cleanup
	delete p;

	CoUninitialize();

	return (failed) ? 1 : 0;
}
<commit_msg>normalize url before attempting the download<commit_after>/**
 * Appcelerator Titanium - licensed under the Apache Public License 2
 * see LICENSE in the root folder for details on the license.
 * Copyright (c) 2009 Appcelerator, Inc. All Rights Reserved.
 */

#include <windows.h>
#include <objbase.h>
#include <Wininet.h>
#include <vector>
#include <iostream>
#include <fstream>
#include <io.h>
#include <stdlib.h>
#include <stdio.h>
#include "Progress.h"
#include "Resource.h"

#define alert(msg) MessageBoxA(GetDesktopWindow(), msg, "Message", MB_OK);
#define walert(msg) MessageBoxW(GetDesktopWindow(), msg, L"Message", MB_OK);

#define DISTRIBUTION_UUID	L"7F7FA377-E695-4280-9F1F-96126F3D2C2A"
#define RUNTIME_UUID		L"A2AC5CB5-8C52-456C-9525-601A5B0725DA"
#define MODULE_UUID			L"1ACE5D3A-2B52-43FB-A136-007BD166CFD0"

std::wstring ParseQueryParam(std::wstring uri, std::wstring name)
{
	std::wstring key = name;
	key+=L"=";
	size_t pos = uri.find(key);
	if (pos!=std::wstring::npos)
	{
		std::wstring p = uri.substr(pos + key.length());
		pos = p.find(L"&");
		if (pos!=std::wstring::npos)
		{
			p = p.substr(0,pos);
		}

		// decode
		WCHAR szOut[INTERNET_MAX_URL_LENGTH];
		DWORD cchDecodedUrl = INTERNET_MAX_URL_LENGTH;
		CoInternetParseUrl(p.c_str(), PARSE_UNESCAPE, 0, szOut, INTERNET_MAX_URL_LENGTH, &cchDecodedUrl, 0);
		p.assign(szOut);

		return p;
	}
	return L"";
}

std::wstring ProgressString(DWORD size, DWORD total)
{
	char str[1024];

#define KB 1024
#define MB KB * 1024

	if (total < KB) {
		sprintf(str, "%0.2f of %0.2f bytes", size, total);
	}

	else if (size < MB) {
		double totalKB = total/1024.0;
		double sizeKB = size/1024.0;
		sprintf(str, "%0.2f of %0.2f KB", sizeKB, totalKB);
	}

	else {
		// hopefully we shouldn't ever need to count more than 1023 in a single file!
		double totalMB = total/1024.0/1024.0;
		double sizeMB = size/1024.0/1024.0;
		sprintf(str, "%0.2f of %0.2f MB", size, total);
	}

	wchar_t wstr[1024];
	mbtowc(wstr, str, strlen(str));
	return std::wstring(wstr);
}

bool DownloadURL(Progress *p, HINTERNET hINet, std::wstring url, std::wstring outFilename)
{
	WCHAR szDecodedUrl[INTERNET_MAX_URL_LENGTH];
	DWORD cchDecodedUrl = INTERNET_MAX_URL_LENGTH;
	WCHAR szDomainName[INTERNET_MAX_URL_LENGTH];

	// parse the URL
	HRESULT hr = CoInternetParseUrl(url.c_str(), PARSE_DECODE, URL_ENCODING_NONE, szDecodedUrl,
                    INTERNET_MAX_URL_LENGTH, &cchDecodedUrl, 0);
	if (hr != S_OK)
	{
		return false;
	}

	// figure out the domain/hostname
	hr = CoInternetParseUrl(szDecodedUrl, PARSE_DOMAIN, 0, szDomainName, INTERNET_MAX_URL_LENGTH, &cchDecodedUrl, 0);
	if (hr != S_OK)
	{
		return false;
	}

	// TODO - how to cancel download if user presses the Cancel button

	// start the HTTP fetch
	HINTERNET hConnection = InternetConnectW( hINet, szDomainName, 80, L" ", L" ", INTERNET_SERVICE_HTTP, 0, 0 );
	if ( !hConnection )
	{
		return false;
	}

	std::wstring wurl(szDecodedUrl);
	std::wstring path = wurl.substr(wurl.find(szDomainName)+wcslen(szDomainName));
	//std::wstring queryString = url.substr(url.rfind("?")+1);
	//astd::wstring object = path + "?" + queryString;

	HINTERNET hRequest = HttpOpenRequestW( hConnection, L"GET", path.c_str(), NULL, NULL, NULL, INTERNET_FLAG_RELOAD|INTERNET_FLAG_NO_CACHE_WRITE|INTERNET_FLAG_NO_COOKIES|INTERNET_FLAG_NO_UI|INTERNET_FLAG_IGNORE_CERT_CN_INVALID|INTERNET_FLAG_IGNORE_CERT_DATE_INVALID, 0 );

	if ( !hRequest )
	{
		InternetCloseHandle(hConnection);
		return false;
	}

	// now stream the resulting HTTP into a file
	std::ofstream ostr;
	ostr.open(outFilename.c_str(), std::ios_base::binary | std::ios_base::trunc);

	bool failed = false;

	CHAR buffer[2048];
	DWORD dwRead;
	DWORD total = 0;
	wchar_t msg[255];

	HttpSendRequest( hRequest, NULL, 0, NULL, 0);
	while( InternetReadFile( hRequest, buffer, 2047, &dwRead ) )
	{
		if ( dwRead == 0)
		{
			break;
		}
		if (p->IsCancelled())
		{
			failed = true;
			break;
		}
		buffer[dwRead] = '\0';
		total+=dwRead;
		ostr.write(buffer, dwRead);
		wsprintfW(msg,L"Downloaded %d KB",total/1024);
		p->SetLineText(2,msg,true);
	}
	ostr.close();
	InternetCloseHandle(hConnection);
	InternetCloseHandle(hRequest);

	return ! failed;
}

bool DownloadURL2(Progress *p, HINTERNET hINet, std::wstring url, std::wstring outFilename)
{
	WCHAR szDecodedUrl[INTERNET_MAX_URL_LENGTH];
	DWORD cchDecodedUrl = INTERNET_MAX_URL_LENGTH;
	HRESULT hr = CoInternetParseUrl(url.c_str(), PARSE_DECODE, URL_ENCODING_NONE, szDecodedUrl, INTERNET_MAX_URL_LENGTH, &cchDecodedUrl, 0);
	if (hr != S_OK)
	{
		return false;
	}

	// start the HTTP fetch
	HINTERNET urlConn = InternetOpenUrlW(hINet, szDecodedUrl, NULL, 0, 0, 0);
	if ( !urlConn )
	{
		return false;
	}

	bool failed = false;

	// now stream the resulting HTTP into a file
	std::ofstream ostr;
	ostr.open(outFilename.c_str(), std::ios_base::binary | std::ios_base::trunc);

	CHAR buffer[2048];
	DWORD dwRead;
	DWORD total = 0;
	wchar_t msg[255];
	while( InternetReadFile(urlConn, buffer, 2047, &dwRead ) )
	{
		if ( dwRead == 0)
		{
			break;
		}
		if (p->IsCancelled())
		{
			failed = true;
			break;
		}
		buffer[dwRead] = '\0';
		total+=dwRead;
		ostr.write(buffer, dwRead);
		wsprintfW(msg,L"Downloaded %d KB",total/1024);
		p->SetLineText(2,msg,true);
	}
	ostr.close();
	InternetCloseHandle(urlConn);

	return ! failed;
}

BOOL DirectoryExists(std::wstring dirName)
{
	DWORD attribs = ::GetFileAttributesW(dirName.c_str());

	if (attribs == INVALID_FILE_ATTRIBUTES) {
		return false;
	}

	return (attribs & FILE_ATTRIBUTE_DIRECTORY);
}

void CreateDirectoryTree(std::wstring dir)
{
	if(! DirectoryExists(dir))
	{
		// ensure parent dir exits
		int lastSlash = dir.find_last_of(L"\\");
		if(lastSlash != std::wstring::npos)
		{
			std::wstring parent = dir.substr(0, lastSlash);

			CreateDirectoryTree(parent);
		}

		// by this point, all parent dirs are created
		CreateDirectoryW(dir.c_str(), NULL);
	}
}

bool UnzipFile(std::wstring unzipper, std::wstring zipFile, std::wstring destdir)
{
	// ensure destdir exists
	CreateDirectoryTree(destdir);

	// now we're going to invoke back into the boot to unzip our file and install
	STARTUPINFOW si;
	PROCESS_INFORMATION pi;
	ZeroMemory( &si, sizeof(si) );
	ZeroMemory( &pi, sizeof(pi) );
	si.cb = sizeof(si);

	std::wstring cmdline = L"\"";
	cmdline+=unzipper;
	cmdline+=L"\" --tiunzip \"";
	cmdline+=zipFile;
	cmdline+=L"\" \"";
	cmdline+=destdir;
	cmdline+=L"\"";

	// in win32, we just invoke back the same process and let him unzip
	if (!CreateProcessW(NULL,(LPWSTR)cmdline.c_str(),NULL,NULL,FALSE,NULL,NULL,NULL,&si,&pi))
	{
		return false;
	}

	// wait for the process to finish unzipping
	WaitForSingleObject(pi.hProcess,INFINITE);

	return true;
}
int WINAPI WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nCmdShow
)
{
	//TODO: set the icon for this app and dialogs
//	HICON icon = LoadIcon(hInstance,MAKEINTRESOURCE(IDR_MAINFRAME));
//	SendMessage(NULL, WM_SETICON, (WPARAM)true, (LPARAM)icon);

	// get the command line
	LPWSTR cmdline = GetCommandLineW();
    int argcount = 0;
	LPWSTR *args = CommandLineToArgvW(cmdline,&argcount);

	// we must have at least the mandatory args + 1 URL
	if (argcount < 7)
	{
		MessageBoxW(GetDesktopWindow(),L"Invalid arguments passed to Installer",L"Application Error",MB_OK|MB_SYSTEMMODAL|MB_ICONEXCLAMATION);
		return 1;
	}

	std::wstring appname = args[1];
	std::wstring title = args[2];
	std::wstring message = args[3];
	std::wstring appTitle = appname + L" Installer";
	std::wstring tempdir = args[4];
	std::wstring installdir = args[5];
	std::wstring unzipper = args[6];

	// verify the installation
	if (IDOK != MessageBoxW(GetDesktopWindow(),message.c_str(),title.c_str(),MB_ICONINFORMATION|MB_OKCANCEL))
	{
		MessageBoxW(GetDesktopWindow(),L"Installation Aborted. To install later, re-run the application again.", title.c_str(), MB_OK|MB_ICONWARNING);
		return 1;
	}

	int count = argcount - 7;
	int startAt = 7;

	CoInitialize(NULL);

	// create our progress indicator class
	Progress *p = new Progress;
	p->SetTitle(appTitle.c_str());
	p->SetCancelMessage(L"Cancelling, one moment...");

	wchar_t buf[255];
	wsprintfW(buf,L"Preparing to download %d file%s", count, (count > 1 ? L"s" : L""));

	p->SetLineText(1,std::wstring(buf),true);
	p->Show();
	p->Update(0,count);

	// initialize interent DLL
	HINTERNET hINet = InternetOpenW(L"Mozilla/5.0 (compatible; Titanium_Downloader/0.1; Win32)", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0 );

	// for each URL, fetch the URL and then unzip it
	bool failed = false;
	DWORD x = 0;
	for (int c=startAt;c<argcount;c++)
	{
		p->Update(x++,count);
		std::wstring url = args[c];

		std::wstring uuid = ParseQueryParam(url,L"uuid");
		std::wstring name = ParseQueryParam(url,L"name");
		std::wstring version = ParseQueryParam(url,L"version");
		std::wstring filename = name;
		filename+=L"-";
		filename+=version;
		filename+=L".zip";

		// figure out the path and destination
		std::wstring path = tempdir + L"\\" + filename;
		std::wstring destdir;

		if (RUNTIME_UUID == uuid)
        {
			destdir = installdir + L"\\runtime\\win32\\" + version;
        }
        else if (MODULE_UUID == uuid)
        {
			destdir = installdir + L"\\modules\\win32\\" + name + L"\\" + version;
        }
        else
        {
			continue;
        }

		bool downloaded = DownloadURL(p, hINet, url, path);

		if(downloaded)
		{
			wchar_t msg[255];
			wsprintfW(msg, L"Installing %s/%s ...",name.c_str(),version.c_str());
			p->SetLineText(2,msg,true);
			UnzipFile(unzipper, path, destdir);
		}
	}

	// done with iNet - so close it
	InternetCloseHandle(hINet);

	if (p->IsCancelled())
	{
		failed = true;
	}

	// cleanup
	delete p;

	CoUninitialize();

	return (failed) ? 1 : 0;
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: testfactreg.hxx,v $
 *
 *  $Revision: 1.1.1.1 $
 *
 *  last change: $Author: hr $ $Date: 2000-09-18 17:24:19 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/
#include <rtl/strbuf.hxx>

Reference< XInterface > SAL_CALL OPipeTest_CreateInstance( const Reference< XMultiServiceFactory > & rSMgr ) throw( Exception );
Sequence<OUString> OPipeTest_getSupportedServiceNames(void) throw();
OUString     OPipeTest_getServiceName() throw();
OUString    OPipeTest_getImplementationName() throw();

Reference< XInterface > SAL_CALL OPumpTest_CreateInstance( const Reference< XMultiServiceFactory > & rSMgr ) throw( Exception );
Sequence<OUString> OPumpTest_getSupportedServiceNames(void) throw();
OUString     OPumpTest_getServiceName() throw();
OUString    OPumpTest_getImplementationName() throw();

Reference< XInterface > SAL_CALL ODataStreamTest_CreateInstance( const Reference< XMultiServiceFactory > & rSMgr ) throw(Exception);
Sequence<OUString> ODataStreamTest_getSupportedServiceNames( int i) throw();
OUString     ODataStreamTest_getServiceName( int i) throw();
OUString    ODataStreamTest_getImplementationName( int i) throw();

Reference< XInterface > SAL_CALL OMarkableOutputStreamTest_CreateInstance( const Reference< XMultiServiceFactory > & rSMgr ) throw(Exception);
Sequence<OUString> OMarkableOutputStreamTest_getSupportedServiceNames(void) throw();
OUString     OMarkableOutputStreamTest_getServiceName() throw();
OUString    OMarkableOutputStreamTest_getImplementationName() throw();

Reference< XInterface > SAL_CALL OMarkableInputStreamTest_CreateInstance( const Reference< XMultiServiceFactory > & rSMgr ) throw(Exception);
Sequence<OUString> OMarkableInputStreamTest_getSupportedServiceNames(void) throw();
OUString     OMarkableInputStreamTest_getServiceName() throw();
OUString    OMarkableInputStreamTest_getImplementationName() throw();

Reference< XInterface > SAL_CALL OObjectStreamTest_CreateInstance( const Reference< XMultiServiceFactory > & rSMgr ) throw(Exception);
Sequence<OUString> OObjectStreamTest_getSupportedServiceNames( int i) throw();
OUString     OObjectStreamTest_getServiceName( int i) throw();
OUString    OObjectStreamTest_getImplementationName( int i) throw();

Reference< XInterface > SAL_CALL OMyPersistObject_CreateInstance( const Reference< XMultiServiceFactory > & rSMgr ) throw(Exception);
Sequence<OUString>  OMyPersistObject_getSupportedServiceNames( ) throw();
OUString            OMyPersistObject_getServiceName( ) throw();
OUString            OMyPersistObject_getImplementationName( ) throw();

Sequence<sal_Int8> createSeq( char * p );
Sequence<sal_Int8> createIntSeq( sal_Int32 i );

#define BUILD_ERROR(expr, Message)\
        {\
            m_seqErrors.realloc( m_seqErrors.getLength() + 1 ); \
        m_seqExceptions.realloc(  m_seqExceptions.getLength() + 1 ); \
        OStringBuffer str(128); \
        str.append( __FILE__ );\
        str.append( " " ); \
        str.append( "(" ); \
        str.append( OString::valueOf( (sal_Int32)__LINE__) );\
        str.append(")\n" );\
        str.append( "[ " ); \
        str.append( #expr ); \
        str.append( " ] : " ); \
        str.append( Message ); \
        m_seqErrors.getArray()[ m_seqErrors.getLength()-1] =\
                       OStringToOUString( str.makeStringAndClear() , RTL_TEXTENCODING_ASCII_US ); \
        }\
        ((void)0)

#define WARNING_ASSERT(expr, Message) \
        if( ! (expr) ) { \
            m_seqWarnings.realloc( m_seqErrors.getLength() +1 ); \
            OStringBuffer str(128);\
            str.append( __FILE__);\
            str.append( " "); \
            str.append( "(" ); \
            str.append(OString::valueOf( (sal_Int32)__LINE__)) ;\
            str.append( ")\n");\
            str.append( "[ " ); \
            str.append( #expr ); \
            str.append( " ] : ") ; \
            str.append( Message); \
            m_seqWarnings.getArray()[ m_seqWarnings.getLength()-1] =\
                          OStringToOUString( str.makeStringAndClear() , RTL_TEXTENCODING_ASCII_US ); \
            return; \
        }\
        ((void)0)

#define ERROR_ASSERT(expr, Message) \
        if( ! (expr) ) { \
            BUILD_ERROR(expr, Message );\
            return; \
        }\
        ((void)0)

#define ERROR_EXCEPTION_ASSERT(expr, Message, Exception) \
    if( !(expr)) { \
        BUILD_ERROR(expr,Message);\
        m_seqExceptions.getArray()[ m_seqExceptions.getLength()-1] = Any( Exception );\
        return; \
    } \
    ((void)0)
<commit_msg>INTEGRATION: CWS ooo19126 (1.1.1.1.146); FILE MERGED 2005/09/05 17:14:55 rt 1.1.1.1.146.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: testfactreg.hxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-07 18:34:53 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library 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
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/
#include <rtl/strbuf.hxx>

Reference< XInterface > SAL_CALL OPipeTest_CreateInstance( const Reference< XMultiServiceFactory > & rSMgr ) throw( Exception );
Sequence<OUString> OPipeTest_getSupportedServiceNames(void) throw();
OUString     OPipeTest_getServiceName() throw();
OUString    OPipeTest_getImplementationName() throw();

Reference< XInterface > SAL_CALL OPumpTest_CreateInstance( const Reference< XMultiServiceFactory > & rSMgr ) throw( Exception );
Sequence<OUString> OPumpTest_getSupportedServiceNames(void) throw();
OUString     OPumpTest_getServiceName() throw();
OUString    OPumpTest_getImplementationName() throw();

Reference< XInterface > SAL_CALL ODataStreamTest_CreateInstance( const Reference< XMultiServiceFactory > & rSMgr ) throw(Exception);
Sequence<OUString> ODataStreamTest_getSupportedServiceNames( int i) throw();
OUString     ODataStreamTest_getServiceName( int i) throw();
OUString    ODataStreamTest_getImplementationName( int i) throw();

Reference< XInterface > SAL_CALL OMarkableOutputStreamTest_CreateInstance( const Reference< XMultiServiceFactory > & rSMgr ) throw(Exception);
Sequence<OUString> OMarkableOutputStreamTest_getSupportedServiceNames(void) throw();
OUString     OMarkableOutputStreamTest_getServiceName() throw();
OUString    OMarkableOutputStreamTest_getImplementationName() throw();

Reference< XInterface > SAL_CALL OMarkableInputStreamTest_CreateInstance( const Reference< XMultiServiceFactory > & rSMgr ) throw(Exception);
Sequence<OUString> OMarkableInputStreamTest_getSupportedServiceNames(void) throw();
OUString     OMarkableInputStreamTest_getServiceName() throw();
OUString    OMarkableInputStreamTest_getImplementationName() throw();

Reference< XInterface > SAL_CALL OObjectStreamTest_CreateInstance( const Reference< XMultiServiceFactory > & rSMgr ) throw(Exception);
Sequence<OUString> OObjectStreamTest_getSupportedServiceNames( int i) throw();
OUString     OObjectStreamTest_getServiceName( int i) throw();
OUString    OObjectStreamTest_getImplementationName( int i) throw();

Reference< XInterface > SAL_CALL OMyPersistObject_CreateInstance( const Reference< XMultiServiceFactory > & rSMgr ) throw(Exception);
Sequence<OUString>  OMyPersistObject_getSupportedServiceNames( ) throw();
OUString            OMyPersistObject_getServiceName( ) throw();
OUString            OMyPersistObject_getImplementationName( ) throw();

Sequence<sal_Int8> createSeq( char * p );
Sequence<sal_Int8> createIntSeq( sal_Int32 i );

#define BUILD_ERROR(expr, Message)\
        {\
            m_seqErrors.realloc( m_seqErrors.getLength() + 1 ); \
        m_seqExceptions.realloc(  m_seqExceptions.getLength() + 1 ); \
        OStringBuffer str(128); \
        str.append( __FILE__ );\
        str.append( " " ); \
        str.append( "(" ); \
        str.append( OString::valueOf( (sal_Int32)__LINE__) );\
        str.append(")\n" );\
        str.append( "[ " ); \
        str.append( #expr ); \
        str.append( " ] : " ); \
        str.append( Message ); \
        m_seqErrors.getArray()[ m_seqErrors.getLength()-1] =\
                       OStringToOUString( str.makeStringAndClear() , RTL_TEXTENCODING_ASCII_US ); \
        }\
        ((void)0)

#define WARNING_ASSERT(expr, Message) \
        if( ! (expr) ) { \
            m_seqWarnings.realloc( m_seqErrors.getLength() +1 ); \
            OStringBuffer str(128);\
            str.append( __FILE__);\
            str.append( " "); \
            str.append( "(" ); \
            str.append(OString::valueOf( (sal_Int32)__LINE__)) ;\
            str.append( ")\n");\
            str.append( "[ " ); \
            str.append( #expr ); \
            str.append( " ] : ") ; \
            str.append( Message); \
            m_seqWarnings.getArray()[ m_seqWarnings.getLength()-1] =\
                          OStringToOUString( str.makeStringAndClear() , RTL_TEXTENCODING_ASCII_US ); \
            return; \
        }\
        ((void)0)

#define ERROR_ASSERT(expr, Message) \
        if( ! (expr) ) { \
            BUILD_ERROR(expr, Message );\
            return; \
        }\
        ((void)0)

#define ERROR_EXCEPTION_ASSERT(expr, Message, Exception) \
    if( !(expr)) { \
        BUILD_ERROR(expr,Message);\
        m_seqExceptions.getArray()[ m_seqExceptions.getLength()-1] = Any( Exception );\
        return; \
    } \
    ((void)0)
<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author:  Axel Naumann <axel@cern.ch>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

#include "cling/MetaProcessor/MetaProcessor.h"

#include "Display.h"
#include "InputValidator.h"
#include "MetaParser.h"
#include "MetaSema.h"
#include "cling/Interpreter/Interpreter.h"
#include "cling/Interpreter/Value.h"

#include "clang/Basic/FileManager.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Preprocessor.h"

#include "llvm/Support/Path.h"

#include <fstream>
#include <cstdlib>
#include <cctype>
#include <stdio.h>
#ifndef WIN32
#include <unistd.h>
#else
#include <io.h>
#define STDIN_FILENO  0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
#endif

using namespace clang;

namespace cling {

  MetaProcessor::MaybeRedirectOutputRAII::MaybeRedirectOutputRAII(
                                          MetaProcessor* p)
  :m_MetaProcessor(p), m_isCurrentlyRedirecting(0) {
    StringRef redirectionFile;
    if (!m_MetaProcessor->m_PrevStdoutFileName.empty()) {
      redirectionFile = m_MetaProcessor->m_PrevStdoutFileName.back();
      redirect(stdout, redirectionFile.str(), kSTDOUT);
    }
    if (!m_MetaProcessor->m_PrevStderrFileName.empty()) {
      redirectionFile = m_MetaProcessor->m_PrevStderrFileName.back();
      // Deal with the case 2>&1 and 2&>1
      if (strcmp(redirectionFile.data(), "_IO_2_1_stdout_") == 0) {
        // If out is redirected to a file.
        if (!m_MetaProcessor->m_PrevStdoutFileName.empty()) {
          redirectionFile = m_MetaProcessor->m_PrevStdoutFileName.back();
        } else {
          unredirect(m_MetaProcessor->m_backupFDStderr, STDERR_FILENO, stderr);
        }
      }
      redirect(stderr, redirectionFile.str(), kSTDERR);
    }
  }

  void MetaProcessor::MaybeRedirectOutputRAII::redirect(FILE* file,
                                        const std::string& fileName,
                                        MetaProcessor::RedirectionScope scope) {
    if (!fileName.empty()) {
      FILE* redirectionFile = freopen(fileName.c_str(), "a", file);
      if (!redirectionFile) {
        llvm::errs()<<"cling::MetaProcessor::MaybeRedirectOutputRAII::redirect:"
                    " Not succefully reopened the redirection file "
                    << fileName.c_str() << "\n.";
      } else {
        m_isCurrentlyRedirecting |= scope;
      }
    }
  }

  void MetaProcessor::MaybeRedirectOutputRAII::pop() {
    if (m_isCurrentlyRedirecting & kSTDOUT) {
      unredirect(m_MetaProcessor->m_backupFDStdout, STDOUT_FILENO, stdout);
    }
    if (m_isCurrentlyRedirecting & kSTDERR) {
      unredirect(m_MetaProcessor->m_backupFDStderr, STDERR_FILENO, stderr);
    }
  }

  void MetaProcessor::MaybeRedirectOutputRAII::unredirect(int backupFD,
                                                          int expectedFD,
                                                          FILE* file) {
    // Switch back to previous file after line is processed.

    // Flush the current content if there is any.
    if (!feof(file)) {
      fflush(file);
    }
    // Copy the original fd for the std.
    if (dup2(backupFD, expectedFD) != expectedFD) {
        llvm::errs() << "cling::MetaProcessor::unredirect "
                     << "The unredirection file descriptor not valid "
                     << backupFD << ".\n";
    }
  }

  MetaProcessor::MetaProcessor(Interpreter& interp, raw_ostream& outs)
    : m_Interp(interp), m_Outs(&outs) {
    m_InputValidator.reset(new InputValidator());
    m_MetaParser.reset(new MetaParser(new MetaSema(interp, *this)));
    m_backupFDStdout = copyFileDescriptor(STDOUT_FILENO);
    m_backupFDStderr = copyFileDescriptor(STDERR_FILENO);
  }

  MetaProcessor::~MetaProcessor() {}

  int MetaProcessor::process(const char* input_text,
                             Interpreter::CompilationResult& compRes,
                             Value* result) {
    if (result)
      *result = Value();
    compRes = Interpreter::kSuccess;
    int expectedIndent = m_InputValidator->getExpectedIndent();

    if (expectedIndent)
      compRes = Interpreter::kMoreInputExpected;
    if (!input_text || !input_text[0]) {
      // nullptr / empty string, nothing to do.
      return expectedIndent;
    }
    std::string input_line(input_text);
    if (input_line == "\n") { // just a blank line, nothing to do.
      return expectedIndent;
    }
    //  Check for and handle meta commands.
    m_MetaParser->enterNewInputLine(input_line);
    MetaSema::ActionResult actionResult = MetaSema::AR_Success;
    if (m_MetaParser->isMetaCommand(actionResult, result)) {

      if (m_MetaParser->isQuitRequested())
        return -1;

      if (actionResult != MetaSema::AR_Success)
        compRes = Interpreter::kFailure;
       // ExpectedIndent might have changed after meta command.
       return m_InputValidator->getExpectedIndent();
    }

    // Check if the current statement is now complete. If not, return to
    // prompt for more.
    if (m_InputValidator->validate(input_line) == InputValidator::kIncomplete) {
      compRes = Interpreter::kMoreInputExpected;
      return m_InputValidator->getExpectedIndent();
    }

    //  We have a complete statement, compile and execute it.
    std::string input = m_InputValidator->getInput();
    m_InputValidator->reset();
    // if (m_Options.RawInput)
    //   compResLocal = m_Interp.declare(input);
    // else
    compRes = m_Interp.process(input, result);

    return 0;
  }

  void MetaProcessor::cancelContinuation() const {
    m_InputValidator->reset();
  }

  int MetaProcessor::getExpectedIndent() const {
    return m_InputValidator->getExpectedIndent();
  }

  Interpreter::CompilationResult
  MetaProcessor::readInputFromFile(llvm::StringRef filename,
                                   Value* result,
                                   bool ignoreOutmostBlock /*=false*/) {

    {
      // check that it's not binary:
      std::ifstream in(filename.str().c_str(), std::ios::in | std::ios::binary);
      char magic[1024] = {0};
      in.read(magic, sizeof(magic));
      size_t readMagic = in.gcount();
      if (readMagic >= 4) {
        llvm::StringRef magicStr(magic,in.gcount());
        llvm::sys::fs::file_magic fileType
          = llvm::sys::fs::identify_magic(magicStr);
        if (fileType != llvm::sys::fs::file_magic::unknown) {
          llvm::errs() << "Error in cling::MetaProcessor: "
            "cannot read input from a binary file!\n";
          return Interpreter::kFailure;
        }
        unsigned printable = 0;
        for (size_t i = 0; i < readMagic; ++i)
          if (isprint(magic[i]))
            ++printable;
        if (10 * printable <  5 * readMagic) {
          // 50% printable for ASCII files should be a safe guess.
          llvm::errs() << "Error in cling::MetaProcessor: "
            "cannot read input from a (likely) binary file!\n" << printable;
          return Interpreter::kFailure;
        }
      }
    }

    std::ifstream in(filename.str().c_str());
    in.seekg(0, std::ios::end);
    size_t size = in.tellg();
    std::string content(size, ' ');
    in.seekg(0);
    in.read(&content[0], size);

    if (ignoreOutmostBlock && !content.empty()) {
      static const char whitespace[] = " \t\r\n";
      std::string::size_type posNonWS = content.find_first_not_of(whitespace);
      // Handle comments before leading {
      while (content[posNonWS] == '/' && content[posNonWS+1] == '/') {
        // Remove the comment line
        posNonWS = content.find_first_of('\n', posNonWS+2)+1;
      }
      std::string::size_type replaced = posNonWS;
      if (posNonWS != std::string::npos) {
        if (content[posNonWS] == '{') {
          // hide the curly brace:
          content[posNonWS] = ' ';
          // and the matching closing '}'
          posNonWS = content.find_last_not_of(whitespace);
          if (posNonWS != std::string::npos) {
            if (content[posNonWS] == ';' && content[posNonWS-1] == '}') {
              content[posNonWS--] = ' '; // replace ';' and enter next if
            }
            if (content[posNonWS] == '}') {
              content[posNonWS] = ' '; // replace '}'
            } else {
              std::string::size_type posBlockClose = content.find_last_of('}');
              if (posBlockClose != std::string::npos) {
                content[posBlockClose] = ' '; // replace '}'
              }
              std::string::size_type posComment
                = content.find_first_not_of(whitespace, posComment);
              if (content[posComment] == '/' && content[posComment+1] == '/') {
              // More text (comments) are okay after the last '}', but
              // we can not easily find it to remove it (so we need to upgrade
              // this code to better handle the case with comments or
              // preprocessor code before and after the leading { and
              // trailing })
                while (posComment <= posNonWS) {
                  content[posComment++] = ' '; // replace '}' and comment
                }
              } else {
                content[replaced] = '{';
                // By putting the '{' back, we keep the code as consistent as
                // the user wrote it ... but we should still warn that we not
                // goint to treat this file an unamed macro.
                llvm::errs()
                << "Warning in cling::MetaProcessor: can not find the closing '}', "
                << llvm::sys::path::filename(filename)
                << " is not handled as an unamed script!\n";
              } // did not find '}''
            } // remove comments after the trailing '}'
          } // find '}'
        } // have '{'
      } // have non-whitespace
    } // ignore outmost block

    std::string strFilename(filename.str());
    m_CurrentlyExecutingFile = strFilename;
    bool topmost = !m_TopExecutingFile.data();
    if (topmost)
      m_TopExecutingFile = m_CurrentlyExecutingFile;
    Interpreter::CompilationResult ret;
    // We don't want to value print the results of a unnamed macro.
    content = "#line 2 \"" + filename.str() + "\" \n" + content;
    if (process((content + ";").c_str(), ret, result)) {
      // Input file has to be complete.
       llvm::errs()
          << "Error in cling::MetaProcessor: file "
          << llvm::sys::path::filename(filename)
          << " is incomplete (missing parenthesis or similar)!\n";
      ret = Interpreter::kFailure;
    }
    m_CurrentlyExecutingFile = llvm::StringRef();
    if (topmost)
      m_TopExecutingFile = llvm::StringRef();
    return ret;
  }

  void MetaProcessor::setFileStream(llvm::StringRef file, bool append, int fd,
              llvm::SmallVector<llvm::SmallString<128>, 2>& prevFileStack) {
    // If we have a fileName to redirect to store it.
    if (!file.empty()) {
      prevFileStack.push_back(file);
      // pop and push a null terminating 0.
      // SmallVectorImpl<T> does not have a c_str(), thus instead of casting to
      // a SmallString<T> we null terminate the data that we have and pop the
      // 0 char back.
      prevFileStack.back().push_back(0);
      prevFileStack.back().pop_back();
      if (!append) {
        FILE * f;
        if (!(f = fopen(file.data(), "w"))) {
          llvm::errs() << "cling::MetaProcessor::setFileStream:"
                       " The file path " << file.data() << "is not valid.";
        } else {
          fclose(f);
        }
      }
    // Else unredirection, so switch to the previous file.
    } else {
      // If there is no previous file on the stack we pop the file
      if (!prevFileStack.empty()) {
        prevFileStack.pop_back();
      }
    }
  }

  void MetaProcessor::setStdStream(llvm::StringRef file,
                                   RedirectionScope stream, bool append) {

    if (stream & kSTDOUT) {
      setFileStream(file, append, STDOUT_FILENO, m_PrevStdoutFileName);
    }
    if (stream & kSTDERR) {
      setFileStream(file, append, STDERR_FILENO, m_PrevStderrFileName);
    }
  }

  int MetaProcessor::copyFileDescriptor(int fd) {
    int backupFD = dup(fd);
    if (backupFD < 0) {
      llvm::errs() << "MetaProcessor::copyFileDescriptor: Duplicating the file"
                   " descriptor " << fd << "resulted in an error."
                   " Will not be able to unredirect.";
    }
    return backupFD;
  }

  void MetaProcessor::registerUnloadPoint(const Transaction* T,
                                          llvm::StringRef filename) {
    m_MetaParser->getActions().registerUnloadPoint(T, filename);
  }

} // end namespace cling
<commit_msg>Handle npos / not found (Coverity 55587).<commit_after>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author:  Axel Naumann <axel@cern.ch>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

#include "cling/MetaProcessor/MetaProcessor.h"

#include "Display.h"
#include "InputValidator.h"
#include "MetaParser.h"
#include "MetaSema.h"
#include "cling/Interpreter/Interpreter.h"
#include "cling/Interpreter/Value.h"

#include "clang/Basic/FileManager.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Preprocessor.h"

#include "llvm/Support/Path.h"

#include <fstream>
#include <cstdlib>
#include <cctype>
#include <stdio.h>
#ifndef WIN32
#include <unistd.h>
#else
#include <io.h>
#define STDIN_FILENO  0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
#endif

using namespace clang;

namespace cling {

  MetaProcessor::MaybeRedirectOutputRAII::MaybeRedirectOutputRAII(
                                          MetaProcessor* p)
  :m_MetaProcessor(p), m_isCurrentlyRedirecting(0) {
    StringRef redirectionFile;
    if (!m_MetaProcessor->m_PrevStdoutFileName.empty()) {
      redirectionFile = m_MetaProcessor->m_PrevStdoutFileName.back();
      redirect(stdout, redirectionFile.str(), kSTDOUT);
    }
    if (!m_MetaProcessor->m_PrevStderrFileName.empty()) {
      redirectionFile = m_MetaProcessor->m_PrevStderrFileName.back();
      // Deal with the case 2>&1 and 2&>1
      if (strcmp(redirectionFile.data(), "_IO_2_1_stdout_") == 0) {
        // If out is redirected to a file.
        if (!m_MetaProcessor->m_PrevStdoutFileName.empty()) {
          redirectionFile = m_MetaProcessor->m_PrevStdoutFileName.back();
        } else {
          unredirect(m_MetaProcessor->m_backupFDStderr, STDERR_FILENO, stderr);
        }
      }
      redirect(stderr, redirectionFile.str(), kSTDERR);
    }
  }

  void MetaProcessor::MaybeRedirectOutputRAII::redirect(FILE* file,
                                        const std::string& fileName,
                                        MetaProcessor::RedirectionScope scope) {
    if (!fileName.empty()) {
      FILE* redirectionFile = freopen(fileName.c_str(), "a", file);
      if (!redirectionFile) {
        llvm::errs()<<"cling::MetaProcessor::MaybeRedirectOutputRAII::redirect:"
                    " Not succefully reopened the redirection file "
                    << fileName.c_str() << "\n.";
      } else {
        m_isCurrentlyRedirecting |= scope;
      }
    }
  }

  void MetaProcessor::MaybeRedirectOutputRAII::pop() {
    if (m_isCurrentlyRedirecting & kSTDOUT) {
      unredirect(m_MetaProcessor->m_backupFDStdout, STDOUT_FILENO, stdout);
    }
    if (m_isCurrentlyRedirecting & kSTDERR) {
      unredirect(m_MetaProcessor->m_backupFDStderr, STDERR_FILENO, stderr);
    }
  }

  void MetaProcessor::MaybeRedirectOutputRAII::unredirect(int backupFD,
                                                          int expectedFD,
                                                          FILE* file) {
    // Switch back to previous file after line is processed.

    // Flush the current content if there is any.
    if (!feof(file)) {
      fflush(file);
    }
    // Copy the original fd for the std.
    if (dup2(backupFD, expectedFD) != expectedFD) {
        llvm::errs() << "cling::MetaProcessor::unredirect "
                     << "The unredirection file descriptor not valid "
                     << backupFD << ".\n";
    }
  }

  MetaProcessor::MetaProcessor(Interpreter& interp, raw_ostream& outs)
    : m_Interp(interp), m_Outs(&outs) {
    m_InputValidator.reset(new InputValidator());
    m_MetaParser.reset(new MetaParser(new MetaSema(interp, *this)));
    m_backupFDStdout = copyFileDescriptor(STDOUT_FILENO);
    m_backupFDStderr = copyFileDescriptor(STDERR_FILENO);
  }

  MetaProcessor::~MetaProcessor() {}

  int MetaProcessor::process(const char* input_text,
                             Interpreter::CompilationResult& compRes,
                             Value* result) {
    if (result)
      *result = Value();
    compRes = Interpreter::kSuccess;
    int expectedIndent = m_InputValidator->getExpectedIndent();

    if (expectedIndent)
      compRes = Interpreter::kMoreInputExpected;
    if (!input_text || !input_text[0]) {
      // nullptr / empty string, nothing to do.
      return expectedIndent;
    }
    std::string input_line(input_text);
    if (input_line == "\n") { // just a blank line, nothing to do.
      return expectedIndent;
    }
    //  Check for and handle meta commands.
    m_MetaParser->enterNewInputLine(input_line);
    MetaSema::ActionResult actionResult = MetaSema::AR_Success;
    if (m_MetaParser->isMetaCommand(actionResult, result)) {

      if (m_MetaParser->isQuitRequested())
        return -1;

      if (actionResult != MetaSema::AR_Success)
        compRes = Interpreter::kFailure;
       // ExpectedIndent might have changed after meta command.
       return m_InputValidator->getExpectedIndent();
    }

    // Check if the current statement is now complete. If not, return to
    // prompt for more.
    if (m_InputValidator->validate(input_line) == InputValidator::kIncomplete) {
      compRes = Interpreter::kMoreInputExpected;
      return m_InputValidator->getExpectedIndent();
    }

    //  We have a complete statement, compile and execute it.
    std::string input = m_InputValidator->getInput();
    m_InputValidator->reset();
    // if (m_Options.RawInput)
    //   compResLocal = m_Interp.declare(input);
    // else
    compRes = m_Interp.process(input, result);

    return 0;
  }

  void MetaProcessor::cancelContinuation() const {
    m_InputValidator->reset();
  }

  int MetaProcessor::getExpectedIndent() const {
    return m_InputValidator->getExpectedIndent();
  }

  Interpreter::CompilationResult
  MetaProcessor::readInputFromFile(llvm::StringRef filename,
                                   Value* result,
                                   bool ignoreOutmostBlock /*=false*/) {

    {
      // check that it's not binary:
      std::ifstream in(filename.str().c_str(), std::ios::in | std::ios::binary);
      char magic[1024] = {0};
      in.read(magic, sizeof(magic));
      size_t readMagic = in.gcount();
      if (readMagic >= 4) {
        llvm::StringRef magicStr(magic,in.gcount());
        llvm::sys::fs::file_magic fileType
          = llvm::sys::fs::identify_magic(magicStr);
        if (fileType != llvm::sys::fs::file_magic::unknown) {
          llvm::errs() << "Error in cling::MetaProcessor: "
            "cannot read input from a binary file!\n";
          return Interpreter::kFailure;
        }
        unsigned printable = 0;
        for (size_t i = 0; i < readMagic; ++i)
          if (isprint(magic[i]))
            ++printable;
        if (10 * printable <  5 * readMagic) {
          // 50% printable for ASCII files should be a safe guess.
          llvm::errs() << "Error in cling::MetaProcessor: "
            "cannot read input from a (likely) binary file!\n" << printable;
          return Interpreter::kFailure;
        }
      }
    }

    std::ifstream in(filename.str().c_str());
    in.seekg(0, std::ios::end);
    size_t size = in.tellg();
    std::string content(size, ' ');
    in.seekg(0);
    in.read(&content[0], size);

    if (ignoreOutmostBlock && !content.empty()) {
      static const char whitespace[] = " \t\r\n";
      std::string::size_type posNonWS = content.find_first_not_of(whitespace);
      // Handle comments before leading {
      while (content[posNonWS] == '/' && content[posNonWS+1] == '/') {
        // Remove the comment line
        posNonWS = content.find_first_of('\n', posNonWS+2)+1;
      }
      std::string::size_type replaced = posNonWS;
      if (posNonWS != std::string::npos) {
        if (content[posNonWS] == '{') {
          // hide the curly brace:
          content[posNonWS] = ' ';
          // and the matching closing '}'
          posNonWS = content.find_last_not_of(whitespace);
          if (posNonWS != std::string::npos) {
            if (content[posNonWS] == ';' && content[posNonWS-1] == '}') {
              content[posNonWS--] = ' '; // replace ';' and enter next if
            }
            if (content[posNonWS] == '}') {
              content[posNonWS] = ' '; // replace '}'
            } else {
              std::string::size_type posBlockClose = content.find_last_of('}');
              if (posBlockClose != std::string::npos) {
                content[posBlockClose] = ' '; // replace '}'
              }
              std::string::size_type posComment
                = content.find_first_not_of(whitespace, posComment);
              if (posComment != std::string::npos) {
                if (content[posComment] == '/' && content[posComment+1] == '/') {
                  // More text (comments) are okay after the last '}', but
                  // we can not easily find it to remove it (so we need to upgrade
                  // this code to better handle the case with comments or
                  // preprocessor code before and after the leading { and
                  // trailing })
                  while (posComment <= posNonWS) {
                    content[posComment++] = ' '; // replace '}' and comment
                  }
                } else {
                  content[replaced] = '{';
                  // By putting the '{' back, we keep the code as consistent as
                  // the user wrote it ... but we should still warn that we not
                  // goint to treat this file an unamed macro.
                  llvm::errs()
                  << "Warning in cling::MetaProcessor: can not find the closing '}', "
                  << llvm::sys::path::filename(filename)
                  << " is not handled as an unamed script!\n";
                } // did not find '}''
              } // no whitespace
            } // remove comments after the trailing '}'
          } // find '}'
        } // have '{'
      } // have non-whitespace
    } // ignore outmost block

    std::string strFilename(filename.str());
    m_CurrentlyExecutingFile = strFilename;
    bool topmost = !m_TopExecutingFile.data();
    if (topmost)
      m_TopExecutingFile = m_CurrentlyExecutingFile;
    Interpreter::CompilationResult ret;
    // We don't want to value print the results of a unnamed macro.
    content = "#line 2 \"" + filename.str() + "\" \n" + content;
    if (process((content + ";").c_str(), ret, result)) {
      // Input file has to be complete.
       llvm::errs()
          << "Error in cling::MetaProcessor: file "
          << llvm::sys::path::filename(filename)
          << " is incomplete (missing parenthesis or similar)!\n";
      ret = Interpreter::kFailure;
    }
    m_CurrentlyExecutingFile = llvm::StringRef();
    if (topmost)
      m_TopExecutingFile = llvm::StringRef();
    return ret;
  }

  void MetaProcessor::setFileStream(llvm::StringRef file, bool append, int fd,
              llvm::SmallVector<llvm::SmallString<128>, 2>& prevFileStack) {
    // If we have a fileName to redirect to store it.
    if (!file.empty()) {
      prevFileStack.push_back(file);
      // pop and push a null terminating 0.
      // SmallVectorImpl<T> does not have a c_str(), thus instead of casting to
      // a SmallString<T> we null terminate the data that we have and pop the
      // 0 char back.
      prevFileStack.back().push_back(0);
      prevFileStack.back().pop_back();
      if (!append) {
        FILE * f;
        if (!(f = fopen(file.data(), "w"))) {
          llvm::errs() << "cling::MetaProcessor::setFileStream:"
                       " The file path " << file.data() << "is not valid.";
        } else {
          fclose(f);
        }
      }
    // Else unredirection, so switch to the previous file.
    } else {
      // If there is no previous file on the stack we pop the file
      if (!prevFileStack.empty()) {
        prevFileStack.pop_back();
      }
    }
  }

  void MetaProcessor::setStdStream(llvm::StringRef file,
                                   RedirectionScope stream, bool append) {

    if (stream & kSTDOUT) {
      setFileStream(file, append, STDOUT_FILENO, m_PrevStdoutFileName);
    }
    if (stream & kSTDERR) {
      setFileStream(file, append, STDERR_FILENO, m_PrevStderrFileName);
    }
  }

  int MetaProcessor::copyFileDescriptor(int fd) {
    int backupFD = dup(fd);
    if (backupFD < 0) {
      llvm::errs() << "MetaProcessor::copyFileDescriptor: Duplicating the file"
                   " descriptor " << fd << "resulted in an error."
                   " Will not be able to unredirect.";
    }
    return backupFD;
  }

  void MetaProcessor::registerUnloadPoint(const Transaction* T,
                                          llvm::StringRef filename) {
    m_MetaParser->getActions().registerUnloadPoint(T, filename);
  }

} // end namespace cling
<|endoftext|>


<commit_before>/** @file

  SSL Context management

  @section license License

  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you under the Apache License, Version 2.0 (the
  "License"); you may not use this file except in compliance
  with the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
 */

#include "ink_config.h"

#include "P_SSLCertLookup.h"
#include "P_SSLUtils.h"
#include "P_SSLConfig.h"
#include "I_EventSystem.h"
#include "I_Layout.h"
#include "Regex.h"
#include "Trie.h"
#include "ts/TestBox.h"

struct SSLAddressLookupKey
{
  explicit
  SSLAddressLookupKey(const IpEndpoint& ip) : sep(0)
  {
    static const char hextab[16] = {
      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
    };

    int nbytes;
    uint16_t port = ntohs(ip.port());

    // For IP addresses, the cache key is the hex address with the port concatenated. This makes the lookup
    // insensitive to address formatting and also allow the longest match semantic to produce different matches
    // if there is a certificate on the port.
    nbytes = ats_ip_to_hex(&ip.sa, key, sizeof(key));
    if (port) {
      sep = nbytes;
      key[nbytes++] = '.';
      key[nbytes++] = hextab[ (port >> 12) & 0x000F ];
      key[nbytes++] = hextab[ (port >>  8) & 0x000F ];
      key[nbytes++] = hextab[ (port >>  4) & 0x000F ];
      key[nbytes++] = hextab[ (port      ) & 0x000F ];
    }
    key[nbytes++] = 0;
  }

  const char * get() const { return key; }
  void split() { key[sep] = '\0'; }
  void unsplit() { key[sep] = '.'; }

private:
  char key[(TS_IP6_SIZE * 2) /* hex addr */ + 1 /* dot */ + 4 /* port */ + 1 /* NULL */];
  unsigned char sep; // offset of address/port separator
};

struct SSLContextStorage
{
public:
  SSLContextStorage();
  ~SSLContextStorage();

  /// Add a cert context to storage
  /// @return The @a host_store index or -1 on error.
  int insert(const char * name, SSLCertContext const& cc);
  /// Add a cert context to storage.
  /// @a idx must be a value returned by a previous call to insert.
  /// This creates an alias, a different @a name referring to the same
  /// cert context.
  /// @return @a idx
  int insert(const char * name, int idx);
  SSLCertContext* lookup(const char * name) const;
  unsigned count() const { return this->ctx_store.length(); }
  SSLCertContext* get(unsigned i) const { return &this->ctx_store[i]; }

private:
  /** A struct that can stored a @c Trie.
      It contains the index of the real certificate and the
      linkage required by @c Trie.
  */
  struct ContextRef
  {
    ContextRef(): idx(-1) {}
    explicit ContextRef(int n) : idx(n) {}
    void Print() const { Debug("ssl", "Item=%p SSL_CTX=#%d", this, idx); }
    int idx; ///< Index in the context store.
    LINK(ContextRef, link); ///< Require by @c Trie
  };

  /// Items tored by wildcard name
  Trie<ContextRef>  wildcards;
  /// Contexts store by IP address or FQDN
  InkHashTable *  hostnames;
  /// List for cleanup.
  /// Exactly one pointer to each SSL context is stored here.
  Vec<SSLCertContext>  ctx_store;

  /// Add a context to the clean up list.
  /// @return The index of the added context.
  int store(SSLCertContext const& cc);
  /// Remove last added context
  void unstore();

};

SSLCertLookup::SSLCertLookup()
  : ssl_storage(new SSLContextStorage()), ssl_default(NULL)
{
}

SSLCertLookup::~SSLCertLookup()
{
  delete this->ssl_storage;
}

SSLCertContext *
SSLCertLookup::find(const char * address) const
{
  return this->ssl_storage->lookup(address);
}

SSLCertContext *
SSLCertLookup::find(const IpEndpoint& address) const
{
  SSLCertContext * cc;
  SSLAddressLookupKey key(address);

  // First try the full address.
  if ((cc = this->ssl_storage->lookup(key.get()))) {
    return cc;
  }

  // If that failed, try the address without the port.
  if (address.port()) {
    key.split();
    return this->ssl_storage->lookup(key.get());
  }

  return NULL;
}

int
SSLCertLookup::insert(const char *name, SSLCertContext const &cc)
{
  return this->ssl_storage->insert(name, cc);
}

int
SSLCertLookup::insert(const IpEndpoint& address, SSLCertContext const &cc)
{
  SSLAddressLookupKey key(address);
  return this->ssl_storage->insert(key.get(), cc);
}

unsigned
SSLCertLookup::count() const
{
  return ssl_storage->count();
}

SSLCertContext *
SSLCertLookup::get(unsigned i) const
{
  return ssl_storage->get(i);
}

struct ats_wildcard_matcher
{
  ats_wildcard_matcher() {
    if (regex.compile("^\\*\\.[^\\*.]+") != 0) {
      Fatal("failed to compile TLS wildcard matching regex");
    }
  }

  ~ats_wildcard_matcher() {
  }

  bool match(const char * hostname) const {
    return regex.match(hostname) != -1;
  }

private:
  DFA regex;
};

static char *
reverse_dns_name(const char * hostname, char (&reversed)[TS_MAX_HOST_NAME_LEN+1])
{
  char * ptr = reversed + sizeof(reversed);
  const char * part = hostname;

  *(--ptr) = '\0'; // NUL-terminate

  while (*part) {
    ssize_t len = strcspn(part, ".");
    ssize_t remain = ptr - reversed;

    if (remain < (len + 1)) {
      return NULL;
    }

    ptr -= len;
    memcpy(ptr, part, len);

    // Skip to the next domain component. This will take us to either a '.' or a NUL.
    // If it's a '.' we need to skip over it.
    part += len;
    if (*part == '.') {
      ++part;
      *(--ptr) = '.';
    }
  }

  return ptr;
}

SSLContextStorage::SSLContextStorage()
  :wildcards(), hostnames(ink_hash_table_create(InkHashTableKeyType_String))
{
}

SSLContextStorage::~SSLContextStorage()
{
  for (unsigned i = 0; i < this->ctx_store.length(); ++i) {
    SSLReleaseContext(this->ctx_store[i].ctx);
  }

  ink_hash_table_destroy(this->hostnames);
}

int
SSLContextStorage::store(SSLCertContext const& cc)
{
  int idx = this->ctx_store.length();
  this->ctx_store.add(cc);
  return idx;
}

void
SSLContextStorage::unstore()
{
  this->ctx_store.drop();
}

int
SSLContextStorage::insert(const char* name, SSLCertContext const& cc)
{
  int idx = this->store(cc);
  idx = this->insert(name, idx);
  if (idx < 0) this->unstore();
  return idx;
}

int
SSLContextStorage::insert(const char* name, int idx)
{
  ats_wildcard_matcher wildcard;
  bool inserted = false;

  if (wildcard.match(name)) {
    // We turn wildcards into the reverse DNS form, then insert them into the trie
    // so that we can do a longest match lookup.
    char namebuf[TS_MAX_HOST_NAME_LEN + 1];
    char * reversed;
    ats_scoped_obj<ContextRef> ref;

    reversed = reverse_dns_name(name + 1, namebuf);
    if (!reversed) {
      Error("wildcard name '%s' is too long", name);
      return -1;
    }

    ref = new ContextRef(idx);
    inserted = this->wildcards.Insert(reversed, ref, 0 /* rank */, -1 /* keylen */);
    if (!inserted) {
      ContextRef * found;

      // We fail to insert, so the longest wildcard match search should return the full match value.
      found = this->wildcards.Search(reversed);
      // Fail even if we are reinserting the exact same value
      // Otherwise we cannot detect and recover from a doupble insert
      // into the references array
      //if (found != NULL && found->idx != idx) {
      if (found != NULL) {
        Warning("previously indexed wildcard certificate for '%s' as '%s', cannot index it with SSL_CTX #%d now",
            name, reversed, idx);
      }
      idx = -1;
    }

    Debug("ssl", "%s wildcard certificate for '%s' as '%s' with SSL_CTX %p [%d]",
      idx >= 0 ? "index" : "failed to index", name, reversed, this->ctx_store[(*ref).idx].ctx, (*ref).idx);
    ref.release();
  } else {
    InkHashTableValue value;

    if (ink_hash_table_lookup(this->hostnames, name, &value) && reinterpret_cast<InkHashTableValue>(idx) != value) {
      Warning("previously indexed '%s' with SSL_CTX %p, cannot index it with SSL_CTX #%d now", name, value, idx);
      idx = -1;
    } else {
      inserted = true;
      ink_hash_table_insert(this->hostnames, name, reinterpret_cast<void*>(static_cast<intptr_t>(idx)));
      Debug("ssl", "indexed '%s' with SSL_CTX %p [%d]",
        name, this->ctx_store[idx].ctx, idx);
    }
  }
  return idx;
}

SSLCertContext *
SSLContextStorage::lookup(const char * name) const
{
  InkHashTableValue value;

  if (ink_hash_table_lookup(const_cast<InkHashTable *>(this->hostnames), name, &value)) {
    return &(this->ctx_store[reinterpret_cast<intptr_t>(value)]);
  }

  if (!this->wildcards.Empty()) {
    char namebuf[TS_MAX_HOST_NAME_LEN + 1];
    char * reversed;
    ContextRef * ref;

    reversed = reverse_dns_name(name, namebuf);
    if (!reversed) {
      Error("failed to reverse hostname name '%s' is too long", name);
      return NULL;
    }

    Debug("ssl", "attempting wildcard match for %s", reversed);
    ref = this->wildcards.Search(reversed);
    if (ref) {
      return &(this->ctx_store[ref->idx]);
    }
  }

  return NULL;
}

#if TS_HAS_TESTS

REGRESSION_TEST(SSLWildcardMatch)(RegressionTest * t, int /* atype ATS_UNUSED */, int * pstatus)
{
  TestBox box(t, pstatus);
  ats_wildcard_matcher wildcard;

  box = REGRESSION_TEST_PASSED;

  box.check(wildcard.match("foo.com") == false, "foo.com is not a wildcard");
  box.check(wildcard.match("*.foo.com") == true, "*.foo.com not a wildcard");
  box.check(wildcard.match("bar*.foo.com") == false, "bar*.foo.com not a wildcard");
  box.check(wildcard.match("*") == false, "* is not a wildcard");
  box.check(wildcard.match("") == false, "'' is not a wildcard");
}

REGRESSION_TEST(SSLReverseHostname)(RegressionTest * t, int /* atype ATS_UNUSED */, int * pstatus)
{
  TestBox box(t, pstatus);

  char reversed[TS_MAX_HOST_NAME_LEN + 1];

#define _R(name) reverse_dns_name(name, reversed)

  box = REGRESSION_TEST_PASSED;

  box.check(strcmp(_R("foo.com"), "com.foo") == 0, "reversed foo.com");
  box.check(strcmp(_R("bar.foo.com"), "com.foo.bar") == 0, "reversed bar.foo.com");
  box.check(strcmp(_R("foo"), "foo") == 0, "reversed foo");

#undef _R
}

#endif // TS_HAS_TESTS
<commit_msg>Clean up whitespace<commit_after>/** @file

  SSL Context management

  @section license License

  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you under the Apache License, Version 2.0 (the
  "License"); you may not use this file except in compliance
  with the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
 */

#include "ink_config.h"

#include "P_SSLCertLookup.h"
#include "P_SSLUtils.h"
#include "P_SSLConfig.h"
#include "I_EventSystem.h"
#include "I_Layout.h"
#include "Regex.h"
#include "Trie.h"
#include "ts/TestBox.h"

struct SSLAddressLookupKey
{
  explicit
  SSLAddressLookupKey(const IpEndpoint& ip) : sep(0)
  {
    static const char hextab[16] = {
      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
    };

    int nbytes;
    uint16_t port = ntohs(ip.port());

    // For IP addresses, the cache key is the hex address with the port concatenated. This makes the lookup
    // insensitive to address formatting and also allow the longest match semantic to produce different matches
    // if there is a certificate on the port.
    nbytes = ats_ip_to_hex(&ip.sa, key, sizeof(key));
    if (port) {
      sep = nbytes;
      key[nbytes++] = '.';
      key[nbytes++] = hextab[ (port >> 12) & 0x000F ];
      key[nbytes++] = hextab[ (port >>  8) & 0x000F ];
      key[nbytes++] = hextab[ (port >>  4) & 0x000F ];
      key[nbytes++] = hextab[ (port      ) & 0x000F ];
    }
    key[nbytes++] = 0;
  }

  const char * get() const { return key; }
  void split() { key[sep] = '\0'; }
  void unsplit() { key[sep] = '.'; }

private:
  char key[(TS_IP6_SIZE * 2) /* hex addr */ + 1 /* dot */ + 4 /* port */ + 1 /* NULL */];
  unsigned char sep; // offset of address/port separator
};

struct SSLContextStorage
{
public:
  SSLContextStorage();
  ~SSLContextStorage();

  /// Add a cert context to storage
  /// @return The @a host_store index or -1 on error.
  int insert(const char * name, SSLCertContext const& cc);

  /// Add a cert context to storage.
  /// @a idx must be a value returned by a previous call to insert.
  /// This creates an alias, a different @a name referring to the same
  /// cert context.
  /// @return @a idx
  int insert(const char * name, int idx);
  SSLCertContext* lookup(const char * name) const;
  unsigned count() const { return this->ctx_store.length(); }
  SSLCertContext* get(unsigned i) const { return &this->ctx_store[i]; }

private:
  /** A struct that can be stored a @c Trie.
      It contains the index of the real certificate and the
      linkage required by @c Trie.
  */
  struct ContextRef
  {
    ContextRef(): idx(-1) {}
    explicit ContextRef(int n) : idx(n) {}
    void Print() const { Debug("ssl", "Item=%p SSL_CTX=#%d", this, idx); }
    int idx; ///< Index in the context store.
    LINK(ContextRef, link); ///< Require by @c Trie
  };

  /// Items tored by wildcard name
  Trie<ContextRef>  wildcards;
  /// Contexts stored by IP address or FQDN
  InkHashTable *  hostnames;
  /// List for cleanup.
  /// Exactly one pointer to each SSL context is stored here.
  Vec<SSLCertContext>  ctx_store;

  /// Add a context to the clean up list.
  /// @return The index of the added context.
  int store(SSLCertContext const& cc);
  /// Remove last added context
  void unstore();

};

SSLCertLookup::SSLCertLookup()
  : ssl_storage(new SSLContextStorage()), ssl_default(NULL)
{
}

SSLCertLookup::~SSLCertLookup()
{
  delete this->ssl_storage;
}

SSLCertContext *
SSLCertLookup::find(const char * address) const
{
  return this->ssl_storage->lookup(address);
}

SSLCertContext *
SSLCertLookup::find(const IpEndpoint& address) const
{
  SSLCertContext * cc;
  SSLAddressLookupKey key(address);

  // First try the full address.
  if ((cc = this->ssl_storage->lookup(key.get()))) {
    return cc;
  }

  // If that failed, try the address without the port.
  if (address.port()) {
    key.split();
    return this->ssl_storage->lookup(key.get());
  }

  return NULL;
}

int
SSLCertLookup::insert(const char *name, SSLCertContext const &cc)
{
  return this->ssl_storage->insert(name, cc);
}

int
SSLCertLookup::insert(const IpEndpoint& address, SSLCertContext const &cc)
{
  SSLAddressLookupKey key(address);
  return this->ssl_storage->insert(key.get(), cc);
}

unsigned
SSLCertLookup::count() const
{
  return ssl_storage->count();
}

SSLCertContext *
SSLCertLookup::get(unsigned i) const
{
  return ssl_storage->get(i);
}

struct ats_wildcard_matcher
{
  ats_wildcard_matcher() {
    if (regex.compile("^\\*\\.[^\\*.]+") != 0) {
      Fatal("failed to compile TLS wildcard matching regex");
    }
  }

  ~ats_wildcard_matcher() {
  }

  bool match(const char * hostname) const {
    return regex.match(hostname) != -1;
  }

private:
  DFA regex;
};

static char *
reverse_dns_name(const char * hostname, char (&reversed)[TS_MAX_HOST_NAME_LEN+1])
{
  char * ptr = reversed + sizeof(reversed);
  const char * part = hostname;

  *(--ptr) = '\0'; // NUL-terminate

  while (*part) {
    ssize_t len = strcspn(part, ".");
    ssize_t remain = ptr - reversed;

    if (remain < (len + 1)) {
      return NULL;
    }

    ptr -= len;
    memcpy(ptr, part, len);

    // Skip to the next domain component. This will take us to either a '.' or a NUL.
    // If it's a '.' we need to skip over it.
    part += len;
    if (*part == '.') {
      ++part;
      *(--ptr) = '.';
    }
  }

  return ptr;
}

SSLContextStorage::SSLContextStorage()
  :wildcards(), hostnames(ink_hash_table_create(InkHashTableKeyType_String))
{
}

SSLContextStorage::~SSLContextStorage()
{
  for (unsigned i = 0; i < this->ctx_store.length(); ++i) {
    SSLReleaseContext(this->ctx_store[i].ctx);
  }

  ink_hash_table_destroy(this->hostnames);
}

int
SSLContextStorage::store(SSLCertContext const& cc)
{
  int idx = this->ctx_store.length();
  this->ctx_store.add(cc);
  return idx;
}

void
SSLContextStorage::unstore()
{
  this->ctx_store.drop();
}

int
SSLContextStorage::insert(const char* name, SSLCertContext const& cc)
{
  int idx = this->store(cc);
  idx = this->insert(name, idx);
  if (idx < 0) this->unstore();
  return idx;
}

int
SSLContextStorage::insert(const char* name, int idx)
{
  ats_wildcard_matcher wildcard;
  bool inserted = false;

  if (wildcard.match(name)) {
    // We turn wildcards into the reverse DNS form, then insert them into the trie
    // so that we can do a longest match lookup.
    char namebuf[TS_MAX_HOST_NAME_LEN + 1];
    char * reversed;
    ats_scoped_obj<ContextRef> ref;

    reversed = reverse_dns_name(name + 1, namebuf);
    if (!reversed) {
      Error("wildcard name '%s' is too long", name);
      return -1;
    }

    ref = new ContextRef(idx);
    inserted = this->wildcards.Insert(reversed, ref, 0 /* rank */, -1 /* keylen */);
    if (!inserted) {
      ContextRef * found;

      // We fail to insert, so the longest wildcard match search should return the full match value.
      found = this->wildcards.Search(reversed);
      // Fail even if we are reinserting the exact same value
      // Otherwise we cannot detect and recover from a doupble insert
      // into the references array
      //if (found != NULL && found->idx != idx) {
      if (found != NULL) {
        Warning("previously indexed wildcard certificate for '%s' as '%s', cannot index it with SSL_CTX #%d now",
            name, reversed, idx);
      }
      idx = -1;
    }

    Debug("ssl", "%s wildcard certificate for '%s' as '%s' with SSL_CTX %p [%d]",
      idx >= 0 ? "index" : "failed to index", name, reversed, this->ctx_store[(*ref).idx].ctx, (*ref).idx);
    ref.release();
  } else {
    InkHashTableValue value;

    if (ink_hash_table_lookup(this->hostnames, name, &value) && reinterpret_cast<InkHashTableValue>(idx) != value) {
      Warning("previously indexed '%s' with SSL_CTX %p, cannot index it with SSL_CTX #%d now", name, value, idx);
      idx = -1;
    } else {
      inserted = true;
      ink_hash_table_insert(this->hostnames, name, reinterpret_cast<void*>(static_cast<intptr_t>(idx)));
      Debug("ssl", "indexed '%s' with SSL_CTX %p [%d]",
        name, this->ctx_store[idx].ctx, idx);
    }
  }
  return idx;
}

SSLCertContext *
SSLContextStorage::lookup(const char * name) const
{
  InkHashTableValue value;

  if (ink_hash_table_lookup(const_cast<InkHashTable *>(this->hostnames), name, &value)) {
    return &(this->ctx_store[reinterpret_cast<intptr_t>(value)]);
  }

  if (!this->wildcards.Empty()) {
    char namebuf[TS_MAX_HOST_NAME_LEN + 1];
    char * reversed;
    ContextRef * ref;

    reversed = reverse_dns_name(name, namebuf);
    if (!reversed) {
      Error("failed to reverse hostname name '%s' is too long", name);
      return NULL;
    }

    Debug("ssl", "attempting wildcard match for %s", reversed);
    ref = this->wildcards.Search(reversed);
    if (ref) {
      return &(this->ctx_store[ref->idx]);
    }
  }

  return NULL;
}

#if TS_HAS_TESTS

REGRESSION_TEST(SSLWildcardMatch)(RegressionTest * t, int /* atype ATS_UNUSED */, int * pstatus)
{
  TestBox box(t, pstatus);
  ats_wildcard_matcher wildcard;

  box = REGRESSION_TEST_PASSED;

  box.check(wildcard.match("foo.com") == false, "foo.com is not a wildcard");
  box.check(wildcard.match("*.foo.com") == true, "*.foo.com not a wildcard");
  box.check(wildcard.match("bar*.foo.com") == false, "bar*.foo.com not a wildcard");
  box.check(wildcard.match("*") == false, "* is not a wildcard");
  box.check(wildcard.match("") == false, "'' is not a wildcard");
}

REGRESSION_TEST(SSLReverseHostname)(RegressionTest * t, int /* atype ATS_UNUSED */, int * pstatus)
{
  TestBox box(t, pstatus);

  char reversed[TS_MAX_HOST_NAME_LEN + 1];

#define _R(name) reverse_dns_name(name, reversed)

  box = REGRESSION_TEST_PASSED;

  box.check(strcmp(_R("foo.com"), "com.foo") == 0, "reversed foo.com");
  box.check(strcmp(_R("bar.foo.com"), "com.foo.bar") == 0, "reversed bar.foo.com");
  box.check(strcmp(_R("foo"), "foo") == 0, "reversed foo");

#undef _R
}

#endif // TS_HAS_TESTS
<|endoftext|>