text:
// Copyright 2011-2020 Wason Technology, LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "HardwareTransport_libusb_private.h"
#include "HardwareTransport_private.h"
#include <boost/foreach.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/locale.hpp>
#include <boost/algorithm/string.hpp>
#include <dlfcn.h>

namespace RobotRaconteur
{
namespace detail
{

//LibUsb_Functions
LibUsb_Functions::LibUsb_Functions()
{
	lib_handle=NULL;
	LIBUSB_FUNCTIONS_INIT(LIBUSB_FUNCTIONS_PTR_VOID);
}

bool LibUsb_Functions::LoadFunctions()
{
	if (lib_handle) throw InvalidOperationException("libusb functions already loaded");
	lib_handle=dlopen("libusb-1.0.so", RTLD_LAZY);
	if (!lib_handle) return false;

	LIBUSB_FUNCTIONS_INIT(LIBUSB_FUNCTIONS_PTR_INIT);

	return true;
}

LibUsb_Functions::~LibUsb_Functions()
{
	if (lib_handle)
	{
		dlclose(lib_handle);
		lib_handle=NULL;
	}
}
//End LibUsb_Functions

static void libusb_status_to_ec(libusb_transfer_status err, boost::system::error_code& ec)
{
	switch (err)
	{
	case LIBUSB_TRANSFER_COMPLETED:
		ec=boost::system::error_code();
		break;
	case LIBUSB_TRANSFER_TIMED_OUT:
		ec=boost::asio::error::make_error_code(boost::asio::error::timed_out);
		break;
	case LIBUSB_TRANSFER_CANCELLED:
		ec=boost::asio::error::make_error_code(boost::asio::error::operation_aborted);
		break;
	case LIBUSB_TRANSFER_NO_DEVICE:
		ec=boost::asio::error::make_error_code(boost::asio::error::host_not_found);
		break;
	default:
		ec=boost::asio::error::make_error_code(boost::asio::error::broken_pipe);
		break;
	}
}

static void libusb_error_to_ec(int err, boost::system::error_code& ec)
{
	if (err >= 0)
	{
		ec=boost::system::error_code();
		return;
	}

	switch (err)
	{
	case LIBUSB_ERROR_TIMEOUT:
		ec=boost::asio::error::make_error_code(boost::asio::error::timed_out);
		break;
	case LIBUSB_ERROR_NO_DEVICE:
	case LIBUSB_ERROR_NOT_FOUND:
		ec=boost::asio::error::make_error_code(boost::asio::error::host_not_found);
		break;
	case LIBUSB_ERROR_BUSY:
		ec=boost::asio::error::make_error_code(boost::asio::error::connection_refused);
		break;
	default:
		ec=boost::asio::error::make_error_code(boost::asio::error::broken_pipe);
		break;
	}
}

//LibUsb_Transfer

LibUsb_Transfer::LibUsb_Transfer(RR_SHARED_PTR<LibUsb_Functions> f, RR_SHARED_PTR<libusb_device_handle> device_handle, RR_SHARED_PTR<LibUsbDeviceManager> device_manager)
{
	this->f=f;
	this->device_handle=device_handle;
	this->transfer=NULL;
	this->device_manager=device_manager;

	thread_pool=device_manager->GetNode()->GetThreadPool();
	ref_count.store(0);
}

LibUsb_Transfer::~LibUsb_Transfer()
{
	if (transfer != NULL)
	{
		f->libusb_free_transfer(transfer);
		transfer=NULL;
	}
}

void intrusive_ptr_release(LibUsb_Transfer* p)
{
	if (!p) return;
	p->ref_count--;
	if (p->ref_count == 0)
	{
		delete p;
	}
}
void intrusive_ptr_add_ref(LibUsb_Transfer * p)
{
	if (!p) return;
	p->ref_count++;
}

LibUsb_Transfer_control::LibUsb_Transfer_control(RR_SHARED_PTR<LibUsb_Functions> f, RR_SHARED_PTR<libusb_device_handle> device_handle, RR_SHARED_PTR<LibUsbDeviceManager> device_manager)
	: LibUsb_Transfer(f, device_handle, device_manager)
{
	temp_buf=NULL;
}

void LibUsb_Transfer_control::FillTransfer(uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, boost::asio::mutable_buffer& buf, boost::function< void(const boost::system::error_code&, size_t) > handler)
{
	size_t s=8 + boost::asio::buffer_size(buf);
	transfer=f->libusb_alloc_transfer(0);
	if (!transfer) throw SystemResourceException("Memory error");
	this->temp_buf=new uint8_t[s];
	this->data_buf=buf;
	boost::asio::buffer_copy(boost::asio::buffer(this->temp_buf,s) + 8, buf);
	libusb_fill_control_setup(this->temp_buf, bmRequestType, bRequest, wValue, wIndex, boost::asio::buffer_size(buf));

	libusb_fill_control_transfer(transfer, device_handle.get(), temp_buf, &LibUsbDeviceManager::transfer_complete, this, 5000);

	this->handler=handler;
	ref_count++;
}

void LibUsb_Transfer_control::CompleteTransfer()
{
	RR_SHARED_PTR<ThreadPool> p=thread_pool.lock();
	if (!p) return;

	if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
	{
		boost::system::error_code ec1;

		libusb_status_to_ec(transfer->status, ec1);

		p->Post(boost::bind(handler, ec1, 0));

		return;
	}

	size_t actual_length=transfer->actual_length;
	uint8_t* temp_buf1=libusb_control_transfer_get_data(transfer);
	boost::asio::buffer_copy(data_buf, boost::asio::buffer(temp_buf1, actual_length));

	boost::system::error_code ec2;

	p->Post(boost::bind(handler, ec2, actual_length));
}

LibUsb_Transfer_control::~LibUsb_Transfer_control()
{
	if (temp_buf)
	{
		delete[] temp_buf;
		temp_buf=NULL;
	}
}

void LibUsb_Transfer_bulk::FillTransfer(uint8_t ep, boost::asio::mutable_buffer& buf, boost::function< void(const boost::system::error_code&, size_t) > handler)
{
	transfer=f->libusb_alloc_transfer(0);
	if (!transfer) throw SystemResourceException("Memory error");
	this->data_buf=buf;

	libusb_fill_bulk_transfer(transfer, device_handle.get(), ep, boost::asio::buffer_cast<uint8_t*>(buf), boost::asio::buffer_size(buf), &LibUsbDeviceManager::transfer_complete, this, 0);

	transfer->flags |= LIBUSB_TRANSFER_ADD_ZERO_PACKET;

	this->handler=handler;
	ref_count++;
}

void LibUsb_Transfer_bulk::CompleteTransfer()
{
	RR_SHARED_PTR<ThreadPool> p=thread_pool.lock();
	if (!p) return;

	if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
	{
		boost::system::error_code ec1;

		libusb_status_to_ec(transfer->status, ec1);

		p->Post(boost::bind(handler, ec1, 0));

		return;
	}

	size_t actual_length=transfer->actual_length;

	boost::system::error_code ec2;

	handler(ec2, actual_length);
}

LibUsb_Transfer_bulk::LibUsb_Transfer_bulk(RR_SHARED_PTR<LibUsb_Functions> f, RR_SHARED_PTR<libusb_device_handle> device_handle, RR_SHARED_PTR<LibUsbDeviceManager> device_manager)
	: LibUsb_Transfer(f, device_handle, device_manager)
{

}

LibUsb_Transfer_bulk::~LibUsb_Transfer_bulk()
{

}

//End LibUsb_Transfer

//LibUsbDeviceManager

LibUsbDeviceManager::LibUsbDeviceManager(RR_SHARED_PTR<HardwareTransport> parent)
	: UsbDeviceManager(parent)
{
	running=false;
	hotplug_cb_handle=0;
}

LibUsbDeviceManager::~LibUsbDeviceManager()
{

}

static void LibUsb_Functions_libusb_exit(RR_SHARED_PTR<LibUsb_Functions> f, libusb_context* c)
{
	f->libusb_exit(c);
}

static void LibUsb_Functions_libusb_unref(RR_SHARED_PTR<LibUsb_Functions> f, libusb_device* dev)
{
	f->libusb_unref_device(dev);
}

static UsbDeviceStatus LibUsbDevice_open_device(RR_SHARED_PTR<LibUsbDeviceManager> m, RR_SHARED_PTR<LibUsb_Functions> f, RR_SHARED_PTR<libusb_device> device, RR_SHARED_PTR<void>& device_handle)
{
	libusb_device_handle* dev_h;
	int res=f->libusb_open(device.get(), &dev_h);
	if (res !=0 || !dev_h)
	{
		if (res == LIBUSB_ERROR_BUSY)
		{
			return Busy;
		}
		if (res == LIBUSB_ERROR_ACCESS)
		{
			return Unauthorized;
		}
		return Error;
	}

	RR_WEAK_PTR<LibUsbDeviceManager> m1=m;

	RR_SHARED_PTR<libusb_device_handle> dev_h1(dev_h, boost::bind(&LibUsbDeviceManager::LibUsbCloseDevice, m1, f, RR_BOOST_PLACEHOLDERS(_1)));

	device_handle=dev_h1;
	return Open;
}

bool LibUsbDeviceManager::InitUpdateDevices()
{
	if (!f)
	{
		RR_SHARED_PTR<LibUsb_Functions> f1 = RR_MAKE_SHARED<LibUsb_Functions>();
		if (!f1->LoadFunctions())
		{
			return false;
		}

		f = f1;
	}

	if (!context)
	{
		libusb_context* c;
		if (f->libusb_init(&c) != 0)
		{
			return false;
		}

		context.reset(c, boost::bind(&LibUsb_Functions_libusb_exit, f, RR_BOOST_PLACEHOLDERS(_1)));

		f->libusb_hotplug_register_callback(context.get(), static_cast<libusb_hotplug_event>(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED |
				LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT), static_cast<libusb_hotplug_flag>(0), LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,
				LIBUSB_HOTPLUG_MATCH_ANY, &LibUsbDeviceManager::OnUsbHotplugEvent, this,
				&hotplug_cb_handle);

		running=true;
		usb_thread=boost::thread(boost::bind(&LibUsbDeviceManager::UsbThread, RR_STATIC_POINTER_CAST<LibUsbDeviceManager>(shared_from_this())));
	}

	return true;
}

std::list<UsbDeviceManager_detected_device> LibUsbDeviceManager::GetDetectedDevicesPaths()
{
	std::list<UsbDeviceManager_detected_device> devices;

	libusb_device** list1;
	ssize_t device_count=f->libusb_get_device_list(context.get(), &list1);
	if (device_count <= 0)
	{
		f->libusb_free_device_list(list1, 1);
		return devices;
	}

	for (ssize_t i=0; i < device_count; i++)
	{
		uint8_t interface_number=0;
		int bus_num=f->libusb_get_bus_number(list1[i]);
		int port_num=f->libusb_get_device_address(list1[i]);
		if (bus_num <= 0 || port_num <= 0) continue;

		std::wstring ws_path=boost::lexical_cast<std::wstring>(bus_num) + std::wstring(L",") + boost::lexical_cast<std::wstring>(port_num);

		libusb_device_descriptor device_descriptor;
		if (f->libusb_get_device_descriptor(list1[i], &device_descriptor)!= 0)
		{
			continue;
		}

		libusb_config_descriptor* config_desc;
		if (f->libusb_get_active_config_descriptor(list1[i], &config_desc) != 0)
		{
			continue;
		}

		uint8_t interface_=0;
		bool rr_found=false;

		RR_SHARED_PTR<HardwareTransport> t=GetParent();

		for (uint8_t j=0; j<config_desc->bNumInterfaces; j++)
		{
			if (config_desc->interface[j].num_altsetting < 1)
			{
				continue;
			}

			const libusb_interface_descriptor& inter=config_desc->interface[j].altsetting[0];

			if (t->IsValidUsbDevice(device_descriptor.idVendor, device_descriptor.idProduct, inter.bInterfaceNumber))
			{
				interface_=inter.bInterfaceNumber;
				rr_found=true;
				break;
			}

			if (device_descriptor.bDeviceClass!=0xFF && device_descriptor.bDeviceClass!=0x00)
			{
				continue;
			}

			if (inter.bInterfaceClass != 0xFF)
			{
				continue;
			}

			if (inter.extra_length < sizeof(robotraconteur_interface_descriptor))
			{
				continue;
			}

			boost::asio::const_buffer extra_desc(inter.extra, inter.extra_length);

			while (boost::asio::buffer_size(extra_desc) >= 2)
			{
				const robotraconteur_usb_common_descriptor* c1 = boost::asio::buffer_cast<const robotraconteur_usb_common_descriptor*>(extra_desc);
				if (c1->bLength > boost::asio::buffer_size(extra_desc))
				{
					continue;
				}

				if (c1->bDescriptorType == RR_USB_CS_INTERFACE_DESCRIPTOR_TYPE)
				{
					const robotraconteur_interface_common_descriptor* c3 = reinterpret_cast<const robotraconteur_interface_common_descriptor*>(c1);
					if (c3->bDescriptorSubType == 0)
					{

						if (c3->bLength != sizeof(robotraconteur_interface_descriptor))
						{
							continue;
						}
						const robotraconteur_interface_descriptor* c4 = reinterpret_cast<const robotraconteur_interface_descriptor*>(c3);

						if (memcmp(RR_USB_CS_INTERFACE_UUID_DETECT, c4->uuidRobotRaconteurDetect, sizeof(RR_USB_CS_INTERFACE_UUID_DETECT)) == 0)
						{
							interface_=inter.bInterfaceNumber;
							rr_found=true;
							break;
						}
					}
				}

				extra_desc = extra_desc + c1->bLength;
			}
		}

		f->libusb_free_config_descriptor(config_desc);

		if (!rr_found) continue;

		RR_SHARED_PTR<libusb_device> dev_sp(list1[i], boost::bind(&LibUsb_Functions_libusb_unref, f, RR_BOOST_PLACEHOLDERS(_1)));
		f->libusb_ref_device(dev_sp.get());
		UsbDeviceManager_detected_device d;
		d.handle=dev_sp;
		d.interface_=interface_;
		d.path=ws_path;
		devices.push_back(d);
	}

	f->libusb_free_device_list(list1, 1);

	return devices;
}

RR_SHARED_PTR<UsbDevice> LibUsbDeviceManager::CreateDevice(const UsbDeviceManager_detected_device& device)
{
	return RR_MAKE_SHARED<LibUsbDevice>(RR_STATIC_POINTER_CAST<LibUsbDeviceManager>(shared_from_this()), f, device);
}

void LibUsbDeviceManager::UsbThread()
{
	RR_SHARED_PTR<libusb_context> context=this->context;

	if (!context) return;

	while(true)
	{
		{
			boost::mutex::scoped_lock lock(manager_transfer_lock);
			if (!running)
			{
				if (manager_transfer_list.empty())
				{
					return;
				}
			}
		}

		int rv=f->libusb_handle_events(context.get());
		if (rv < 0)
		{
			if(rv != LIBUSB_ERROR_TIMEOUT)
			{
				{
					boost::mutex::scoped_lock lock(this_lock);

					BOOST_FOREACH(RR_SHARED_PTR<UsbDevice>& d, devices | boost::adaptors::map_values)
					{
						d->Close();
					}

					devices.clear();

					context.reset();
					hotplug_cb_handle=0;
					try
					{
						std::string message="Internal usb event loop failure: " + boost::lexical_cast<std::string>(rv);
						ConnectionException exp(message);
						RobotRaconteurNode::TryHandleException(node, &exp);
					}
					catch (std::exception&) {}
				}
				return;
			}
		}
	}
}

void LibUsb_Functions_libusb_close(RR_SHARED_PTR<LibUsb_Functions> f, libusb_device_handle* h)
{
	f->libusb_close(h);
}

void LibUsbDeviceManager::LibUsbCloseDevice(RR_WEAK_PTR<LibUsbDeviceManager> d, RR_SHARED_PTR<LibUsb_Functions> f, libusb_device_handle* h)
{
	RR_SHARED_PTR<LibUsbDeviceManager> d1=d.lock();
	if (!d1)
	{
		//We lost the USB device manager...
		f->libusb_close(h);
		return;
	}

	{
		boost::mutex::scoped_lock lock(d1->manager_transfer_lock);
		d1->closing_device_handles.erase(h);
	}

	if (boost::this_thread::get_id() == d1->usb_thread.get_id())
	{
		// Send the close request to the thread pool to avoid deadlock
		RobotRaconteurNode::TryPostToThreadPool(d1->node, boost::bind(&LibUsb_Functions_libusb_close, f, h), true);
	}
	else
	{
		f->libusb_close(h);
	}
}

int LibUsbDeviceManager::OnUsbHotplugEvent(libusb_context* ctx, libusb_device* device, libusb_hotplug_event event, void* user_data)
{
	return 0;
}

void LibUsbDeviceManager::submit_transfer(boost::intrusive_ptr<LibUsb_Transfer>& transfer)
{
	{
		boost::mutex::scoped_lock lock(manager_transfer_lock);

		if (!running)
		{
			transfer->transfer->actual_length=0;
			transfer->transfer->status=LIBUSB_TRANSFER_ERROR;
			RobotRaconteurNode::TryPostToThreadPool(node, boost::bind(&LibUsb_Transfer::CompleteTransfer, transfer), true);
			return;
		}

		if (!closing_device_handles.empty())
		{
			if (closing_device_handles.find(transfer->device_handle.get()) != closing_device_handles.end())
			{
				transfer->transfer->actual_length=0;
				transfer->transfer->status=LIBUSB_TRANSFER_ERROR;
				RobotRaconteurNode::TryPostToThreadPool(node, boost::bind(&LibUsb_Transfer::CompleteTransfer, transfer), true);
				return;
			}
		}

		manager_transfer_list.push_back(*transfer);
	}
	int rv=f->libusb_submit_transfer(transfer->transfer);
	if (rv != 0)
	{
		transfer->transfer->actual_length=0;
		transfer->transfer->status=LIBUSB_TRANSFER_ERROR;
		RobotRaconteurNode::TryPostToThreadPool(node, boost::bind(&LibUsb_Transfer::CompleteTransfer, transfer), true);
	}
}

void LibUsbDeviceManager::transfer_complete(libusb_transfer* transfer)
{
	boost::intrusive_ptr<LibUsb_Transfer> t1(static_cast<LibUsb_Transfer*>(transfer->user_data), false);

	{
		RR_SHARED_PTR<LibUsbDeviceManager> m=t1->device_manager.lock();
		if (m)
		{
			boost::mutex::scoped_lock lock(m->manager_transfer_lock);
			m->manager_transfer_list.erase(m->manager_transfer_list.iterator_to(*t1));

			if (!m->closing_device_handles.empty())
			{
				std::map<libusb_device_handle*, boost::function<void()> >::iterator e=m->closing_device_handles.find(t1->device_handle.get());
				if (e!=m->closing_device_handles.end())
				{
					int count=0;
					for (LibUsb_Transfer::manager_transfer_list_t::iterator e2=m->manager_transfer_list.begin(); e2!= m->manager_transfer_list.end(); e2++)
					{
						if (e2->device_handle == t1->device_handle)
						{
							count++;
						}
					}
					if (count == 0)
					{
						boost::function<void()> draw_down_handler=e->second;
						e->second.clear();
						if (draw_down_handler)
						{
							try
							{
								RobotRaconteurNode::TryPostToThreadPool(m->GetNode(),draw_down_handler,true);
							}
							catch (std::exception&) {}
						}
					}
				}
			}

		}
	}

	t1->CompleteTransfer();
}

void LibUsbDeviceManager::DrawDownRequests(RR_SHARED_PTR<libusb_device_handle> h, boost::function<void()> handler)
{
	//TODO: draw down requests
	//GetNode()->GetThreadPool()->Post(handler);

	boost::mutex::scoped_lock lock(manager_transfer_lock);

	if (closing_device_handles.find(h.get()) != closing_device_handles.end())
	{
		RobotRaconteurNode::TryPostToThreadPool(node, handler, true);
		return;
	}

	int count=0;
	for (LibUsb_Transfer::manager_transfer_list_t::iterator e=manager_transfer_list.begin(); e!= manager_transfer_list.end(); e++)
	{
		if (e->device_handle == h)
		{
			f->libusb_cancel_transfer(e->transfer);
			count++;
		}
	}

	if (count == 0)
	{
		//This will most likely never happen...
		RobotRaconteurNode::TryPostToThreadPool(node, handler, true);
	}
	else
	{
		closing_device_handles.insert(std::make_pair(h.get(), handler));
	}

}

void LibUsbDeviceManager::Shutdown()
{
	UsbDeviceManager::Shutdown();
	bool r;
	{
		boost::mutex::scoped_lock lock(manager_transfer_lock);
		r=running;
		running=false;
	}

	if (r)
	{
		if (f)
		{
			f->libusb_hotplug_deregister_callback(context.get(), hotplug_cb_handle);
		}
		usb_thread.join();
	}
}

//End LibUsbDeviceManager

//LibUsbDevice_Initialize

LibUsbDevice_Initialize::LibUsbDevice_Initialize(RR_SHARED_PTR<UsbDevice> parent, RR_SHARED_PTR<LibUsb_Functions> f, const UsbDeviceManager_detected_device& detected_device)
	 : UsbDevice_Initialize(parent, detected_device)
{
	this->f = f;
	this->m=RR_STATIC_POINTER_CAST<LibUsbDeviceManager>(parent->GetParent());
}

void LibUsbDevice_Initialize::AsyncControlTransfer(uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, boost::asio::mutable_buffer& buf, boost::function< void(const boost::system::error_code&, size_t) > handler, RR_SHARED_PTR<void> dev_h)
{
	boost::mutex::scoped_lock lock(this_lock);
	AsyncControlTransferNoLock(bmRequestType, bRequest, wValue, wIndex, buf, handler, dev_h);
}

//Call with lock
void LibUsbDevice_Initialize::AsyncControlTransferNoLock(uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, boost::asio::mutable_buffer& buf, boost::function< void(const boost::system::error_code&, size_t) > handler, RR_SHARED_PTR<void> dev_h)
{
	RR_SHARED_PTR<LibUsbDeviceManager> m1=m.lock();
	if (!m1)
	{
		throw InvalidOperationException("Device manager lost");
	}
	RR_SHARED_PTR<libusb_device_handle> h=RR_STATIC_POINTER_CAST<libusb_device_handle>(dev_h);
	boost::intrusive_ptr<LibUsb_Transfer> t(new LibUsb_Transfer_control(f, h, m1));
	boost::static_pointer_cast<LibUsb_Transfer_control>(t)->FillTransfer(bmRequestType, bRequest, wValue, wIndex, buf, handler);

	m1->submit_transfer(t);
}

//Call with lock
UsbDeviceStatus LibUsbDevice_Initialize::OpenDevice(RR_SHARED_PTR<void>& dev_h)
{
	RR_SHARED_PTR<LibUsbDeviceManager> m1=m.lock();
	if (!m1) return Error;
	return LibUsbDevice_open_device(m1, f, RR_STATIC_POINTER_CAST<libusb_device>(detected_device.handle), dev_h);
}

//Call with lock
UsbDeviceStatus LibUsbDevice_Initialize::ReadPipeSettings(RR_SHARED_PTR<void> dev_h, RR_SHARED_PTR<UsbDevice_Settings>& settings)
{

	RR_SHARED_PTR<libusb_device_handle> h=RR_STATIC_POINTER_CAST<libusb_device_handle>(dev_h);

	libusb_config_descriptor* config_desc;
	int desired_config;
	if (f->libusb_get_config_descriptor(f->libusb_get_device(h.get()), 0, &config_desc)!=0)
	{
		return Error;
	}

	desired_config=config_desc->bConfigurationValue;

	const libusb_interface_descriptor* interface_desc=NULL;
	for (uint8_t i=0; i<config_desc->bNumInterfaces; i++)
	{
		if (config_desc->interface[i].num_altsetting < 1)
		{
			continue;
		}
		if (config_desc->interface[i].altsetting[0].bInterfaceNumber != settings->interface_number)
		{
			continue;
		}
		interface_desc=&config_desc->interface[i].altsetting[0];
	}

	if (!interface_desc)
	{
		f->libusb_free_config_descriptor(config_desc);
		return Error;
	}

	bool in_found=false;
	bool out_found=false;

	for (uint8_t i=0; i<interface_desc->bNumEndpoints; i++)
	{
		const libusb_endpoint_descriptor* ep=&interface_desc->endpoint[i];
		if (USB_ENDPOINT_TYPE_BULK(ep->bmAttributes)
				&& USB_ENDPOINT_DIRECTION_IN(ep->bEndpointAddress)
				&& !in_found)
		{
			in_found=true;
			settings->in_pipe_id=ep->bEndpointAddress;
			settings->in_pipe_maxpacket=RR_USB_MAX_PACKET_SIZE;
			settings->in_pipe_buffer_size=settings->in_pipe_maxpacket;
		}

		if (USB_ENDPOINT_TYPE_BULK(ep->bmAttributes)
				&& USB_ENDPOINT_DIRECTION_OUT(ep->bEndpointAddress)
				&& !out_found)
		{
			out_found=true;
			settings->out_pipe_id=ep->bEndpointAddress;
			settings->out_pipe_maxpacket=RR_USB_MAX_PACKET_SIZE;
			settings->out_pipe_buffer_size=settings->out_pipe_maxpacket;
		}
	}

	settings->interface_alt_setting=interface_desc->bAlternateSetting;
	settings->device_desired_config=desired_config;

	f->libusb_free_config_descriptor(config_desc);

	if (!in_found && !out_found)
	{
		return Error;
	}

	return Open;

}

UsbDeviceStatus LibUsbDevice_Initialize::ReadInterfaceSettings(RR_SHARED_PTR<void> dev_h, RR_SHARED_PTR<UsbDevice_Settings>& settings)
{
	RR_SHARED_PTR<libusb_device_handle> h=RR_STATIC_POINTER_CAST<libusb_device_handle>(dev_h);

	libusb_config_descriptor* config_desc;
	int desired_config;
	if (f->libusb_get_config_descriptor(f->libusb_get_device(h.get()), 0, &config_desc)!=0)
	{
		return Error;
	}

	desired_config=config_desc->bConfigurationValue;

	const libusb_interface_descriptor* interface_desc=NULL;
	for (uint8_t i=0; i<config_desc->bNumInterfaces; i++)
	{
		if (config_desc->interface[i].num_altsetting < 1)
		{
			continue;
		}
		if (config_desc->interface[i].altsetting[0].bInterfaceNumber != settings->interface_number)
		{
			continue;
		}
		interface_desc=&config_desc->interface[i].altsetting[0];
	}

	if (!interface_desc)
	{
		f->libusb_free_config_descriptor(config_desc);
		return Error;
	}

	boost::asio::const_buffer desc(interface_desc->extra, interface_desc->extra_length);

	bool cs_interface_found = false;
	uint16_t num_protocols;

	while (boost::asio::buffer_size(desc) >= 2)
	{
		const robotraconteur_usb_common_descriptor* c1 = boost::asio::buffer_cast<const robotraconteur_usb_common_descriptor*>(desc);
		if (c1->bLength > boost::asio::buffer_size(desc))
		{
			return Error;
		}

		if (c1->bDescriptorType == RR_USB_CS_INTERFACE_DESCRIPTOR_TYPE)
		{
			const robotraconteur_interface_common_descriptor* c3 = reinterpret_cast<const robotraconteur_interface_common_descriptor*>(c1);
			if (c3->bDescriptorSubType == 0)
			{

				if (c3->bLength != sizeof(robotraconteur_interface_descriptor))
				{
					f->libusb_free_config_descriptor(config_desc);
					return Invalid;
				}
				const robotraconteur_interface_descriptor* c4 = reinterpret_cast<const robotraconteur_interface_descriptor*>(c3);

				if (memcmp(RR_USB_CS_INTERFACE_UUID_DETECT, c4->uuidRobotRaconteurDetect, sizeof(RR_USB_CS_INTERFACE_UUID_DETECT)) != 0)
				{
					f->libusb_free_config_descriptor(config_desc);
					return Invalid;
				}

				settings->string_lang_index = 0;
				settings->string_nodeid_index = c4->iNodeID;
				settings->string_nodename_index = c4->iNodeName;
				num_protocols = c4->wNumProtocols;

				cs_interface_found = true;
			}

			if (c3->bDescriptorSubType == 1)
			{
				if (c3->bLength < sizeof(robotraconteur_protocol_descriptor))
				{
					f->libusb_free_config_descriptor(config_desc);
					return Error;
				}

				const robotraconteur_protocol_descriptor* c4 = reinterpret_cast<const robotraconteur_protocol_descriptor*>(c3);

				settings->supported_protocols.push_back(c4->wRRProtocol);
			}
		}

		desc = desc + c1->bLength;
	}

	f->libusb_free_config_descriptor(config_desc);

	if (!cs_interface_found)
	{
		return Invalid;
	}

	if (num_protocols != settings->supported_protocols.size())
	{
		return Invalid;
	}

	return Open;
}

//End LibUsbDevice_Initialize

//LibUsbDevice_Claim
LibUsbDevice_Claim::LibUsbDevice_Claim(RR_SHARED_PTR<UsbDevice> parent, RR_SHARED_PTR<LibUsb_Functions> f, const UsbDeviceManager_detected_device& detected_device)
	: UsbDevice_Claim(parent, detected_device)
{
	this->f=f;
	m=RR_STATIC_POINTER_CAST<LibUsbDeviceManager>(parent->GetParent());
}
LibUsbDevice_Claim::~LibUsbDevice_Claim()
{

}

void LibUsbDevice_Claim::AsyncControlTransfer(uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, boost::asio::mutable_buffer& buf, boost::function< void(const boost::system::error_code&, size_t) > handler, RR_SHARED_PTR<void> dev_h)
{
	boost::mutex::scoped_lock lock(this_lock);
	AsyncControlTransferNoLock(bmRequestType, bRequest, wValue, wIndex, buf, handler, dev_h);
}

void LibUsbDevice_Claim::AsyncControlTransferNoLock(uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, boost::asio::mutable_buffer& buf, boost::function< void(const boost::system::error_code&, size_t) > handler, RR_SHARED_PTR<void> dev_h)
{
	RR_SHARED_PTR<LibUsbDeviceManager> m1=m.lock();
	if (!m1)
	{
		throw InvalidOperationException("Device manager lost");
	}

	if (!dev_h)
	{
		dev_h=device_handle;
	}

	if (!device_handle)
	{
		RobotRaconteurNode::TryPostToThreadPool(node, boost::bind(handler, boost::asio::error::broken_pipe, 0), true);
		return;
	}

	RR_SHARED_PTR<libusb_device_handle> h=RR_STATIC_POINTER_CAST<libusb_device_handle>(dev_h);
	boost::intrusive_ptr<LibUsb_Transfer> t(new LibUsb_Transfer_control(f, h, m1));
	boost::static_pointer_cast<LibUsb_Transfer_control>(t)->FillTransfer(bmRequestType, bRequest, wValue, wIndex, buf, handler);

	m1->submit_transfer(t);
}

void LibUsbDevice_Claim::AsyncReadPipe(uint8_t ep, boost::asio::mutable_buffer& buf, boost::function< void(const boost::system::error_code&, size_t) > handler)
{
	boost::mutex::scoped_lock lock(this_lock);
	AsyncPipeOp(ep, buf, handler);
}

void LibUsbDevice_Claim::AsyncReadPipeNoLock(uint8_t ep, boost::asio::mutable_buffer& buf, boost::function< void(const boost::system::error_code&, size_t) > handler)
{
	AsyncPipeOp(ep, buf, handler);
}

void LibUsbDevice_Claim::AsyncWritePipe(uint8_t ep, boost::asio::mutable_buffer& buf, boost::function< void(const boost::system::error_code&, size_t) > handler)
{
	boost::mutex::scoped_lock lock(this_lock);
	AsyncPipeOp(ep, buf, handler);
}

void LibUsbDevice_Claim::AsyncWritePipeNoLock(uint8_t ep, boost::asio::mutable_buffer& buf, boost::function< void(const boost::system::error_code&, size_t) > handler)
{
	AsyncPipeOp(ep, buf, handler);
}

void LibUsbDevice_Claim::AsyncPipeOp(uint8_t ep, boost::asio::mutable_buffer& buf, boost::function< void(const boost::system::error_code&, size_t) > handler)
{
	RR_SHARED_PTR<LibUsbDeviceManager> m1=m.lock();
	if (!m1)
	{
		throw InvalidOperationException("Device manager lost");
	}

	if (!device_handle)
	{
		RobotRaconteurNode::TryPostToThreadPool(node, boost::bind(handler, boost::asio::error::broken_pipe, 0), true);
		return;
	}

	RR_SHARED_PTR<libusb_device_handle> h=RR_STATIC_POINTER_CAST<libusb_device_handle>(device_handle);
	boost::intrusive_ptr<LibUsb_Transfer> t(new LibUsb_Transfer_bulk(f, h, m1));
	boost::static_pointer_cast<LibUsb_Transfer_bulk>(t)->FillTransfer(ep, buf, handler);

	m1->submit_transfer(t);
}

//Call with lock
UsbDeviceStatus LibUsbDevice_Claim::ClaimDevice(RR_SHARED_PTR<void>& dev_h)
{
	RR_SHARED_PTR<LibUsbDeviceManager> m1=m.lock();
	if (!m1) return Error;
	UsbDeviceStatus res = LibUsbDevice_open_device(m1, f, RR_STATIC_POINTER_CAST<libusb_device>(detected_device.handle), dev_h);
	if (res != Open)
	{
		return res;
	}

	RR_SHARED_PTR<libusb_device_handle> dev_h1=RR_STATIC_POINTER_CAST<libusb_device_handle>(dev_h);

	int current_config=0;
	if (f->libusb_get_configuration(dev_h1.get(),&current_config)!= 0)
	{
		return Error;
	}

	if (current_config!=settings->device_desired_config)
	{
		f->libusb_set_configuration(dev_h1.get(), 0);
	}

	int res1=f->libusb_claim_interface(dev_h1.get(), settings->interface_number);
	if (res1 != 0)
	{
		if (res1 == LIBUSB_ERROR_BUSY)
		{
			return Busy;
		}

		if (res1 == LIBUSB_ERROR_ACCESS)
		{
			return Unauthorized;
		}

		return Error;
	}

	f->libusb_clear_halt(dev_h1.get(), settings->in_pipe_id);
	f->libusb_clear_halt(dev_h1.get(), settings->out_pipe_id);

	device_handle=dev_h1;
	return Open;
}

//Call with lock
void LibUsbDevice_Claim::ReleaseClaim()
{
	if (!device_handle) return;

	f->libusb_release_interface(device_handle.get(), settings->interface_number);

	device_handle.reset();
}

void LibUsbDevice_Claim::DrawDownRequests(boost::function<void()> handler)
{
	if (!device_handle) return;

	//TODO: Draw down active requests

	//f->libusb_(device_handle.get(), settings->in_pipe_id);
	//f->WinUsb_AbortPipe(device_handle.get(), settings->out_pipe_id);

	RR_SHARED_PTR<LibUsbDeviceManager> m;
	try
	{
	m=RR_STATIC_POINTER_CAST<LibUsbDeviceManager>(GetParent()->GetParent());
	}
	catch (std::exception&) {}

	if (m)
	{
		m->DrawDownRequests(device_handle, handler);
	}
	else
	{
		RobotRaconteurNode::TryPostToThreadPool(node, handler, true);
	}
}

void LibUsbDevice_Claim::ClearHalt(uint8_t ep)
{
	boost::mutex::scoped_lock lock(this_lock);
	if (!device_handle) return;
	f->libusb_clear_halt(device_handle.get(), ep);
}

//End LibUsbDevice_Claim

//LibUsbDevice

LibUsbDevice::LibUsbDevice(RR_SHARED_PTR<LibUsbDeviceManager> parent, RR_SHARED_PTR<LibUsb_Functions> f, const UsbDeviceManager_detected_device& device)
	: UsbDevice(parent, device)
{

	this->f = f;

}

LibUsbDevice::~LibUsbDevice()
{

}

RR_SHARED_PTR<UsbDevice_Initialize> LibUsbDevice::CreateInitialize()
{
	return RR_MAKE_SHARED<LibUsbDevice_Initialize>(shared_from_this(), f, detected_device);
}
RR_SHARED_PTR<UsbDevice_Claim> LibUsbDevice::CreateClaim()
{
	return RR_MAKE_SHARED<LibUsbDevice_Claim>(shared_from_this(), f, detected_device);
}

//End LibUsbDevice
}
}

max_stars_repo_name: robotraconteur/robotraconteur

max_issues_repo_name: robotraconteur/robotraconteur

max_forks_repo_name: robotraconteur/robotraconteur


text:
/**
 * @file   llxmlrpclistener.cpp
 * @author Nat Goodspeed
 * @date   2009-03-18
 * @brief  Implementation for llxmlrpclistener.
 *
 * $LicenseInfo:firstyear=2009&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

// Precompiled header
#include "llviewerprecompiledheaders.h"
// associated header
#include "llxmlrpclistener.h"
// STL headers
#include <map>
#include <set>
// std headers
// external library headers
#include <boost/scoped_ptr.hpp>
#include <boost/range.hpp>          // boost::begin(), boost::end()
#include <xmlrpc-epi/xmlrpc.h>
#include "curl/curl.h"

// other Linden headers
#include "llerror.h"
#include "lleventcoro.h"
#include "stringize.h"
#include "llxmlrpctransaction.h"
#include "llsecapi.h"

#if LL_WINDOWS
#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
#endif

template <typename STATUS>
class StatusMapperBase
{
    typedef std::map<STATUS, std::string> MapType;

public:
    StatusMapperBase(const std::string& desc):
        mDesc(desc)
    {}

    std::string lookup(STATUS status) const
    {
        typename MapType::const_iterator found = mMap.find(status);
        if (found != mMap.end())
        {
            return found->second;
        }
        return STRINGIZE("<unknown " << mDesc << " " << status << ">");
    }

protected:
    std::string mDesc;
    MapType mMap;
};

class StatusMapper: public StatusMapperBase<LLXMLRPCTransaction::EStatus>
{
public:
    StatusMapper(): StatusMapperBase<LLXMLRPCTransaction::EStatus>("Status")
    {
		mMap[LLXMLRPCTransaction::StatusNotStarted]  = "NotStarted";
		mMap[LLXMLRPCTransaction::StatusStarted]     = "Started";
		mMap[LLXMLRPCTransaction::StatusDownloading] = "Downloading";
		mMap[LLXMLRPCTransaction::StatusComplete]    = "Complete";
		mMap[LLXMLRPCTransaction::StatusCURLError]   = "CURLError";
		mMap[LLXMLRPCTransaction::StatusXMLRPCError] = "XMLRPCError";
		mMap[LLXMLRPCTransaction::StatusOtherError]  = "OtherError";
    }
};

static const StatusMapper sStatusMapper;

class CURLcodeMapper: public StatusMapperBase<CURLcode>
{
public:
    CURLcodeMapper(): StatusMapperBase<CURLcode>("CURLcode")
    {
        // from curl.h
// skip the "CURLE_" prefix for each of these strings
#define def(sym) (mMap[sym] = &#sym[6])
        def(CURLE_OK);
        def(CURLE_UNSUPPORTED_PROTOCOL);    /* 1 */
        def(CURLE_FAILED_INIT);             /* 2 */
        def(CURLE_URL_MALFORMAT);           /* 3 */
        def(CURLE_COULDNT_RESOLVE_PROXY);   /* 5 */
        def(CURLE_COULDNT_RESOLVE_HOST);    /* 6 */
        def(CURLE_COULDNT_CONNECT);         /* 7 */
        def(CURLE_PARTIAL_FILE);            /* 18 */
        def(CURLE_HTTP_RETURNED_ERROR);     /* 22 */
        def(CURLE_WRITE_ERROR);             /* 23 */
        def(CURLE_UPLOAD_FAILED);           /* 25 - failed upload "command" */
        def(CURLE_READ_ERROR);              /* 26 - could open/read from file */
        def(CURLE_OUT_OF_MEMORY);           /* 27 */
        /* Note: CURLE_OUT_OF_MEMORY may sometimes indicate a conversion error
                 instead of a memory allocation error if CURL_DOES_CONVERSIONS
                 is defined
        */
        def(CURLE_OPERATION_TIMEDOUT);     /* 28 - the timeout time was reached */
        def(CURLE_HTTP_RANGE_ERROR);        /* 33 - RANGE "command" didn't work */
        def(CURLE_HTTP_POST_ERROR);         /* 34 */
        def(CURLE_SSL_CONNECT_ERROR);       /* 35 - wrong when connecting with SSL */
        def(CURLE_BAD_DOWNLOAD_RESUME);     /* 36 - couldn't resume download */
        def(CURLE_FILE_COULDNT_READ_FILE);  /* 37 */
        def(CURLE_LIBRARY_NOT_FOUND);       /* 40 */
        def(CURLE_FUNCTION_NOT_FOUND);      /* 41 */
        def(CURLE_ABORTED_BY_CALLBACK);     /* 42 */
        def(CURLE_BAD_FUNCTION_ARGUMENT);   /* 43 */
        def(CURLE_INTERFACE_FAILED);        /* 45 - CURLOPT_INTERFACE failed */
        def(CURLE_TOO_MANY_REDIRECTS );     /* 47 - catch endless re-direct loops */
        def(CURLE_SSL_PEER_CERTIFICATE);    /* 51 - peer's certificate wasn't ok */
        def(CURLE_GOT_NOTHING);             /* 52 - when this is a specific error */
        def(CURLE_SSL_ENGINE_NOTFOUND);     /* 53 - SSL crypto engine not found */
        def(CURLE_SSL_ENGINE_SETFAILED);    /* 54 - can not set SSL crypto engine as
                                          default */
        def(CURLE_SEND_ERROR);              /* 55 - failed sending network data */
        def(CURLE_RECV_ERROR);              /* 56 - failure in receiving network data */

        def(CURLE_SSL_CERTPROBLEM);         /* 58 - problem with the local certificate */
        def(CURLE_SSL_CIPHER);              /* 59 - couldn't use specified cipher */
        def(CURLE_SSL_CACERT);              /* 60 - problem with the CA cert (path?) */
        def(CURLE_BAD_CONTENT_ENCODING);    /* 61 - Unrecognized transfer encoding */

        def(CURLE_FILESIZE_EXCEEDED);       /* 63 - Maximum file size exceeded */

        def(CURLE_SEND_FAIL_REWIND);        /* 65 - Sending the data requires a rewind
                                          that failed */
        def(CURLE_SSL_ENGINE_INITFAILED);   /* 66 - failed to initialise ENGINE */
        def(CURLE_LOGIN_DENIED);            /* 67 - user); password or similar was not
                                          accepted and we failed to login */
        def(CURLE_CONV_FAILED);             /* 75 - conversion failed */
        def(CURLE_CONV_REQD);               /* 76 - caller must register conversion
                                          callbacks using curl_easy_setopt options
                                          CURLOPT_CONV_FROM_NETWORK_FUNCTION);
                                          CURLOPT_CONV_TO_NETWORK_FUNCTION); and
                                          CURLOPT_CONV_FROM_UTF8_FUNCTION */
        def(CURLE_SSL_CACERT_BADFILE);      /* 77 - could not load CACERT file); missing
                                          or wrong format */
        def(CURLE_REMOTE_FILE_NOT_FOUND);   /* 78 - remote file not found */
        def(CURLE_SSH);                     /* 79 - error from the SSH layer); somewhat
                                          generic so the error message will be of
                                          interest when this has happened */

        def(CURLE_SSL_SHUTDOWN_FAILED);     /* 80 - Failed to shut down the SSL
                                          connection */
#undef  def
    }
};

static const CURLcodeMapper sCURLcodeMapper;

LLXMLRPCListener::LLXMLRPCListener(const std::string& pumpname):
    mBoundListener(LLEventPumps::instance().
                   obtain(pumpname).
                   listen("LLXMLRPCListener", boost::bind(&LLXMLRPCListener::process, this, _1)))
{
}

/**
 * Capture an outstanding LLXMLRPCTransaction and poll it periodically until
 * done.
 *
 * The sequence is:
 * # Instantiate Poller, which instantiates, populates and initiates an
 *   LLXMLRPCTransaction. Poller self-registers on the LLEventPump named
 *   "mainloop".
 * # "mainloop" is conventionally pumped once per frame. On each such call,
 *   Poller checks its LLXMLRPCTransaction for completion.
 * # When the LLXMLRPCTransaction completes, Poller collects results (if any)
 *   and sends notification.
 * # The tricky part: Poller frees itself (and thus its LLXMLRPCTransaction)
 *   when done. The only external reference to it is the connection to the
 *   "mainloop" LLEventPump.
 */
class Poller
{
public:
    /// Validate the passed request for required fields, then use it to
    /// populate an XMLRPC_REQUEST and an associated LLXMLRPCTransaction. Send
    /// the request.
    Poller(const LLSD& command):
        mReqID(command),
        mUri(command["uri"]),
        mMethod(command["method"]),
        mReplyPump(command["reply"])
    {
        // LL_ERRS if any of these are missing
        const char* required[] = { "uri", "method", "reply" };
        // optional: "options" (array of string)
        // Validate the request
        std::set<std::string> missing;
        for (const char** ri = boost::begin(required); ri != boost::end(required); ++ri)
        {
            // If the command does not contain this required entry, add it to 'missing'.
            if (! command.has(*ri))
            {
                missing.insert(*ri);
            }
        }
        if (! missing.empty())
        {
            LL_ERRS("LLXMLRPCListener") << mMethod << " request missing params: ";
            const char* separator = "";
            for (std::set<std::string>::const_iterator mi(missing.begin()), mend(missing.end());
                 mi != mend; ++mi)
            {
                LL_CONT << separator << *mi;
                separator = ", ";
            }
            LL_CONT << LL_ENDL;
        }

        // Build the XMLRPC request.
        XMLRPC_REQUEST request = XMLRPC_RequestNew();
        XMLRPC_RequestSetMethodName(request, mMethod.c_str());
        XMLRPC_RequestSetRequestType(request, xmlrpc_request_call);
        XMLRPC_VALUE xparams = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
        LLSD params(command["params"]);
        if (params.isMap())
        {
            for (LLSD::map_const_iterator pi(params.beginMap()), pend(params.endMap());
                 pi != pend; ++pi)
            {
                std::string name(pi->first);
                LLSD param(pi->second);
                if (param.isString())
                {
                    XMLRPC_VectorAppendString(xparams, name.c_str(), param.asString().c_str(), 0);
                }
                else if (param.isInteger() || param.isBoolean())
                {
                    XMLRPC_VectorAppendInt(xparams, name.c_str(), param.asInteger());
                }
                else if (param.isReal())
                {
                    XMLRPC_VectorAppendDouble(xparams, name.c_str(), param.asReal());
                }
                else
                {
                    LL_ERRS("LLXMLRPCListener") << mMethod << " request param "
                                                << name << " has unknown type: " << param << LL_ENDL;
                }
            }
        }
        LLSD options(command["options"]);
        if (options.isArray())
        {
            XMLRPC_VALUE xoptions = XMLRPC_CreateVector("options", xmlrpc_vector_array);
            for (LLSD::array_const_iterator oi(options.beginArray()), oend(options.endArray());
                 oi != oend; ++oi)
            {
                XMLRPC_VectorAppendString(xoptions, NULL, oi->asString().c_str(), 0);
            }
            XMLRPC_AddValueToVector(xparams, xoptions);
        }
        XMLRPC_RequestSetData(request, xparams);

        mTransaction.reset(new LLXMLRPCTransaction(mUri, request, true, command.has("http_params")? LLSD(command["http_params"]) : LLSD()));
		mPreviousStatus = mTransaction->status(NULL);

        // Free the XMLRPC_REQUEST object and the attached data values.
        XMLRPC_RequestFree(request, 1);

        // Now ensure that we get regular callbacks to poll for completion.
        mBoundListener =
            LLEventPumps::instance().
            obtain("mainloop").
            listen(LLEventPump::ANONYMOUS, boost::bind(&Poller::poll, this, _1));

        LL_INFOS("LLXMLRPCListener") << mMethod << " request sent to " << mUri << LL_ENDL;
    }

    /// called by "mainloop" LLEventPump
    bool poll(const LLSD&)
    {
        bool done = mTransaction->process();

        CURLcode curlcode;
        LLXMLRPCTransaction::EStatus status;
        {
            // LLXMLRPCTransaction::status() is defined to accept int* rather
            // than CURLcode*. I don't feel the urge to fix the signature, but
            // we want a CURLcode rather than an int. So fetch it as a local
            // int, but then assign to a CURLcode for the remainder of this
            // method.
            int curlint;
            status = mTransaction->status(&curlint);
            curlcode = CURLcode(curlint);
        }

        LLSD data(mReqID.makeResponse());
        data["status"] = sStatusMapper.lookup(status);
        data["errorcode"] = sCURLcodeMapper.lookup(curlcode);
        data["error"] = "";
        data["transfer_rate"] = 0.0;
        LLEventPump& replyPump(LLEventPumps::instance().obtain(mReplyPump));
		if (! done)
        {
            // Not done yet, carry on.
			if (status == LLXMLRPCTransaction::StatusDownloading
				&& status != mPreviousStatus)
			{
				// If a response has been received, send the
				// 'downloading' status if it hasn't been sent.
				replyPump.post(data);
			}

			mPreviousStatus = status;
            return false;
        }

        // Here the transaction is complete. Check status.
        data["error"] = mTransaction->statusMessage();
		data["transfer_rate"] = mTransaction->transferRate();
        LL_INFOS("LLXMLRPCListener") << mMethod << " result from " << mUri << ": status "
                                     << data["status"].asString() << ", errorcode "
                                     << data["errorcode"].asString()
                                     << " (" << data["error"].asString() << ")"
                                     << LL_ENDL;

		switch (curlcode)
		{
			case CURLE_SSL_PEER_CERTIFICATE:
			case CURLE_SSL_CACERT:
                data["certificate"] = mTransaction->getErrorCertData();
				break;

			default:
				break;
		}
        // values of 'curlcode':
        // CURLE_COULDNT_RESOLVE_HOST,
        // CURLE_SSL_PEER_CERTIFICATE,
        // CURLE_SSL_CACERT,
        // CURLE_SSL_CONNECT_ERROR.
        // Given 'message', need we care?
        if (status == LLXMLRPCTransaction::StatusComplete)
        {
            // Success! Parse data.
            std::string status_string(data["status"]);
            data["responses"] = parseResponse(status_string);
            data["status"] = status_string;
        }

        // whether successful or not, send reply on requested LLEventPump
        replyPump.post(data);
        // need to wake up the loginCoro now
        llcoro::suspend();

        // Because mTransaction is a boost::scoped_ptr, deleting this object
        // frees our LLXMLRPCTransaction object.
        // Because mBoundListener is an LLTempBoundListener, deleting this
        // object disconnects it from "mainloop".
        // *** MUST BE LAST ***
        delete this;
        return false;
    }

private:
    /// Derived from LLUserAuth::parseResponse() and parseOptionInto()
    LLSD parseResponse(std::string& status_string)
    {
        // Extract every member into data["responses"] (a map of string
        // values).
        XMLRPC_REQUEST response = mTransaction->response();
        if (! response)
        {
            LL_DEBUGS("LLXMLRPCListener") << "No response" << LL_ENDL;
            return LLSD();
        }

        XMLRPC_VALUE param = XMLRPC_RequestGetData(response);
        if (! param)
        {
            LL_DEBUGS("LLXMLRPCListener") << "Response contains no data" << LL_ENDL;
            return LLSD();
        }

        // Now, parse everything
        return parseValues(status_string, "", param);
    }

    /**
     * Parse key/value pairs from a given XMLRPC_VALUE into an LLSD map.
     * @param key_pfx Used to describe a given key in log messages. At top
     * level, pass "". When parsing an options array, pass the top-level key
     * name of the array plus the index of the array entry; to this we'll
     * append the subkey of interest.
     * @param param XMLRPC_VALUE iterator. At top level, pass
     * XMLRPC_RequestGetData(XMLRPC_REQUEST).
     */
    LLSD parseValues(std::string& status_string, const std::string& key_pfx, XMLRPC_VALUE param)
    {
        LLSD responses;
        for (XMLRPC_VALUE current = XMLRPC_VectorRewind(param); current;
             current = XMLRPC_VectorNext(param))
        {
            std::string key(XMLRPC_GetValueID(current));
            LL_DEBUGS("LLXMLRPCListener") << "key: " << key_pfx << key << LL_ENDL;
            XMLRPC_VALUE_TYPE_EASY type = XMLRPC_GetValueTypeEasy(current);
            if (xmlrpc_type_string == type)
            {
                LLSD::String val(XMLRPC_GetValueString(current));
                LL_DEBUGS("LLXMLRPCListener") << "val: " << val << LL_ENDL;
                responses.insert(key, val);
            }
            else if (xmlrpc_type_int == type)
            {
                LLSD::Integer val(XMLRPC_GetValueInt(current));
                LL_DEBUGS("LLXMLRPCListener") << "val: " << val << LL_ENDL;
                responses.insert(key, val);
            }
            else if (xmlrpc_type_double == type)
            {
                LLSD::Real val(XMLRPC_GetValueDouble(current));
                LL_DEBUGS("LLXMLRPCListener") << "val: " << val << LL_ENDL;
                responses.insert(key, val);
            }
            else if (xmlrpc_type_array == type)
            {
                // We expect this to be an array of submaps. Walk the array,
                // recursively parsing each submap and collecting them.
                LLSD array;
                int i = 0;          // for descriptive purposes
                for (XMLRPC_VALUE row = XMLRPC_VectorRewind(current); row;
                     row = XMLRPC_VectorNext(current), ++i)
                {
                    // Recursive call. For the lower-level key_pfx, if 'key'
                    // is "foo", pass "foo[0]:", then "foo[1]:", etc. In the
                    // nested call, a subkey "bar" will then be logged as
                    // "foo[0]:bar", and so forth.
                    // Parse the scalar subkey/value pairs from this array
                    // entry into a temp submap. Collect such submaps in 'array'.
                    array.append(parseValues(status_string,
                                             STRINGIZE(key_pfx << key << '[' << i << "]:"),
                                             row));
                }
                // Having collected an 'array' of 'submap's, insert that whole
                // 'array' as the value of this 'key'.
                responses.insert(key, array);
            }
            else if (xmlrpc_type_struct == type)
            {
                LLSD submap = parseValues(status_string,
                                          STRINGIZE(key_pfx << key << ':'),
                                          current);
                responses.insert(key, submap);
            }
            else
            {
                // whoops - unrecognized type
                LL_WARNS("LLXMLRPCListener") << "Unhandled xmlrpc type " << type << " for key "
                                             << key_pfx << key << LL_ENDL;
                responses.insert(key, STRINGIZE("<bad XMLRPC type " << type << '>'));
                status_string = "BadType";
            }
        }
        return responses;
    }

    const LLReqID mReqID;
    const std::string mUri;
    const std::string mMethod;
    const std::string mReplyPump;
    LLTempBoundListener mBoundListener;
    boost::scoped_ptr<LLXMLRPCTransaction> mTransaction;
	LLXMLRPCTransaction::EStatus mPreviousStatus; // To detect state changes.
};

bool LLXMLRPCListener::process(const LLSD& command)
{
    // Allocate a new heap Poller, but do not save a pointer to it. Poller
    // will check its own status and free itself on completion of the request.
    (new Poller(command));
    // conventional event listener return
    return false;
}

ext: cpp

max_stars_repo_name: SaladDais/LLUDP-Encryption

max_issues_repo_name: bloomsirenix/Firestorm-manikineko

max_forks_repo_name: bloomsirenix/Firestorm-manikineko


text:
#pragma once
#include <stdexcept>
#include <typeinfo>
#include <type_traits>
#include <fc/exception/exception.hpp>
#include <boost/core/typeinfo.hpp>
#include <variant>

namespace fc {

template<typename Result>
struct visitor {};

template <typename variant, int32_t i = 0>
void from_index(variant& v, int index)
{
  if constexpr(i >= std::variant_size_v<variant>)
  {
    FC_THROW_EXCEPTION(fc::assert_exception, "Provided index out of range for variant.");
  }
  else if (index == 0)
  {
     auto value = variant(std::in_place_index<i>);
     v = std::move(value);
  }
  else
  {
     from_index<variant, i + 1>(v, index - 1);
  }
}

template<typename VariantType, typename T, std::size_t index = 0>
constexpr std::size_t get_index()
{
  if constexpr (index == std::variant_size_v<VariantType>)
  {
    return index;
  }
  else if constexpr (std::is_same_v<std::variant_alternative_t<index, VariantType>, T>)
  {
    return index;
  }
  else
  {
    return get_index<VariantType, T, index + 1>();
  }
}

struct from_static_variant
{
  variant& var;
  from_static_variant( variant& dv ):var(dv){}

  template<typename T> void operator()( const T& v )const
  {
      to_variant( v, var );
  }
};

struct to_static_variant
{
  const variant& var;
  to_static_variant( const variant& dv ):var(dv){}

  template<typename T> void operator()( T& v )const
  {
      from_variant( var, v );
  }
};

template<typename... T> void to_variant( const std::variant<T...>& s, fc::variant& v )
{
  variants vars(2);
  vars[0] = s.index();
  std::visit( from_static_variant(vars[1]), s );
  v = std::move(vars);
}

template<typename... T> void from_variant( const fc::variant& v, std::variant<T...>& s )
{
  auto ar = v.get_array();
  if( ar.size() < 2 )
  {
    s = std::variant<T...>();
    return;
  }
  from_index(s, ar[0].as_uint64());
  std::visit( to_static_variant(ar[1]), s );
}

template<typename... T> struct get_typename { static const char* name() { return BOOST_CORE_TYPEID(std::variant<T...>).name(); } };

}

ext: hpp

max_stars_repo_name: noir-protocol/fc

max_issues_repo_name: noir-protocol/fc

max_forks_repo_name: YosemiteLabs/fc


text:
#include "ZoneServerPCH.h"
#include "CheatService.h"
#include "../arena/ArenaService.h"
#include "../../model/gameobject/EntityEvent.h"
#include "../../model/gameobject/Entity.h"
#include "../../model/gameobject/AbstractAnchor.h"
#include "../../model/gameobject/ability/Cheatable.h"
#include "../../model/gameobject/ability/Networkable.h"
#include "../../controller/EntityController.h"
#include "../../controller/callback/BotCommandCallback.h"
#include "../../controller/callback/BuildingStateCallback.h"
#include "../../helper/InventoryHelper.h"
#include "../../service/anchor/AnchorService.h"
#include "../../service/arena/ArenaService.h"
#include "../../world/World.h"
#include "../../ZoneService.h"
#include "../../s2s/ZoneArenaServerProxy.h"
#include <sne/core/utility/Unicode.h>
#include <boost/lexical_cast.hpp>

namespace gideon { namespace zoneserver {

/**
 * @class BotMoveEvent
 */
class BotMoveEvent : public go::EntityEvent,
    public sne::core::ThreadSafeMemoryPoolMixin<BotMoveEvent>
{
public:
    BotMoveEvent(const Position& position) :
        position_(position) {}

private:
    virtual void call(go::Entity& entity) {
        gc::BotCommandCallback* callback = entity.getController().queryBotCommandCallback();
        if (callback != nullptr) {
            callback->commandMoved(position_);
        }
    }

private:
    const Position position_;
};

/**
 * @class BotSkillCastEvent
 */
class BotSkillCastEvent : public go::EntityEvent,
    public sne::core::ThreadSafeMemoryPoolMixin<BotSkillCastEvent>
{
public:
    BotSkillCastEvent() {}

private:
    virtual void call(go::Entity& entity) {
        gc::BotCommandCallback* callback = entity.getController().queryBotCommandCallback();
        if (callback != nullptr) {
            callback->commandSkillCasted();
        }
    }
};

void CheatService::execute(go::Cheatable& cheatable,
    ObjectId ownerId, const wstring_t& command, const wstring_t& params)
{
	if (! cheatable.canCheatable()) {
		return;
	}

    try {
        if (command == L"/additem") {
            const size_t find_pos = params.find_first_of(L" ");
            const string_t param1 = sne::core::toUtf8(params.substr(0, find_pos));
            const string_t param2 = sne::core::toUtf8(params.substr(find_pos + 1));
            const DataCode itemCode = boost::lexical_cast<DataCode>(param1);
            const uint8_t itemCount = uint8_t(boost::lexical_cast<uint32_t>(param2));
            const uint8_t revisionItemCount = itemCount > getStackItemCount(itemCode) ?
                getStackItemCount(itemCode) : itemCount;
            const CodeType ct = getCodeType(itemCode);
            if (isItemType(ct)) {
                cheatable.cheatAddItem(itemCode, revisionItemCount);
            }
        }
        else if (command == L"/move") {
            const size_t find_pos = params.find_first_of(L" ");
            const wstring_t param1 = params.substr(0, find_pos);
            const wstring_t param2 = params.substr(find_pos + 1);
            const Position position(boost::lexical_cast<float32_t>(param1),
                boost::lexical_cast<float32_t>(param2), 0.0f);
            BotMoveEvent::Ref event(new BotMoveEvent(position));
            WORLD->broadcast(event);
            // 브로드 캐스팅
        }
        else if (command == L"/castskill") {
            BotSkillCastEvent::Ref event(new BotSkillCastEvent());
            WORLD->broadcast(event);
        }
        else if (command == L"/fullpoints") {
            cheatable.cheatFullCharacterPoints();
        }
        else if (command == L"/rewardexp") {
            const ExpPoint rewardExp = toExpPoint(boost::lexical_cast<int>(params));
            cheatable.cheatRewardExp(rewardExp < ceMin ? ceMin: rewardExp);
        }
        else if (command == L"/showmethemoney") {
            const GameMoney money = boost::lexical_cast<GameMoney>(params);
            cheatable.cheatUpGameMoney(money < 0 ? 0 : money);
        }
        else if (command == L"/addskill") {
            const SkillCode skillCode = boost::lexical_cast<SkillCode>(params);
            cheatable.cheatAddSkill(skillCode);
        }
        else if (command == L"/recall") {
            const size_t find_pos = params.find_first_of(L" ");
            const Nickname nickname = params.substr(0, find_pos);
            cheatable.cheatRecall(nickname);
        }
        else if (command == L"/toplayer") {
            const size_t find_pos = params.find_first_of(L" ");
            const Nickname nickname = params.substr(0, find_pos);
            cheatable.cheatTeleportToPlayer(nickname);
        }
        else if (command == L"/toposition") {
            wstring_t tempParams = params;
            size_t find_pos = params.find_first_of(L" ");
            const wstring_t param1 = tempParams.substr(0, find_pos);
            tempParams = tempParams.substr(find_pos + 1);
            find_pos = tempParams.find_first_of(L" ");
            const wstring_t param2 = tempParams.substr(0, find_pos);
            tempParams = tempParams.substr(find_pos + 1);
            const wstring_t param3 = tempParams.substr(0, find_pos);
            const float32_t positionX = boost::lexical_cast<float32_t>(param1);
            const float32_t positionY = boost::lexical_cast<float32_t>(param2);
            const float32_t positionZ = boost::lexical_cast<float32_t>(param3);
            cheatable.cheatTeleportToPosition(Position(positionX, positionY, positionZ));
        }
        else if (command == L"/mercenarypoint") {
            const MercenaryPoint mercenaryPoint = boost::lexical_cast<MercenaryPoint>(params);
            cheatable.cheatMercenaryPoint(mercenaryPoint < 0 ? 0 : mercenaryPoint);
        }
        else if (command == L"/logout") {
            const size_t find_pos = params.find_first_of(L" ");
            const Nickname nickname = params.substr(0, find_pos);
            go::Entity* player = WORLD->getPlayer(nickname);
            if (player) {
                player->queryNetworkable()->logout();
            }
        }
        else if (command == L"/arenapoint") {
            const ArenaPoint arenaPoint = boost::lexical_cast<ArenaPoint>(params);
            cheatable.cheatArenaPoint(arenaPoint < 0 ? 0 : arenaPoint);
        }
        else if (command == L"/releasedeserter") {
            if (ZONE_SERVICE->isArenaServer()) {
                ARENA_SERVICE->releaseDeserter(ownerId);
            }
            else {
                ZONE_SERVICE->getArenaServerProxy().z2a_releaseDeserter(ownerId);
            }
        }
        else if (command == L"/who") {
            cheatable.cheatWhoIsInZone();
        }
        else if (command == L"/destorybuilding") {
            const ObjectId buildingId = boost::lexical_cast<ObjectId>(params);
            go::AbstractAnchor* abstractAnchor = ANCHOR_SERVICE->getAnchor(GameObjectInfo(otBuilding, buildingId));
            if (abstractAnchor) {
                gc::BuildingStateCallback* callback = abstractAnchor->getController().queryBuildingStateCallback();
                if (callback) {
                    callback->buildDestroyed();
                    static_cast<go::Entity*>(abstractAnchor)->despawn();
                }
            }
        }
        else if (command == L"/destoryanchor") {
            const ObjectId buildingId = boost::lexical_cast<ObjectId>(params);
            go::AbstractAnchor* abstractAnchor = ANCHOR_SERVICE->getAnchor(GameObjectInfo(otAnchor, buildingId));
            if (abstractAnchor) {
                static_cast<go::Entity*>(abstractAnchor)->despawn();
            }
        }
		else if (command == L"/resetcooltime") {
			cheatable.cheatResetCooldown();
		}
		else if (command == L"/acceptquest") {
			const QuestCode questCode = boost::lexical_cast<QuestCode>(params);
			cheatable.cheatAcceptQuest(questCode);
		}
		else if (command == L"/forgeCoin") {
			const ForgeCoin forgeCoin = boost::lexical_cast<ForgeCoin>(params);
			cheatable.cheatForgeCoin(forgeCoin < 0 ? 0 : forgeCoin);
		}
    }
    catch (boost::bad_lexical_cast&) {
        SNE_LOG_ERROR("bad cheat key")
    }

}

}} // namespace gideon { namespace zoneserver {

max_stars_repo_name: mark-online/server

max_issues_repo_name: mark-online/server

max_forks_repo_name: mark-online/server


text:
#include "config.h"

#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif /* HAVE_STDLIB_H */

#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif /* HAVE_SYS_STAT_H */

#include "MakedicGenerator.h"
#include <map>
#include <iostream>
#include <fstream>
#include <sstream>
#include <cstring>
#include <stdio.h>
#include <dirent.h>
#include <stdexcept>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include <boost/scope_exit.hpp>
#include "GeowordJudge.h"
#include "MakedicItem.h"
#include "MakedicCsvFileIO.h"
#include "DBAccessor.h"

#define MAX_RETRY_TIMES 20
#define SEP "{$GEOWORD}"

MakedicGenerator::MakedicGenerator(
				 const MakedicGeneratorConfiguration& conf,
				 const MakedicGeneratorAppArgument& arg) {
  // MeCab 動作環境を取得
  std::cout << "MeCab の実行環境を調査しています。" << std::endl;
  MeCab::Tagger* tagger = MeCab::createTagger("");
  const MeCab::DictionaryInfo* d = tagger->dictionary_info();
  std::string charset(d->charset);
  std::cout << "  charset      = " << charset << std::endl;
  transform(charset.begin(), charset.end(), charset.begin(), tolower);
  if (charset != "utf-8" && charset != "utf8") {
    std::cerr << "  MeCab の辞書が UTF-8 ではありません。\n処理を中断します。" << std::endl;
    exit(1);
  }
  // "/usr/local/lib/mecab/dic/ipadic/" の部分を取得する
  std::string dic_path = std::string(d->filename);
  int last_slash_pos = dic_path.find_last_of('/');
  if (last_slash_pos != std::string::npos) {
    this->sysdic_directory_path = dic_path.substr(0, last_slash_pos);
  } else {
    this->sysdic_directory_path = dic_path;
  }
  std::cout << "  mecab_sysdic = " << this->sysdic_directory_path << std::endl;

  mecab_dict_index_path = conf.get_mecab_dict_index_path();
  example_sentences = conf.get_example_sentences();
  deleteTempFileFlag = conf.get_delete_temp_file_flag();

  verboseMode = arg.hasOption("v");
  //  geowordFeatureCheckMode = !arg.hasOption("c");
  profile = arg.getProfile();
  csv_output_filename = arg.getOptCsvOutputFilename();
  /*
  geodic_filepath = arg.getProfile().get_sqlite3_file();
  darts_filepath = arg.getProfile().get_darts_file();
  ipadic_filepath = arg.getProfile().get_mecab_userdic();
  */

  // 地名語の品詞ID
  if (arg.hasOption("d")) {
    // 直接品詞ID指定を最優先
    geoword_hinshi_id = arg.getOptGeowordHinshiId();
    std::cout << "  -d オプションで指定されたID（="
	      << geoword_hinshi_id
	      << "）を地名語IDとして利用します。" << std::endl;
  } else if (arg.hasOption("n")) {
    // naist-jdic の「名詞-固有名詞-地名-一般」品詞ID = "1365"
    geoword_hinshi_id = "1365";
    std::cout << "  naist-jdic を利用します（-n オプション）。" << std::endl;
  } else if (arg.hasOption("i")) {
    // IPADIC の「名詞-固有名詞-地名-一般」品詞ID = "1293"
    geoword_hinshi_id = "1293";
    std::cout << "  IPADIC を利用します（-i オプション）。" << std::endl;
  } else {
    if (sysdic_directory_path.find("ipadic") != std::string::npos) { // IPADIC
      geoword_hinshi_id = "1293";
      std::cout << "  IPADIC を利用します（dictionary path による自動判別）。" << std::endl;
    } else if (sysdic_directory_path.find("naist") != std::string::npos) { // NAIST
      geoword_hinshi_id = "1365";
      std::cout << "  naist-jdic を利用します（dictionary path による自動判別）。" << std::endl;
    } else { // MeCab を起動して調査
      MeCab::Tagger* tmp_tagger = MeCab::createTagger("-F %phl");
      std::string left_id(tmp_tagger->parse("東京"));
      delete tmp_tagger;
      int pos = left_id.find("EOS", 0);
      if (pos == std::string::npos) {
	std::cerr << "  MeCab が GeoNLP 互換辞書（IPADIC, naist-jdic）以外を利用していて、固有名詞-地名のIDが識別できません。\nMeCab が以下の値を返しました。\n" << left_id << "\n処理を中断します。" << std::endl;
	exit(1);
      }
      geoword_hinshi_id = left_id.substr(0, pos);
      if (geoword_hinshi_id == "1365") {
	std::cout << "  naist-jdic を利用します（MeCab 左文脈IDによる自動判別）。" << std::endl;
      } else if (geoword_hinshi_id == "1293") {
	std::cout << "  IPADIC を利用します（MeCab 左文脈IDによる自動判別）。" << std::endl;
      } else {
	std::cout << "  MeCab が返したID (=" << geoword_hinshi_id << ") を地名語IDとして利用します。" << std::endl;
      }
    }
  }

  // 一時生成ファイルの prefix 作成
  char tmpfile_prefix_tmp[] = "/tmp/tmp_XXXXXX";
  close(mkstemp(tmpfile_prefix_tmp));
  remove(tmpfile_prefix_tmp);
  tmpfile_prefix = tmpfile_prefix_tmp;
}

void MakedicGenerator::generate(bool doUpdateWordlist) const
  throw (MakedicException) {

  std::vector<geonlp::Wordlist> wordlists;

  if (doUpdateWordlist) {
    // wordlist と darts ファイルを更新する
    printf("単語リストからインデックスを更新しています。\n");
    _createDartsIndex(wordlists);
  } else {
    // wordlist を DB から読みだす（更新はしない）
    printf("単語リストを読み込んでいます。\n");
    _read_db(wordlists);
  }

  //　終了時のCSV処理
  std::set<std::string> tmpfiles;
  std::string last_tmpfile = "";
  BOOST_SCOPE_EXIT ((&deleteTempFileFlag)(&tmpfiles)(&csv_output_filename)(&last_tmpfile)) {
    if (csv_output_filename != "" && last_tmpfile != "") {
      if (boost::filesystem::exists(csv_output_filename)) {
	boost::filesystem::remove(csv_output_filename);
      }
      // 最後の CSV ファイルをコピーする
      boost::filesystem::copy_file(last_tmpfile, csv_output_filename);
    }
    if (deleteTempFileFlag) {
      // 一時ファイルを削除する
      for (std::set<std::string>::iterator it = tmpfiles.begin(); it != tmpfiles.end(); it++) {
	remove(it->c_str());
      }
    }
  } BOOST_SCOPE_EXIT_END

      // ユーザ辞書に登録する地名語およびその生起コストを求める。
      std::map<std::string, long> wcosts;
  std::string user_dic_file_path = "";
  int retry_counter = 0;
  int prev_inconsistency = -1;
  while (retry_counter < MAX_RETRY_TIMES) {

    printf("コスト収束計算 %d 回目\n", retry_counter + 1);

    // MeCab の生成
    mecab_t *mecab;
    {
      std::string arg = "";
      if (retry_counter > 0) {
	arg += "--userdic=" + user_dic_file_path;
      }
      arg += " --unk-format=\"%m\t未知語,*,*,*,*,*,*,*,*\n\"";
#ifdef DEBUG
      std::cout << arg << std::endl;
#endif /* DEBUG */
      mecab = mecab_new2(arg.c_str());
      if (mecab == NULL) {
	throw MakedicException(mecab_strerror(NULL));
      }
    }

    // 地名語が 地名語として識別可能かどうか判定するための判定オブジェクトを設定する
    std::vector<GeowordJudge> judges;
    for (std::vector<std::string>::const_iterator it = example_sentences.begin(); it != example_sentences.end(); it++) {
      std::string::size_type index = (*it).find(SEP);
      if (index == std::string::npos) continue;
      std::string pre_sentence = (*it).substr(0, index);
      std::string pst_sentence = (*it).substr(index + strlen(SEP), (*it).length());
      GeowordJudge judge(mecab, pre_sentence, pst_sentence, profile);
      judges.push_back(judge);
    }

    // 見出し語毎に生起コストを求める。ただし、必要なものだけ。
    int inconsistency = 0;
    std::string surface;
    for (std::vector<geonlp::Wordlist>::iterator it_w = wordlists.begin(); it_w != wordlists.end(); it_w++) {
      geonlp::Wordlist wordlist = *it_w;
      surface = wordlist.get_surface();

      // 地名語を様々なパターンで形態素解析して、/^P?HB*$/ にマッチするかどうか判定する
      bool judge_result = true;
      for (std::vector<GeowordJudge>::iterator it = judges.begin(); it != judges.end(); it++) {
	try {
	  judge_result &= (*it).judge(surface);
	} catch (const GeowordJudgeFailedException &e) {
	  throw MakedicException("failed to judge as a geoword. (case 1)");
	}
	if (!judge_result) break;
      }

      // /^P?HB*$/ にマッチしない場合は、生起コストを求めてマップに登録
      if (!judge_result) {
	long wcost = -1;
	bool is_undefined = true;
	for (std::vector<GeowordJudge>::iterator it = judges.begin(); it != judges.end(); it++) {
	  try {
	    long wcost_new = (*it).cost(surface, is_undefined);
	    if (wcost_new < 0) continue; // 固有名詞として登録済み
	    if (wcost < 0) wcost = wcost_new;
	    else {
	      // 要検討
	      // if (is_undefined) wcost = std::min(wcost, wcost_new);
	      // else wcost = std::max(wcost, wcost_new);
	      wcost = std::min(wcost, wcost_new);
	    }
	  } catch (const GeowordJudgeFailedException &e) {
	    throw MakedicException("failed to judge as a geoword. (case 2)");
	  }
	}

	if (verboseMode) {
	  std::cout << surface << "\tcost= " << wcost << std::endl;
	}
	if (wcost < 0) { // 登録済み地名語
	  continue;
	}

	if (wcosts.find(surface) == wcosts.end()) {
	  inconsistency++;
	} else {
	  long wcost_before = wcosts[surface];
	  if (wcost_before != wcost) inconsistency++;
	}
	wcosts[surface] = wcost;
      } else {
	//      if (verboseMode) {
	//	  std::cout << surface << "\tcost= --" << std::endl;
	//	}
      }
    }

    // 各種クローズ処理
    mecab_destroy(mecab);

    // 辞書が収束していたらここでループを抜ける
    printf("  不整合数: % 6d 件\n", inconsistency);
    if (inconsistency == 0) break;
    if (prev_inconsistency > 0 && prev_inconsistency <= inconsistency) {
      std::cerr << "  これ以上不整合箇所を減らせないため、収束処理を終了します。" << std::endl;
      break;
    }
    prev_inconsistency = inconsistency;

    // 形態素辞書に登録するアイテムセットを作成する
    // IPADICファイルを作成する
    std::string csv_file_path = _create_tmpfile_name(".csv", retry_counter);
    {
      std::vector<MakedicItem> ipadic_items;
      ipadic_items.clear();
      for (std::vector<geonlp::Wordlist>::iterator it_w = wordlists.begin(); it_w != wordlists.end(); it_w++) {
	geonlp::Wordlist& wordlist = *it_w;
	long cost = wcosts[wordlist.get_surface()];
	if (cost == 0L) continue;
	MakedicItem item;
	_create_ipadic_item(wordlist, cost, item);
	ipadic_items.push_back(item);
      }

      MakedicCsvFileIO::write(csv_file_path, ipadic_items);
      tmpfiles.insert(csv_file_path);
      last_tmpfile = csv_file_path; // 最後に生成した CSV ファイル
    }

    std::string dic_file_path = _create_tmpfile_name(".dic", retry_counter);
    std::string compile_command;
    {
      std::ostringstream stream;
      stream << mecab_dict_index_path
	     << " -d "
	     << sysdic_directory_path
	     << " -u "
	     << dic_file_path
	     << " -f utf-8 -t utf-8 "
	     << csv_file_path;
      compile_command = stream.str();
      //#ifdef DEBUG
      std::cout << compile_command << std::endl;
      //#endif /* DEBUG */
    }
    std::cout << "  ユーザ辞書を作成中。\n";

    int rc = system(compile_command.c_str());
    if (rc == -1) {
      perror("Error:");
      std::cerr << compile_command << std::endl;
      throw std::runtime_error("mecab-dict-index コマンドが実行できません。");
    }
    tmpfiles.insert(dic_file_path);

    // 次ループ向け処理
    user_dic_file_path = dic_file_path;
    retry_counter++;
  }

  // 出力先にコピー
  if (user_dic_file_path.length() > 0) {
    std::ifstream ifs(user_dic_file_path.c_str());
    std::ofstream ofs(profile.get_mecab_userdic().c_str());
    if (!ifs) {
      throw MakedicException("生成したユーザ辞書データファイルが開けません");
    }
    if (!ofs) {
      throw MakedicException("プロファイルで指定されているユーザ辞書データファイルに書き込めません");
    }
    ofs << ifs.rdbuf() << std::flush;
    if (!ifs) {
      throw MakedicException("生成したユーザ辞書データファイルからの読み込みに失敗しました");
    }
    if (!ofs) {
      throw MakedicException("ユーザ辞書データファイルへの書き込みに失敗しました");
    }
  }

}

// 辞書に登録されている全見出し語（Wordlist）を取得する
void MakedicGenerator::_read_db(std::vector<geonlp::Wordlist>& wordlists) const
  throw(MakedicException) {
  geonlp::DBAccessor dao = geonlp::DBAccessor(this->profile);
  try {
    dao.open();
  } catch (std::runtime_error& e) {
    throw MakedicException(e.what());
  }
  try {
    dao.findAllWordlist(wordlists);

    BOOST_SCOPE_EXIT ((&dao)) {
      dao.close();
    } BOOST_SCOPE_EXIT_END
	} catch (std::runtime_error& e) {
    throw MakedicException(e.what());
  }
}

std::string MakedicGenerator::_create_tmpfile_name(
						  const char* suffix, int count) const {
  std::ostringstream stream;
  stream << tmpfile_prefix << "__geoword";
  stream.width(2);
  stream.fill('0');
  stream << count << suffix;
  return stream.str();
}

void MakedicGenerator::_create_ipadic_item(const geonlp::Wordlist& wordlist, const long& cost, MakedicItem& item) const {
  item.set_surface(wordlist.get_surface());
  item.set_leftContextId(geoword_hinshi_id);
  item.set_rightContextId(geoword_hinshi_id);
  item.set_cost(cost);
  item.set_partOfSpeech("名詞");
  item.set_subclassification1("固有名詞");
  item.set_subclassification2("地域");
  item.set_subclassification3("一般");
  item.set_conjugatedForm("*");
  item.set_conjugationType("*");
  item.set_originalForm(wordlist.get_surface());
  item.set_yomi(wordlist.get_yomi());
  item.set_pronunciation(wordlist.get_yomi());
}

// Wordlist と Darts ファイルを更新、保存、結果を取得する
void MakedicGenerator::_createDartsIndex(std::vector<geonlp::Wordlist>& wordlists) const {
  geonlp::DBAccessor dao = geonlp::DBAccessor(this->profile);
  dao.open();
  try {
    dao.updateWordlists(wordlists);

    BOOST_SCOPE_EXIT ((&dao)) {
      dao.close();
    } BOOST_SCOPE_EXIT_END
	} catch (std::runtime_error& e) {
    throw MakedicException(e.what());
  }
  std::cout << wordlists.size() << "件の地名語表記を登録しました。" << std::endl;
}

max_stars_repo_name: t-sagara/geonlp-software

max_issues_repo_name: t-sagara/geonlp-software

max_forks_repo_name: t-sagara/geonlp-software


text:
/*
 * Copyright 2017-2019 Janek Bevendorff, Webis Group
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef OBFUSCATION_CONTEXT_HPP
#define OBFUSCATION_CONTEXT_HPP

#include "util/NgramProfile.hpp"

#include <boost/optional.hpp>

/**
 * Global search context.
 */
struct Context {
    /**
     * DTO for context meta data.
     */
    struct MetaData
    {
        boost::optional<std::size_t> originalTextLength;
        boost::optional<double> originalJsd;
        boost::optional<double> goalJSDist;
    };

    typedef std::shared_ptr<NgramProfile> NgramPtr;
    typedef std::shared_ptr<NgramProfile const> ConstNgramPtr;

    Context() = default;
    explicit Context(ConstNgramPtr targetProfile);
    Context(Context const& other) = default;
    Context(Context&& other) noexcept = default;
    Context& operator=(Context const& other) = default;
    Context& operator=(Context&& other) noexcept = default;

    ConstNgramPtr targetNgramProfile;

    /**
     * Pointer to mutable meta data for this context.
     * The target object may be modified during execution.
     */
    std::shared_ptr<MetaData> mutableMetaData = std::make_shared<MetaData>();
};

#endif // OBFUSCATION_CONTEXT_HPP