Changeset - 8b6a86d528ce
[Not reviewed]
Merge
! ! !
HanzZ - 13 years ago 2012-09-01 07:22:24
hanzz.k@gmail.com
Merge branch 'master' of git://github.com/vitalyster/libtransport
34 files changed:
Changeset was too big and was cut off... Show full diff anyway
0 comments (0 inline, 0 general)
.gitignore
Show inline comments
 
new file 100644
 
*.pb.cc
 
*.pb.h
 
plugin/python/protocol_pb2.py
 
\ No newline at end of file
CMakeLists.txt
Show inline comments
 
@@ -10,8 +10,16 @@ set(CMAKE_MODULE_PATH "cmake_modules")
 
set(cppunit_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(cppunit)
 

	
 
if (NOT CMAKE_COMPILER_IS_GNUCXX)
 
ADD_SUBDIRECTORY(msvc-deps)
 
else()
 
if (WIN32)
 
ADD_SUBDIRECTORY(msvc-deps/sqlite3)
 
else()
 
set(sqlite3_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(sqlite3)
 
endif()
 
endif()
 

	
 
set(mysql_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(mysql)
 
@@ -22,6 +30,10 @@ find_package(purple)
 
set(glib_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(glib)
 

	
 
set(libxml2_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(libxml2)
 

	
 

	
 
if (NOT WIN32)
 
	set(popt_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
	find_package(popt)
 
@@ -33,8 +45,10 @@ find_package(event)
 
set(Swiften_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(Swiften)
 

	
 
if (CMAKE_COMPILER_IS_GNUCXX)
 
set(openssl_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(openssl)
 
endif()
 

	
 
set(Boost_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
if (WIN32)
 
@@ -60,8 +74,10 @@ find_package(event)
 
set(pqxx_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(pqxx)
 

	
 
if (NOT WIN32)
 
set(dbus_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(dbus)
 
endif()
 

	
 
set(yahoo2_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(yahoo2)
 
@@ -82,7 +98,10 @@ if (SPECTRUM_VERSION)
 
	ADD_DEFINITIONS(-DSPECTRUM_VERSION="${SPECTRUM_VERSION}")
 
else (SPECTRUM_VERSION)
 
	if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git)
 
		execute_process(COMMAND git "--git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git" rev-parse --short HEAD
 
		if (NOT GIT_EXECUTABLE)
 
		set (GIT_EXECUTABLE git)
 
		endif()
 
		execute_process(COMMAND ${GIT_EXECUTABLE} "--git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git" rev-parse --short HEAD
 
						OUTPUT_VARIABLE GIT_REVISION
 
						OUTPUT_STRIP_TRAILING_WHITESPACE
 
		)
 
@@ -101,8 +120,14 @@ if (SQLITE3_FOUND)
 
	include_directories(${SQLITE3_INCLUDE_DIR})
 
	message("SQLite3           : yes")
 
else (SQLITE3_FOUND)
 
if (WIN32)
 
	ADD_DEFINITIONS(-DWITH_SQLITE)
 
	include_directories(msvc-deps/sqlite3)
 
	message("SQLite3           : bundled")
 
else()
 
	set(SQLITE3_LIBRARIES "")
 
	message("SQLite3           : no")
 
endif()
 
endif (SQLITE3_FOUND)
 

	
 
if (MYSQL_FOUND)
 
@@ -155,15 +180,19 @@ if (PROTOBUF_FOUND)
 
		message("IRC plugin        : no (install libCommuni and libprotobuf-dev)")
 
	endif()
 

	
 
if (NOT WIN32)
 
	message("Frotz plugin      : yes")
 
	message("SMSTools3 plugin  : yes")
 

	
 
else()
 
	message("Frotz plugin      : no")
 
	message("SMSTools3 plugin  : no")
 
	if(${LIBDBUSGLIB_FOUND})
 
		message("Skype plugin      : yes")
 
		include_directories(${LIBDBUSGLIB_INCLUDE_DIRS})
 
	else()
 
		message("Skype plugin      : no (install dbus-glib-devel)")
 
	endif()
 
endif()
 

	
 
#	We have our own copy now...
 
# 	if(YAHOO2_FOUND)
 
@@ -196,25 +225,17 @@ else()
 
endif()
 

	
 
if (WIN32)
 
ADD_DEFINITIONS(-DLOG4CXX_STATIC)
 
ADD_DEFINITIONS(-D_WIN32_WINNT=0x501)
 
ADD_DEFINITIONS(-DWIN32_LEAN_AND_MEAN)
 
ADD_DEFINITIONS(-DBOOST_USE_WINDOWS_H)
 
ADD_DEFINITIONS(-DBOOST_THREAD_USE_LIB)
 
endif()
 

	
 
if(CMAKE_BUILD_TYPE MATCHES Debug)
 
if (CMAKE_COMPILER_IS_GNUCXX)
 
	ADD_DEFINITIONS(-O0)
 
	ADD_DEFINITIONS(-ggdb)
 
	ADD_DEFINITIONS(-Wall)
 
	ADD_DEFINITIONS(-W)
 
	ADD_DEFINITIONS(-Wcast-align)
 
	ADD_DEFINITIONS(-Wextra -Wno-sign-compare -Wno-unused-parameter)
 
	ADD_DEFINITIONS(-Winit-self)
 
	ADD_DEFINITIONS(-Wmissing-declarations)
 
	ADD_DEFINITIONS(-Wpointer-arith)
 
	ADD_DEFINITIONS(-Wreorder)
 
	ADD_DEFINITIONS(-Woverloaded-virtual)
 
	ADD_DEFINITIONS(-Wsign-promo)
 
	ADD_DEFINITIONS(-Wundef -Wunused)
 
endif()
 
	ADD_DEFINITIONS(-DDEBUG)
 
	message("Debug             : yes")
 
@@ -231,7 +252,10 @@ include_directories(include)
 
include_directories(${EVENT_INCLUDE_DIRS})
 
include_directories(${SWIFTEN_INCLUDE_DIR})
 
include_directories(${Boost_INCLUDE_DIRS})
 

	
 
if (CMAKE_COMPILER_IS_GNUCXX)
 
include_directories(${OPENSSL_INCLUDE_DIR})
 
endif()
 

	
 
ADD_SUBDIRECTORY(src)
 
ADD_SUBDIRECTORY(plugin)
README.win32
Show inline comments
 
new file 100644
 
Prerequisites
 
=============
 
 
1. Microsoft Visual C++ 2010 Express or higher edition (http://www.microsoft.com/visualstudio/en-us/products/2010-editions/visual-cpp-express)
 
2. Git for Windows (http://code.google.com/p/msysgit/downloads/list)
 
3. CMake 2.8 or newer (http://www.cmake.org/cmake/resources/software.html)
 
 
Libraries
 
=========
 
3. Swiften library and Python for run scons (http://swift.im/git/swift)
 
4. Boost 1.48 or newer (http://sourceforge.net/projects/boost/files/boost/1.49.0/)
 
5. Google ProtoBuf library (http://code.google.com/p/protobuf/downloads/list)
 
 
 
Environment
 
===========
 
 
To create spectrum build environment do:
 
 
0. Create directory where we'll install all dependencies, e.g. C:\env-msvc-x64
 
Assuming you have git, python and cmake in %PATH%, 
 
launch "Visual Studio 2010 command prompt" or
 
"Visual Studio 2010(x64) command prompt", depends on your target (Windows x86 or Windows x86_64).
 
1. unpack and build boost libraries:
 
 
	bootstrap.bat
 
	b2.exe --without-mpi --without-python
 
	b2.exe --without-mpi --without-python install --prefix=C:\env-msvc-x64 --layout=system
 
 
2. clone swift repository and build it. Don't forget to point it to our env directory:
 
 
	git clone http://swift.im/git/swift
 
	cd swift
 
	echo boost_includedir="c:/env-msvc-x64/include" > config.py
 
	echo boost_libdir="c:/env-msvc-x64/lib" >> config.py 
 
	scons.bat debug=no SWIFTEN_INSTALLDIR=C:\env-msvc-x64
 
	scons.bat debug=no SWIFTEN_INSTALLDIR=C:\env-msvc-x64 C:\env-msvc-x64
 
 
TODO: fix in upstream
 
You may need manually copy compiled 3rdParty libs to C:\env-msvc-x64\lib\3rdParty\Expat, 
 
C:\env-msvc-x64\lib\3rdParty\LibIDN, C:\env-msvc-x64\lib\3rdParty\Zlib
 
 
3. unpack and compile protobuf as described in its documentation. 
 
 
Run extract_includes.bat in vsprojects/ directory and move resulting google/ directory to our C:\env-msvc-x64\include
 
 
Move protoc.exe to C:\env-msvc-x64\bin\ and libprotobuf.lib to C:\env-msvc-x64\lib
 
 
4. You're ready! :) Clone libtransport and compile it as:
 
	set CMAKE_INCLUDE_PATH=C:\env-msvc-x64\include
 
	cmake . -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=C:\env-msvc-x64 -DGIT_EXECUTABLE="c:\Program Files (x86)\git\bin\git.exe"
 
	nmake
 
 
TODO: libpurple_backend compilation
backends/CMakeLists.txt
Show inline comments
 
@@ -7,8 +7,6 @@ if (PROTOBUF_FOUND)
 
		ADD_SUBDIRECTORY(libcommuni)
 
	endif()
 

	
 
	ADD_SUBDIRECTORY(smstools3)
 

	
 
	ADD_SUBDIRECTORY(swiften)
 

	
 
# 	if(YAHOO2_FOUND)
 
@@ -20,6 +18,7 @@ if (PROTOBUF_FOUND)
 
	ADD_SUBDIRECTORY(twitter)
 

	
 
if (NOT WIN32)
 
	ADD_SUBDIRECTORY(smstools3)
 
	ADD_SUBDIRECTORY(frotz)
 
	if (${LIBDBUSGLIB_FOUND})
 
		ADD_SUBDIRECTORY(skype)
backends/libpurple/CMakeLists.txt
Show inline comments
 
@@ -3,11 +3,16 @@ FILE(GLOB SRC *.cpp)
 
 
ADD_EXECUTABLE(spectrum2_libpurple_backend ${SRC})
 
 
if(CMAKE_COMPILER_IS_GNUCXX)
 
	if (NOT WIN32)
 
	target_link_libraries(spectrum2_libpurple_backend ${PURPLE_LIBRARY} ${GLIB2_LIBRARIES} ${EVENT_LIBRARIES} transport-plugin pthread)
 
	else()
 
	target_link_libraries(spectrum2_libpurple_backend ${PURPLE_LIBRARY} ${GLIB2_LIBRARIES} ${EVENT_LIBRARIES} transport-plugin)
 
	endif()
 
else()
 
include_directories("${CMAKE_SOURCE_DIR}/msvc-deps/protobuf/libprotobuf")
 
target_link_libraries(spectrum2_libpurple_backend ${PURPLE_LIBRARY} ${GLIB2_LIBRARIES} ${LIBXML2_LIBRARIES} ${EVENT_LIBRARIES} transport-plugin libprotobuf)
 
endif()
 
 
INSTALL(TARGETS spectrum2_libpurple_backend RUNTIME DESTINATION bin)
 
backends/swiften/CMakeLists.txt
Show inline comments
 
@@ -4,7 +4,11 @@ FILE(GLOB SRC *.cpp)
 
 
ADD_EXECUTABLE(spectrum2_swiften_backend ${SRC})
 
 
IF (NOT WIN32)
 
target_link_libraries(spectrum2_swiften_backend transport pthread ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES})
 
else()
 
target_link_libraries(spectrum2_swiften_backend transport ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES})
 
endif()
 
 
INSTALL(TARGETS spectrum2_swiften_backend RUNTIME DESTINATION bin)
 
backends/swiften/main.cpp
Show inline comments
 
@@ -6,11 +6,13 @@
 
// Swiften
 
#include "Swiften/Swiften.h"
 

	
 
#ifndef WIN32
 
// for signal handler
 
#include "unistd.h"
 
#include "signal.h"
 
#include "sys/wait.h"
 
#include "sys/signal.h"
 
#endif
 

	
 
// malloc_trim
 
#include "malloc.h"
 
@@ -232,6 +234,7 @@ class SwiftenPlugin : public NetworkPlugin {
 
		std::map<std::string, boost::shared_ptr<Swift::Client> > m_users;
 
};
 

	
 
#ifndef WIN32
 
static void spectrum_sigchld_handler(int sig)
 
{
 
	int status;
 
@@ -247,16 +250,19 @@ static void spectrum_sigchld_handler(int sig)
 
		perror(errmsg);
 
	}
 
}
 
#endif
 

	
 

	
 
int main (int argc, char* argv[]) {
 
	std::string host;
 
	int port;
 

	
 
#ifndef WIN32
 
	if (signal(SIGCHLD, spectrum_sigchld_handler) == SIG_ERR) {
 
		std::cout << "SIGCHLD handler can't be set\n";
 
		return -1;
 
	}
 
#endif
 

	
 
	boost::program_options::options_description desc("Usage: spectrum [OPTIONS] <config_file.cfg>\nAllowed options");
 
	desc.add_options()
backends/template/CMakeLists.txt
Show inline comments
 
@@ -4,7 +4,16 @@ FILE(GLOB SRC *.c *.cpp)
 
 
ADD_EXECUTABLE(spectrum2_template_backend ${SRC})
 
 
if (CMAKE_COMPILER_IS_GNUCXX)
 
if (NOT WIN32)
 
target_link_libraries(spectrum2_template_backend transport pthread ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES})
 
else()
 
target_link_libraries(spectrum2_template_backend transport ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES})
 
endif()
 
else()
 
include_directories("${CMAKE_SOURCE_DIR}/msvc-deps/protobuf/libprotobuf")
 
target_link_libraries(spectrum2_template_backend transport libprotobuf ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES})
 
endif()
 
 
#INSTALL(TARGETS spectrum2_template_backend RUNTIME DESTINATION bin)
 
backends/template/main.cpp
Show inline comments
 
@@ -6,12 +6,13 @@
 
// Swiften
 
#include "Swiften/Swiften.h"
 

	
 
#ifndef _WIN32
 
// for signal handler
 
#include "unistd.h"
 
#include "signal.h"
 
#include "sys/wait.h"
 
#include "sys/signal.h"
 

	
 
#endif
 
// Boost
 
#include <boost/algorithm/string.hpp>
 
using namespace boost::filesystem;
 
@@ -83,6 +84,8 @@ class TemplatePlugin : public NetworkPlugin {
 
		Config *config;
 
};
 

	
 
#ifndef _WIN32
 

	
 
static void spectrum_sigchld_handler(int sig)
 
{
 
	int status;
 
@@ -98,17 +101,18 @@ static void spectrum_sigchld_handler(int sig)
 
		perror(errmsg);
 
	}
 
}
 

	
 
#endif
 

	
 
int main (int argc, char* argv[]) {
 
	std::string host;
 
	int port;
 

	
 
#ifndef _WIN32
 
	if (signal(SIGCHLD, spectrum_sigchld_handler) == SIG_ERR) {
 
		std::cout << "SIGCHLD handler can't be set\n";
 
		return -1;
 
	}
 

	
 
#endif
 
	boost::program_options::options_description desc("Usage: spectrum [OPTIONS] <config_file.cfg>\nAllowed options");
 
	desc.add_options()
 
		("host,h", value<std::string>(&host), "host")
cmake_modules/CommuniConfig.cmake
Show inline comments
 
find_package(Qt4 REQUIRED)
 
find_package(Qt4)
 
include( ${QT_USE_FILE} )
 

	
 
FIND_LIBRARY(IRC_LIBRARY NAMES Communi PATHS ${QT_LIBRARY_DIR})
cmake_modules/SwiftenConfig.cmake
Show inline comments
 
FIND_LIBRARY(SWIFTEN_LIBRARY NAMES Swiften)
 
FIND_PATH(SWIFTEN_INCLUDE_DIR NAMES "Swiften.h" PATH_SUFFIXES libSwiften Swiften )
 
FIND_PATH(SWIFTEN_INCLUDE_DIR NAMES "Swiften/Swiften.h" PATH_SUFFIXES libSwiften Swiften )
 

	
 
if( SWIFTEN_LIBRARY AND SWIFTEN_INCLUDE_DIR )
 
	find_program(SWIFTEN_CONFIG_EXECUTABLE NAMES swiften-config DOC "swiften-config executable")
 
@@ -27,7 +27,7 @@ if( SWIFTEN_LIBRARY AND SWIFTEN_INCLUDE_DIR )
 
		message( FATAL_ERROR "Could NOT find swiften-config" )
 
	endif()
 

	
 
	set( SWIFTEN_INCLUDE_DIR ${SWIFTEN_INCLUDE_DIR}/.. )
 
	set( SWIFTEN_INCLUDE_DIR ${SWIFTEN_INCLUDE_DIR} )
 
	message( STATUS "Found libSwiften: ${SWIFTEN_LIBRARY}, ${SWIFTEN_INCLUDE_DIR}")
 
	set( SWIFTEN_FOUND 1 )
 
else( SWIFTEN_LIBRARY AND SWIFTEN_INCLUDE_DIR )
cmake_modules/log4cxxConfig.cmake
Show inline comments
 
@@ -2,7 +2,7 @@
 
# LOG4CXX_INCLUDE_DIR - the liblog4cxx include directory
 
# LOG4CXX_LIBRARIES - liblog4cxx library
 

	
 
FIND_PATH(LOG4CXX_INCLUDE_DIR logger.h PATHS /include/log4cxx /usr/include/log4cxx /usr/local/include/log4cxx )
 
FIND_PATH(LOG4CXX_INCLUDE_DIR log4cxx/logger.h PATHS /include /usr/include /usr/local/include )
 
FIND_LIBRARY(LOG4CXX_LIBRARIES NAMES log4cxx log4cxxd PATHS /lib /usr/lib /usr/local/lib )
 

	
 
IF(LOG4CXX_INCLUDE_DIR AND LOG4CXX_LIBRARIES)
include/Swiften/Server/Server.cpp
Show inline comments
 
@@ -149,7 +149,7 @@ void Server::handleSessionFinished(boost::shared_ptr<ServerFromClientSession> se
 
			boost::bind(&Server::handleSessionFinished, this, session));
 
}
 

	
 
void Server::addTLSEncryption(TLSServerContextFactory* tlsContextFactory, const PKCS12Certificate& cert) {
 
void Server::addTLSEncryption(TLSServerContextFactory* tlsContextFactory, CertificateWithKey::ref cert) {
 
	tlsFactory = tlsContextFactory;
 
	this->cert = cert;
 
}
include/Swiften/Server/Server.h
Show inline comments
 
@@ -21,7 +21,7 @@
 
#include "Swiften/Entity/Entity.h"
 
#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h"
 
#include "Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h"
 
#include "Swiften/TLS/PKCS12Certificate.h"
 
#include <Swiften/TLS/CertificateWithKey.h>
 
#include <Swiften/Parser/PlatformXMLParserFactory.h>
 

	
 
namespace Swift {
 
@@ -60,7 +60,7 @@ namespace Swift {
 
			boost::signal<void (const SafeByteArray&)> onDataRead;
 
			boost::signal<void (const SafeByteArray&)> onDataWritten;
 

	
 
			void addTLSEncryption(TLSServerContextFactory* tlsContextFactory, const PKCS12Certificate& cert);
 
			void addTLSEncryption(TLSServerContextFactory* tlsContextFactory, CertificateWithKey::ref cert);
 

	
 
		private:
 
			void handleNewClientConnection(boost::shared_ptr<Connection> c);
 
@@ -84,7 +84,7 @@ namespace Swift {
 
			StanzaChannel *stanzaChannel_;
 
			IQRouter *iqRouter_;
 
			TLSServerContextFactory *tlsFactory;
 
			PKCS12Certificate cert;
 
			CertificateWithKey::ref cert;
 
			PlatformXMLParserFactory *parserFactory_;
 
	};
 
}
include/Swiften/Server/ServerFromClientSession.cpp
Show inline comments
 
@@ -27,6 +27,7 @@
 
#include <Swiften/Elements/StartTLSRequest.h>
 
#include <Swiften/Elements/TLSProceed.h>
 
#include <iostream>
 
#include <Swiften/TLS/CertificateWithKey.h>
 

	
 
namespace Swift {
 

	
 
@@ -162,7 +163,7 @@ void ServerFromClientSession::handleSessionFinished(const boost::optional<Sessio
 
	userRegistry_->stopLogin(JID(user_, getLocalJID().getDomain()), this);
 
}
 

	
 
void ServerFromClientSession::addTLSEncryption(TLSServerContextFactory* tlsContextFactory, const PKCS12Certificate& cert) {
 
void ServerFromClientSession::addTLSEncryption(TLSServerContextFactory* tlsContextFactory, CertificateWithKey::ref cert) {
 
	tlsLayer = new TLSServerLayer(tlsContextFactory);
 
	if (!tlsLayer->setServerCertificate(cert)) {
 
// 		std::cout << "error\n";
include/Swiften/Server/ServerFromClientSession.h
Show inline comments
 
@@ -15,6 +15,7 @@
 
#include <Swiften/JID/JID.h>
 
#include <Swiften/Network/Connection.h>
 
#include <Swiften/Base/ByteArray.h>
 
#include <Swiften/TLS/CertificateWithKey.h>
 

	
 
namespace Swift {
 
	class ProtocolHeader;
 
@@ -49,7 +50,7 @@ namespace Swift {
 
				return user_;
 
			}
 

	
 
			void addTLSEncryption(TLSServerContextFactory* tlsContextFactory, const PKCS12Certificate& cert);
 
			void addTLSEncryption(TLSServerContextFactory* tlsContextFactory, CertificateWithKey::ref cert);
 

	
 
			Swift::JID getBareJID() {
 
				return Swift::JID(user_, getLocalJID().getDomain());
include/Swiften/StreamStack/TLSServerLayer.cpp
Show inline comments
 
@@ -37,7 +37,7 @@ void TLSServerLayer::handleDataRead(const SafeByteArray& data) {
 
	context->handleDataFromNetwork(data);
 
}
 

	
 
bool TLSServerLayer::setServerCertificate(const PKCS12Certificate& certificate) {
 
bool TLSServerLayer::setServerCertificate(CertificateWithKey::ref certificate) {
 
	return context->setServerCertificate(certificate);
 
}
 

	
include/Swiften/StreamStack/TLSServerLayer.h
Show inline comments
 
@@ -9,6 +9,7 @@
 
#include "Swiften/Base/SafeByteArray.h"
 
#include "Swiften/StreamStack/StreamLayer.h"
 
#include "Swiften/TLS/Certificate.h"
 
#include <Swiften/TLS/CertificateWithKey.h>
 
#include "Swiften/TLS/CertificateVerificationError.h"
 

	
 
namespace Swift {
 
@@ -22,7 +23,7 @@ namespace Swift {
 
			~TLSServerLayer();
 

	
 
			void connect();
 
			bool setServerCertificate(const PKCS12Certificate&);
 
			bool setServerCertificate(CertificateWithKey::ref cert);
 

	
 
			Certificate::ref getPeerCertificate() const;
 
			boost::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const;
include/Swiften/TLS/OpenSSL/OpenSSLServerContext.cpp
Show inline comments
 
@@ -180,15 +180,16 @@ void OpenSSLServerContext::sendPendingDataToApplication() {
 
	}
 
}
 

	
 
bool OpenSSLServerContext::setServerCertificate(const PKCS12Certificate& certificate) {
 
	if (certificate.isNull()) {
 
bool OpenSSLServerContext::setServerCertificate(CertificateWithKey::ref certref) {
 
	boost::shared_ptr<PKCS12Certificate> certificate = boost::dynamic_pointer_cast<PKCS12Certificate>(certref);
 
	if (certificate->isNull()) {
 
		LOG4CXX_ERROR(logger, "TLS WILL NOT WORK: Certificate can't be loaded.");
 
		return false;
 
	}
 

	
 
	// Create a PKCS12 structure
 
	BIO* bio = BIO_new(BIO_s_mem());
 
	BIO_write(bio, vecptr(certificate.getData()), certificate.getData().size());
 
	BIO_write(bio, vecptr(certificate->getData()), certificate->getData().size());
 
	boost::shared_ptr<PKCS12> pkcs12(d2i_PKCS12_bio(bio, NULL), PKCS12_free);
 
	BIO_free(bio);
 
	if (!pkcs12) {
 
@@ -200,7 +201,7 @@ bool OpenSSLServerContext::setServerCertificate(const PKCS12Certificate& certifi
 
	X509 *certPtr = 0;
 
	EVP_PKEY* privateKeyPtr = 0;
 
	STACK_OF(X509)* caCertsPtr = 0;
 
	int result = PKCS12_parse(pkcs12.get(), reinterpret_cast<const char*>(vecptr(certificate.getPassword())), &privateKeyPtr, &certPtr, &caCertsPtr);
 
	int result = PKCS12_parse(pkcs12.get(), reinterpret_cast<const char*>(vecptr(certificate->getPassword())), &privateKeyPtr, &certPtr, &caCertsPtr);
 
	if (result != 1) { 
 
		LOG4CXX_ERROR(logger, "TLS WILL NOT WORK: Certificate is not in PKCS#12 format.");
 
		return false;
include/Swiften/TLS/OpenSSL/OpenSSLServerContext.h
Show inline comments
 
@@ -12,6 +12,7 @@
 

	
 
#include "Swiften/TLS/TLSServerContext.h"
 
#include "Swiften/Base/ByteArray.h"
 
#include <Swiften/TLS/CertificateWithKey.h>
 

	
 
namespace Swift {
 
	class PKCS12Certificate;
 
@@ -22,7 +23,7 @@ namespace Swift {
 
			~OpenSSLServerContext();
 

	
 
			void connect();
 
			bool setServerCertificate(const PKCS12Certificate& cert);
 
			bool setServerCertificate(CertificateWithKey::ref cert);
 

	
 
			void handleDataFromNetwork(const SafeByteArray&);
 
			void handleDataFromApplication(const SafeByteArray&);
include/Swiften/TLS/Schannel/SchannelServerContext.cpp
Show inline comments
 
new file 100644
 
/*
 
 * Copyright (c) 2011 Soren Dreijer
 
 * Licensed under the simplified BSD license.
 
 * See Documentation/Licenses/BSD-simplified.txt for more information.
 
 */
 

	
 
#include <Swiften/TLS/Schannel/SchannelServerContext.h>
 
#include <Swiften/TLS/Schannel/SchannelCertificate.h>
 
#include <Swiften/TLS/CAPICertificate.h>
 

	
 
namespace Swift {
 

	
 
//------------------------------------------------------------------------
 

	
 
SchannelServerContext::SchannelServerContext() 
 
: m_state(Start)
 
, m_secContext(0)
 
, m_verificationError(CertificateVerificationError::UnknownError)
 
, m_my_cert_store(NULL)
 
, m_cert_store_name("MY")
 
, m_cert_name()
 
{
 
	m_ctxtFlags = ISC_REQ_ALLOCATE_MEMORY | 
 
				  ISC_REQ_CONFIDENTIALITY |
 
				  ISC_REQ_EXTENDED_ERROR  |
 
				  ISC_REQ_INTEGRITY		  |
 
				  ISC_REQ_REPLAY_DETECT   |	
 
				  ISC_REQ_SEQUENCE_DETECT |
 
				  ISC_REQ_USE_SUPPLIED_CREDS |
 
				  ISC_REQ_STREAM;
 

	
 
	ZeroMemory(&m_streamSizes, sizeof(m_streamSizes));
 
}
 

	
 
//------------------------------------------------------------------------
 

	
 
SchannelServerContext::~SchannelServerContext()
 
{
 
	if (m_my_cert_store) CertCloseStore(m_my_cert_store, 0);
 
}
 

	
 
//------------------------------------------------------------------------
 

	
 
void SchannelServerContext::determineStreamSizes()
 
{
 
	QueryContextAttributes(m_ctxtHandle, SECPKG_ATTR_STREAM_SIZES, &m_streamSizes);
 
}
 

	
 
//------------------------------------------------------------------------
 

	
 
void SchannelServerContext::connect() 
 
{
 
	PCCERT_CONTEXT   pCertContext = NULL;
 

	
 
	m_state = Connecting;
 

	
 
	// If a user name is specified, then attempt to find a client
 
	// certificate. Otherwise, just create a NULL credential.
 
	if (!m_cert_name.empty())
 
	{
 
		if (m_my_cert_store == NULL)
 
		{
 
			m_my_cert_store = CertOpenSystemStore(0, m_cert_store_name.c_str());
 
			if (!m_my_cert_store)
 
			{
 
/////			printf( "**** Error 0x%x returned by CertOpenSystemStore\n", GetLastError() );
 
				indicateError();
 
				return;
 
			}
 
		}
 

	
 
		// Find client certificate. Note that this sample just searches for a 
 
		// certificate that contains the user name somewhere in the subject name.
 
		pCertContext = CertFindCertificateInStore( m_my_cert_store,
 
			X509_ASN_ENCODING,
 
			0,				// dwFindFlags
 
			CERT_FIND_SUBJECT_STR_A,
 
			m_cert_name.c_str(),		// *pvFindPara
 
			NULL );				// pPrevCertContext
 

	
 
		if (pCertContext == NULL)
 
		{
 
/////		printf("**** Error 0x%x returned by CertFindCertificateInStore\n", GetLastError());
 
			indicateError();
 
			return;
 
		}
 
	}
 

	
 
	// We use an empty list for client certificates
 
	PCCERT_CONTEXT clientCerts[1] = {0};
 

	
 
	SCHANNEL_CRED sc = {0};
 
	sc.dwVersion = SCHANNEL_CRED_VERSION;
 

	
 
/////SSL3?
 
	sc.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT | SP_PROT_TLS1_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT;
 
/////Check SCH_CRED_REVOCATION_CHECK_CHAIN
 
	sc.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION | SCH_CRED_REVOCATION_CHECK_CHAIN;
 

	
 
	if (pCertContext)
 
	{
 
		sc.cCreds = 1;
 
		sc.paCred = &pCertContext;
 
		sc.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;
 
	}
 
	else
 
	{
 
		sc.cCreds = 0; // Let Crypto API find the appropriate certificate for us
 
		sc.paCred = clientCerts;
 
		sc.dwFlags |= SCH_CRED_USE_DEFAULT_CREDS;
 
	}
 

	
 
	// Swiften performs the server name check for us
 
	sc.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
 

	
 
	SECURITY_STATUS status = AcquireCredentialsHandle(
 
		NULL,
 
		UNISP_NAME,
 
		SECPKG_CRED_OUTBOUND,
 
		NULL,
 
		&sc,
 
		NULL,
 
		NULL,
 
		m_credHandle.Reset(),
 
		NULL);
 

	
 
	// cleanup: Free the certificate context. Schannel has already made its own copy.
 
	if (pCertContext) CertFreeCertificateContext(pCertContext);
 

	
 
	if (status != SEC_E_OK) 
 
	{
 
		// We failed to obtain the credentials handle
 
		indicateError();
 
		return;
 
	}
 

	
 
	SecBuffer outBuffers[2];
 

	
 
	// We let Schannel allocate the output buffer for us
 
	outBuffers[0].pvBuffer   = NULL;
 
	outBuffers[0].cbBuffer   = 0;
 
	outBuffers[0].BufferType = SECBUFFER_TOKEN;
 

	
 
	// Contains alert data if an alert is generated
 
	outBuffers[1].pvBuffer   = NULL;
 
	outBuffers[1].cbBuffer   = 0;
 
	outBuffers[1].BufferType = SECBUFFER_ALERT;
 

	
 
	// Make sure the output buffers are freed
 
	ScopedSecBuffer scopedOutputData(&outBuffers[0]);
 
	ScopedSecBuffer scopedOutputAlertData(&outBuffers[1]);
 

	
 
	SecBufferDesc outBufferDesc = {0};
 
	outBufferDesc.cBuffers   = 2;
 
	outBufferDesc.pBuffers   = outBuffers;
 
	outBufferDesc.ulVersion  = SECBUFFER_VERSION;
 

	
 
	// Create the initial security context
 
	status = InitializeSecurityContext(
 
		m_credHandle,
 
		NULL,
 
		NULL,
 
		m_ctxtFlags,
 
		0,
 
		0,
 
		NULL,
 
		0,
 
		m_ctxtHandle.Reset(),
 
		&outBufferDesc,
 
		&m_secContext,
 
		NULL);
 

	
 
	if (status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) 
 
	{
 
		// We failed to initialize the security context
 
		indicateError();
 
		return;
 
	}
 

	
 
	// Start the handshake
 
	sendDataOnNetwork(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer);
 

	
 
	if (status == SEC_E_OK) 
 
	{
 
		m_state = Connected;
 
		determineStreamSizes();
 

	
 
		onConnected();
 
	}
 
}
 

	
 
//------------------------------------------------------------------------
 

	
 
void SchannelServerContext::appendNewData(const SafeByteArray& data)
 
{
 
	size_t originalSize = m_receivedData.size();
 
	m_receivedData.resize( originalSize + data.size() );
 
	memcpy( &m_receivedData[0] + originalSize, &data[0], data.size() );
 
}
 

	
 
//------------------------------------------------------------------------
 

	
 
void SchannelServerContext::continueHandshake(const SafeByteArray& data) 
 
{
 
	appendNewData(data);
 

	
 
	while (!m_receivedData.empty())
 
	{
 
		SecBuffer inBuffers[2];
 

	
 
		// Provide Schannel with the remote host's handshake data
 
		inBuffers[0].pvBuffer	 = (char*)(&m_receivedData[0]);
 
		inBuffers[0].cbBuffer	 = (unsigned long)m_receivedData.size();
 
		inBuffers[0].BufferType  = SECBUFFER_TOKEN;
 

	
 
		inBuffers[1].pvBuffer   = NULL;
 
		inBuffers[1].cbBuffer   = 0;
 
		inBuffers[1].BufferType = SECBUFFER_EMPTY;
 

	
 
		SecBufferDesc inBufferDesc = {0};
 
		inBufferDesc.cBuffers   = 2;
 
		inBufferDesc.pBuffers   = inBuffers;
 
		inBufferDesc.ulVersion  = SECBUFFER_VERSION;
 

	
 
		SecBuffer outBuffers[2];
 

	
 
		// We let Schannel allocate the output buffer for us
 
		outBuffers[0].pvBuffer   = NULL;
 
		outBuffers[0].cbBuffer   = 0;
 
		outBuffers[0].BufferType = SECBUFFER_TOKEN;
 

	
 
		// Contains alert data if an alert is generated
 
		outBuffers[1].pvBuffer   = NULL;
 
		outBuffers[1].cbBuffer   = 0;
 
		outBuffers[1].BufferType = SECBUFFER_ALERT;
 

	
 
		// Make sure the output buffers are freed
 
		ScopedSecBuffer scopedOutputData(&outBuffers[0]);
 
		ScopedSecBuffer scopedOutputAlertData(&outBuffers[1]);
 

	
 
		SecBufferDesc outBufferDesc = {0};
 
		outBufferDesc.cBuffers   = 2;
 
		outBufferDesc.pBuffers   = outBuffers;
 
		outBufferDesc.ulVersion  = SECBUFFER_VERSION;
 

	
 
		SECURITY_STATUS status = InitializeSecurityContext(
 
			m_credHandle,
 
			m_ctxtHandle,
 
			NULL,
 
			m_ctxtFlags,
 
			0,
 
			0,
 
			&inBufferDesc,
 
			0,
 
			NULL,
 
			&outBufferDesc,
 
			&m_secContext,
 
			NULL);
 

	
 
		if (status == SEC_E_INCOMPLETE_MESSAGE)	
 
		{
 
			// Wait for more data to arrive
 
			break;
 
		}
 
		else if (status == SEC_I_CONTINUE_NEEDED) 
 
		{
 
			SecBuffer* pDataBuffer = &outBuffers[0];
 
			SecBuffer* pExtraBuffer = &inBuffers[1];
 
			
 
			if (pDataBuffer && pDataBuffer->cbBuffer > 0 && pDataBuffer->pvBuffer != NULL)
 
				sendDataOnNetwork(pDataBuffer->pvBuffer, pDataBuffer->cbBuffer);
 

	
 
			if (pExtraBuffer->BufferType == SECBUFFER_EXTRA)
 
				m_receivedData.erase(m_receivedData.begin(), m_receivedData.end() - pExtraBuffer->cbBuffer);
 
			else
 
				m_receivedData.clear();
 

	
 
			break;
 
		}
 
		else if (status == SEC_E_OK) 
 
		{
 
			SecBuffer* pExtraBuffer = &inBuffers[1];
 
			
 
			if (pExtraBuffer && pExtraBuffer->cbBuffer > 0)
 
				m_receivedData.erase(m_receivedData.begin(), m_receivedData.end() - pExtraBuffer->cbBuffer);
 
			else
 
				m_receivedData.clear();
 

	
 
			m_state = Connected;
 
			determineStreamSizes();
 

	
 
			onConnected();
 
		} 
 
		else 
 
		{
 
			// We failed to initialize the security context
 
			indicateError();
 
			return;
 
		}
 
	}
 
}
 

	
 
//------------------------------------------------------------------------
 

	
 
void SchannelServerContext::sendDataOnNetwork(const void* pData, size_t dataSize) 
 
{
 
	if (dataSize > 0 && pData) 
 
	{
 
		SafeByteArray byteArray(dataSize);
 
		memcpy(&byteArray[0], pData, dataSize);
 

	
 
		onDataForNetwork(byteArray);
 
	}
 
}
 

	
 
//------------------------------------------------------------------------
 

	
 
void SchannelServerContext::forwardDataToApplication(const void* pData, size_t dataSize) 
 
{
 
	SafeByteArray byteArray(dataSize);
 
	memcpy(&byteArray[0], pData, dataSize);
 

	
 
	onDataForApplication(byteArray);
 
}
 

	
 
//------------------------------------------------------------------------
 

	
 
void SchannelServerContext::handleDataFromApplication(const SafeByteArray& data) 
 
{
 
	// Don't attempt to send data until we're fully connected
 
	if (m_state == Connecting)
 
		return;
 

	
 
	// Encrypt the data
 
	encryptAndSendData(data);
 
}
 

	
 
//------------------------------------------------------------------------
 

	
 
void SchannelServerContext::handleDataFromNetwork(const SafeByteArray& data) 
 
{
 
	switch (m_state)
 
	{
 
	case Connecting:
 
		{
 
			// We're still establishing the connection, so continue the handshake
 
			continueHandshake(data);
 
		}
 
		break;
 

	
 
	case Connected:
 
		{
 
			// Decrypt the data
 
			decryptAndProcessData(data);
 
		}
 
		break;
 

	
 
	default:
 
		return;
 
	}
 
}
 

	
 
//------------------------------------------------------------------------
 

	
 
void SchannelServerContext::indicateError() 
 
{
 
	m_state = Error;
 
	m_receivedData.clear();
 
	onError();
 
}
 

	
 
//------------------------------------------------------------------------
 

	
 
void SchannelServerContext::decryptAndProcessData(const SafeByteArray& data) 
 
{
 
	SecBuffer inBuffers[4]	= {0};
 

	
 
	appendNewData(data);
 
	
 
	while (!m_receivedData.empty())
 
	{
 
		//
 
		// MSDN: 
 
		//   When using the Schannel SSP with contexts that are not connection oriented, on input, 
 
		//   the structure must contain four SecBuffer structures. Exactly one buffer must be of type 
 
		//   SECBUFFER_DATA and contain an encrypted message, which is decrypted in place. The remaining 
 
		//   buffers are used for output and must be of type SECBUFFER_EMPTY. For connection-oriented 
 
		//   contexts, a SECBUFFER_DATA type buffer must be supplied, as noted for nonconnection-oriented 
 
		//   contexts. Additionally, a second SECBUFFER_TOKEN type buffer that contains a security token 
 
		//   must also be supplied.
 
		//
 
		inBuffers[0].pvBuffer	 = (char*)(&m_receivedData[0]);
 
		inBuffers[0].cbBuffer	 = (unsigned long)m_receivedData.size();
 
		inBuffers[0].BufferType  = SECBUFFER_DATA;
 

	
 
		inBuffers[1].BufferType  = SECBUFFER_EMPTY;
 
		inBuffers[2].BufferType  = SECBUFFER_EMPTY;
 
		inBuffers[3].BufferType  = SECBUFFER_EMPTY;
 

	
 
		SecBufferDesc inBufferDesc = {0};
 
		inBufferDesc.cBuffers      = 4;
 
		inBufferDesc.pBuffers      = inBuffers;
 
		inBufferDesc.ulVersion     = SECBUFFER_VERSION;
 

	
 
		size_t inData = m_receivedData.size();
 
		SECURITY_STATUS status = DecryptMessage(m_ctxtHandle, &inBufferDesc, 0, NULL);
 

	
 
		if (status == SEC_E_INCOMPLETE_MESSAGE) 
 
		{
 
			// Wait for more data to arrive
 
			break;
 
		} 
 
		else if (status == SEC_I_RENEGOTIATE) 
 
		{
 
			// TODO: Handle renegotiation scenarios
 
			indicateError();
 
			break;
 
		} 
 
		else if (status == SEC_I_CONTEXT_EXPIRED) 
 
		{
 
			indicateError();
 
			break;
 
		} 
 
		else if (status != SEC_E_OK) 
 
		{
 
			indicateError();
 
			break;
 
		}
 

	
 
		SecBuffer* pDataBuffer = NULL;
 
		SecBuffer* pExtraBuffer = NULL;
 
		for (int i = 0; i < 4; ++i) 
 
		{
 
			if (inBuffers[i].BufferType == SECBUFFER_DATA)
 
				pDataBuffer = &inBuffers[i];
 

	
 
			else if (inBuffers[i].BufferType == SECBUFFER_EXTRA)
 
				pExtraBuffer = &inBuffers[i];
 
		}
 

	
 
		if (pDataBuffer && pDataBuffer->cbBuffer > 0 && pDataBuffer->pvBuffer != NULL)
 
			forwardDataToApplication(pDataBuffer->pvBuffer, pDataBuffer->cbBuffer);
 

	
 
		// If there is extra data left over from the decryption operation, we call DecryptMessage() again
 
		if (pExtraBuffer) 
 
		{
 
			m_receivedData.erase(m_receivedData.begin(), m_receivedData.end() - pExtraBuffer->cbBuffer);
 
		} 
 
		else 
 
		{
 
			// We're done
 
			m_receivedData.erase(m_receivedData.begin(), m_receivedData.begin() + inData);
 
		}
 
	}
 
}
 

	
 
//------------------------------------------------------------------------
 

	
 
void SchannelServerContext::encryptAndSendData(const SafeByteArray& data) 
 
{	
 
	SecBuffer outBuffers[4]	= {0};
 

	
 
	// Calculate the largest required size of the send buffer
 
	size_t messageBufferSize = (data.size() > m_streamSizes.cbMaximumMessage) 
 
							 ? m_streamSizes.cbMaximumMessage 
 
							 : data.size();
 

	
 
	// Allocate a packet for the encrypted data
 
	SafeByteArray sendBuffer;
 
	sendBuffer.resize(m_streamSizes.cbHeader + messageBufferSize + m_streamSizes.cbTrailer);
 

	
 
	size_t bytesSent = 0;
 
	do 
 
	{
 
		size_t bytesLeftToSend = data.size() - bytesSent;
 

	
 
		// Calculate how much of the send buffer we'll be using for this chunk
 
		size_t bytesToSend = (bytesLeftToSend > m_streamSizes.cbMaximumMessage) 
 
						   ? m_streamSizes.cbMaximumMessage 
 
						   : bytesLeftToSend;
 
		
 
		// Copy the plain text data into the send buffer
 
		memcpy(&sendBuffer[0] + m_streamSizes.cbHeader, &data[0] + bytesSent, bytesToSend);
 

	
 
		outBuffers[0].pvBuffer	 = &sendBuffer[0];
 
		outBuffers[0].cbBuffer	 = m_streamSizes.cbHeader;
 
		outBuffers[0].BufferType = SECBUFFER_STREAM_HEADER;
 

	
 
		outBuffers[1].pvBuffer	 = &sendBuffer[0] + m_streamSizes.cbHeader;
 
		outBuffers[1].cbBuffer	 = (unsigned long)bytesToSend;
 
		outBuffers[1].BufferType = SECBUFFER_DATA;
 

	
 
		outBuffers[2].pvBuffer	 = &sendBuffer[0] + m_streamSizes.cbHeader + bytesToSend;
 
		outBuffers[2].cbBuffer	 = m_streamSizes.cbTrailer;
 
		outBuffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
 

	
 
		outBuffers[3].pvBuffer   = 0;
 
		outBuffers[3].cbBuffer   = 0;
 
		outBuffers[3].BufferType = SECBUFFER_EMPTY;
 

	
 
		SecBufferDesc outBufferDesc = {0};
 
		outBufferDesc.cBuffers   = 4;
 
		outBufferDesc.pBuffers   = outBuffers;
 
		outBufferDesc.ulVersion  = SECBUFFER_VERSION;
 

	
 
		SECURITY_STATUS status = EncryptMessage(m_ctxtHandle, 0, &outBufferDesc, 0);
 
		if (status != SEC_E_OK) 
 
		{
 
			indicateError();
 
			return;
 
		}
 

	
 
		sendDataOnNetwork(&sendBuffer[0], outBuffers[0].cbBuffer + outBuffers[1].cbBuffer + outBuffers[2].cbBuffer);
 
		bytesSent += bytesToSend;
 

	
 
	} while (bytesSent < data.size());
 
}
 

	
 
//------------------------------------------------------------------------
 

	
 
bool SchannelServerContext::setServerCertificate(CertificateWithKey::ref certificate)
 
{
 
	boost::shared_ptr<CAPICertificate> capiCertificate = boost::dynamic_pointer_cast<CAPICertificate>(certificate);
 
	if (!capiCertificate || capiCertificate->isNull()) {
 
		return false;
 
	}
 

	
 
	// We assume that the Certificate Store Name/Certificate Name
 
	// are valid at this point
 
	m_cert_store_name = capiCertificate->getCertStoreName();
 
	m_cert_name = capiCertificate->getCertName();
 
	return true;
 
}
 

	
 
//------------------------------------------------------------------------
 

	
 
Certificate::ref SchannelServerContext::getPeerCertificate() const 
 
{
 
	SchannelCertificate::ref pCertificate;
 

	
 
	ScopedCertContext pServerCert;
 
	SECURITY_STATUS status = QueryContextAttributes(m_ctxtHandle, SECPKG_ATTR_REMOTE_CERT_CONTEXT, pServerCert.Reset());
 
	if (status != SEC_E_OK)
 
		return pCertificate;
 

	
 
	pCertificate.reset( new SchannelCertificate(pServerCert) );
 
	return pCertificate;
 
}
 

	
 
//------------------------------------------------------------------------
 

	
 
CertificateVerificationError::ref SchannelServerContext::getPeerCertificateVerificationError() const 
 
{
 
	boost::shared_ptr<CertificateVerificationError> pCertError;
 

	
 
	if (m_state == Error)
 
		pCertError.reset( new CertificateVerificationError(m_verificationError) );
 
	
 
	return pCertError;
 
}
 

	
 
//------------------------------------------------------------------------
 

	
 
ByteArray SchannelServerContext::getFinishMessage() const
 
{
 
	// TODO: Implement
 

	
 
	ByteArray emptyArray;
 
	return emptyArray;
 
}
 

	
 
//------------------------------------------------------------------------
 

	
 
}
include/Swiften/TLS/Schannel/SchannelServerContext.h
Show inline comments
 
new file 100644
 
/*
 
 * Copyright (c) 2011 Soren Dreijer
 
 * Licensed under the simplified BSD license.
 
 * See Documentation/Licenses/BSD-simplified.txt for more information.
 
 */
 

	
 
#pragma once
 

	
 
#include "Swiften/Base/boost_bsignals.h"
 

	
 
#include "Swiften/TLS/TLSServerContext.h"
 
#include "Swiften/TLS/Schannel/SchannelUtil.h"
 
#include <Swiften/TLS/CertificateWithKey.h>
 
#include "Swiften/Base/ByteArray.h"
 

	
 
#define SECURITY_WIN32
 
#include <Windows.h>
 
#include <Schannel.h>
 
#include <security.h>
 
#include <schnlsp.h>
 

	
 
#include <boost/noncopyable.hpp>
 

	
 
namespace Swift 
 
{	
 
	class SchannelServerContext : public TLSServerContext, boost::noncopyable 
 
	{
 
	public:
 
		typedef boost::shared_ptr<SchannelServerContext> sp_t;
 

	
 
	public:
 
		SchannelServerContext();
 

	
 
		~SchannelServerContext();
 

	
 
		//
 
		// TLSContext
 
		//
 
		virtual void	connect();
 
		virtual bool	setServerCertificate(CertificateWithKey::ref cert);
 

	
 
		virtual void	handleDataFromNetwork(const SafeByteArray& data);
 
		virtual void	handleDataFromApplication(const SafeByteArray& data);
 

	
 
		virtual Certificate::ref getPeerCertificate() const;
 
		virtual CertificateVerificationError::ref getPeerCertificateVerificationError() const;
 

	
 
		virtual ByteArray getFinishMessage() const;
 

	
 
	private:
 
		void			determineStreamSizes();
 
		void			continueHandshake(const SafeByteArray& data);
 
		void			indicateError();
 

	
 
		void			sendDataOnNetwork(const void* pData, size_t dataSize);
 
		void			forwardDataToApplication(const void* pData, size_t dataSize);
 

	
 
		void			decryptAndProcessData(const SafeByteArray& data);
 
		void			encryptAndSendData(const SafeByteArray& data);
 

	
 
		void			appendNewData(const SafeByteArray& data);
 

	
 
	private:
 
		enum SchannelState
 
		{
 
			Start,
 
			Connecting,
 
			Connected,
 
			Error
 

	
 
		};
 

	
 
		SchannelState		m_state;
 
		CertificateVerificationError m_verificationError;
 

	
 
		ULONG				m_secContext;
 
		ScopedCredHandle	m_credHandle;
 
		ScopedCtxtHandle	m_ctxtHandle;
 
		DWORD				m_ctxtFlags;
 
		SecPkgContext_StreamSizes m_streamSizes;
 

	
 
		std::vector<char>	m_receivedData;
 

	
 
		HCERTSTORE		m_my_cert_store;
 
		std::string		m_cert_store_name;
 
		std::string		m_cert_name;
 
	};
 
}
include/Swiften/TLS/Schannel/SchannelServerContextFactory.cpp
Show inline comments
 
new file 100644
 
/*
 
 * Copyright (c) 2011 Soren Dreijer
 
 * Licensed under the simplified BSD license.
 
 * See Documentation/Licenses/BSD-simplified.txt for more information.
 
 */
 

	
 
#include "Swiften/TLS/Schannel/SchannelServerContextFactory.h"
 
#include "Swiften/TLS/Schannel/SchannelServerContext.h"
 

	
 
namespace Swift {
 

	
 
bool SchannelServerContextFactory::canCreate() const {
 
	return true;
 
}
 

	
 
TLSServerContext* SchannelServerContextFactory::createTLSServerContext() {
 
	return new SchannelServerContext();
 
}
 

	
 
}
include/Swiften/TLS/Schannel/SchannelServerContextFactory.h
Show inline comments
 
new file 100644
 
/*
 
 * Copyright (c) 2011 Soren Dreijer
 
 * Licensed under the simplified BSD license.
 
 * See Documentation/Licenses/BSD-simplified.txt for more information.
 
 */
 

	
 
#pragma once
 

	
 
#include "Swiften/TLS/TLSServerContextFactory.h"
 

	
 
namespace Swift {
 
	class SchannelServerContextFactory : public TLSServerContextFactory {
 
		public:
 
			bool canCreate() const;
 
			virtual TLSServerContext* createTLSServerContext();
 
	};
 
}
include/Swiften/TLS/TLSServerContext.h
Show inline comments
 
@@ -11,6 +11,7 @@
 

	
 
#include "Swiften/Base/SafeByteArray.h"
 
#include "Swiften/TLS/Certificate.h"
 
#include <Swiften/TLS/CertificateWithKey.h>
 
#include "Swiften/TLS/CertificateVerificationError.h"
 

	
 
namespace Swift {
 
@@ -22,7 +23,7 @@ namespace Swift {
 

	
 
			virtual void connect() = 0;
 

	
 
			virtual bool setServerCertificate(const PKCS12Certificate& cert) = 0;
 
			virtual bool setServerCertificate(CertificateWithKey::ref cert) = 0;
 

	
 
			virtual void handleDataFromNetwork(const SafeByteArray&) = 0;
 
			virtual void handleDataFromApplication(const SafeByteArray&) = 0;
include/transport/CMakeLists.txt
Show inline comments
 
if (PROTOBUF_FOUND)
 
    if (NOT CMAKE_COMPILER_IS_GNUCXX)
 
	set (PROTOBUF_PROTOC_EXECUTABLE protoc)
 
    endif()
 
    ADD_CUSTOM_COMMAND(
 
        OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/protocol.pb.cc ${CMAKE_CURRENT_SOURCE_DIR}/protocol.pb.h
 
        COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} --cpp_out  ${CMAKE_CURRENT_SOURCE_DIR} --proto_path ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/protocol.proto
msvc-deps/CMakeLists.txt
Show inline comments
 
new file 100644
 
ADD_SUBDIRECTORY(sqlite3)
msvc-deps/protobuf/CMakeLists.txt
Show inline comments
 
new file 100644
 
add_subdirectory(libprotobuf)
 
add_subdirectory(libprotoc)
 
add_subdirectory(protoc)
 
\ No newline at end of file
msvc-deps/protobuf/libprotobuf/CMakeLists.txt
Show inline comments
 
new file 100644
 
cmake_minimum_required(VERSION 2.6)
 
FILE(GLOB SRC google/protobuf/*.cc google/protobuf/*.h google/protobuf/io/*.cc google/protobuf/io/*.h google/protobuf/stubs/*.cc google/protobuf/stubs/*.h google/protobuf/compiler/*.cc google/protobuf/compiler/*.h)
 

	
 
include_directories(.)
 

	
 
ADD_LIBRARY(libprotobuf STATIC ${HEADERS} ${SRC})
 

	
 
INSTALL(TARGETS libprotobuf LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries)
 
\ No newline at end of file
msvc-deps/protobuf/libprotobuf/config.h
Show inline comments
 
new file 100644
 
/* protobuf config.h for MSVC.  On other platforms, this is generated
 
 * automatically by autoheader / autoconf / configure. */
 

	
 
/* the location of <hash_map> */
 
#define HASH_MAP_H <hash_map>
 

	
 
/* the namespace of hash_map/hash_set */
 
// Apparently Microsoft decided to move hash_map *back* to the std namespace
 
// in MSVC 2010:
 
//   http://blogs.msdn.com/vcblog/archive/2009/05/25/stl-breaking-changes-in-visual-studio-2010-beta-1.aspx
 
// TODO(kenton):  Use unordered_map instead, which is available in MSVC 2010.
 
#if _MSC_VER < 1310 || _MSC_VER >= 1600
 
#define HASH_NAMESPACE ext
 
#else
 
#define HASH_NAMESPACE stdext
 
#endif
 

	
 
/* the location of <hash_set> */
 
#define HASH_SET_H <hash_set>
 

	
 
/* define if the compiler has hash_map */
 
#define HAVE_HASH_MAP 1
 

	
 
/* define if the compiler has hash_set */
 
#define HAVE_HASH_SET 1
 

	
 
/* define if you want to use zlib.  See readme.txt for additional
 
 * requirements. */
 
// #define HAVE_ZLIB 1
msvc-deps/protobuf/libprotobuf/google/protobuf/compiler/importer.cc
Show inline comments
 
new file 100644
 
// Protocol Buffers - Google's data interchange format
 
// Copyright 2008 Google Inc.  All rights reserved.
 
// http://code.google.com/p/protobuf/
 
//
 
// Redistribution and use in source and binary forms, with or without
 
// modification, are permitted provided that the following conditions are
 
// met:
 
//
 
//     * Redistributions of source code must retain the above copyright
 
// notice, this list of conditions and the following disclaimer.
 
//     * Redistributions in binary form must reproduce the above
 
// copyright notice, this list of conditions and the following disclaimer
 
// in the documentation and/or other materials provided with the
 
// distribution.
 
//     * Neither the name of Google Inc. nor the names of its
 
// contributors may be used to endorse or promote products derived from
 
// this software without specific prior written permission.
 
//
 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 

	
 
// Author: kenton@google.com (Kenton Varda)
 
//  Based on original Protocol Buffers design by
 
//  Sanjay Ghemawat, Jeff Dean, and others.
 

	
 
#ifdef _MSC_VER
 
#include <io.h>
 
#else
 
#include <unistd.h>
 
#endif
 
#include <sys/types.h>
 
#include <sys/stat.h>
 
#include <fcntl.h>
 
#include <errno.h>
 

	
 
#include <algorithm>
 

	
 
#include <google/protobuf/compiler/importer.h>
 

	
 
#include <google/protobuf/compiler/parser.h>
 
#include <google/protobuf/io/tokenizer.h>
 
#include <google/protobuf/io/zero_copy_stream_impl.h>
 
#include <google/protobuf/stubs/strutil.h>
 

	
 
namespace google {
 
namespace protobuf {
 
namespace compiler {
 

	
 
#ifdef _WIN32
 
#ifndef F_OK
 
#define F_OK 00  // not defined by MSVC for whatever reason
 
#endif
 
#include <ctype.h>
 
#endif
 

	
 
// Returns true if the text looks like a Windows-style absolute path, starting
 
// with a drive letter.  Example:  "C:\foo".  TODO(kenton):  Share this with
 
// copy in command_line_interface.cc?
 
static bool IsWindowsAbsolutePath(const string& text) {
 
#if defined(_WIN32) || defined(__CYGWIN__)
 
  return text.size() >= 3 && text[1] == ':' &&
 
         isalpha(text[0]) &&
 
         (text[2] == '/' || text[2] == '\\') &&
 
         text.find_last_of(':') == 1;
 
#else
 
  return false;
 
#endif
 
}
 

	
 
MultiFileErrorCollector::~MultiFileErrorCollector() {}
 

	
 
// This class serves two purposes:
 
// - It implements the ErrorCollector interface (used by Tokenizer and Parser)
 
//   in terms of MultiFileErrorCollector, using a particular filename.
 
// - It lets us check if any errors have occurred.
 
class SourceTreeDescriptorDatabase::SingleFileErrorCollector
 
    : public io::ErrorCollector {
 
 public:
 
  SingleFileErrorCollector(const string& filename,
 
                           MultiFileErrorCollector* multi_file_error_collector)
 
    : filename_(filename),
 
      multi_file_error_collector_(multi_file_error_collector),
 
      had_errors_(false) {}
 
  ~SingleFileErrorCollector() {}
 

	
 
  bool had_errors() { return had_errors_; }
 

	
 
  // implements ErrorCollector ---------------------------------------
 
  void AddError(int line, int column, const string& message) {
 
    if (multi_file_error_collector_ != NULL) {
 
      multi_file_error_collector_->AddError(filename_, line, column, message);
 
    }
 
    had_errors_ = true;
 
  }
 

	
 
 private:
 
  string filename_;
 
  MultiFileErrorCollector* multi_file_error_collector_;
 
  bool had_errors_;
 
};
 

	
 
// ===================================================================
 

	
 
SourceTreeDescriptorDatabase::SourceTreeDescriptorDatabase(
 
    SourceTree* source_tree)
 
  : source_tree_(source_tree),
 
    error_collector_(NULL),
 
    using_validation_error_collector_(false),
 
    validation_error_collector_(this) {}
 

	
 
SourceTreeDescriptorDatabase::~SourceTreeDescriptorDatabase() {}
 

	
 
bool SourceTreeDescriptorDatabase::FindFileByName(
 
    const string& filename, FileDescriptorProto* output) {
 
  scoped_ptr<io::ZeroCopyInputStream> input(source_tree_->Open(filename));
 
  if (input == NULL) {
 
    if (error_collector_ != NULL) {
 
      error_collector_->AddError(filename, -1, 0, "File not found.");
 
    }
 
    return false;
 
  }
 

	
 
  // Set up the tokenizer and parser.
 
  SingleFileErrorCollector file_error_collector(filename, error_collector_);
 
  io::Tokenizer tokenizer(input.get(), &file_error_collector);
 

	
 
  Parser parser;
 
  if (error_collector_ != NULL) {
 
    parser.RecordErrorsTo(&file_error_collector);
 
  }
 
  if (using_validation_error_collector_) {
 
    parser.RecordSourceLocationsTo(&source_locations_);
 
  }
 

	
 
  // Parse it.
 
  output->set_name(filename);
 
  return parser.Parse(&tokenizer, output) &&
 
         !file_error_collector.had_errors();
 
}
 

	
 
bool SourceTreeDescriptorDatabase::FindFileContainingSymbol(
 
    const string& symbol_name, FileDescriptorProto* output) {
 
  return false;
 
}
 

	
 
bool SourceTreeDescriptorDatabase::FindFileContainingExtension(
 
    const string& containing_type, int field_number,
 
    FileDescriptorProto* output) {
 
  return false;
 
}
 

	
 
// -------------------------------------------------------------------
 

	
 
SourceTreeDescriptorDatabase::ValidationErrorCollector::
 
ValidationErrorCollector(SourceTreeDescriptorDatabase* owner)
 
  : owner_(owner) {}
 

	
 
SourceTreeDescriptorDatabase::ValidationErrorCollector::
 
~ValidationErrorCollector() {}
 

	
 
void SourceTreeDescriptorDatabase::ValidationErrorCollector::AddError(
 
    const string& filename,
 
    const string& element_name,
 
    const Message* descriptor,
 
    ErrorLocation location,
 
    const string& message) {
 
  if (owner_->error_collector_ == NULL) return;
 

	
 
  int line, column;
 
  owner_->source_locations_.Find(descriptor, location, &line, &column);
 
  owner_->error_collector_->AddError(filename, line, column, message);
 
}
 

	
 
// ===================================================================
 

	
 
Importer::Importer(SourceTree* source_tree,
 
                   MultiFileErrorCollector* error_collector)
 
  : database_(source_tree),
 
    pool_(&database_, database_.GetValidationErrorCollector()) {
 
  database_.RecordErrorsTo(error_collector);
 
}
 

	
 
Importer::~Importer() {}
 

	
 
const FileDescriptor* Importer::Import(const string& filename) {
 
  return pool_.FindFileByName(filename);
 
}
 

	
 
// ===================================================================
 

	
 
SourceTree::~SourceTree() {}
 

	
 
DiskSourceTree::DiskSourceTree() {}
 

	
 
DiskSourceTree::~DiskSourceTree() {}
 

	
 
static inline char LastChar(const string& str) {
 
  return str[str.size() - 1];
 
}
 

	
 
// Given a path, returns an equivalent path with these changes:
 
// - On Windows, any backslashes are replaced with forward slashes.
 
// - Any instances of the directory "." are removed.
 
// - Any consecutive '/'s are collapsed into a single slash.
 
// Note that the resulting string may be empty.
 
//
 
// TODO(kenton):  It would be nice to handle "..", e.g. so that we can figure
 
//   out that "foo/bar.proto" is inside "baz/../foo".  However, if baz is a
 
//   symlink or doesn't exist, then things get complicated, and we can't
 
//   actually determine this without investigating the filesystem, probably
 
//   in non-portable ways.  So, we punt.
 
//
 
// TODO(kenton):  It would be nice to use realpath() here except that it
 
//   resolves symbolic links.  This could cause problems if people place
 
//   symbolic links in their source tree.  For example, if you executed:
 
//     protoc --proto_path=foo foo/bar/baz.proto
 
//   then if foo/bar is a symbolic link, foo/bar/baz.proto will canonicalize
 
//   to a path which does not appear to be under foo, and thus the compiler
 
//   will complain that baz.proto is not inside the --proto_path.
 
static string CanonicalizePath(string path) {
 
#ifdef _WIN32
 
  // The Win32 API accepts forward slashes as a path delimiter even though
 
  // backslashes are standard.  Let's avoid confusion and use only forward
 
  // slashes.
 
  if (HasPrefixString(path, "\\\\")) {
 
    // Avoid converting two leading backslashes.
 
    path = "\\\\" + StringReplace(path.substr(2), "\\", "/", true);
 
  } else {
 
    path = StringReplace(path, "\\", "/", true);
 
  }
 
#endif
 

	
 
  vector<string> parts;
 
  vector<string> canonical_parts;
 
  SplitStringUsing(path, "/", &parts);  // Note:  Removes empty parts.
 
  for (int i = 0; i < parts.size(); i++) {
 
    if (parts[i] == ".") {
 
      // Ignore.
 
    } else {
 
      canonical_parts.push_back(parts[i]);
 
    }
 
  }
 
  string result = JoinStrings(canonical_parts, "/");
 
  if (!path.empty() && path[0] == '/') {
 
    // Restore leading slash.
 
    result = '/' + result;
 
  }
 
  if (!path.empty() && LastChar(path) == '/' &&
 
      !result.empty() && LastChar(result) != '/') {
 
    // Restore trailing slash.
 
    result += '/';
 
  }
 
  return result;
 
}
 

	
 
static inline bool ContainsParentReference(const string& path) {
 
  return path == ".." ||
 
         HasPrefixString(path, "../") ||
 
         HasSuffixString(path, "/..") ||
 
         path.find("/../") != string::npos;
 
}
 

	
 
// Maps a file from an old location to a new one.  Typically, old_prefix is
 
// a virtual path and new_prefix is its corresponding disk path.  Returns
 
// false if the filename did not start with old_prefix, otherwise replaces
 
// old_prefix with new_prefix and stores the result in *result.  Examples:
 
//   string result;
 
//   assert(ApplyMapping("foo/bar", "", "baz", &result));
 
//   assert(result == "baz/foo/bar");
 
//
 
//   assert(ApplyMapping("foo/bar", "foo", "baz", &result));
 
//   assert(result == "baz/bar");
 
//
 
//   assert(ApplyMapping("foo", "foo", "bar", &result));
 
//   assert(result == "bar");
 
//
 
//   assert(!ApplyMapping("foo/bar", "baz", "qux", &result));
 
//   assert(!ApplyMapping("foo/bar", "baz", "qux", &result));
 
//   assert(!ApplyMapping("foobar", "foo", "baz", &result));
 
static bool ApplyMapping(const string& filename,
 
                         const string& old_prefix,
 
                         const string& new_prefix,
 
                         string* result) {
 
  if (old_prefix.empty()) {
 
    // old_prefix matches any relative path.
 
    if (ContainsParentReference(filename)) {
 
      // We do not allow the file name to use "..".
 
      return false;
 
    }
 
    if (HasPrefixString(filename, "/") ||
 
        IsWindowsAbsolutePath(filename)) {
 
      // This is an absolute path, so it isn't matched by the empty string.
 
      return false;
 
    }
 
    result->assign(new_prefix);
 
    if (!result->empty()) result->push_back('/');
 
    result->append(filename);
 
    return true;
 
  } else if (HasPrefixString(filename, old_prefix)) {
 
    // old_prefix is a prefix of the filename.  Is it the whole filename?
 
    if (filename.size() == old_prefix.size()) {
 
      // Yep, it's an exact match.
 
      *result = new_prefix;
 
      return true;
 
    } else {
 
      // Not an exact match.  Is the next character a '/'?  Otherwise,
 
      // this isn't actually a match at all.  E.g. the prefix "foo/bar"
 
      // does not match the filename "foo/barbaz".
 
      int after_prefix_start = -1;
 
      if (filename[old_prefix.size()] == '/') {
 
        after_prefix_start = old_prefix.size() + 1;
 
      } else if (filename[old_prefix.size() - 1] == '/') {
 
        // old_prefix is never empty, and canonicalized paths never have
 
        // consecutive '/' characters.
 
        after_prefix_start = old_prefix.size();
 
      }
 
      if (after_prefix_start != -1) {
 
        // Yep.  So the prefixes are directories and the filename is a file
 
        // inside them.
 
        string after_prefix = filename.substr(after_prefix_start);
 
        if (ContainsParentReference(after_prefix)) {
 
          // We do not allow the file name to use "..".
 
          return false;
 
        }
 
        result->assign(new_prefix);
 
        if (!result->empty()) result->push_back('/');
 
        result->append(after_prefix);
 
        return true;
 
      }
 
    }
 
  }
 

	
 
  return false;
 
}
 

	
 
void DiskSourceTree::MapPath(const string& virtual_path,
 
                             const string& disk_path) {
 
  mappings_.push_back(Mapping(virtual_path, CanonicalizePath(disk_path)));
 
}
 

	
 
DiskSourceTree::DiskFileToVirtualFileResult
 
DiskSourceTree::DiskFileToVirtualFile(
 
    const string& disk_file,
 
    string* virtual_file,
 
    string* shadowing_disk_file) {
 
  int mapping_index = -1;
 
  string canonical_disk_file = CanonicalizePath(disk_file);
 

	
 
  for (int i = 0; i < mappings_.size(); i++) {
 
    // Apply the mapping in reverse.
 
    if (ApplyMapping(canonical_disk_file, mappings_[i].disk_path,
 
                     mappings_[i].virtual_path, virtual_file)) {
 
      // Success.
 
      mapping_index = i;
 
      break;
 
    }
 
  }
 

	
 
  if (mapping_index == -1) {
 
    return NO_MAPPING;
 
  }
 

	
 
  // Iterate through all mappings with higher precedence and verify that none
 
  // of them map this file to some other existing file.
 
  for (int i = 0; i < mapping_index; i++) {
 
    if (ApplyMapping(*virtual_file, mappings_[i].virtual_path,
 
                     mappings_[i].disk_path, shadowing_disk_file)) {
 
      if (access(shadowing_disk_file->c_str(), F_OK) >= 0) {
 
        // File exists.
 
        return SHADOWED;
 
      }
 
    }
 
  }
 
  shadowing_disk_file->clear();
 

	
 
  // Verify that we can open the file.  Note that this also has the side-effect
 
  // of verifying that we are not canonicalizing away any non-existent
 
  // directories.
 
  scoped_ptr<io::ZeroCopyInputStream> stream(OpenDiskFile(disk_file));
 
  if (stream == NULL) {
 
    return CANNOT_OPEN;
 
  }
 

	
 
  return SUCCESS;
 
}
 

	
 
bool DiskSourceTree::VirtualFileToDiskFile(const string& virtual_file,
 
                                           string* disk_file) {
 
  scoped_ptr<io::ZeroCopyInputStream> stream(OpenVirtualFile(virtual_file,
 
                                                             disk_file));
 
  return stream != NULL;
 
}
 

	
 
io::ZeroCopyInputStream* DiskSourceTree::Open(const string& filename) {
 
  return OpenVirtualFile(filename, NULL);
 
}
 

	
 
io::ZeroCopyInputStream* DiskSourceTree::OpenVirtualFile(
 
    const string& virtual_file,
 
    string* disk_file) {
 
  if (virtual_file != CanonicalizePath(virtual_file) ||
 
      ContainsParentReference(virtual_file)) {
 
    // We do not allow importing of paths containing things like ".." or
 
    // consecutive slashes since the compiler expects files to be uniquely
 
    // identified by file name.
 
    return NULL;
 
  }
 

	
 
  for (int i = 0; i < mappings_.size(); i++) {
 
    string temp_disk_file;
 
    if (ApplyMapping(virtual_file, mappings_[i].virtual_path,
 
                     mappings_[i].disk_path, &temp_disk_file)) {
 
      io::ZeroCopyInputStream* stream = OpenDiskFile(temp_disk_file);
 
      if (stream != NULL) {
 
        if (disk_file != NULL) {
 
          *disk_file = temp_disk_file;
 
        }
 
        return stream;
 
      }
 

	
 
      if (errno == EACCES) {
 
        // The file exists but is not readable.
 
        // TODO(kenton):  Find a way to report this more nicely.
 
        GOOGLE_LOG(WARNING) << "Read access is denied for file: " << temp_disk_file;
 
        return NULL;
 
      }
 
    }
 
  }
 

	
 
  return NULL;
 
}
 

	
 
io::ZeroCopyInputStream* DiskSourceTree::OpenDiskFile(
 
    const string& filename) {
 
  int file_descriptor;
 
  do {
 
    file_descriptor = open(filename.c_str(), O_RDONLY);
 
  } while (file_descriptor < 0 && errno == EINTR);
 
  if (file_descriptor >= 0) {
 
    io::FileInputStream* result = new io::FileInputStream(file_descriptor);
 
    result->SetCloseOnDelete(true);
 
    return result;
 
  } else {
 
    return NULL;
 
  }
 
}
 

	
 
}  // namespace compiler
 
}  // namespace protobuf
 
}  // namespace google
msvc-deps/protobuf/libprotobuf/google/protobuf/compiler/importer.h
Show inline comments
 
new file 100644
 
// Protocol Buffers - Google's data interchange format
 
// Copyright 2008 Google Inc.  All rights reserved.
 
// http://code.google.com/p/protobuf/
 
//
 
// Redistribution and use in source and binary forms, with or without
 
// modification, are permitted provided that the following conditions are
 
// met:
 
//
 
//     * Redistributions of source code must retain the above copyright
 
// notice, this list of conditions and the following disclaimer.
 
//     * Redistributions in binary form must reproduce the above
 
// copyright notice, this list of conditions and the following disclaimer
 
// in the documentation and/or other materials provided with the
 
// distribution.
 
//     * Neither the name of Google Inc. nor the names of its
 
// contributors may be used to endorse or promote products derived from
 
// this software without specific prior written permission.
 
//
 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 

	
 
// Author: kenton@google.com (Kenton Varda)
 
//  Based on original Protocol Buffers design by
 
//  Sanjay Ghemawat, Jeff Dean, and others.
 
//
 
// This file is the public interface to the .proto file parser.
 

	
 
#ifndef GOOGLE_PROTOBUF_COMPILER_IMPORTER_H__
 
#define GOOGLE_PROTOBUF_COMPILER_IMPORTER_H__
 

	
 
#include <string>
 
#include <vector>
 
#include <set>
 
#include <utility>
 
#include <google/protobuf/descriptor.h>
 
#include <google/protobuf/descriptor_database.h>
 
#include <google/protobuf/compiler/parser.h>
 

	
 
namespace google {
 
namespace protobuf {
 

	
 
namespace io { class ZeroCopyInputStream; }
 

	
 
namespace compiler {
 

	
 
// Defined in this file.
 
class Importer;
 
class MultiFileErrorCollector;
 
class SourceTree;
 
class DiskSourceTree;
 

	
 
// TODO(kenton):  Move all SourceTree stuff to a separate file?
 

	
 
// An implementation of DescriptorDatabase which loads files from a SourceTree
 
// and parses them.
 
//
 
// Note:  This class is not thread-safe since it maintains a table of source
 
//   code locations for error reporting.  However, when a DescriptorPool wraps
 
//   a DescriptorDatabase, it uses mutex locking to make sure only one method
 
//   of the database is called at a time, even if the DescriptorPool is used
 
//   from multiple threads.  Therefore, there is only a problem if you create
 
//   multiple DescriptorPools wrapping the same SourceTreeDescriptorDatabase
 
//   and use them from multiple threads.
 
//
 
// Note:  This class does not implement FindFileContainingSymbol() or
 
//   FindFileContainingExtension(); these will always return false.
 
class LIBPROTOBUF_EXPORT SourceTreeDescriptorDatabase : public DescriptorDatabase {
 
 public:
 
  SourceTreeDescriptorDatabase(SourceTree* source_tree);
 
  ~SourceTreeDescriptorDatabase();
 

	
 
  // Instructs the SourceTreeDescriptorDatabase to report any parse errors
 
  // to the given MultiFileErrorCollector.  This should be called before
 
  // parsing.  error_collector must remain valid until either this method
 
  // is called again or the SourceTreeDescriptorDatabase is destroyed.
 
  void RecordErrorsTo(MultiFileErrorCollector* error_collector) {
 
    error_collector_ = error_collector;
 
  }
 

	
 
  // Gets a DescriptorPool::ErrorCollector which records errors to the
 
  // MultiFileErrorCollector specified with RecordErrorsTo().  This collector
 
  // has the ability to determine exact line and column numbers of errors
 
  // from the information given to it by the DescriptorPool.
 
  DescriptorPool::ErrorCollector* GetValidationErrorCollector() {
 
    using_validation_error_collector_ = true;
 
    return &validation_error_collector_;
 
  }
 

	
 
  // implements DescriptorDatabase -----------------------------------
 
  bool FindFileByName(const string& filename, FileDescriptorProto* output);
 
  bool FindFileContainingSymbol(const string& symbol_name,
 
                                FileDescriptorProto* output);
 
  bool FindFileContainingExtension(const string& containing_type,
 
                                   int field_number,
 
                                   FileDescriptorProto* output);
 

	
 
 private:
 
  class SingleFileErrorCollector;
 

	
 
  SourceTree* source_tree_;
 
  MultiFileErrorCollector* error_collector_;
 

	
 
  class LIBPROTOBUF_EXPORT ValidationErrorCollector : public DescriptorPool::ErrorCollector {
 
   public:
 
    ValidationErrorCollector(SourceTreeDescriptorDatabase* owner);
 
    ~ValidationErrorCollector();
 

	
 
    // implements ErrorCollector ---------------------------------------
 
    void AddError(const string& filename,
 
                  const string& element_name,
 
                  const Message* descriptor,
 
                  ErrorLocation location,
 
                  const string& message);
 

	
 
   private:
 
    SourceTreeDescriptorDatabase* owner_;
 
  };
 
  friend class ValidationErrorCollector;
 

	
 
  bool using_validation_error_collector_;
 
  SourceLocationTable source_locations_;
 
  ValidationErrorCollector validation_error_collector_;
 
};
 

	
 
// Simple interface for parsing .proto files.  This wraps the process
 
// of opening the file, parsing it with a Parser, recursively parsing all its
 
// imports, and then cross-linking the results to produce a FileDescriptor.
 
//
 
// This is really just a thin wrapper around SourceTreeDescriptorDatabase.
 
// You may find that SourceTreeDescriptorDatabase is more flexible.
 
//
 
// TODO(kenton):  I feel like this class is not well-named.
 
class LIBPROTOBUF_EXPORT Importer {
 
 public:
 
  Importer(SourceTree* source_tree,
 
           MultiFileErrorCollector* error_collector);
 
  ~Importer();
 

	
 
  // Import the given file and build a FileDescriptor representing it.  If
 
  // the file is already in the DescriptorPool, the existing FileDescriptor
 
  // will be returned.  The FileDescriptor is property of the DescriptorPool,
 
  // and will remain valid until it is destroyed.  If any errors occur, they
 
  // will be reported using the error collector and Import() will return NULL.
 
  //
 
  // A particular Importer object will only report errors for a particular
 
  // file once.  All future attempts to import the same file will return NULL
 
  // without reporting any errors.  The idea is that you might want to import
 
  // a lot of files without seeing the same errors over and over again.  If
 
  // you want to see errors for the same files repeatedly, you can use a
 
  // separate Importer object to import each one (but use the same
 
  // DescriptorPool so that they can be cross-linked).
 
  const FileDescriptor* Import(const string& filename);
 

	
 
  // The DescriptorPool in which all imported FileDescriptors and their
 
  // contents are stored.
 
  inline const DescriptorPool* pool() const {
 
    return &pool_;
 
  }
 

	
 
 private:
 
  SourceTreeDescriptorDatabase database_;
 
  DescriptorPool pool_;
 

	
 
  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Importer);
 
};
 

	
 
// If the importer encounters problems while trying to import the proto files,
 
// it reports them to a MultiFileErrorCollector.
 
class LIBPROTOBUF_EXPORT MultiFileErrorCollector {
 
 public:
 
  inline MultiFileErrorCollector() {}
 
  virtual ~MultiFileErrorCollector();
 

	
 
  // Line and column numbers are zero-based.  A line number of -1 indicates
 
  // an error with the entire file (e.g. "not found").
 
  virtual void AddError(const string& filename, int line, int column,
 
                        const string& message) = 0;
 

	
 
 private:
 
  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MultiFileErrorCollector);
 
};
 

	
 
// Abstract interface which represents a directory tree containing proto files.
 
// Used by the default implementation of Importer to resolve import statements
 
// Most users will probably want to use the DiskSourceTree implementation,
 
// below.
 
class LIBPROTOBUF_EXPORT SourceTree {
 
 public:
 
  inline SourceTree() {}
 
  virtual ~SourceTree();
 

	
 
  // Open the given file and return a stream that reads it, or NULL if not
 
  // found.  The caller takes ownership of the returned object.  The filename
 
  // must be a path relative to the root of the source tree and must not
 
  // contain "." or ".." components.
 
  virtual io::ZeroCopyInputStream* Open(const string& filename) = 0;
 

	
 
 private:
 
  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(SourceTree);
 
};
 

	
 
// An implementation of SourceTree which loads files from locations on disk.
 
// Multiple mappings can be set up to map locations in the DiskSourceTree to
 
// locations in the physical filesystem.
 
class LIBPROTOBUF_EXPORT DiskSourceTree : public SourceTree {
 
 public:
 
  DiskSourceTree();
 
  ~DiskSourceTree();
 

	
 
  // Map a path on disk to a location in the SourceTree.  The path may be
 
  // either a file or a directory.  If it is a directory, the entire tree
 
  // under it will be mapped to the given virtual location.  To map a directory
 
  // to the root of the source tree, pass an empty string for virtual_path.
 
  //
 
  // If multiple mapped paths apply when opening a file, they will be searched
 
  // in order.  For example, if you do:
 
  //   MapPath("bar", "foo/bar");
 
  //   MapPath("", "baz");
 
  // and then you do:
 
  //   Open("bar/qux");
 
  // the DiskSourceTree will first try to open foo/bar/qux, then baz/bar/qux,
 
  // returning the first one that opens successfuly.
 
  //
 
  // disk_path may be an absolute path or relative to the current directory,
 
  // just like a path you'd pass to open().
 
  void MapPath(const string& virtual_path, const string& disk_path);
 

	
 
  // Return type for DiskFileToVirtualFile().
 
  enum DiskFileToVirtualFileResult {
 
    SUCCESS,
 
    SHADOWED,
 
    CANNOT_OPEN,
 
    NO_MAPPING
 
  };
 

	
 
  // Given a path to a file on disk, find a virtual path mapping to that
 
  // file.  The first mapping created with MapPath() whose disk_path contains
 
  // the filename is used.  However, that virtual path may not actually be
 
  // usable to open the given file.  Possible return values are:
 
  // * SUCCESS: The mapping was found.  *virtual_file is filled in so that
 
  //   calling Open(*virtual_file) will open the file named by disk_file.
 
  // * SHADOWED: A mapping was found, but using Open() to open this virtual
 
  //   path will end up returning some different file.  This is because some
 
  //   other mapping with a higher precedence also matches this virtual path
 
  //   and maps it to a different file that exists on disk.  *virtual_file
 
  //   is filled in as it would be in the SUCCESS case.  *shadowing_disk_file
 
  //   is filled in with the disk path of the file which would be opened if
 
  //   you were to call Open(*virtual_file).
 
  // * CANNOT_OPEN: The mapping was found and was not shadowed, but the
 
  //   file specified cannot be opened.  When this value is returned,
 
  //   errno will indicate the reason the file cannot be opened.  *virtual_file
 
  //   will be set to the virtual path as in the SUCCESS case, even though
 
  //   it is not useful.
 
  // * NO_MAPPING: Indicates that no mapping was found which contains this
 
  //   file.
 
  DiskFileToVirtualFileResult
 
    DiskFileToVirtualFile(const string& disk_file,
 
                          string* virtual_file,
 
                          string* shadowing_disk_file);
 

	
 
  // Given a virtual path, find the path to the file on disk.
 
  // Return true and update disk_file with the on-disk path if the file exists.
 
  // Return false and leave disk_file untouched if the file doesn't exist.
 
  bool VirtualFileToDiskFile(const string& virtual_file, string* disk_file);
 

	
 
  // implements SourceTree -------------------------------------------
 
  io::ZeroCopyInputStream* Open(const string& filename);
 

	
 
 private:
 
  struct Mapping {
 
    string virtual_path;
 
    string disk_path;
 

	
 
    inline Mapping(const string& virtual_path, const string& disk_path)
 
      : virtual_path(virtual_path), disk_path(disk_path) {}
 
  };
 
  vector<Mapping> mappings_;
 

	
 
  // Like Open(), but returns the on-disk path in disk_file if disk_file is
 
  // non-NULL and the file could be successfully opened.
 
  io::ZeroCopyInputStream* OpenVirtualFile(const string& virtual_file,
 
                                           string* disk_file);
 

	
 
  // Like Open() but given the actual on-disk path.
 
  io::ZeroCopyInputStream* OpenDiskFile(const string& filename);
 

	
 
  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DiskSourceTree);
 
};
 

	
 
}  // namespace compiler
 
}  // namespace protobuf
 

	
 
}  // namespace google
 
#endif  // GOOGLE_PROTOBUF_COMPILER_IMPORTER_H__
msvc-deps/protobuf/libprotobuf/google/protobuf/compiler/parser.cc
Show inline comments
 
new file 100644
 
// Protocol Buffers - Google's data interchange format
 
// Copyright 2008 Google Inc.  All rights reserved.
 
// http://code.google.com/p/protobuf/
 
//
 
// Redistribution and use in source and binary forms, with or without
 
// modification, are permitted provided that the following conditions are
 
// met:
 
//
 
//     * Redistributions of source code must retain the above copyright
 
// notice, this list of conditions and the following disclaimer.
 
//     * Redistributions in binary form must reproduce the above
 
// copyright notice, this list of conditions and the following disclaimer
 
// in the documentation and/or other materials provided with the
 
// distribution.
 
//     * Neither the name of Google Inc. nor the names of its
 
// contributors may be used to endorse or promote products derived from
 
// this software without specific prior written permission.
 
//
 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 

	
 
// Author: kenton@google.com (Kenton Varda)
 
//  Based on original Protocol Buffers design by
 
//  Sanjay Ghemawat, Jeff Dean, and others.
 
//
 
// Recursive descent FTW.
 

	
 
#include <float.h>
 
#include <google/protobuf/stubs/hash.h>
 
#include <limits>
 

	
 

	
 
#include <google/protobuf/compiler/parser.h>
 
#include <google/protobuf/descriptor.h>
 
#include <google/protobuf/descriptor.pb.h>
 
#include <google/protobuf/wire_format.h>
 
#include <google/protobuf/io/tokenizer.h>
 
#include <google/protobuf/stubs/common.h>
 
#include <google/protobuf/stubs/strutil.h>
 
#include <google/protobuf/stubs/map-util.h>
 

	
 
namespace google {
 
namespace protobuf {
 
namespace compiler {
 

	
 
using internal::WireFormat;
 

	
 
namespace {
 

	
 
typedef hash_map<string, FieldDescriptorProto::Type> TypeNameMap;
 

	
 
TypeNameMap MakeTypeNameTable() {
 
  TypeNameMap result;
 

	
 
  result["double"  ] = FieldDescriptorProto::TYPE_DOUBLE;
 
  result["float"   ] = FieldDescriptorProto::TYPE_FLOAT;
 
  result["uint64"  ] = FieldDescriptorProto::TYPE_UINT64;
 
  result["fixed64" ] = FieldDescriptorProto::TYPE_FIXED64;
 
  result["fixed32" ] = FieldDescriptorProto::TYPE_FIXED32;
 
  result["bool"    ] = FieldDescriptorProto::TYPE_BOOL;
 
  result["string"  ] = FieldDescriptorProto::TYPE_STRING;
 
  result["group"   ] = FieldDescriptorProto::TYPE_GROUP;
 

	
 
  result["bytes"   ] = FieldDescriptorProto::TYPE_BYTES;
 
  result["uint32"  ] = FieldDescriptorProto::TYPE_UINT32;
 
  result["sfixed32"] = FieldDescriptorProto::TYPE_SFIXED32;
 
  result["sfixed64"] = FieldDescriptorProto::TYPE_SFIXED64;
 
  result["int32"   ] = FieldDescriptorProto::TYPE_INT32;
 
  result["int64"   ] = FieldDescriptorProto::TYPE_INT64;
 
  result["sint32"  ] = FieldDescriptorProto::TYPE_SINT32;
 
  result["sint64"  ] = FieldDescriptorProto::TYPE_SINT64;
 

	
 
  return result;
 
}
 

	
 
const TypeNameMap kTypeNames = MakeTypeNameTable();
 

	
 
}  // anonymous namespace
 

	
 
// Makes code slightly more readable.  The meaning of "DO(foo)" is
 
// "Execute foo and fail if it fails.", where failure is indicated by
 
// returning false.
 
#define DO(STATEMENT) if (STATEMENT) {} else return false
 

	
 
// ===================================================================
 

	
 
Parser::Parser()
 
  : input_(NULL),
 
    error_collector_(NULL),
 
    source_location_table_(NULL),
 
    had_errors_(false),
 
    require_syntax_identifier_(false),
 
    stop_after_syntax_identifier_(false) {
 
}
 

	
 
Parser::~Parser() {
 
}
 

	
 
// ===================================================================
 

	
 
inline bool Parser::LookingAt(const char* text) {
 
  return input_->current().text == text;
 
}
 

	
 
inline bool Parser::LookingAtType(io::Tokenizer::TokenType token_type) {
 
  return input_->current().type == token_type;
 
}
 

	
 
inline bool Parser::AtEnd() {
 
  return LookingAtType(io::Tokenizer::TYPE_END);
 
}
 

	
 
bool Parser::TryConsume(const char* text) {
 
  if (LookingAt(text)) {
 
    input_->Next();
 
    return true;
 
  } else {
 
    return false;
 
  }
 
}
 

	
 
bool Parser::Consume(const char* text, const char* error) {
 
  if (TryConsume(text)) {
 
    return true;
 
  } else {
 
    AddError(error);
 
    return false;
 
  }
 
}
 

	
 
bool Parser::Consume(const char* text) {
 
  if (TryConsume(text)) {
 
    return true;
 
  } else {
 
    AddError("Expected \"" + string(text) + "\".");
 
    return false;
 
  }
 
}
 

	
 
bool Parser::ConsumeIdentifier(string* output, const char* error) {
 
  if (LookingAtType(io::Tokenizer::TYPE_IDENTIFIER)) {
 
    *output = input_->current().text;
 
    input_->Next();
 
    return true;
 
  } else {
 
    AddError(error);
 
    return false;
 
  }
 
}
 

	
 
bool Parser::ConsumeInteger(int* output, const char* error) {
 
  if (LookingAtType(io::Tokenizer::TYPE_INTEGER)) {
 
    uint64 value = 0;
 
    if (!io::Tokenizer::ParseInteger(input_->current().text,
 
                                     kint32max, &value)) {
 
      AddError("Integer out of range.");
 
      // We still return true because we did, in fact, parse an integer.
 
    }
 
    *output = value;
 
    input_->Next();
 
    return true;
 
  } else {
 
    AddError(error);
 
    return false;
 
  }
 
}
 

	
 
bool Parser::ConsumeInteger64(uint64 max_value, uint64* output,
 
                              const char* error) {
 
  if (LookingAtType(io::Tokenizer::TYPE_INTEGER)) {
 
    if (!io::Tokenizer::ParseInteger(input_->current().text, max_value,
 
                                     output)) {
 
      AddError("Integer out of range.");
 
      // We still return true because we did, in fact, parse an integer.
 
      *output = 0;
 
    }
 
    input_->Next();
 
    return true;
 
  } else {
 
    AddError(error);
 
    return false;
 
  }
 
}
 

	
 
bool Parser::ConsumeNumber(double* output, const char* error) {
 
  if (LookingAtType(io::Tokenizer::TYPE_FLOAT)) {
 
    *output = io::Tokenizer::ParseFloat(input_->current().text);
 
    input_->Next();
 
    return true;
 
  } else if (LookingAtType(io::Tokenizer::TYPE_INTEGER)) {
 
    // Also accept integers.
 
    uint64 value = 0;
 
    if (!io::Tokenizer::ParseInteger(input_->current().text,
 
                                     kuint64max, &value)) {
 
      AddError("Integer out of range.");
 
      // We still return true because we did, in fact, parse a number.
 
    }
 
    *output = value;
 
    input_->Next();
 
    return true;
 
  } else if (LookingAt("inf")) {
 
    *output = numeric_limits<double>::infinity();
 
    input_->Next();
 
    return true;
 
  } else if (LookingAt("nan")) {
 
    *output = numeric_limits<double>::quiet_NaN();
 
    input_->Next();
 
    return true;
 
  } else {
 
    AddError(error);
 
    return false;
 
  }
 
}
 

	
 
bool Parser::ConsumeString(string* output, const char* error) {
 
  if (LookingAtType(io::Tokenizer::TYPE_STRING)) {
 
    io::Tokenizer::ParseString(input_->current().text, output);
 
    input_->Next();
 
    // Allow C++ like concatenation of adjacent string tokens.
 
    while (LookingAtType(io::Tokenizer::TYPE_STRING)) {
 
      io::Tokenizer::ParseStringAppend(input_->current().text, output);
 
      input_->Next();
 
    }
 
    return true;
 
  } else {
 
    AddError(error);
 
    return false;
 
  }
 
}
 

	
 
// -------------------------------------------------------------------
 

	
 
void Parser::AddError(int line, int column, const string& error) {
 
  if (error_collector_ != NULL) {
 
    error_collector_->AddError(line, column, error);
 
  }
 
  had_errors_ = true;
 
}
 

	
 
void Parser::AddError(const string& error) {
 
  AddError(input_->current().line, input_->current().column, error);
 
}
 

	
 
// -------------------------------------------------------------------
 

	
 
Parser::LocationRecorder::LocationRecorder(Parser* parser)
 
  : parser_(parser),
 
    location_(parser_->source_code_info_->add_location()) {
 
  location_->add_span(parser_->input_->current().line);
 
  location_->add_span(parser_->input_->current().column);
 
}
 

	
 
Parser::LocationRecorder::LocationRecorder(const LocationRecorder& parent) {
 
  Init(parent);
 
}
 

	
 
Parser::LocationRecorder::LocationRecorder(const LocationRecorder& parent,
 
                                           int path1) {
 
  Init(parent);
 
  AddPath(path1);
 
}
 

	
 
Parser::LocationRecorder::LocationRecorder(const LocationRecorder& parent,
 
                                           int path1, int path2) {
 
  Init(parent);
 
  AddPath(path1);
 
  AddPath(path2);
 
}
 

	
 
void Parser::LocationRecorder::Init(const LocationRecorder& parent) {
 
  parser_ = parent.parser_;
 
  location_ = parser_->source_code_info_->add_location();
 
  location_->mutable_path()->CopyFrom(parent.location_->path());
 

	
 
  location_->add_span(parser_->input_->current().line);
 
  location_->add_span(parser_->input_->current().column);
 
}
 

	
 
Parser::LocationRecorder::~LocationRecorder() {
 
  if (location_->span_size() <= 2) {
 
    EndAt(parser_->input_->previous());
 
  }
 
}
 

	
 
void Parser::LocationRecorder::AddPath(int path_component) {
 
  location_->add_path(path_component);
 
}
 

	
 
void Parser::LocationRecorder::StartAt(const io::Tokenizer::Token& token) {
 
  location_->set_span(0, token.line);
 
  location_->set_span(1, token.column);
 
}
 

	
 
void Parser::LocationRecorder::EndAt(const io::Tokenizer::Token& token) {
 
  if (token.line != location_->span(0)) {
 
    location_->add_span(token.line);
 
  }
 
  location_->add_span(token.end_column);
 
}
 

	
 
void Parser::LocationRecorder::RecordLegacyLocation(const Message* descriptor,
 
    DescriptorPool::ErrorCollector::ErrorLocation location) {
 
  if (parser_->source_location_table_ != NULL) {
 
    parser_->source_location_table_->Add(
 
        descriptor, location, location_->span(0), location_->span(1));
 
  }
 
}
 

	
 
// -------------------------------------------------------------------
 

	
 
void Parser::SkipStatement() {
 
  while (true) {
 
    if (AtEnd()) {
 
      return;
 
    } else if (LookingAtType(io::Tokenizer::TYPE_SYMBOL)) {
 
      if (TryConsume(";")) {
 
        return;
 
      } else if (TryConsume("{")) {
 
        SkipRestOfBlock();
 
        return;
 
      } else if (LookingAt("}")) {
 
        return;
 
      }
 
    }
 
    input_->Next();
 
  }
 
}
 

	
 
void Parser::SkipRestOfBlock() {
 
  while (true) {
 
    if (AtEnd()) {
 
      return;
 
    } else if (LookingAtType(io::Tokenizer::TYPE_SYMBOL)) {
 
      if (TryConsume("}")) {
 
        return;
 
      } else if (TryConsume("{")) {
 
        SkipRestOfBlock();
 
      }
 
    }
 
    input_->Next();
 
  }
 
}
 

	
 
// ===================================================================
 

	
 
bool Parser::Parse(io::Tokenizer* input, FileDescriptorProto* file) {
 
  input_ = input;
 
  had_errors_ = false;
 
  syntax_identifier_.clear();
 

	
 
  // Note that |file| could be NULL at this point if
 
  // stop_after_syntax_identifier_ is true.  So, we conservatively allocate
 
  // SourceCodeInfo on the stack, then swap it into the FileDescriptorProto
 
  // later on.
 
  SourceCodeInfo source_code_info;
 
  source_code_info_ = &source_code_info;
 

	
 
  if (LookingAtType(io::Tokenizer::TYPE_START)) {
 
    // Advance to first token.
 
    input_->Next();
 
  }
 

	
 
  {
 
    LocationRecorder root_location(this);
 

	
 
    if (require_syntax_identifier_ || LookingAt("syntax")) {
 
      if (!ParseSyntaxIdentifier()) {
 
        // Don't attempt to parse the file if we didn't recognize the syntax
 
        // identifier.
 
        return false;
 
      }
 
    } else if (!stop_after_syntax_identifier_) {
 
      syntax_identifier_ = "proto2";
 
    }
 

	
 
    if (stop_after_syntax_identifier_) return !had_errors_;
 

	
 
    // Repeatedly parse statements until we reach the end of the file.
 
    while (!AtEnd()) {
 
      if (!ParseTopLevelStatement(file, root_location)) {
 
        // This statement failed to parse.  Skip it, but keep looping to parse
 
        // other statements.
 
        SkipStatement();
 

	
 
        if (LookingAt("}")) {
 
          AddError("Unmatched \"}\".");
 
          input_->Next();
 
        }
 
      }
 
    }
 
  }
 

	
 
  input_ = NULL;
 
  source_code_info_ = NULL;
 
  source_code_info.Swap(file->mutable_source_code_info());
 
  return !had_errors_;
 
}
 

	
 
bool Parser::ParseSyntaxIdentifier() {
 
  DO(Consume("syntax", "File must begin with 'syntax = \"proto2\";'."));
 
  DO(Consume("="));
 
  io::Tokenizer::Token syntax_token = input_->current();
 
  string syntax;
 
  DO(ConsumeString(&syntax, "Expected syntax identifier."));
 
  DO(Consume(";"));
 

	
 
  syntax_identifier_ = syntax;
 

	
 
  if (syntax != "proto2" && !stop_after_syntax_identifier_) {
 
    AddError(syntax_token.line, syntax_token.column,
 
      "Unrecognized syntax identifier \"" + syntax + "\".  This parser "
 
      "only recognizes \"proto2\".");
 
    return false;
 
  }
 

	
 
  return true;
 
}
 

	
 
bool Parser::ParseTopLevelStatement(FileDescriptorProto* file,
 
                                    const LocationRecorder& root_location) {
 
  if (TryConsume(";")) {
 
    // empty statement; ignore
 
    return true;
 
  } else if (LookingAt("message")) {
 
    LocationRecorder location(root_location,
 
      FileDescriptorProto::kMessageTypeFieldNumber, file->message_type_size());
 
    return ParseMessageDefinition(file->add_message_type(), location);
 
  } else if (LookingAt("enum")) {
 
    LocationRecorder location(root_location,
 
      FileDescriptorProto::kEnumTypeFieldNumber, file->enum_type_size());
 
    return ParseEnumDefinition(file->add_enum_type(), location);
 
  } else if (LookingAt("service")) {
 
    LocationRecorder location(root_location,
 
      FileDescriptorProto::kServiceFieldNumber, file->service_size());
 
    return ParseServiceDefinition(file->add_service(), location);
 
  } else if (LookingAt("extend")) {
 
    LocationRecorder location(root_location,
 
        FileDescriptorProto::kExtensionFieldNumber);
 
    return ParseExtend(file->mutable_extension(),
 
                       file->mutable_message_type(),
 
                       root_location,
 
                       FileDescriptorProto::kMessageTypeFieldNumber,
 
                       location);
 
  } else if (LookingAt("import")) {
 
    int index = file->dependency_size();
 
    return ParseImport(file->add_dependency(), root_location, index);
 
  } else if (LookingAt("package")) {
 
    return ParsePackage(file, root_location);
 
  } else if (LookingAt("option")) {
 
    LocationRecorder location(root_location,
 
        FileDescriptorProto::kOptionsFieldNumber);
 
    return ParseOption(file->mutable_options(), location);
 
  } else {
 
    AddError("Expected top-level statement (e.g. \"message\").");
 
    return false;
 
  }
 
}
 

	
 
// -------------------------------------------------------------------
 
// Messages
 

	
 
bool Parser::ParseMessageDefinition(DescriptorProto* message,
 
                                    const LocationRecorder& message_location) {
 
  DO(Consume("message"));
 
  {
 
    LocationRecorder location(message_location,
 
                              DescriptorProto::kNameFieldNumber);
 
    location.RecordLegacyLocation(
 
        message, DescriptorPool::ErrorCollector::NAME);
 
    DO(ConsumeIdentifier(message->mutable_name(), "Expected message name."));
 
  }
 
  DO(ParseMessageBlock(message, message_location));
 
  return true;
 
}
 

	
 
bool Parser::ParseMessageBlock(DescriptorProto* message,
 
                               const LocationRecorder& message_location) {
 
  DO(Consume("{"));
 

	
 
  while (!TryConsume("}")) {
 
    if (AtEnd()) {
 
      AddError("Reached end of input in message definition (missing '}').");
 
      return false;
 
    }
 

	
 
    if (!ParseMessageStatement(message, message_location)) {
 
      // This statement failed to parse.  Skip it, but keep looping to parse
 
      // other statements.
 
      SkipStatement();
 
    }
 
  }
 

	
 
  return true;
 
}
 

	
 
bool Parser::ParseMessageStatement(DescriptorProto* message,
 
                                   const LocationRecorder& message_location) {
 
  if (TryConsume(";")) {
 
    // empty statement; ignore
 
    return true;
 
  } else if (LookingAt("message")) {
 
    LocationRecorder location(message_location,
 
                              DescriptorProto::kNestedTypeFieldNumber,
 
                              message->nested_type_size());
 
    return ParseMessageDefinition(message->add_nested_type(), location);
 
  } else if (LookingAt("enum")) {
 
    LocationRecorder location(message_location,
 
                              DescriptorProto::kEnumTypeFieldNumber,
 
                              message->enum_type_size());
 
    return ParseEnumDefinition(message->add_enum_type(), location);
 
  } else if (LookingAt("extensions")) {
 
    LocationRecorder location(message_location,
 
                              DescriptorProto::kExtensionRangeFieldNumber);
 
    return ParseExtensions(message, location);
 
  } else if (LookingAt("extend")) {
 
    LocationRecorder location(message_location,
 
                              DescriptorProto::kExtensionFieldNumber);
 
    return ParseExtend(message->mutable_extension(),
 
                       message->mutable_nested_type(),
 
                       message_location,
 
                       DescriptorProto::kNestedTypeFieldNumber,
 
                       location);
 
  } else if (LookingAt("option")) {
 
    LocationRecorder location(message_location,
 
                              DescriptorProto::kOptionsFieldNumber);
 
    return ParseOption(message->mutable_options(), location);
 
  } else {
 
    LocationRecorder location(message_location,
 
                              DescriptorProto::kFieldFieldNumber,
 
                              message->field_size());
 
    return ParseMessageField(message->add_field(),
 
                             message->mutable_nested_type(),
 
                             message_location,
 
                             DescriptorProto::kNestedTypeFieldNumber,
 
                             location);
 
  }
 
}
 

	
 
bool Parser::ParseMessageField(FieldDescriptorProto* field,
 
                               RepeatedPtrField<DescriptorProto>* messages,
 
                               const LocationRecorder& parent_location,
 
                               int location_field_number_for_nested_type,
 
                               const LocationRecorder& field_location) {
 
  // Parse label and type.
 
  io::Tokenizer::Token label_token = input_->current();
 
  {
 
    LocationRecorder location(field_location,
 
                              FieldDescriptorProto::kLabelFieldNumber);
 
    FieldDescriptorProto::Label label;
 
    DO(ParseLabel(&label));
 
    field->set_label(label);
 
  }
 

	
 
  {
 
    LocationRecorder location(field_location);  // add path later
 
    location.RecordLegacyLocation(field, DescriptorPool::ErrorCollector::TYPE);
 

	
 
    FieldDescriptorProto::Type type = FieldDescriptorProto::TYPE_INT32;
 
    string type_name;
 
    DO(ParseType(&type, &type_name));
 
    if (type_name.empty()) {
 
      location.AddPath(FieldDescriptorProto::kTypeFieldNumber);
 
      field->set_type(type);
 
    } else {
 
      location.AddPath(FieldDescriptorProto::kTypeNameFieldNumber);
 
      field->set_type_name(type_name);
 
    }
 
  }
 

	
 
  // Parse name and '='.
 
  io::Tokenizer::Token name_token = input_->current();
 
  {
 
    LocationRecorder location(field_location,
 
                              FieldDescriptorProto::kNameFieldNumber);
 
    location.RecordLegacyLocation(field, DescriptorPool::ErrorCollector::NAME);
 
    DO(ConsumeIdentifier(field->mutable_name(), "Expected field name."));
 
  }
 
  DO(Consume("=", "Missing field number."));
 

	
 
  // Parse field number.
 
  {
 
    LocationRecorder location(field_location,
 
                              FieldDescriptorProto::kNumberFieldNumber);
 
    location.RecordLegacyLocation(
 
        field, DescriptorPool::ErrorCollector::NUMBER);
 
    int number;
 
    DO(ConsumeInteger(&number, "Expected field number."));
 
    field->set_number(number);
 
  }
 

	
 
  // Parse options.
 
  DO(ParseFieldOptions(field, field_location));
 

	
 
  // Deal with groups.
 
  if (field->has_type() && field->type() == FieldDescriptorProto::TYPE_GROUP) {
 
    // Awkward:  Since a group declares both a message type and a field, we
 
    //   have to create overlapping locations.
 
    LocationRecorder group_location(parent_location);
 
    group_location.StartAt(label_token);
 
    group_location.AddPath(location_field_number_for_nested_type);
 
    group_location.AddPath(messages->size());
 

	
 
    DescriptorProto* group = messages->Add();
 
    group->set_name(field->name());
 

	
 
    // Record name location to match the field name's location.
 
    {
 
      LocationRecorder location(group_location,
 
                                DescriptorProto::kNameFieldNumber);
 
      location.StartAt(name_token);
 
      location.EndAt(name_token);
 
      location.RecordLegacyLocation(
 
          group, DescriptorPool::ErrorCollector::NAME);
 
    }
 

	
 
    // The field's type_name also comes from the name.  Confusing!
 
    {
 
      LocationRecorder location(field_location,
 
                                FieldDescriptorProto::kTypeNameFieldNumber);
 
      location.StartAt(name_token);
 
      location.EndAt(name_token);
 
    }
 

	
 
    // As a hack for backwards-compatibility, we force the group name to start
 
    // with a capital letter and lower-case the field name.  New code should
 
    // not use groups; it should use nested messages.
 
    if (group->name()[0] < 'A' || 'Z' < group->name()[0]) {
 
      AddError(name_token.line, name_token.column,
 
        "Group names must start with a capital letter.");
 
    }
 
    LowerString(field->mutable_name());
 

	
 
    field->set_type_name(group->name());
 
    if (LookingAt("{")) {
 
      DO(ParseMessageBlock(group, group_location));
 
    } else {
 
      AddError("Missing group body.");
 
      return false;
 
    }
 
  } else {
 
    DO(Consume(";"));
 
  }
 

	
 
  return true;
 
}
 

	
 
bool Parser::ParseFieldOptions(FieldDescriptorProto* field,
 
                               const LocationRecorder& field_location) {
 
  if (!LookingAt("[")) return true;
 

	
 
  LocationRecorder location(field_location,
 
                            FieldDescriptorProto::kOptionsFieldNumber);
 

	
 
  DO(Consume("["));
 

	
 
  // Parse field options.
 
  do {
 
    if (LookingAt("default")) {
 
      // We intentionally pass field_location rather than location here, since
 
      // the default value is not actually an option.
 
      DO(ParseDefaultAssignment(field, field_location));
 
    } else {
 
      DO(ParseOptionAssignment(field->mutable_options(), location));
 
    }
 
  } while (TryConsume(","));
 

	
 
  DO(Consume("]"));
 
  return true;
 
}
 

	
 
bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field,
 
                                    const LocationRecorder& field_location) {
 
  if (field->has_default_value()) {
 
    AddError("Already set option \"default\".");
 
    field->clear_default_value();
 
  }
 

	
 
  DO(Consume("default"));
 
  DO(Consume("="));
 

	
 
  LocationRecorder location(field_location,
 
                            FieldDescriptorProto::kDefaultValueFieldNumber);
 
  location.RecordLegacyLocation(
 
      field, DescriptorPool::ErrorCollector::DEFAULT_VALUE);
 
  string* default_value = field->mutable_default_value();
 

	
 
  if (!field->has_type()) {
 
    // The field has a type name, but we don't know if it is a message or an
 
    // enum yet.  Assume an enum for now.
 
    DO(ConsumeIdentifier(default_value, "Expected identifier."));
 
    return true;
 
  }
 

	
 
  switch (field->type()) {
 
    case FieldDescriptorProto::TYPE_INT32:
 
    case FieldDescriptorProto::TYPE_INT64:
 
    case FieldDescriptorProto::TYPE_SINT32:
 
    case FieldDescriptorProto::TYPE_SINT64:
 
    case FieldDescriptorProto::TYPE_SFIXED32:
 
    case FieldDescriptorProto::TYPE_SFIXED64: {
 
      uint64 max_value = kint64max;
 
      if (field->type() == FieldDescriptorProto::TYPE_INT32 ||
 
          field->type() == FieldDescriptorProto::TYPE_SINT32 ||
 
          field->type() == FieldDescriptorProto::TYPE_SFIXED32) {
 
        max_value = kint32max;
 
      }
 

	
 
      // These types can be negative.
 
      if (TryConsume("-")) {
 
        default_value->append("-");
 
        // Two's complement always has one more negative value than positive.
 
        ++max_value;
 
      }
 
      // Parse the integer to verify that it is not out-of-range.
 
      uint64 value;
 
      DO(ConsumeInteger64(max_value, &value, "Expected integer."));
 
      // And stringify it again.
 
      default_value->append(SimpleItoa(value));
 
      break;
 
    }
 

	
 
    case FieldDescriptorProto::TYPE_UINT32:
 
    case FieldDescriptorProto::TYPE_UINT64:
 
    case FieldDescriptorProto::TYPE_FIXED32:
 
    case FieldDescriptorProto::TYPE_FIXED64: {
 
      uint64 max_value = kuint64max;
 
      if (field->type() == FieldDescriptorProto::TYPE_UINT32 ||
 
          field->type() == FieldDescriptorProto::TYPE_FIXED32) {
 
        max_value = kuint32max;
 
      }
 

	
 
      // Numeric, not negative.
 
      if (TryConsume("-")) {
 
        AddError("Unsigned field can't have negative default value.");
 
      }
 
      // Parse the integer to verify that it is not out-of-range.
 
      uint64 value;
 
      DO(ConsumeInteger64(max_value, &value, "Expected integer."));
 
      // And stringify it again.
 
      default_value->append(SimpleItoa(value));
 
      break;
 
    }
 

	
 
    case FieldDescriptorProto::TYPE_FLOAT:
 
    case FieldDescriptorProto::TYPE_DOUBLE:
 
      // These types can be negative.
 
      if (TryConsume("-")) {
 
        default_value->append("-");
 
      }
 
      // Parse the integer because we have to convert hex integers to decimal
 
      // floats.
 
      double value;
 
      DO(ConsumeNumber(&value, "Expected number."));
 
      // And stringify it again.
 
      default_value->append(SimpleDtoa(value));
 
      break;
 

	
 
    case FieldDescriptorProto::TYPE_BOOL:
 
      if (TryConsume("true")) {
 
        default_value->assign("true");
 
      } else if (TryConsume("false")) {
 
        default_value->assign("false");
 
      } else {
 
        AddError("Expected \"true\" or \"false\".");
 
        return false;
 
      }
 
      break;
 

	
 
    case FieldDescriptorProto::TYPE_STRING:
 
      DO(ConsumeString(default_value, "Expected string."));
 
      break;
 

	
 
    case FieldDescriptorProto::TYPE_BYTES:
 
      DO(ConsumeString(default_value, "Expected string."));
 
      *default_value = CEscape(*default_value);
 
      break;
 

	
 
    case FieldDescriptorProto::TYPE_ENUM:
 
      DO(ConsumeIdentifier(default_value, "Expected identifier."));
 
      break;
 

	
 
    case FieldDescriptorProto::TYPE_MESSAGE:
 
    case FieldDescriptorProto::TYPE_GROUP:
 
      AddError("Messages can't have default values.");
 
      return false;
 
  }
 

	
 
  return true;
 
}
 

	
 
bool Parser::ParseOptionNamePart(UninterpretedOption* uninterpreted_option,
 
                                 const LocationRecorder& part_location) {
 
  UninterpretedOption::NamePart* name = uninterpreted_option->add_name();
 
  string identifier;  // We parse identifiers into this string.
 
  if (LookingAt("(")) {  // This is an extension.
 
    DO(Consume("("));
 

	
 
    {
 
      LocationRecorder location(
 
          part_location, UninterpretedOption::NamePart::kNamePartFieldNumber);
 
      // An extension name consists of dot-separated identifiers, and may begin
 
      // with a dot.
 
      if (LookingAtType(io::Tokenizer::TYPE_IDENTIFIER)) {
 
        DO(ConsumeIdentifier(&identifier, "Expected identifier."));
 
        name->mutable_name_part()->append(identifier);
 
      }
 
      while (LookingAt(".")) {
 
        DO(Consume("."));
 
        name->mutable_name_part()->append(".");
 
        DO(ConsumeIdentifier(&identifier, "Expected identifier."));
 
        name->mutable_name_part()->append(identifier);
 
      }
 
    }
 

	
 
    DO(Consume(")"));
 
    name->set_is_extension(true);
 
  } else {  // This is a regular field.
 
    LocationRecorder location(
 
        part_location, UninterpretedOption::NamePart::kNamePartFieldNumber);
 
    DO(ConsumeIdentifier(&identifier, "Expected identifier."));
 
    name->mutable_name_part()->append(identifier);
 
    name->set_is_extension(false);
 
  }
 
  return true;
 
}
 

	
 
bool Parser::ParseUninterpretedBlock(string* value) {
 
  // Note that enclosing braces are not added to *value.
 
  DO(Consume("{"));
 
  int brace_depth = 1;
 
  while (!AtEnd()) {
 
    if (LookingAt("{")) {
 
      brace_depth++;
 
    } else if (LookingAt("}")) {
 
      brace_depth--;
 
      if (brace_depth == 0) {
 
        input_->Next();
 
        return true;
 
      }
 
    }
 
    // TODO(sanjay): Interpret line/column numbers to preserve formatting
 
    if (!value->empty()) value->push_back(' ');
 
    value->append(input_->current().text);
 
    input_->Next();
 
  }
 
  AddError("Unexpected end of stream while parsing aggregate value.");
 
  return false;
 
}
 

	
 
// We don't interpret the option here. Instead we store it in an
 
// UninterpretedOption, to be interpreted later.
 
bool Parser::ParseOptionAssignment(Message* options,
 
                                   const LocationRecorder& options_location) {
 
  // Create an entry in the uninterpreted_option field.
 
  const FieldDescriptor* uninterpreted_option_field = options->GetDescriptor()->
 
      FindFieldByName("uninterpreted_option");
 
  GOOGLE_CHECK(uninterpreted_option_field != NULL)
 
      << "No field named \"uninterpreted_option\" in the Options proto.";
 

	
 
  const Reflection* reflection = options->GetReflection();
 

	
 
  LocationRecorder location(
 
      options_location, uninterpreted_option_field->number(),
 
      reflection->FieldSize(*options, uninterpreted_option_field));
 

	
 
  UninterpretedOption* uninterpreted_option = down_cast<UninterpretedOption*>(
 
      options->GetReflection()->AddMessage(options,
 
                                           uninterpreted_option_field));
 

	
 
  // Parse dot-separated name.
 
  {
 
    LocationRecorder name_location(location,
 
                                   UninterpretedOption::kNameFieldNumber);
 
    name_location.RecordLegacyLocation(
 
        uninterpreted_option, DescriptorPool::ErrorCollector::OPTION_NAME);
 

	
 
    {
 
      LocationRecorder part_location(name_location,
 
                                     uninterpreted_option->name_size());
 
      DO(ParseOptionNamePart(uninterpreted_option, part_location));
 
    }
 

	
 
    while (LookingAt(".")) {
 
      DO(Consume("."));
 
      LocationRecorder part_location(name_location,
 
                                     uninterpreted_option->name_size());
 
      DO(ParseOptionNamePart(uninterpreted_option, part_location));
 
    }
 
  }
 

	
 
  DO(Consume("="));
 

	
 
  LocationRecorder value_location(location);
 
  value_location.RecordLegacyLocation(
 
      uninterpreted_option, DescriptorPool::ErrorCollector::OPTION_VALUE);
 

	
 
  // All values are a single token, except for negative numbers, which consist
 
  // of a single '-' symbol, followed by a positive number.
 
  bool is_negative = TryConsume("-");
 

	
 
  switch (input_->current().type) {
 
    case io::Tokenizer::TYPE_START:
 
      GOOGLE_LOG(FATAL) << "Trying to read value before any tokens have been read.";
 
      return false;
 

	
 
    case io::Tokenizer::TYPE_END:
 
      AddError("Unexpected end of stream while parsing option value.");
 
      return false;
 

	
 
    case io::Tokenizer::TYPE_IDENTIFIER: {
 
      value_location.AddPath(UninterpretedOption::kIdentifierValueFieldNumber);
 
      if (is_negative) {
 
        AddError("Invalid '-' symbol before identifier.");
 
        return false;
 
      }
 
      string value;
 
      DO(ConsumeIdentifier(&value, "Expected identifier."));
 
      uninterpreted_option->set_identifier_value(value);
 
      break;
 
    }
 

	
 
    case io::Tokenizer::TYPE_INTEGER: {
 
      uint64 value;
 
      uint64 max_value =
 
          is_negative ? static_cast<uint64>(kint64max) + 1 : kuint64max;
 
      DO(ConsumeInteger64(max_value, &value, "Expected integer."));
 
      if (is_negative) {
 
        value_location.AddPath(
 
            UninterpretedOption::kNegativeIntValueFieldNumber);
 
        uninterpreted_option->set_negative_int_value(-static_cast<int64>(value));
 
      } else {
 
        value_location.AddPath(
 
            UninterpretedOption::kPositiveIntValueFieldNumber);
 
        uninterpreted_option->set_positive_int_value(value);
 
      }
 
      break;
 
    }
 

	
 
    case io::Tokenizer::TYPE_FLOAT: {
 
      value_location.AddPath(UninterpretedOption::kDoubleValueFieldNumber);
 
      double value;
 
      DO(ConsumeNumber(&value, "Expected number."));
 
      uninterpreted_option->set_double_value(is_negative ? -value : value);
 
      break;
 
    }
 

	
 
    case io::Tokenizer::TYPE_STRING: {
 
      value_location.AddPath(UninterpretedOption::kStringValueFieldNumber);
 
      if (is_negative) {
 
        AddError("Invalid '-' symbol before string.");
 
        return false;
 
      }
 
      string value;
 
      DO(ConsumeString(&value, "Expected string."));
 
      uninterpreted_option->set_string_value(value);
 
      break;
 
    }
 

	
 
    case io::Tokenizer::TYPE_SYMBOL:
 
      if (LookingAt("{")) {
 
        value_location.AddPath(UninterpretedOption::kAggregateValueFieldNumber);
 
        DO(ParseUninterpretedBlock(
 
            uninterpreted_option->mutable_aggregate_value()));
 
      } else {
 
        AddError("Expected option value.");
 
        return false;
 
      }
 
      break;
 
  }
 

	
 
  return true;
 
}
 

	
 
bool Parser::ParseExtensions(DescriptorProto* message,
 
                             const LocationRecorder& extensions_location) {
 
  // Parse the declaration.
 
  DO(Consume("extensions"));
 

	
 
  do {
 
    // Note that kExtensionRangeFieldNumber was already pushed by the parent.
 
    LocationRecorder location(extensions_location,
 
                              message->extension_range_size());
 

	
 
    DescriptorProto::ExtensionRange* range = message->add_extension_range();
 
    location.RecordLegacyLocation(
 
        range, DescriptorPool::ErrorCollector::NUMBER);
 

	
 
    int start, end;
 
    io::Tokenizer::Token start_token;
 

	
 
    {
 
      LocationRecorder start_location(
 
          location, DescriptorProto::ExtensionRange::kStartFieldNumber);
 
      start_token = input_->current();
 
      DO(ConsumeInteger(&start, "Expected field number range."));
 
    }
 

	
 
    if (TryConsume("to")) {
 
      LocationRecorder end_location(
 
          location, DescriptorProto::ExtensionRange::kEndFieldNumber);
 
      if (TryConsume("max")) {
 
        end = FieldDescriptor::kMaxNumber;
 
      } else {
 
        DO(ConsumeInteger(&end, "Expected integer."));
 
      }
 
    } else {
 
      LocationRecorder end_location(
 
          location, DescriptorProto::ExtensionRange::kEndFieldNumber);
 
      end_location.StartAt(start_token);
 
      end_location.EndAt(start_token);
 
      end = start;
 
    }
 

	
 
    // Users like to specify inclusive ranges, but in code we like the end
 
    // number to be exclusive.
 
    ++end;
 

	
 
    range->set_start(start);
 
    range->set_end(end);
 
  } while (TryConsume(","));
 

	
 
  DO(Consume(";"));
 
  return true;
 
}
 

	
 
bool Parser::ParseExtend(RepeatedPtrField<FieldDescriptorProto>* extensions,
 
                         RepeatedPtrField<DescriptorProto>* messages,
 
                         const LocationRecorder& parent_location,
 
                         int location_field_number_for_nested_type,
 
                         const LocationRecorder& extend_location) {
 
  DO(Consume("extend"));
 

	
 
  // Parse the extendee type.
 
  io::Tokenizer::Token extendee_start = input_->current();
 
  string extendee;
 
  DO(ParseUserDefinedType(&extendee));
 
  io::Tokenizer::Token extendee_end = input_->previous();
 

	
 
  // Parse the block.
 
  DO(Consume("{"));
 

	
 
  bool is_first = true;
 

	
 
  do {
 
    if (AtEnd()) {
 
      AddError("Reached end of input in extend definition (missing '}').");
 
      return false;
 
    }
 

	
 
    // Note that kExtensionFieldNumber was already pushed by the parent.
 
    LocationRecorder location(extend_location, extensions->size());
 

	
 
    FieldDescriptorProto* field = extensions->Add();
 

	
 
    {
 
      LocationRecorder extendee_location(
 
          location, FieldDescriptorProto::kExtendeeFieldNumber);
 
      extendee_location.StartAt(extendee_start);
 
      extendee_location.EndAt(extendee_end);
 

	
 
      if (is_first) {
 
        extendee_location.RecordLegacyLocation(
 
            field, DescriptorPool::ErrorCollector::EXTENDEE);
 
        is_first = false;
 
      }
 
    }
 

	
 
    field->set_extendee(extendee);
 

	
 
    if (!ParseMessageField(field, messages, parent_location,
 
                           location_field_number_for_nested_type,
 
                           location)) {
 
      // This statement failed to parse.  Skip it, but keep looping to parse
 
      // other statements.
 
      SkipStatement();
 
    }
 
  } while(!TryConsume("}"));
 

	
 
  return true;
 
}
 

	
 
// -------------------------------------------------------------------
 
// Enums
 

	
 
bool Parser::ParseEnumDefinition(EnumDescriptorProto* enum_type,
 
                                 const LocationRecorder& enum_location) {
 
  DO(Consume("enum"));
 

	
 
  {
 
    LocationRecorder location(enum_location,
 
                              EnumDescriptorProto::kNameFieldNumber);
 
    location.RecordLegacyLocation(
 
        enum_type, DescriptorPool::ErrorCollector::NAME);
 
    DO(ConsumeIdentifier(enum_type->mutable_name(), "Expected enum name."));
 
  }
 

	
 
  DO(ParseEnumBlock(enum_type, enum_location));
 
  return true;
 
}
 

	
 
bool Parser::ParseEnumBlock(EnumDescriptorProto* enum_type,
 
                            const LocationRecorder& enum_location) {
 
  DO(Consume("{"));
 

	
 
  while (!TryConsume("}")) {
 
    if (AtEnd()) {
 
      AddError("Reached end of input in enum definition (missing '}').");
 
      return false;
 
    }
 

	
 
    if (!ParseEnumStatement(enum_type, enum_location)) {
 
      // This statement failed to parse.  Skip it, but keep looping to parse
 
      // other statements.
 
      SkipStatement();
 
    }
 
  }
 

	
 
  return true;
 
}
 

	
 
bool Parser::ParseEnumStatement(EnumDescriptorProto* enum_type,
 
                                const LocationRecorder& enum_location) {
 
  if (TryConsume(";")) {
 
    // empty statement; ignore
 
    return true;
 
  } else if (LookingAt("option")) {
 
    LocationRecorder location(enum_location,
 
                              EnumDescriptorProto::kOptionsFieldNumber);
 
    return ParseOption(enum_type->mutable_options(), location);
 
  } else {
 
    LocationRecorder location(enum_location,
 
        EnumDescriptorProto::kValueFieldNumber, enum_type->value_size());
 
    return ParseEnumConstant(enum_type->add_value(), location);
 
  }
 
}
 

	
 
bool Parser::ParseEnumConstant(EnumValueDescriptorProto* enum_value,
 
                               const LocationRecorder& enum_value_location) {
 
  // Parse name.
 
  {
 
    LocationRecorder location(enum_value_location,
 
                              EnumValueDescriptorProto::kNameFieldNumber);
 
    location.RecordLegacyLocation(
 
        enum_value, DescriptorPool::ErrorCollector::NAME);
 
    DO(ConsumeIdentifier(enum_value->mutable_name(),
 
                         "Expected enum constant name."));
 
  }
 

	
 
  DO(Consume("=", "Missing numeric value for enum constant."));
 

	
 
  // Parse value.
 
  {
 
    LocationRecorder location(
 
        enum_value_location, EnumValueDescriptorProto::kNumberFieldNumber);
 
    location.RecordLegacyLocation(
 
        enum_value, DescriptorPool::ErrorCollector::NUMBER);
 

	
 
    bool is_negative = TryConsume("-");
 
    int number;
 
    DO(ConsumeInteger(&number, "Expected integer."));
 
    if (is_negative) number *= -1;
 
    enum_value->set_number(number);
 
  }
 

	
 
  DO(ParseEnumConstantOptions(enum_value, enum_value_location));
 

	
 
  DO(Consume(";"));
 

	
 
  return true;
 
}
 

	
 
bool Parser::ParseEnumConstantOptions(
 
    EnumValueDescriptorProto* value,
 
    const LocationRecorder& enum_value_location) {
 
  if (!LookingAt("[")) return true;
 

	
 
  LocationRecorder location(
 
      enum_value_location, EnumValueDescriptorProto::kOptionsFieldNumber);
 

	
 
  DO(Consume("["));
 

	
 
  do {
 
    DO(ParseOptionAssignment(value->mutable_options(), location));
 
  } while (TryConsume(","));
 

	
 
  DO(Consume("]"));
 
  return true;
 
}
 

	
 
// -------------------------------------------------------------------
 
// Services
 

	
 
bool Parser::ParseServiceDefinition(ServiceDescriptorProto* service,
 
                                    const LocationRecorder& service_location) {
 
  DO(Consume("service"));
 

	
 
  {
 
    LocationRecorder location(service_location,
 
                              ServiceDescriptorProto::kNameFieldNumber);
 
    location.RecordLegacyLocation(
 
        service, DescriptorPool::ErrorCollector::NAME);
 
    DO(ConsumeIdentifier(service->mutable_name(), "Expected service name."));
 
  }
 

	
 
  DO(ParseServiceBlock(service, service_location));
 
  return true;
 
}
 

	
 
bool Parser::ParseServiceBlock(ServiceDescriptorProto* service,
 
                               const LocationRecorder& service_location) {
 
  DO(Consume("{"));
 

	
 
  while (!TryConsume("}")) {
 
    if (AtEnd()) {
 
      AddError("Reached end of input in service definition (missing '}').");
 
      return false;
 
    }
 

	
 
    if (!ParseServiceStatement(service, service_location)) {
 
      // This statement failed to parse.  Skip it, but keep looping to parse
 
      // other statements.
 
      SkipStatement();
 
    }
 
  }
 

	
 
  return true;
 
}
 

	
 
bool Parser::ParseServiceStatement(ServiceDescriptorProto* service,
 
                                   const LocationRecorder& service_location) {
 
  if (TryConsume(";")) {
 
    // empty statement; ignore
 
    return true;
 
  } else if (LookingAt("option")) {
 
    LocationRecorder location(
 
        service_location, ServiceDescriptorProto::kOptionsFieldNumber);
 
    return ParseOption(service->mutable_options(), location);
 
  } else {
 
    LocationRecorder location(service_location,
 
        ServiceDescriptorProto::kMethodFieldNumber, service->method_size());
 
    return ParseServiceMethod(service->add_method(), location);
 
  }
 
}
 

	
 
bool Parser::ParseServiceMethod(MethodDescriptorProto* method,
 
                                const LocationRecorder& method_location) {
 
  DO(Consume("rpc"));
 

	
 
  {
 
    LocationRecorder location(method_location,
 
                              MethodDescriptorProto::kNameFieldNumber);
 
    location.RecordLegacyLocation(
 
        method, DescriptorPool::ErrorCollector::NAME);
 
    DO(ConsumeIdentifier(method->mutable_name(), "Expected method name."));
 
  }
 

	
 
  // Parse input type.
 
  DO(Consume("("));
 
  {
 
    LocationRecorder location(method_location,
 
                              MethodDescriptorProto::kInputTypeFieldNumber);
 
    location.RecordLegacyLocation(
 
        method, DescriptorPool::ErrorCollector::INPUT_TYPE);
 
    DO(ParseUserDefinedType(method->mutable_input_type()));
 
  }
 
  DO(Consume(")"));
 

	
 
  // Parse output type.
 
  DO(Consume("returns"));
 
  DO(Consume("("));
 
  {
 
    LocationRecorder location(method_location,
 
                              MethodDescriptorProto::kOutputTypeFieldNumber);
 
    location.RecordLegacyLocation(
 
        method, DescriptorPool::ErrorCollector::OUTPUT_TYPE);
 
    DO(ParseUserDefinedType(method->mutable_output_type()));
 
  }
 
  DO(Consume(")"));
 

	
 
  if (TryConsume("{")) {
 
    // Options!
 
    while (!TryConsume("}")) {
 
      if (AtEnd()) {
 
        AddError("Reached end of input in method options (missing '}').");
 
        return false;
 
      }
 

	
 
      if (TryConsume(";")) {
 
        // empty statement; ignore
 
      } else {
 
        LocationRecorder location(method_location,
 
                                  MethodDescriptorProto::kOptionsFieldNumber);
 
        if (!ParseOption(method->mutable_options(), location)) {
 
          // This statement failed to parse.  Skip it, but keep looping to
 
          // parse other statements.
 
          SkipStatement();
 
        }
 
      }
 
    }
 
  } else {
 
    DO(Consume(";"));
 
  }
 

	
 
  return true;
 
}
 

	
 
// -------------------------------------------------------------------
 

	
 
bool Parser::ParseLabel(FieldDescriptorProto::Label* label) {
 
  if (TryConsume("optional")) {
 
    *label = FieldDescriptorProto::LABEL_OPTIONAL;
 
    return true;
 
  } else if (TryConsume("repeated")) {
 
    *label = FieldDescriptorProto::LABEL_REPEATED;
 
    return true;
 
  } else if (TryConsume("required")) {
 
    *label = FieldDescriptorProto::LABEL_REQUIRED;
 
    return true;
 
  } else {
 
    AddError("Expected \"required\", \"optional\", or \"repeated\".");
 
    // We can actually reasonably recover here by just assuming the user
 
    // forgot the label altogether.
 
    *label = FieldDescriptorProto::LABEL_OPTIONAL;
 
    return true;
 
  }
 
}
 

	
 
bool Parser::ParseType(FieldDescriptorProto::Type* type,
 
                       string* type_name) {
 
  TypeNameMap::const_iterator iter = kTypeNames.find(input_->current().text);
 
  if (iter != kTypeNames.end()) {
 
    *type = iter->second;
 
    input_->Next();
 
  } else {
 
    DO(ParseUserDefinedType(type_name));
 
  }
 
  return true;
 
}
 

	
 
bool Parser::ParseUserDefinedType(string* type_name) {
 
  type_name->clear();
 

	
 
  TypeNameMap::const_iterator iter = kTypeNames.find(input_->current().text);
 
  if (iter != kTypeNames.end()) {
 
    // Note:  The only place enum types are allowed is for field types, but
 
    //   if we are parsing a field type then we would not get here because
 
    //   primitives are allowed there as well.  So this error message doesn't
 
    //   need to account for enums.
 
    AddError("Expected message type.");
 

	
 
    // Pretend to accept this type so that we can go on parsing.
 
    *type_name = input_->current().text;
 
    input_->Next();
 
    return true;
 
  }
 

	
 
  // A leading "." means the name is fully-qualified.
 
  if (TryConsume(".")) type_name->append(".");
 

	
 
  // Consume the first part of the name.
 
  string identifier;
 
  DO(ConsumeIdentifier(&identifier, "Expected type name."));
 
  type_name->append(identifier);
 

	
 
  // Consume more parts.
 
  while (TryConsume(".")) {
 
    type_name->append(".");
 
    DO(ConsumeIdentifier(&identifier, "Expected identifier."));
 
    type_name->append(identifier);
 
  }
 

	
 
  return true;
 
}
 

	
 
// ===================================================================
 

	
 
bool Parser::ParsePackage(FileDescriptorProto* file,
 
                          const LocationRecorder& root_location) {
 
  if (file->has_package()) {
 
    AddError("Multiple package definitions.");
 
    // Don't append the new package to the old one.  Just replace it.  Not
 
    // that it really matters since this is an error anyway.
 
    file->clear_package();
 
  }
 

	
 
  DO(Consume("package"));
 

	
 
  {
 
    LocationRecorder location(root_location,
 
                              FileDescriptorProto::kPackageFieldNumber);
 
    location.RecordLegacyLocation(file, DescriptorPool::ErrorCollector::NAME);
 

	
 
    while (true) {
 
      string identifier;
 
      DO(ConsumeIdentifier(&identifier, "Expected identifier."));
 
      file->mutable_package()->append(identifier);
 
      if (!TryConsume(".")) break;
 
      file->mutable_package()->append(".");
 
    }
 
  }
 

	
 
  DO(Consume(";"));
 
  return true;
 
}
 

	
 
bool Parser::ParseImport(string* import_filename,
 
                         const LocationRecorder& root_location,
 
                         int index) {
 
  DO(Consume("import"));
 
  {
 
    LocationRecorder location(root_location,
 
                              FileDescriptorProto::kDependencyFieldNumber,
 
                              index);
 
    DO(ConsumeString(import_filename,
 
      "Expected a string naming the file to import."));
 
  }
 
  DO(Consume(";"));
 
  return true;
 
}
 

	
 
bool Parser::ParseOption(Message* options,
 
                         const LocationRecorder& options_location) {
 
  DO(Consume("option"));
 
  DO(ParseOptionAssignment(options, options_location));
 
  DO(Consume(";"));
 
  return true;
 
}
 

	
 
// ===================================================================
 

	
 
SourceLocationTable::SourceLocationTable() {}
 
SourceLocationTable::~SourceLocationTable() {}
 

	
 
bool SourceLocationTable::Find(
 
    const Message* descriptor,
 
    DescriptorPool::ErrorCollector::ErrorLocation location,
 
    int* line, int* column) const {
 
  const pair<int, int>* result =
 
    FindOrNull(location_map_, make_pair(descriptor, location));
 
  if (result == NULL) {
 
    *line   = -1;
 
    *column = 0;
 
    return false;
 
  } else {
 
    *line   = result->first;
 
    *column = result->second;
 
    return true;
 
  }
 
}
 

	
 
void SourceLocationTable::Add(
 
    const Message* descriptor,
 
    DescriptorPool::ErrorCollector::ErrorLocation location,
 
    int line, int column) {
 
  location_map_[make_pair(descriptor, location)] = make_pair(line, column);
 
}
 

	
 
void SourceLocationTable::Clear() {
 
  location_map_.clear();
 
}
 

	
 
}  // namespace compiler
 
}  // namespace protobuf
 
}  // namespace google
msvc-deps/protobuf/libprotobuf/google/protobuf/compiler/parser.h
Show inline comments
 
new file 100644
 
// Protocol Buffers - Google's data interchange format
 
// Copyright 2008 Google Inc.  All rights reserved.
 
// http://code.google.com/p/protobuf/
 
//
 
// Redistribution and use in source and binary forms, with or without
 
// modification, are permitted provided that the following conditions are
 
// met:
 
//
 
//     * Redistributions of source code must retain the above copyright
 
// notice, this list of conditions and the following disclaimer.
 
//     * Redistributions in binary form must reproduce the above
 
// copyright notice, this list of conditions and the following disclaimer
 
// in the documentation and/or other materials provided with the
 
// distribution.
 
//     * Neither the name of Google Inc. nor the names of its
 
// contributors may be used to endorse or promote products derived from
 
// this software without specific prior written permission.
 
//
 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 

	
 
// Author: kenton@google.com (Kenton Varda)
 
//  Based on original Protocol Buffers design by
 
//  Sanjay Ghemawat, Jeff Dean, and others.
 
//
 
// Implements parsing of .proto files to FileDescriptorProtos.
 

	
 
#ifndef GOOGLE_PROTOBUF_COMPILER_PARSER_H__
 
#define GOOGLE_PROTOBUF_COMPILER_PARSER_H__
 

	
 
#include <map>
 
#include <string>
 
#include <utility>
 
#include <google/protobuf/stubs/common.h>
 
#include <google/protobuf/descriptor.h>
 
#include <google/protobuf/descriptor.pb.h>
 
#include <google/protobuf/repeated_field.h>
 
#include <google/protobuf/io/tokenizer.h>
 

	
 
namespace google {
 
namespace protobuf { class Message; }
 

	
 
namespace protobuf {
 
namespace compiler {
 

	
 
// Defined in this file.
 
class Parser;
 
class SourceLocationTable;
 

	
 
// Implements parsing of protocol definitions (such as .proto files).
 
//
 
// Note that most users will be more interested in the Importer class.
 
// Parser is a lower-level class which simply converts a single .proto file
 
// to a FileDescriptorProto.  It does not resolve import directives or perform
 
// many other kinds of validation needed to construct a complete
 
// FileDescriptor.
 
class LIBPROTOBUF_EXPORT Parser {
 
 public:
 
  Parser();
 
  ~Parser();
 

	
 
  // Parse the entire input and construct a FileDescriptorProto representing
 
  // it.  Returns true if no errors occurred, false otherwise.
 
  bool Parse(io::Tokenizer* input, FileDescriptorProto* file);
 

	
 
  // Optional fetaures:
 

	
 
  // DEPRECATED:  New code should use the SourceCodeInfo embedded in the
 
  //   FileDescriptorProto.
 
  //
 
  // Requests that locations of certain definitions be recorded to the given
 
  // SourceLocationTable while parsing.  This can be used to look up exact line
 
  // and column numbers for errors reported by DescriptorPool during validation.
 
  // Set to NULL (the default) to discard source location information.
 
  void RecordSourceLocationsTo(SourceLocationTable* location_table) {
 
    source_location_table_ = location_table;
 
  }
 

	
 
  // Requests that errors be recorded to the given ErrorCollector while
 
  // parsing.  Set to NULL (the default) to discard error messages.
 
  void RecordErrorsTo(io::ErrorCollector* error_collector) {
 
    error_collector_ = error_collector;
 
  }
 

	
 
  // Returns the identifier used in the "syntax = " declaration, if one was
 
  // seen during the last call to Parse(), or the empty string otherwise.
 
  const string& GetSyntaxIdentifier() { return syntax_identifier_; }
 

	
 
  // If set true, input files will be required to begin with a syntax
 
  // identifier.  Otherwise, files may omit this.  If a syntax identifier
 
  // is provided, it must be 'syntax = "proto2";' and must appear at the
 
  // top of this file regardless of whether or not it was required.
 
  void SetRequireSyntaxIdentifier(bool value) {
 
    require_syntax_identifier_ = value;
 
  }
 

	
 
  // Call SetStopAfterSyntaxIdentifier(true) to tell the parser to stop
 
  // parsing as soon as it has seen the syntax identifier, or lack thereof.
 
  // This is useful for quickly identifying the syntax of the file without
 
  // parsing the whole thing.  If this is enabled, no error will be recorded
 
  // if the syntax identifier is something other than "proto2" (since
 
  // presumably the caller intends to deal with that), but other kinds of
 
  // errors (e.g. parse errors) will still be reported.  When this is enabled,
 
  // you may pass a NULL FileDescriptorProto to Parse().
 
  void SetStopAfterSyntaxIdentifier(bool value) {
 
    stop_after_syntax_identifier_ = value;
 
  }
 

	
 
 private:
 
  // =================================================================
 
  // Error recovery helpers
 

	
 
  // Consume the rest of the current statement.  This consumes tokens
 
  // until it sees one of:
 
  //   ';'  Consumes the token and returns.
 
  //   '{'  Consumes the brace then calls SkipRestOfBlock().
 
  //   '}'  Returns without consuming.
 
  //   EOF  Returns (can't consume).
 
  // The Parser often calls SkipStatement() after encountering a syntax
 
  // error.  This allows it to go on parsing the following lines, allowing
 
  // it to report more than just one error in the file.
 
  void SkipStatement();
 

	
 
  // Consume the rest of the current block, including nested blocks,
 
  // ending after the closing '}' is encountered and consumed, or at EOF.
 
  void SkipRestOfBlock();
 

	
 
  // -----------------------------------------------------------------
 
  // Single-token consuming helpers
 
  //
 
  // These make parsing code more readable.
 

	
 
  // True if the current token is TYPE_END.
 
  inline bool AtEnd();
 

	
 
  // True if the next token matches the given text.
 
  inline bool LookingAt(const char* text);
 
  // True if the next token is of the given type.
 
  inline bool LookingAtType(io::Tokenizer::TokenType token_type);
 

	
 
  // If the next token exactly matches the text given, consume it and return
 
  // true.  Otherwise, return false without logging an error.
 
  bool TryConsume(const char* text);
 

	
 
  // These attempt to read some kind of token from the input.  If successful,
 
  // they return true.  Otherwise they return false and add the given error
 
  // to the error list.
 

	
 
  // Consume a token with the exact text given.
 
  bool Consume(const char* text, const char* error);
 
  // Same as above, but automatically generates the error "Expected \"text\".",
 
  // where "text" is the expected token text.
 
  bool Consume(const char* text);
 
  // Consume a token of type IDENTIFIER and store its text in "output".
 
  bool ConsumeIdentifier(string* output, const char* error);
 
  // Consume an integer and store its value in "output".
 
  bool ConsumeInteger(int* output, const char* error);
 
  // Consume a 64-bit integer and store its value in "output".  If the value
 
  // is greater than max_value, an error will be reported.
 
  bool ConsumeInteger64(uint64 max_value, uint64* output, const char* error);
 
  // Consume a number and store its value in "output".  This will accept
 
  // tokens of either INTEGER or FLOAT type.
 
  bool ConsumeNumber(double* output, const char* error);
 
  // Consume a string literal and store its (unescaped) value in "output".
 
  bool ConsumeString(string* output, const char* error);
 

	
 
  // -----------------------------------------------------------------
 
  // Error logging helpers
 

	
 
  // Invokes error_collector_->AddError(), if error_collector_ is not NULL.
 
  void AddError(int line, int column, const string& error);
 

	
 
  // Invokes error_collector_->AddError() with the line and column number
 
  // of the current token.
 
  void AddError(const string& error);
 

	
 
  // Records a location in the SourceCodeInfo.location table (see
 
  // descriptor.proto).  We use RAII to ensure that the start and end locations
 
  // are recorded -- the constructor records the start location and the
 
  // destructor records the end location.  Since the parser is
 
  // recursive-descent, this works out beautifully.
 
  class LIBPROTOBUF_EXPORT LocationRecorder {
 
   public:
 
    // Construct the file's "root" location.
 
    LocationRecorder(Parser* parser);
 

	
 
    // Construct a location that represents a declaration nested within the
 
    // given parent.  E.g. a field's location is nested within the location
 
    // for a message type.  The parent's path will be copied, so you should
 
    // call AddPath() only to add the path components leading from the parent
 
    // to the child (as opposed to leading from the root to the child).
 
    LocationRecorder(const LocationRecorder& parent);
 

	
 
    // Convenience constructors that call AddPath() one or two times.
 
    LocationRecorder(const LocationRecorder& parent, int path1);
 
    LocationRecorder(const LocationRecorder& parent, int path1, int path2);
 

	
 
    ~LocationRecorder();
 

	
 
    // Add a path component.  See SourceCodeInfo.Location.path in
 
    // descriptor.proto.
 
    void AddPath(int path_component);
 

	
 
    // By default the location is considered to start at the current token at
 
    // the time the LocationRecorder is created.  StartAt() sets the start
 
    // location to the given token instead.
 
    void StartAt(const io::Tokenizer::Token& token);
 

	
 
    // By default the location is considered to end at the previous token at
 
    // the time the LocationRecorder is destroyed.  EndAt() sets the end
 
    // location to the given token instead.
 
    void EndAt(const io::Tokenizer::Token& token);
 

	
 
    // Records the start point of this location to the SourceLocationTable that
 
    // was passed to RecordSourceLocationsTo(), if any.  SourceLocationTable
 
    // is an older way of keeping track of source locations which is still
 
    // used in some places.
 
    void RecordLegacyLocation(const Message* descriptor,
 
        DescriptorPool::ErrorCollector::ErrorLocation location);
 

	
 
   private:
 
    Parser* parser_;
 
    SourceCodeInfo::Location* location_;
 

	
 
    void Init(const LocationRecorder& parent);
 
  };
 

	
 
  // =================================================================
 
  // Parsers for various language constructs
 

	
 
  // Parses the "syntax = \"proto2\";" line at the top of the file.  Returns
 
  // false if it failed to parse or if the syntax identifier was not
 
  // recognized.
 
  bool ParseSyntaxIdentifier();
 

	
 
  // These methods parse various individual bits of code.  They return
 
  // false if they completely fail to parse the construct.  In this case,
 
  // it is probably necessary to skip the rest of the statement to recover.
 
  // However, if these methods return true, it does NOT mean that there
 
  // were no errors; only that there were no *syntax* errors.  For instance,
 
  // if a service method is defined using proper syntax but uses a primitive
 
  // type as its input or output, ParseMethodField() still returns true
 
  // and only reports the error by calling AddError().  In practice, this
 
  // makes logic much simpler for the caller.
 

	
 
  // Parse a top-level message, enum, service, etc.
 
  bool ParseTopLevelStatement(FileDescriptorProto* file,
 
                              const LocationRecorder& root_location);
 

	
 
  // Parse various language high-level language construrcts.
 
  bool ParseMessageDefinition(DescriptorProto* message,
 
                              const LocationRecorder& message_location);
 
  bool ParseEnumDefinition(EnumDescriptorProto* enum_type,
 
                           const LocationRecorder& enum_location);
 
  bool ParseServiceDefinition(ServiceDescriptorProto* service,
 
                              const LocationRecorder& service_location);
 
  bool ParsePackage(FileDescriptorProto* file,
 
                    const LocationRecorder& root_location);
 
  bool ParseImport(string* import_filename,
 
                   const LocationRecorder& root_location,
 
                   int index);
 
  bool ParseOption(Message* options,
 
                   const LocationRecorder& options_location);
 

	
 
  // These methods parse the contents of a message, enum, or service type and
 
  // add them to the given object.  They consume the entire block including
 
  // the beginning and ending brace.
 
  bool ParseMessageBlock(DescriptorProto* message,
 
                         const LocationRecorder& message_location);
 
  bool ParseEnumBlock(EnumDescriptorProto* enum_type,
 
                      const LocationRecorder& enum_location);
 
  bool ParseServiceBlock(ServiceDescriptorProto* service,
 
                         const LocationRecorder& service_location);
 

	
 
  // Parse one statement within a message, enum, or service block, inclunding
 
  // final semicolon.
 
  bool ParseMessageStatement(DescriptorProto* message,
 
                             const LocationRecorder& message_location);
 
  bool ParseEnumStatement(EnumDescriptorProto* message,
 
                          const LocationRecorder& enum_location);
 
  bool ParseServiceStatement(ServiceDescriptorProto* message,
 
                             const LocationRecorder& service_location);
 

	
 
  // Parse a field of a message.  If the field is a group, its type will be
 
  // added to "messages".
 
  //
 
  // parent_location and location_field_number_for_nested_type are needed when
 
  // parsing groups -- we need to generate a nested message type within the
 
  // parent and record its location accordingly.  Since the parent could be
 
  // either a FileDescriptorProto or a DescriptorProto, we must pass in the
 
  // correct field number to use.
 
  bool ParseMessageField(FieldDescriptorProto* field,
 
                         RepeatedPtrField<DescriptorProto>* messages,
 
                         const LocationRecorder& parent_location,
 
                         int location_field_number_for_nested_type,
 
                         const LocationRecorder& field_location);
 

	
 
  // Parse an "extensions" declaration.
 
  bool ParseExtensions(DescriptorProto* message,
 
                       const LocationRecorder& extensions_location);
 

	
 
  // Parse an "extend" declaration.  (See also comments for
 
  // ParseMessageField().)
 
  bool ParseExtend(RepeatedPtrField<FieldDescriptorProto>* extensions,
 
                   RepeatedPtrField<DescriptorProto>* messages,
 
                   const LocationRecorder& parent_location,
 
                   int location_field_number_for_nested_type,
 
                   const LocationRecorder& extend_location);
 

	
 
  // Parse a single enum value within an enum block.
 
  bool ParseEnumConstant(EnumValueDescriptorProto* enum_value,
 
                         const LocationRecorder& enum_value_location);
 

	
 
  // Parse enum constant options, i.e. the list in square brackets at the end
 
  // of the enum constant value definition.
 
  bool ParseEnumConstantOptions(EnumValueDescriptorProto* value,
 
                                const LocationRecorder& enum_value_location);
 

	
 
  // Parse a single method within a service definition.
 
  bool ParseServiceMethod(MethodDescriptorProto* method,
 
                          const LocationRecorder& method_location);
 

	
 
  // Parse "required", "optional", or "repeated" and fill in "label"
 
  // with the value.
 
  bool ParseLabel(FieldDescriptorProto::Label* label);
 

	
 
  // Parse a type name and fill in "type" (if it is a primitive) or
 
  // "type_name" (if it is not) with the type parsed.
 
  bool ParseType(FieldDescriptorProto::Type* type,
 
                 string* type_name);
 
  // Parse a user-defined type and fill in "type_name" with the name.
 
  // If a primitive type is named, it is treated as an error.
 
  bool ParseUserDefinedType(string* type_name);
 

	
 
  // Parses field options, i.e. the stuff in square brackets at the end
 
  // of a field definition.  Also parses default value.
 
  bool ParseFieldOptions(FieldDescriptorProto* field,
 
                         const LocationRecorder& field_location);
 

	
 
  // Parse the "default" option.  This needs special handling because its
 
  // type is the field's type.
 
  bool ParseDefaultAssignment(FieldDescriptorProto* field,
 
                              const LocationRecorder& field_location);
 

	
 
  // Parse a single option name/value pair, e.g. "ctype = CORD".  The name
 
  // identifies a field of the given Message, and the value of that field
 
  // is set to the parsed value.
 
  bool ParseOptionAssignment(Message* options,
 
                             const LocationRecorder& options_location);
 

	
 
  // Parses a single part of a multipart option name. A multipart name consists
 
  // of names separated by dots. Each name is either an identifier or a series
 
  // of identifiers separated by dots and enclosed in parentheses. E.g.,
 
  // "foo.(bar.baz).qux".
 
  bool ParseOptionNamePart(UninterpretedOption* uninterpreted_option,
 
                           const LocationRecorder& part_location);
 

	
 
  // Parses a string surrounded by balanced braces.  Strips off the outer
 
  // braces and stores the enclosed string in *value.
 
  // E.g.,
 
  //     { foo }                     *value gets 'foo'
 
  //     { foo { bar: box } }        *value gets 'foo { bar: box }'
 
  //     {}                          *value gets ''
 
  //
 
  // REQUIRES: LookingAt("{")
 
  // When finished successfully, we are looking at the first token past
 
  // the ending brace.
 
  bool ParseUninterpretedBlock(string* value);
 

	
 
  // =================================================================
 

	
 
  io::Tokenizer* input_;
 
  io::ErrorCollector* error_collector_;
 
  SourceCodeInfo* source_code_info_;
 
  SourceLocationTable* source_location_table_;  // legacy
 
  bool had_errors_;
 
  bool require_syntax_identifier_;
 
  bool stop_after_syntax_identifier_;
 
  string syntax_identifier_;
 

	
 
  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Parser);
 
};
 

	
 
// A table mapping (descriptor, ErrorLocation) pairs -- as reported by
 
// DescriptorPool when validating descriptors -- to line and column numbers
 
// within the original source code.
 
//
 
// This is semi-obsolete:  FileDescriptorProto.source_code_info now contains
 
// far more complete information about source locations.  However, as of this
 
// writing you still need to use SourceLocationTable when integrating with
 
// DescriptorPool.
 
class LIBPROTOBUF_EXPORT SourceLocationTable {
 
 public:
 
  SourceLocationTable();
 
  ~SourceLocationTable();
 

	
 
  // Finds the precise location of the given error and fills in *line and
 
  // *column with the line and column numbers.  If not found, sets *line to
 
  // -1 and *column to 0 (since line = -1 is used to mean "error has no exact
 
  // location" in the ErrorCollector interface).  Returns true if found, false
 
  // otherwise.
 
  bool Find(const Message* descriptor,
 
            DescriptorPool::ErrorCollector::ErrorLocation location,
 
            int* line, int* column) const;
 

	
 
  // Adds a location to the table.
 
  void Add(const Message* descriptor,
 
           DescriptorPool::ErrorCollector::ErrorLocation location,
 
           int line, int column);
 

	
 
  // Clears the contents of the table.
 
  void Clear();
 

	
 
 private:
 
  typedef map<
 
    pair<const Message*, DescriptorPool::ErrorCollector::ErrorLocation>,
 
    pair<int, int> > LocationMap;
 
  LocationMap location_map_;
 
};
 

	
 
}  // namespace compiler
 
}  // namespace protobuf
 

	
 
}  // namespace google
 
#endif  // GOOGLE_PROTOBUF_COMPILER_PARSER_H__

Changeset was too big and was cut off... Show full diff anyway

0 comments (0 inline, 0 general)