<commit_before>/***************************************************************
 *
 * Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 *
 * 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 "condor_common.h"
#include "condor_debug.h"
#include "selector.h"

int Selector::_fd_select_size = -1;

fd_set *Selector::cached_read_fds = NULL;
fd_set *Selector::cached_write_fds = NULL;
fd_set *Selector::cached_except_fds = NULL;

fd_set *Selector::cached_save_read_fds = NULL;
fd_set *Selector::cached_save_write_fds = NULL;
fd_set *Selector::cached_save_except_fds = NULL;

Selector::Selector()
{
#if defined(WIN32)
		// On Windows, we can't treat fd_set as an open-ended bit array.
		// fd_set can take up to FD_SETSIZE sockets (not any socket whose
		// fd is <= FD_SETSIZE) and that's it. Currently, we set
		// FD_SETSIZE to 1024. I'm not sure what we can do if we ever
		// have more than 1024 sockets to select on.
	fd_set_size = 1;
#else
	int nfdbits = 8 * sizeof(fd_set);
	fd_set_size = ( fd_select_size() + (nfdbits - 1) ) / nfdbits;
#endif

	if ( cached_read_fds ) {
		read_fds = cached_read_fds;
		write_fds = cached_write_fds;
		except_fds = cached_except_fds;

		save_read_fds = cached_save_read_fds;
		save_write_fds = cached_save_write_fds;
		save_except_fds = cached_save_except_fds;

		cached_read_fds = NULL;
		cached_write_fds = NULL;
		cached_except_fds = NULL;
		cached_save_read_fds = NULL;
		cached_save_write_fds = NULL;
		cached_save_except_fds = NULL;
	} else {
		read_fds = (fd_set *)calloc( fd_set_size, sizeof(fd_set) );
		write_fds = (fd_set *)calloc( fd_set_size, sizeof(fd_set) );
		except_fds = (fd_set *)calloc( fd_set_size, sizeof(fd_set) );

		save_read_fds = (fd_set *)calloc( fd_set_size, sizeof(fd_set) );
		save_write_fds = (fd_set *)calloc( fd_set_size, sizeof(fd_set) );
		save_except_fds = (fd_set *)calloc( fd_set_size, sizeof(fd_set) );
	}

	reset();
}

Selector::~Selector()
{
	if ( cached_read_fds == NULL ) {
		cached_read_fds = read_fds;
		cached_write_fds = write_fds;
		cached_except_fds = except_fds;

		cached_save_read_fds = save_read_fds;
		cached_save_write_fds = save_write_fds;
		cached_save_except_fds = save_except_fds;
	} else {
		free( read_fds );
		free( write_fds );
		free( except_fds );

		free( save_read_fds );
		free( save_write_fds );
		free( save_except_fds );
	}
}

void
Selector::reset()
{
	_select_retval = -2;
	_select_errno = 0;
	state = VIRGIN;
	timeout_wanted = FALSE;
	max_fd = -1;
#if defined(WIN32)
	FD_ZERO( save_read_fds );
	FD_ZERO( save_write_fds );
	FD_ZERO( save_except_fds );
#else
	memset( save_read_fds, 0, fd_set_size * sizeof(fd_set) );
	memset( save_write_fds, 0, fd_set_size * sizeof(fd_set) );
	memset( save_except_fds, 0, fd_set_size * sizeof(fd_set) );
#endif

	dprintf(D_FULLDEBUG, "selector 0x%lX resetting\n", (unsigned long)this);
}

int
Selector::fd_select_size()
{
	if ( _fd_select_size < 0 ) {
#if defined(WIN32)
		// On Windows, fd_set is not a bit-array, but a structure that can
		// hold up to 1024 different file descriptors (not just file
		// descriptors whose value is less than 1024). We set max_fds to
		// 1024 (FD_SETSIZE) as a reasonable approximation.
		_fd_select_size = FD_SETSIZE;
#elif defined(Solaris)
		// Solaris's select() can't handle fds greater than FD_SETSIZE.
		int max_fds = getdtablesize();
		if ( max_fds < FD_SETSIZE ) {
			_fd_select_size = max_fds;
		} else {
			_fd_select_size = FD_SETSIZE;
		}
#else
		_fd_select_size = getdtablesize();
#endif
	}
	return _fd_select_size;
}

char*
describe_fd(int fd) {
#if defined(LINUX)
#define PROC_BUFSIZE 32
#define LINK_BUFSIZE 256

  char proc_buf[PROC_BUFSIZE];
  char link_buf[LINK_BUFSIZE];
  ssize_t end;

  memset(link_buf, '\0', LINK_BUFSIZE);

  snprintf(proc_buf, PROC_BUFSIZE, "/proc/self/fd/%d", fd);
  end = readlink(proc_buf, link_buf, LINK_BUFSIZE);

  if (end == -1) {
    goto cleanup;
  }

  link_buf[end] = '\0';
  return strdup(link_buf);
 cleanup:
#endif
  return strdup("");
}

void
Selector::add_fd( int fd, IO_FUNC interest )
{
	// update max_fd (the highest valid index in fd_set's array) and also

	// make sure we're not overflowing our fd_set
	// On Windows, we have to check the individual fd_set to see if it's
	// full.
	if( fd > max_fd ) {
		max_fd = fd;
	}
#if !defined(WIN32)
	if ( fd < 0 || fd >= fd_select_size() ) {
		EXCEPT( "Selector::add_fd(): fd %d outside valid range 0-%d",
				fd, _fd_select_size-1 );
	}
#endif

	char *fd_description = describe_fd(fd);
	dprintf(D_FULLDEBUG, "selector 0x%lX adding fd %d (%s)\n", (unsigned long)this, fd, fd_description);
	free(fd_description);

	switch( interest ) {

	  case IO_READ:
#if defined(WIN32)
		if ( save_read_fds->fd_count >= fd_select_size() ) {
			EXCEPT( "Selector::add_fd(): read fd_set is full" );
		}
#endif
		FD_SET( fd, save_read_fds );
		break;

	  case IO_WRITE:
#if defined(WIN32)
		if ( save_write_fds->fd_count >= fd_select_size() ) {
			EXCEPT( "Selector::add_fd(): write fd_set is full" );
		}
#endif
		FD_SET( fd, save_write_fds );
		break;

	  case IO_EXCEPT:
#if defined(WIN32)
		if ( save_except_fds->fd_count >= fd_select_size() ) {
			EXCEPT( "Selector::add_fd(): except fd_set is full" );
		}
#endif
		FD_SET( fd, save_except_fds );
		break;

	}
}

void
Selector::delete_fd( int fd, IO_FUNC interest )
{
#if !defined(WIN32)
	if ( fd < 0 || fd >= fd_select_size() ) {
		EXCEPT( "Selector::delete_fd(): fd %d outside valid range 0-%d",
				fd, _fd_select_size-1 );
	}
#endif

	dprintf(D_FULLDEBUG, "selector 0x%lX deleting fd %d\n", (unsigned long)this, fd);

	switch( interest ) {

	  case IO_READ:
		FD_CLR( fd, save_read_fds );
		break;

	  case IO_WRITE:
		FD_CLR( fd, save_write_fds );
		break;

	  case IO_EXCEPT:
		FD_CLR( fd, save_except_fds );
		break;

	}
}

void
Selector::set_timeout( time_t sec, long usec )
{
	timeout_wanted = TRUE;

	timeout.tv_sec = sec;
	timeout.tv_usec = usec;
}

void
Selector::set_timeout( timeval tv )
{
	timeout_wanted = TRUE;

	timeout = tv;
}

void
Selector::unset_timeout()
{
	timeout_wanted = FALSE;
}

void
Selector::execute()
{
	int		nfds;
	struct timeval	*tp;

	memcpy( read_fds, save_read_fds, fd_set_size * sizeof(fd_set) );
	memcpy( write_fds, save_write_fds, fd_set_size * sizeof(fd_set) );
	memcpy( except_fds, save_except_fds, fd_set_size * sizeof(fd_set) );

	if( timeout_wanted ) {
		tp = &timeout;
	} else {
		tp = NULL;
	}

		// select() ignores its first argument on Windows. We still track
		// max_fd for the display() functions.
	nfds = select( max_fd + 1,
				  (SELECT_FDSET_PTR) read_fds,
				  (SELECT_FDSET_PTR) write_fds,
				  (SELECT_FDSET_PTR) except_fds,
				  tp );
	_select_retval = nfds;

	if( nfds < 0 ) {
		_select_errno = errno;
#if !defined(WIN32)
		if( errno == EINTR ) {
			state = SIGNALLED;
			return;
		}
#endif
		state = FAILED;
		return;
	}
	_select_errno = 0;

	if( nfds == 0 ) {
		state = TIMED_OUT;
	} else {
		state = FDS_READY;
	}
	return;
}

int
Selector::select_retval()
{
	return _select_retval;
}

int
Selector::select_errno()
{
	return _select_errno;
}

BOOLEAN
Selector::fd_ready( int fd, IO_FUNC interest )
{
	if( state != FDS_READY && state != TIMED_OUT ) {
		EXCEPT(
			"Selector::fd_ready() called, but selector not in FDS_READY state"
		);
	}

#if !defined(WIN32)
	// on UNIX, make sure the value of fd makes sense
	//
	if ( fd < 0 || fd >= fd_select_size() ) {
		return FALSE;
	}
#endif

	switch( interest ) {

	  case IO_READ:
		return FD_ISSET( fd, read_fds );
		break;

	  case IO_WRITE:
		return FD_ISSET( fd, write_fds );
		break;

	  case IO_EXCEPT:
		return FD_ISSET( fd, except_fds );
		break;

	}

		// Can never get here
	return FALSE;
}

BOOLEAN
Selector::timed_out()
{
	return state == TIMED_OUT;
}

BOOLEAN
Selector::signalled()
{
	return state == SIGNALLED;
}

BOOLEAN
Selector::failed()
{
	return state == FAILED;
}

BOOLEAN
Selector::has_ready()
{
	return state == FDS_READY;
}

void
Selector::display()
{

	switch( state ) {

	  case VIRGIN:
		dprintf( D_ALWAYS, "State = VIRGIN\n" );
		break;

	  case FDS_READY:
		dprintf( D_ALWAYS, "State = FDS_READY\n" );
		break;

	  case TIMED_OUT:
		dprintf( D_ALWAYS, "State = TIMED_OUT\n" );
		break;

	  case SIGNALLED:
		dprintf( D_ALWAYS, "State = SIGNALLED\n" );
		break;

	  case FAILED:
		dprintf( D_ALWAYS, "State = FAILED\n" );
		break;
	}

	dprintf( D_ALWAYS, "max_fd = %d\n", max_fd );

	dprintf( D_ALWAYS, "Selection FD's\n" );
	bool try_dup = ( (FAILED == state) &&  (EBADF == _select_errno) );
	display_fd_set( "\tRead", save_read_fds, max_fd, try_dup );
	display_fd_set( "\tWrite", save_write_fds, max_fd, try_dup );
	display_fd_set( "\tExcept", save_except_fds, max_fd, try_dup );

	if( state == FDS_READY ) {
		dprintf( D_ALWAYS, "Ready FD's\n" );
		display_fd_set( "\tRead", read_fds, max_fd );
		display_fd_set( "\tWrite", write_fds, max_fd );
		display_fd_set( "\tExcept", except_fds, max_fd );
	}
	if( timeout_wanted ) {
		dprintf( D_ALWAYS,
			"Timeout = %ld.%06ld seconds\n", (long) timeout.tv_sec,
			(long) timeout.tv_usec
		);
	} else {
		dprintf( D_ALWAYS, "Timeout not wanted\n" );
	}

}

void
display_fd_set( char *msg, fd_set *set, int max, bool try_dup )
{
	int		i;

	dprintf( D_ALWAYS, "%s {", msg );
	for( i=0; i<=max; i++ ) {
		if( FD_ISSET(i,set) ) {
			dprintf( D_ALWAYS | D_NOHEADER, "%d", i );

			if( try_dup ) {
#			  if defined( UNIX )
				int newfd = dup( i );
				if ( newfd >= 0 ) {
					close( newfd );
				}
				else if ( EBADF == errno ) {
					dprintf( D_ALWAYS | D_NOHEADER, "<EBADF> " );
				}
				else {
					dprintf( D_ALWAYS | D_NOHEADER, "<%d> ", errno );
				}
#			  endif
			}

			dprintf( D_ALWAYS | D_NOHEADER, " " );
		}
	}
	dprintf( D_ALWAYS | D_NOHEADER, "}\n" );
}
<commit_msg>Selector logging now omits collecting fd info unless at D_FULLDEBUG; previously, only omitted printing fd info.<commit_after>/***************************************************************
 *
 * Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 *
 * 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 "condor_common.h"
#include "condor_debug.h"
#include "selector.h"

int Selector::_fd_select_size = -1;

fd_set *Selector::cached_read_fds = NULL;
fd_set *Selector::cached_write_fds = NULL;
fd_set *Selector::cached_except_fds = NULL;

fd_set *Selector::cached_save_read_fds = NULL;
fd_set *Selector::cached_save_write_fds = NULL;
fd_set *Selector::cached_save_except_fds = NULL;

Selector::Selector()
{
#if defined(WIN32)
		// On Windows, we can't treat fd_set as an open-ended bit array.
		// fd_set can take up to FD_SETSIZE sockets (not any socket whose
		// fd is <= FD_SETSIZE) and that's it. Currently, we set
		// FD_SETSIZE to 1024. I'm not sure what we can do if we ever
		// have more than 1024 sockets to select on.
	fd_set_size = 1;
#else
	int nfdbits = 8 * sizeof(fd_set);
	fd_set_size = ( fd_select_size() + (nfdbits - 1) ) / nfdbits;
#endif

	if ( cached_read_fds ) {
		read_fds = cached_read_fds;
		write_fds = cached_write_fds;
		except_fds = cached_except_fds;

		save_read_fds = cached_save_read_fds;
		save_write_fds = cached_save_write_fds;
		save_except_fds = cached_save_except_fds;

		cached_read_fds = NULL;
		cached_write_fds = NULL;
		cached_except_fds = NULL;
		cached_save_read_fds = NULL;
		cached_save_write_fds = NULL;
		cached_save_except_fds = NULL;
	} else {
		read_fds = (fd_set *)calloc( fd_set_size, sizeof(fd_set) );
		write_fds = (fd_set *)calloc( fd_set_size, sizeof(fd_set) );
		except_fds = (fd_set *)calloc( fd_set_size, sizeof(fd_set) );

		save_read_fds = (fd_set *)calloc( fd_set_size, sizeof(fd_set) );
		save_write_fds = (fd_set *)calloc( fd_set_size, sizeof(fd_set) );
		save_except_fds = (fd_set *)calloc( fd_set_size, sizeof(fd_set) );
	}

	reset();
}

Selector::~Selector()
{
	if ( cached_read_fds == NULL ) {
		cached_read_fds = read_fds;
		cached_write_fds = write_fds;
		cached_except_fds = except_fds;

		cached_save_read_fds = save_read_fds;
		cached_save_write_fds = save_write_fds;
		cached_save_except_fds = save_except_fds;
	} else {
		free( read_fds );
		free( write_fds );
		free( except_fds );

		free( save_read_fds );
		free( save_write_fds );
		free( save_except_fds );
	}
}

void
Selector::reset()
{
	_select_retval = -2;
	_select_errno = 0;
	state = VIRGIN;
	timeout_wanted = FALSE;
	max_fd = -1;
#if defined(WIN32)
	FD_ZERO( save_read_fds );
	FD_ZERO( save_write_fds );
	FD_ZERO( save_except_fds );
#else
	memset( save_read_fds, 0, fd_set_size * sizeof(fd_set) );
	memset( save_write_fds, 0, fd_set_size * sizeof(fd_set) );
	memset( save_except_fds, 0, fd_set_size * sizeof(fd_set) );
#endif

	dprintf(D_FULLDEBUG, "selector 0x%lX resetting\n", (unsigned long)this);
}

int
Selector::fd_select_size()
{
	if ( _fd_select_size < 0 ) {
#if defined(WIN32)
		// On Windows, fd_set is not a bit-array, but a structure that can
		// hold up to 1024 different file descriptors (not just file
		// descriptors whose value is less than 1024). We set max_fds to
		// 1024 (FD_SETSIZE) as a reasonable approximation.
		_fd_select_size = FD_SETSIZE;
#elif defined(Solaris)
		// Solaris's select() can't handle fds greater than FD_SETSIZE.
		int max_fds = getdtablesize();
		if ( max_fds < FD_SETSIZE ) {
			_fd_select_size = max_fds;
		} else {
			_fd_select_size = FD_SETSIZE;
		}
#else
		_fd_select_size = getdtablesize();
#endif
	}
	return _fd_select_size;
}

/*
 * Returns a newly-allocated null-terminated string describing the fd
 * (filename or pipe/socket info).  Currently only implemented on Linux,
 * implemented via /proc/.
 */
char*
describe_fd(int fd) {
#if defined(LINUX)
#define PROC_BUFSIZE 32
#define LINK_BUFSIZE 256
  char proc_buf[PROC_BUFSIZE];
  char link_buf[LINK_BUFSIZE];
  ssize_t end;

  memset(link_buf, '\0', LINK_BUFSIZE);

  snprintf(proc_buf, PROC_BUFSIZE, "/proc/self/fd/%d", fd);
  end = readlink(proc_buf, link_buf, LINK_BUFSIZE);

  if (end == -1) {
    goto cleanup;
  }

  link_buf[end] = '\0';
  return strdup(link_buf);
 cleanup:
#endif
  return strdup("");
}

void
Selector::add_fd( int fd, IO_FUNC interest )
{
	// update max_fd (the highest valid index in fd_set's array) and also

	// make sure we're not overflowing our fd_set
	// On Windows, we have to check the individual fd_set to see if it's
	// full.
	if( fd > max_fd ) {
		max_fd = fd;
	}
#if !defined(WIN32)
	if ( fd < 0 || fd >= fd_select_size() ) {
		EXCEPT( "Selector::add_fd(): fd %d outside valid range 0-%d",
				fd, _fd_select_size-1 );
	}
#endif

	char *fd_description = "";

	if(DebugFlags & D_FULLDEBUG) {
	  fd_description = describe_fd(fd);
	}

	dprintf(D_FULLDEBUG, "selector 0x%lX adding fd %d (%s)\n", (unsigned long)this, fd, fd_description);

	if (DebugFlags & D_FULLDEBUG) {
	  free(fd_description);
	}

	switch( interest ) {

	  case IO_READ:
#if defined(WIN32)
		if ( save_read_fds->fd_count >= fd_select_size() ) {
			EXCEPT( "Selector::add_fd(): read fd_set is full" );
		}
#endif
		FD_SET( fd, save_read_fds );
		break;

	  case IO_WRITE:
#if defined(WIN32)
		if ( save_write_fds->fd_count >= fd_select_size() ) {
			EXCEPT( "Selector::add_fd(): write fd_set is full" );
		}
#endif
		FD_SET( fd, save_write_fds );
		break;

	  case IO_EXCEPT:
#if defined(WIN32)
		if ( save_except_fds->fd_count >= fd_select_size() ) {
			EXCEPT( "Selector::add_fd(): except fd_set is full" );
		}
#endif
		FD_SET( fd, save_except_fds );
		break;

	}
}

void
Selector::delete_fd( int fd, IO_FUNC interest )
{
#if !defined(WIN32)
	if ( fd < 0 || fd >= fd_select_size() ) {
		EXCEPT( "Selector::delete_fd(): fd %d outside valid range 0-%d",
				fd, _fd_select_size-1 );
	}
#endif

	dprintf(D_FULLDEBUG, "selector 0x%lX deleting fd %d\n", (unsigned long)this, fd);

	switch( interest ) {

	  case IO_READ:
		FD_CLR( fd, save_read_fds );
		break;

	  case IO_WRITE:
		FD_CLR( fd, save_write_fds );
		break;

	  case IO_EXCEPT:
		FD_CLR( fd, save_except_fds );
		break;

	}
}

void
Selector::set_timeout( time_t sec, long usec )
{
	timeout_wanted = TRUE;

	timeout.tv_sec = sec;
	timeout.tv_usec = usec;
}

void
Selector::set_timeout( timeval tv )
{
	timeout_wanted = TRUE;

	timeout = tv;
}

void
Selector::unset_timeout()
{
	timeout_wanted = FALSE;
}

void
Selector::execute()
{
	int		nfds;
	struct timeval	*tp;

	memcpy( read_fds, save_read_fds, fd_set_size * sizeof(fd_set) );
	memcpy( write_fds, save_write_fds, fd_set_size * sizeof(fd_set) );
	memcpy( except_fds, save_except_fds, fd_set_size * sizeof(fd_set) );

	if( timeout_wanted ) {
		tp = &timeout;
	} else {
		tp = NULL;
	}

		// select() ignores its first argument on Windows. We still track
		// max_fd for the display() functions.
	nfds = select( max_fd + 1,
				  (SELECT_FDSET_PTR) read_fds,
				  (SELECT_FDSET_PTR) write_fds,
				  (SELECT_FDSET_PTR) except_fds,
				  tp );
	_select_retval = nfds;

	if( nfds < 0 ) {
		_select_errno = errno;
#if !defined(WIN32)
		if( errno == EINTR ) {
			state = SIGNALLED;
			return;
		}
#endif
		state = FAILED;
		return;
	}
	_select_errno = 0;

	if( nfds == 0 ) {
		state = TIMED_OUT;
	} else {
		state = FDS_READY;
	}
	return;
}

int
Selector::select_retval()
{
	return _select_retval;
}

int
Selector::select_errno()
{
	return _select_errno;
}

BOOLEAN
Selector::fd_ready( int fd, IO_FUNC interest )
{
	if( state != FDS_READY && state != TIMED_OUT ) {
		EXCEPT(
			"Selector::fd_ready() called, but selector not in FDS_READY state"
		);
	}

#if !defined(WIN32)
	// on UNIX, make sure the value of fd makes sense
	//
	if ( fd < 0 || fd >= fd_select_size() ) {
		return FALSE;
	}
#endif

	switch( interest ) {

	  case IO_READ:
		return FD_ISSET( fd, read_fds );
		break;

	  case IO_WRITE:
		return FD_ISSET( fd, write_fds );
		break;

	  case IO_EXCEPT:
		return FD_ISSET( fd, except_fds );
		break;

	}

		// Can never get here
	return FALSE;
}

BOOLEAN
Selector::timed_out()
{
	return state == TIMED_OUT;
}

BOOLEAN
Selector::signalled()
{
	return state == SIGNALLED;
}

BOOLEAN
Selector::failed()
{
	return state == FAILED;
}

BOOLEAN
Selector::has_ready()
{
	return state == FDS_READY;
}

void
Selector::display()
{

	switch( state ) {

	  case VIRGIN:
		dprintf( D_ALWAYS, "State = VIRGIN\n" );
		break;

	  case FDS_READY:
		dprintf( D_ALWAYS, "State = FDS_READY\n" );
		break;

	  case TIMED_OUT:
		dprintf( D_ALWAYS, "State = TIMED_OUT\n" );
		break;

	  case SIGNALLED:
		dprintf( D_ALWAYS, "State = SIGNALLED\n" );
		break;

	  case FAILED:
		dprintf( D_ALWAYS, "State = FAILED\n" );
		break;
	}

	dprintf( D_ALWAYS, "max_fd = %d\n", max_fd );

	dprintf( D_ALWAYS, "Selection FD's\n" );
	bool try_dup = ( (FAILED == state) &&  (EBADF == _select_errno) );
	display_fd_set( "\tRead", save_read_fds, max_fd, try_dup );
	display_fd_set( "\tWrite", save_write_fds, max_fd, try_dup );
	display_fd_set( "\tExcept", save_except_fds, max_fd, try_dup );

	if( state == FDS_READY ) {
		dprintf( D_ALWAYS, "Ready FD's\n" );
		display_fd_set( "\tRead", read_fds, max_fd );
		display_fd_set( "\tWrite", write_fds, max_fd );
		display_fd_set( "\tExcept", except_fds, max_fd );
	}
	if( timeout_wanted ) {
		dprintf( D_ALWAYS,
			"Timeout = %ld.%06ld seconds\n", (long) timeout.tv_sec,
			(long) timeout.tv_usec
		);
	} else {
		dprintf( D_ALWAYS, "Timeout not wanted\n" );
	}

}

void
display_fd_set( char *msg, fd_set *set, int max, bool try_dup )
{
	int		i;

	dprintf( D_ALWAYS, "%s {", msg );
	for( i=0; i<=max; i++ ) {
		if( FD_ISSET(i,set) ) {
			dprintf( D_ALWAYS | D_NOHEADER, "%d", i );

			if( try_dup ) {
#			  if defined( UNIX )
				int newfd = dup( i );
				if ( newfd >= 0 ) {
					close( newfd );
				}
				else if ( EBADF == errno ) {
					dprintf( D_ALWAYS | D_NOHEADER, "<EBADF> " );
				}
				else {
					dprintf( D_ALWAYS | D_NOHEADER, "<%d> ", errno );
				}
#			  endif
			}

			dprintf( D_ALWAYS | D_NOHEADER, " " );
		}
	}
	dprintf( D_ALWAYS | D_NOHEADER, "}\n" );
}
<|endoftext|>


<commit_before>#include <cassert>
#include <iostream>
#include <fstream>
#include <stdexcept>
#include <iterator>
#include <algorithm>
#include <syslog.h>
#include <sys/wait.h>
#include <autosprintf.h>
#include "eyekinfig.h"
#include "eyetil.h"
#include "soapeyefiService.h"

static binary_t session_nonce;

static bool detached_child() {
    pid_t p = fork();
    if(p<0) {
	syslog(LOG_ERR,"Failed to fork away for hook execution");
	_exit(-1);
    }
    if(!p) {
	setsid();
	for(int i=getdtablesize();i>=0;--i) close(i);
	int i=open("/dev/null",O_RDWR); assert(i==0);
	i = dup(i); assert(i==1);
	i = dup(i); assert(i==2);
	return true;
    }
    return false;
}

int eyefiService::StartSession(
	std::string macaddress,std::string cnonce,
	int transfermode,long transfermodetimestamp,
	struct rns__StartSessionResponse &r ) try {
#ifndef NDEBUG
    syslog(LOG_DEBUG,
	    "StartSession request from %s with cnonce=%s, transfermode=%d, transfermodetimestamp=%ld",
	    macaddress.c_str(), cnonce.c_str(), transfermode, transfermodetimestamp );
#endif
    eyekinfig_t eyekinfig(macaddress);
    r.credential = binary_t(macaddress+cnonce+eyekinfig.get_upload_key()).md5().hex();

    r.snonce = session_nonce.make_nonce().hex();
    r.transfermode=transfermode;
    r.transfermodetimestamp=transfermodetimestamp;
    r.upsyncallowed=false;

    std::string cmd = eyekinfig.get_on_start_session();
    if(!cmd.empty()) {
	if(detached_child()) {
	    putenv( gnu::autosprintf("EYEFI_MACADDRESS=%s",macaddress.c_str()) );
	    putenv( gnu::autosprintf("EYEFI_TRANSFERMODE=%d",transfermode) );
	    putenv( gnu::autosprintf("EYEFI_TRANSFERMODETIMESTAMP=%ld",transfermodetimestamp) );
	    char *argv[] = { (char*)"/bin/sh", (char*)"-c", (char*)cmd.c_str(), 0 };
	    execv("/bin/sh",argv);
	    syslog(LOG_ERR,"Failed to execute '%s'",cmd.c_str());
	    _exit(-1);
	}
    }
    return SOAP_OK;
}catch(std::runtime_error& e) {
    syslog(LOG_ERR,"error while processing StartSession: %s",e.what());
}

int eyefiService::GetPhotoStatus(
	std::string credential, std::string macaddress,
	std::string filename, long filesize, std::string filesignature,
	int flags,
	struct rns__GetPhotoStatusResponse &r ) {
#ifndef NDEBUG
    syslog(LOG_DEBUG,
	    "GetPhotoStatus request from %s with credential=%s, filename=%s, filesize=%ld, filesignature=%s, flags=%d; session nonce=%s",
	    macaddress.c_str(), credential.c_str(), filename.c_str(), filesize, filesignature.c_str(), flags,
	    session_nonce.hex().c_str() );
#endif

    std::string computed_credential = binary_t(macaddress+eyekinfig_t(macaddress).get_upload_key()+session_nonce.hex()).md5().hex();

#ifndef NDEBUG
    syslog(LOG_DEBUG, " computed credential=%s", computed_credential.c_str());
#endif

    if (credential != computed_credential) throw std::runtime_error("card authentication failed");

    r.fileid = 1; r.offset = 0;
    return SOAP_OK;
}

int eyefiService::MarkLastPhotoInRoll(
	std::string macaddress, int mergedelta,
	struct rns__MarkLastPhotoInRollResponse &r ) {
#ifndef NDEBUG
    syslog(LOG_DEBUG,
	    "MarkLastPhotoInRoll request from %s with mergedelta=%d",
	    macaddress.c_str(), mergedelta );
#endif
    std::string cmd = eyekinfig_t(macaddress).get_on_mark_last_photo_in_roll();
    if(!cmd.empty()) {
	if(detached_child()) {
	    putenv( gnu::autosprintf("EYEFI_MACADDRESS=%s",macaddress.c_str()) );
	    putenv( gnu::autosprintf("EYEFI_MERGEDELTA=%d",mergedelta) );
	    char *argv[] = { (char*)"/bin/sh", (char*)"-c", (char*)cmd.c_str(), 0 };
	    execv("/bin/sh",argv);
	    syslog(LOG_ERR,"Failed to execute '%s'",cmd.c_str());
	    _exit(-1);
	}
    }
    keep_alive = 0;
    return SOAP_OK;
}

int eyefiService::UploadPhoto(
	int fileid, std::string macaddress,
	std::string filename, long filesize, std::string filesignature,
	std::string encryption, int flags,
	struct rns__UploadPhotoResponse& r ) {
#ifndef NDEBUG
    syslog(LOG_DEBUG,
	    "UploadPhoto request from %s with fileid=%d, filename=%s, filesize=%ld,"
	    " filesignature=%s, encryption=%s, flags=%04X",
	    macaddress.c_str(), fileid, filename.c_str(), filesize,
	    filesignature.c_str(), encryption.c_str(), flags );
#endif
    eyekinfig_t eyekinfig(macaddress);

    umask(eyekinfig.get_umask());

    std::string td = eyekinfig.get_targetdir();
    tmpdir_t indir(td+"/.incoming.XXXXXX");

    std::string tf,lf;
    binary_t digest, idigest;

    for(soap_multipart::iterator i=mime.begin(),ie=mime.end();i!=ie;++i) {
#ifndef NDEBUG
	syslog(LOG_DEBUG,
		" MIME attachment with id=%s, type=%s, size=%ld",
		(*i).id, (*i).type, (long)(*i).size );
#endif

	if((*i).id && !strcmp((*i).id,"INTEGRITYDIGEST")) {
	    std::string idigestr((*i).ptr,(*i).size);
#ifndef NDEBUG
	    syslog(LOG_DEBUG, " INTEGRITYDIGEST=%s", idigestr.c_str());
#endif
	    idigest.from_hex(idigestr);
	}
	if( (*i).id && !strcmp((*i).id,"FILENAME") ) {
	    assert( (*i).type && !strcmp((*i).type,"application/x-tar") );
#ifdef III_SAVE_TARS
	    std::string tarfile = indir.get_file(filename);
	    {
		std::ofstream(tarfile.c_str(),std::ios::out|std::ios::binary).write((*i).ptr,(*i).size);
	    }
#endif

	    if(!tf.empty()) throw std::runtime_error("already seen tarball");
	    if(!digest.empty()) throw std::runtime_error("already have integrity digest");
	    digest = integrity_digest((*i).ptr,(*i).size,eyekinfig.get_upload_key());
#ifndef NDEBUG
	    syslog(LOG_DEBUG," computed integrity digest=%s", digest.hex().c_str());
#endif

	    tarchive_t a((*i).ptr,(*i).size);
	    while(a.read_next_header()) {
		std::string f = indir.get_file(a.entry_pathname());
		std::string::size_type fl = f.length();
		if(fl<4) continue;
		const char *s = f.c_str()+fl-4;
		static const char *suffixes[] = { ".JPG",".AVI",".MP4",".NEF",".RAW",".TIF" };
		if(std::find_if(suffixes,suffixes+sizeof(suffixes)/sizeof(*suffixes),
			    std::not1(std::bind1st(std::ptr_fun(strcasecmp),s)))
			!= suffixes+sizeof(suffixes)/sizeof(*suffixes))
		    tf = f;
		else if(!strcasecmp(s,".log"))
		    lf = f;
		else continue;
		int fd=open(f.c_str(),O_CREAT|O_WRONLY,0666);
		if(fd<0)
		    throw std::runtime_error(gnu::autosprintf("failed to create output file '%s'",f.c_str()));
		if(!a.read_data_into_fd(fd))
		    throw std::runtime_error(gnu::autosprintf("failed to untar file into '%s'",f.c_str()));
		close(fd);
	    }
	}
    }

    if(tf.empty()) throw std::runtime_error("haven't seen THE file");
    if(digest!=idigest) throw std::runtime_error("integrity digest verification failed");

    std::string::size_type ls = tf.rfind('/');
    // XXX: actually, lack of '/' signifies error here
    std::string tbn = (ls==std::string::npos)?tf:tf.substr(ls+1);
    ls = lf.rfind('/');
    std::string lbn = (ls==std::string::npos)?lf:lf.substr(ls+1);
    std::string ttf,tlf;
    bool success = false;
    for(int i=0;i<32767;++i) {
	const char *fmt = i ? "%1$s/(%3$05d)%2$s" : "%1$s/%2$s";
	ttf = (const char*)gnu::autosprintf(fmt,td.c_str(),tbn.c_str(),i);
	if(!lf.empty()) tlf = (const char*)gnu::autosprintf(fmt,td.c_str(),lbn.c_str(),i);
	if( (!link(tf.c_str(),ttf.c_str())) && (lf.empty()) || !link(lf.c_str(),tlf.c_str()) ) {
	    unlink(tf.c_str());
	    if(!lf.empty()) unlink(lf.c_str());
	    success=true;
	    break;
	}
    }
    std::string cmd = eyekinfig.get_on_upload_photo();
    if(success && !cmd.empty()) {
	if(detached_child()) {
	    putenv( gnu::autosprintf("EYEFI_UPLOADED_ORIG=%s",tbn.c_str()) );
	    putenv( gnu::autosprintf("EYEFI_MACADDRESS=%s",macaddress.c_str()) );
	    putenv( gnu::autosprintf("EYEFI_UPLOADED=%s",ttf.c_str()) );
	    if(!lf.empty()) putenv( gnu::autosprintf("EYEFI_LOG=%s",tlf.c_str()) );
	    char *argv[] = { (char*)"/bin/sh", (char*)"-c", (char*)cmd.c_str(), 0 };
	    execv("/bin/sh",argv);
	    syslog(LOG_ERR,"Failed to execute '%s'",cmd.c_str());
	    _exit(-1);
	}
    }

    r.success = true;
    return SOAP_OK;
}
<commit_msg>Added CRW and DNG extensions (Raw files by CHDK)<commit_after>#include <cassert>
#include <iostream>
#include <fstream>
#include <stdexcept>
#include <iterator>
#include <algorithm>
#include <syslog.h>
#include <sys/wait.h>
#include <autosprintf.h>
#include "eyekinfig.h"
#include "eyetil.h"
#include "soapeyefiService.h"

static binary_t session_nonce;

static bool detached_child() {
    pid_t p = fork();
    if(p<0) {
	syslog(LOG_ERR,"Failed to fork away for hook execution");
	_exit(-1);
    }
    if(!p) {
	setsid();
	for(int i=getdtablesize();i>=0;--i) close(i);
	int i=open("/dev/null",O_RDWR); assert(i==0);
	i = dup(i); assert(i==1);
	i = dup(i); assert(i==2);
	return true;
    }
    return false;
}

int eyefiService::StartSession(
	std::string macaddress,std::string cnonce,
	int transfermode,long transfermodetimestamp,
	struct rns__StartSessionResponse &r ) try {
#ifndef NDEBUG
    syslog(LOG_DEBUG,
	    "StartSession request from %s with cnonce=%s, transfermode=%d, transfermodetimestamp=%ld",
	    macaddress.c_str(), cnonce.c_str(), transfermode, transfermodetimestamp );
#endif
    eyekinfig_t eyekinfig(macaddress);
    r.credential = binary_t(macaddress+cnonce+eyekinfig.get_upload_key()).md5().hex();

    r.snonce = session_nonce.make_nonce().hex();
    r.transfermode=transfermode;
    r.transfermodetimestamp=transfermodetimestamp;
    r.upsyncallowed=false;

    std::string cmd = eyekinfig.get_on_start_session();
    if(!cmd.empty()) {
	if(detached_child()) {
	    putenv( gnu::autosprintf("EYEFI_MACADDRESS=%s",macaddress.c_str()) );
	    putenv( gnu::autosprintf("EYEFI_TRANSFERMODE=%d",transfermode) );
	    putenv( gnu::autosprintf("EYEFI_TRANSFERMODETIMESTAMP=%ld",transfermodetimestamp) );
	    char *argv[] = { (char*)"/bin/sh", (char*)"-c", (char*)cmd.c_str(), 0 };
	    execv("/bin/sh",argv);
	    syslog(LOG_ERR,"Failed to execute '%s'",cmd.c_str());
	    _exit(-1);
	}
    }
    return SOAP_OK;
}catch(std::runtime_error& e) {
    syslog(LOG_ERR,"error while processing StartSession: %s",e.what());
}

int eyefiService::GetPhotoStatus(
	std::string credential, std::string macaddress,
	std::string filename, long filesize, std::string filesignature,
	int flags,
	struct rns__GetPhotoStatusResponse &r ) {
#ifndef NDEBUG
    syslog(LOG_DEBUG,
	    "GetPhotoStatus request from %s with credential=%s, filename=%s, filesize=%ld, filesignature=%s, flags=%d; session nonce=%s",
	    macaddress.c_str(), credential.c_str(), filename.c_str(), filesize, filesignature.c_str(), flags,
	    session_nonce.hex().c_str() );
#endif

    std::string computed_credential = binary_t(macaddress+eyekinfig_t(macaddress).get_upload_key()+session_nonce.hex()).md5().hex();

#ifndef NDEBUG
    syslog(LOG_DEBUG, " computed credential=%s", computed_credential.c_str());
#endif

    if (credential != computed_credential) throw std::runtime_error("card authentication failed");

    r.fileid = 1; r.offset = 0;
    return SOAP_OK;
}

int eyefiService::MarkLastPhotoInRoll(
	std::string macaddress, int mergedelta,
	struct rns__MarkLastPhotoInRollResponse &r ) {
#ifndef NDEBUG
    syslog(LOG_DEBUG,
	    "MarkLastPhotoInRoll request from %s with mergedelta=%d",
	    macaddress.c_str(), mergedelta );
#endif
    std::string cmd = eyekinfig_t(macaddress).get_on_mark_last_photo_in_roll();
    if(!cmd.empty()) {
	if(detached_child()) {
	    putenv( gnu::autosprintf("EYEFI_MACADDRESS=%s",macaddress.c_str()) );
	    putenv( gnu::autosprintf("EYEFI_MERGEDELTA=%d",mergedelta) );
	    char *argv[] = { (char*)"/bin/sh", (char*)"-c", (char*)cmd.c_str(), 0 };
	    execv("/bin/sh",argv);
	    syslog(LOG_ERR,"Failed to execute '%s'",cmd.c_str());
	    _exit(-1);
	}
    }
    keep_alive = 0;
    return SOAP_OK;
}

int eyefiService::UploadPhoto(
	int fileid, std::string macaddress,
	std::string filename, long filesize, std::string filesignature,
	std::string encryption, int flags,
	struct rns__UploadPhotoResponse& r ) {
#ifndef NDEBUG
    syslog(LOG_DEBUG,
	    "UploadPhoto request from %s with fileid=%d, filename=%s, filesize=%ld,"
	    " filesignature=%s, encryption=%s, flags=%04X",
	    macaddress.c_str(), fileid, filename.c_str(), filesize,
	    filesignature.c_str(), encryption.c_str(), flags );
#endif
    eyekinfig_t eyekinfig(macaddress);

    umask(eyekinfig.get_umask());

    std::string td = eyekinfig.get_targetdir();
    tmpdir_t indir(td+"/.incoming.XXXXXX");

    std::string tf,lf;
    binary_t digest, idigest;

    for(soap_multipart::iterator i=mime.begin(),ie=mime.end();i!=ie;++i) {
#ifndef NDEBUG
	syslog(LOG_DEBUG,
		" MIME attachment with id=%s, type=%s, size=%ld",
		(*i).id, (*i).type, (long)(*i).size );
#endif

	if((*i).id && !strcmp((*i).id,"INTEGRITYDIGEST")) {
	    std::string idigestr((*i).ptr,(*i).size);
#ifndef NDEBUG
	    syslog(LOG_DEBUG, " INTEGRITYDIGEST=%s", idigestr.c_str());
#endif
	    idigest.from_hex(idigestr);
	}
	if( (*i).id && !strcmp((*i).id,"FILENAME") ) {
	    assert( (*i).type && !strcmp((*i).type,"application/x-tar") );
#ifdef III_SAVE_TARS
	    std::string tarfile = indir.get_file(filename);
	    {
		std::ofstream(tarfile.c_str(),std::ios::out|std::ios::binary).write((*i).ptr,(*i).size);
	    }
#endif

	    if(!tf.empty()) throw std::runtime_error("already seen tarball");
	    if(!digest.empty()) throw std::runtime_error("already have integrity digest");
	    digest = integrity_digest((*i).ptr,(*i).size,eyekinfig.get_upload_key());
#ifndef NDEBUG
	    syslog(LOG_DEBUG," computed integrity digest=%s", digest.hex().c_str());
#endif

	    tarchive_t a((*i).ptr,(*i).size);
	    while(a.read_next_header()) {
		std::string f = indir.get_file(a.entry_pathname());
		std::string::size_type fl = f.length();
		if(fl<4) continue;
		const char *s = f.c_str()+fl-4;
		static const char *suffixes[] = { ".JPG",".AVI",".MP4",".NEF",".RAW",".TIF",".DNG",".CRW" };
		if(std::find_if(suffixes,suffixes+sizeof(suffixes)/sizeof(*suffixes),
			    std::not1(std::bind1st(std::ptr_fun(strcasecmp),s)))
			!= suffixes+sizeof(suffixes)/sizeof(*suffixes))
		    tf = f;
		else if(!strcasecmp(s,".log"))
		    lf = f;
		else continue;
		int fd=open(f.c_str(),O_CREAT|O_WRONLY,0666);
		if(fd<0)
		    throw std::runtime_error(gnu::autosprintf("failed to create output file '%s'",f.c_str()));
		if(!a.read_data_into_fd(fd))
		    throw std::runtime_error(gnu::autosprintf("failed to untar file into '%s'",f.c_str()));
		close(fd);
	    }
	}
    }

    if(tf.empty()) throw std::runtime_error("haven't seen THE file");
    if(digest!=idigest) throw std::runtime_error("integrity digest verification failed");

    std::string::size_type ls = tf.rfind('/');
    // XXX: actually, lack of '/' signifies error here
    std::string tbn = (ls==std::string::npos)?tf:tf.substr(ls+1);
    ls = lf.rfind('/');
    std::string lbn = (ls==std::string::npos)?lf:lf.substr(ls+1);
    std::string ttf,tlf;
    bool success = false;
    for(int i=0;i<32767;++i) {
	const char *fmt = i ? "%1$s/(%3$05d)%2$s" : "%1$s/%2$s";
	ttf = (const char*)gnu::autosprintf(fmt,td.c_str(),tbn.c_str(),i);
	if(!lf.empty()) tlf = (const char*)gnu::autosprintf(fmt,td.c_str(),lbn.c_str(),i);
	if( (!link(tf.c_str(),ttf.c_str())) && (lf.empty()) || !link(lf.c_str(),tlf.c_str()) ) {
	    unlink(tf.c_str());
	    if(!lf.empty()) unlink(lf.c_str());
	    success=true;
	    break;
	}
    }
    std::string cmd = eyekinfig.get_on_upload_photo();
    if(success && !cmd.empty()) {
	if(detached_child()) {
	    putenv( gnu::autosprintf("EYEFI_UPLOADED_ORIG=%s",tbn.c_str()) );
	    putenv( gnu::autosprintf("EYEFI_MACADDRESS=%s",macaddress.c_str()) );
	    putenv( gnu::autosprintf("EYEFI_UPLOADED=%s",ttf.c_str()) );
	    if(!lf.empty()) putenv( gnu::autosprintf("EYEFI_LOG=%s",tlf.c_str()) );
	    char *argv[] = { (char*)"/bin/sh", (char*)"-c", (char*)cmd.c_str(), 0 };
	    execv("/bin/sh",argv);
	    syslog(LOG_ERR,"Failed to execute '%s'",cmd.c_str());
	    _exit(-1);
	}
    }

    r.success = true;
    return SOAP_OK;
}
<|endoftext|>


<commit_before>// Copyright 2014 Toggl Desktop developers.

#include "./https_client.h"

#include <string>
#include <sstream>

#include "Poco/Exception.h"
#include "Poco/InflatingStream.h"
#include "Poco/DeflatingStream.h"
#include "Poco/Logger.h"
#include "Poco/URI.h"
#include "Poco/NumberParser.h"
#include "Poco/Net/Context.h"
#include "Poco/Net/NameValueCollection.h"
#include "Poco/Net/HTTPMessage.h"
#include "Poco/Net/HTTPBasicCredentials.h"
#include "Poco/Net/InvalidCertificateHandler.h"
#include "Poco/Net/AcceptCertificateHandler.h"
#include "Poco/Net/SSLManager.h"
#include "Poco/Net/PrivateKeyPassphraseHandler.h"
#include "Poco/Net/SecureStreamSocket.h"
#include "Poco/TextEncoding.h"
#include "Poco/UTF8Encoding.h"

#include "./libjson.h"
#include "./const.h"

namespace kopsik {

std::string HTTPSClient::AppName = std::string("");
std::string HTTPSClient::AppVersion = std::string("");
std::string HTTPSClient::APIURL = std::string(kAPIURL);
bool HTTPSClient::UseProxy = false;
bool HTTPSClient::IgnoreCert = false;
kopsik::Proxy HTTPSClient::ProxySettings = Proxy();

error HTTPSClient::PostJSON(
    const std::string relative_url,
    const std::string json,
    const std::string basic_auth_username,
    const std::string basic_auth_password,
    std::string *response_body) {
    return requestJSON(Poco::Net::HTTPRequest::HTTP_POST,
                       relative_url,
                       json,
                       basic_auth_username,
                       basic_auth_password,
                       response_body);
}

error HTTPSClient::GetJSON(
    const std::string relative_url,
    const std::string basic_auth_username,
    const std::string basic_auth_password,
    std::string *response_body) {
    return requestJSON(Poco::Net::HTTPRequest::HTTP_GET,
                       relative_url,
                       "",
                       basic_auth_username,
                       basic_auth_password,
                       response_body);
}

error HTTPSClient::requestJSON(
    const std::string method,
    const std::string relative_url,
    const std::string json,
    const std::string basic_auth_username,
    const std::string basic_auth_password,
    std::string *response_body) {
    return request(
        method,
        relative_url,
        json,
        basic_auth_username,
        basic_auth_password,
        response_body);
}

error HTTPSClient::request(
    const std::string method,
    const std::string relative_url,
    const std::string payload,
    const std::string basic_auth_username,
    const std::string basic_auth_password,
    std::string *response_body) {

    poco_assert(!method.empty());
    poco_assert(!relative_url.empty());

    poco_check_ptr(response_body);

    *response_body = "";

    try {
        Poco::URI uri(APIURL);

        Poco::SharedPtr<Poco::Net::InvalidCertificateHandler>
        acceptCertHandler =
            new Poco::Net::AcceptCertificateHandler(true);

        Poco::Net::Context::VerificationMode verification_mode =
            Poco::Net::Context::VERIFY_RELAXED;
        if (IgnoreCert) {
            verification_mode = Poco::Net::Context::VERIFY_NONE;
        }
        Poco::Net::Context::Ptr context = new Poco::Net::Context(
            Poco::Net::Context::CLIENT_USE, "",
            verification_mode, 9, true, "ALL");

        Poco::Net::SSLManager::instance().initializeClient(
            0, acceptCertHandler, context);

        Poco::Net::HTTPSClientSession session(uri.getHost(), uri.getPort(),
                                              context);
        if (ProxySettings.IsConfigured()) {
            session.setProxy(ProxySettings.host, ProxySettings.port);
            if (ProxySettings.HasCredentials()) {
                session.setProxyCredentials(ProxySettings.username,
                                            ProxySettings.password);
            }
        }
        session.setKeepAlive(false);
        session.setTimeout(Poco::Timespan(10 * Poco::Timespan::SECONDS));

        Poco::Logger &logger = Poco::Logger::get("https_client");
        {
            std::stringstream ss;
            ss << "Sending request to " << relative_url << " ..";
            logger.debug(ss.str());
        }

        std::string encoded_url("");
        Poco::URI::encode(relative_url, "", encoded_url);
        Poco::Net::HTTPRequest req(method,
                                   encoded_url,
                                   Poco::Net::HTTPMessage::HTTP_1_1);
        req.setKeepAlive(false);
        req.setContentType("application/json");
        req.set("User-Agent", UserAgent());
        req.setChunkedTransferEncoding(true);

        Poco::Net::HTTPBasicCredentials cred(
            basic_auth_username, basic_auth_password);
        if (!basic_auth_username.empty() && !basic_auth_password.empty()) {
            cred.authenticate(req);
        }

        std::istringstream requestStream(payload);
        Poco::DeflatingInputStream gzipRequest(
            requestStream,
            Poco::DeflatingStreamBuf::STREAM_GZIP);
        Poco::DeflatingStreamBuf *pBuff = gzipRequest.rdbuf();

        Poco::Int64 size = pBuff->pubseekoff(0, std::ios::end, std::ios::in);
        pBuff->pubseekpos(0, std::ios::in);

        req.setContentLength(size);
        req.set("Content-Encoding", "gzip");
        req.set("Accept-Encoding", "gzip");

        session.sendRequest(req) << pBuff << std::flush;

        // Log out request contents
        std::stringstream request_string;
        req.write(request_string);
        logger.debug(request_string.str());

        logger.debug("Request sent. Receiving response..");

        // Receive response
        Poco::Net::HTTPResponse response;
        std::istream& is = session.receiveResponse(response);

        {
            std::stringstream ss;
            ss << "Response status code " << response.getStatus()
               << ", content length " << response.getContentLength()
               << ", content type " << response.getContentType();
            if (response.has("Content-Encoding")) {
                ss << ", content encoding " << response.get("Content-Encoding");
            } else {
                ss << ", unknown content encoding";
            }
            logger.debug(ss.str());
        }

        // Inflate, if gzip was sent
        if (response.has("Content-Encoding") &&
                "gzip" == response.get("Content-Encoding")) {
            Poco::InflatingInputStream inflater(
                is,
                Poco::InflatingStreamBuf::STREAM_GZIP);
            {
                std::stringstream ss;
                ss << inflater.rdbuf();
                *response_body = ss.str();
            }
        } else {
            std::istreambuf_iterator<char> eos;
            *response_body =
                std::string(std::istreambuf_iterator<char>(is), eos);
        }

        logger.trace(*response_body);

        if (response.getStatus() < 200 || response.getStatus() >= 300) {
            if (response_body->empty()) {
                std::stringstream description;
                description << "Request to server failed with status code: "
                            << response.getStatus();
                return description.str();
            }
            return "Request failed with error: " + *response_body;
        }

        // Debug encoding
        {
            std::stringstream ss;
            Poco::UTF8Encoding encoding;
            ss << "Response text encoding ASCII=" << encoding.isA("ASCII")
               << " Latin-1=" << encoding.isA("Latin-1")
               << " Latin-9=" << encoding.isA("Latin-9")
               << " Windows-1252=" << encoding.isA("Windows-1252")
               << " UTF-8=" << encoding.isA("UTF-8")
               << " UTF-16=" << encoding.isA("UTF-16");
            logger.debug(ss.str());
        }
    } catch(const Poco::Exception& exc) {
        return exc.displayText();
    } catch(const std::exception& ex) {
        return ex.what();
    } catch(const std::string& ex) {
        return ex;
    }
    return noError;
}

}   // namespace kopsik
<commit_msg>Dont use proxy settings unless told so, fixes #338<commit_after>// Copyright 2014 Toggl Desktop developers.

#include "./https_client.h"

#include <string>
#include <sstream>

#include "Poco/Exception.h"
#include "Poco/InflatingStream.h"
#include "Poco/DeflatingStream.h"
#include "Poco/Logger.h"
#include "Poco/URI.h"
#include "Poco/NumberParser.h"
#include "Poco/Net/Context.h"
#include "Poco/Net/NameValueCollection.h"
#include "Poco/Net/HTTPMessage.h"
#include "Poco/Net/HTTPBasicCredentials.h"
#include "Poco/Net/InvalidCertificateHandler.h"
#include "Poco/Net/AcceptCertificateHandler.h"
#include "Poco/Net/SSLManager.h"
#include "Poco/Net/PrivateKeyPassphraseHandler.h"
#include "Poco/Net/SecureStreamSocket.h"
#include "Poco/TextEncoding.h"
#include "Poco/UTF8Encoding.h"

#include "./libjson.h"
#include "./const.h"

namespace kopsik {

std::string HTTPSClient::AppName = std::string("");
std::string HTTPSClient::AppVersion = std::string("");
std::string HTTPSClient::APIURL = std::string(kAPIURL);
bool HTTPSClient::UseProxy = false;
bool HTTPSClient::IgnoreCert = false;
kopsik::Proxy HTTPSClient::ProxySettings = Proxy();

error HTTPSClient::PostJSON(
    const std::string relative_url,
    const std::string json,
    const std::string basic_auth_username,
    const std::string basic_auth_password,
    std::string *response_body) {
    return requestJSON(Poco::Net::HTTPRequest::HTTP_POST,
                       relative_url,
                       json,
                       basic_auth_username,
                       basic_auth_password,
                       response_body);
}

error HTTPSClient::GetJSON(
    const std::string relative_url,
    const std::string basic_auth_username,
    const std::string basic_auth_password,
    std::string *response_body) {
    return requestJSON(Poco::Net::HTTPRequest::HTTP_GET,
                       relative_url,
                       "",
                       basic_auth_username,
                       basic_auth_password,
                       response_body);
}

error HTTPSClient::requestJSON(
    const std::string method,
    const std::string relative_url,
    const std::string json,
    const std::string basic_auth_username,
    const std::string basic_auth_password,
    std::string *response_body) {
    return request(
        method,
        relative_url,
        json,
        basic_auth_username,
        basic_auth_password,
        response_body);
}

error HTTPSClient::request(
    const std::string method,
    const std::string relative_url,
    const std::string payload,
    const std::string basic_auth_username,
    const std::string basic_auth_password,
    std::string *response_body) {

    poco_assert(!method.empty());
    poco_assert(!relative_url.empty());

    poco_check_ptr(response_body);

    *response_body = "";

    try {
        Poco::URI uri(APIURL);

        Poco::SharedPtr<Poco::Net::InvalidCertificateHandler>
        acceptCertHandler =
            new Poco::Net::AcceptCertificateHandler(true);

        Poco::Net::Context::VerificationMode verification_mode =
            Poco::Net::Context::VERIFY_RELAXED;
        if (IgnoreCert) {
            verification_mode = Poco::Net::Context::VERIFY_NONE;
        }
        Poco::Net::Context::Ptr context = new Poco::Net::Context(
            Poco::Net::Context::CLIENT_USE, "",
            verification_mode, 9, true, "ALL");

        Poco::Net::SSLManager::instance().initializeClient(
            0, acceptCertHandler, context);

        Poco::Net::HTTPSClientSession session(uri.getHost(), uri.getPort(),
                                              context);
        if (UseProxy && ProxySettings.IsConfigured()) {
            session.setProxy(ProxySettings.host, ProxySettings.port);
            if (ProxySettings.HasCredentials()) {
                session.setProxyCredentials(ProxySettings.username,
                                            ProxySettings.password);
            }
        }
        session.setKeepAlive(false);
        session.setTimeout(Poco::Timespan(10 * Poco::Timespan::SECONDS));

        Poco::Logger &logger = Poco::Logger::get("https_client");
        {
            std::stringstream ss;
            ss << "Sending request to " << relative_url << " ..";
            logger.debug(ss.str());
        }

        std::string encoded_url("");
        Poco::URI::encode(relative_url, "", encoded_url);
        Poco::Net::HTTPRequest req(method,
                                   encoded_url,
                                   Poco::Net::HTTPMessage::HTTP_1_1);
        req.setKeepAlive(false);
        req.setContentType("application/json");
        req.set("User-Agent", UserAgent());
        req.setChunkedTransferEncoding(true);

        Poco::Net::HTTPBasicCredentials cred(
            basic_auth_username, basic_auth_password);
        if (!basic_auth_username.empty() && !basic_auth_password.empty()) {
            cred.authenticate(req);
        }

        std::istringstream requestStream(payload);
        Poco::DeflatingInputStream gzipRequest(
            requestStream,
            Poco::DeflatingStreamBuf::STREAM_GZIP);
        Poco::DeflatingStreamBuf *pBuff = gzipRequest.rdbuf();

        Poco::Int64 size = pBuff->pubseekoff(0, std::ios::end, std::ios::in);
        pBuff->pubseekpos(0, std::ios::in);

        req.setContentLength(size);
        req.set("Content-Encoding", "gzip");
        req.set("Accept-Encoding", "gzip");

        session.sendRequest(req) << pBuff << std::flush;

        // Log out request contents
        std::stringstream request_string;
        req.write(request_string);
        logger.debug(request_string.str());

        logger.debug("Request sent. Receiving response..");

        // Receive response
        Poco::Net::HTTPResponse response;
        std::istream& is = session.receiveResponse(response);

        {
            std::stringstream ss;
            ss << "Response status code " << response.getStatus()
               << ", content length " << response.getContentLength()
               << ", content type " << response.getContentType();
            if (response.has("Content-Encoding")) {
                ss << ", content encoding " << response.get("Content-Encoding");
            } else {
                ss << ", unknown content encoding";
            }
            logger.debug(ss.str());
        }

        // Inflate, if gzip was sent
        if (response.has("Content-Encoding") &&
                "gzip" == response.get("Content-Encoding")) {
            Poco::InflatingInputStream inflater(
                is,
                Poco::InflatingStreamBuf::STREAM_GZIP);
            {
                std::stringstream ss;
                ss << inflater.rdbuf();
                *response_body = ss.str();
            }
        } else {
            std::istreambuf_iterator<char> eos;
            *response_body =
                std::string(std::istreambuf_iterator<char>(is), eos);
        }

        logger.trace(*response_body);

        if (response.getStatus() < 200 || response.getStatus() >= 300) {
            if (response_body->empty()) {
                std::stringstream description;
                description << "Request to server failed with status code: "
                            << response.getStatus();
                return description.str();
            }
            return "Request failed with error: " + *response_body;
        }

        // Debug encoding
        {
            std::stringstream ss;
            Poco::UTF8Encoding encoding;
            ss << "Response text encoding ASCII=" << encoding.isA("ASCII")
               << " Latin-1=" << encoding.isA("Latin-1")
               << " Latin-9=" << encoding.isA("Latin-9")
               << " Windows-1252=" << encoding.isA("Windows-1252")
               << " UTF-8=" << encoding.isA("UTF-8")
               << " UTF-16=" << encoding.isA("UTF-16");
            logger.debug(ss.str());
        }
    } catch(const Poco::Exception& exc) {
        return exc.displayText();
    } catch(const std::exception& ex) {
        return ex.what();
    } catch(const std::string& ex) {
        return ex;
    }
    return noError;
}

}   // namespace kopsik
<|endoftext|>


<commit_before>#ifndef LYZA_FUNCTIONAL_VARIANT__
# define LYZA_FUNCTIONAL_VARIANT__

# include <exception>
# include <tuple>
# include <typeinfo>
# include <type_traits>
# include <utility>

# include "mp/union.hpp"
# include "mp/tlist.hpp"

namespace lyza { namespace functional {

typedef void dummy;

template <typename ...Targs>
class variant {
    template <size_t Tn, typename Tdummy>
    struct deduce_copy {
        static inline void copy(size_t n, variant& a, variant const& b)
        {
            if (Tn == n) {
				typedef typename n_to_type<Tn>::type type;

				type& afield = a.unsafe_get<type>();
				new (&afield) type();
				afield = b.unsafe_get<type>();
            } else {
                deduce_copy<Tn - 1, Tdummy>::copy(n, a, b);
            }
        }
    };

    template <typename Tdummy>
    struct deduce_copy<0, Tdummy> {
        static inline void copy(size_t n, variant& a, variant const& b)
        {
            if (n == 0) {
				typedef typename n_to_type<0>::type type;

				type& afield = a.unsafe_get<type>();
				new (&afield) type();
				afield = b.unsafe_get<type>();
            }
        }
    };

    public:
        void copy(variant& v)
        {
            deduce_copy<mp::tlist_length<tlist__>::value - 1, dummy>::copy(v.tag_, *this, v);
        }

        void copy(variant const& v)
        {
            deduce_copy<mp::tlist_length<tlist__>::value - 1, dummy>::copy(v.tag_, *this, v);
        }

        template <typename T>
        variant(T&& t)
            : tag_(tag<T>::value), v_()
        {
            unsafe_get<T>() = std::move(t);
        }

        template <typename T>
        variant(T const& t)
            : tag_(tag<T>::value), v_()
        {
            unsafe_get<T>() = t;
        }

        template <typename T>
        variant(T& t)
            : tag_(tag<T>::value), v_()
        {
            unsafe_get<T>() = t;
        }

        variant(variant const& v)
            : tag_(v.tag_), v_()
        {
            copy(v);
        }

        variant(variant& v)
            : tag_(v.tag_), v_()
        {
            copy(v);
        }

        template <typename T>
        variant& operator=(T& t)
        {
			T& field = mp_union<T>::get(v_);
			new (&field) T();

            field = t;
            tag_ = tag<T>::value;
            return *this;
        }

        template <typename T>
        variant& operator=(T&& t)
        {
			T& field = mp_union<T>::get(v_);
			new (&field) T();

            field = std::move(t);
            tag_ = tag<T>::value;
            return *this;
        }

        template <typename T>
        variant& operator=(T const& t)
        {
			T& field = mp_union<T>::get(v_);
			new (&field) T();

            field = t;
            tag_ = tag<T>::value;
            return *this;
        }

        variant& operator=(variant& v)
        {
			copy(v);
            return *this;
        }

        variant& operator=(variant const& v)
        {
			copy(v);
			return *this;
        }

        template <typename T>
        bool isa() const
        {
            return tag<T>::value == tag_;
        }

        template <typename Tret>
        static Tret otherwise(variant& v)
        {
            throw std::runtime_error("type matching failed");
        }

        template <typename T, typename Tret>
        Tret match(
                std::function<Tret (T&)> const& f,
                std::function<Tret (variant&)> const& fo = &variant::otherwise<Tret>
                )
        {
            if (isa<T>())
                return f(unsafe_get<T>());
            return fo(*this);
        }

        template <typename T>
        void match(
                std::function<void (T&)> const& f,
                std::function<void (variant&)> const& fo = &variant::otherwise<void>
                )
        {
            if (isa<T>())
                f(unsafe_get<T>());
            else
                fo(*this);
        }

        size_t get_current_tag() const
        {
            return tag_;
        }

    public:
        typedef
            typename mp::make_tlist<Targs...>::type
            tlist__;

        typedef
            typename mp::make_union<Targs...>::type
            union__;

        template <typename T>
        struct type_matcher {
            template <typename U>
            struct match {
                enum { value = std::is_same<T, U>::value };
            };
        };

        template <size_t Tn>
        struct n_to_type {
            typedef
                typename lyza::mp::union_nth<union__, Tn>::type
                type;
        };

    private:
        template <size_t Tn, typename Tvisitor>
        struct deduce_visit {
            static inline
				typename Tvisitor::ret_type
				visit(size_t n, Tvisitor const& vt, variant& v)
            {
                if (Tn == n) {
                    return vt(v.unsafe_get<typename n_to_type<Tn>::type>());
                }
				return deduce_visit<Tn - 1, Tvisitor>::visit(n, vt, v);
            }

            static inline
				typename Tvisitor::ret_type
				visit(size_t n, Tvisitor const& vt, variant const& v)
            {
                if (Tn == n) {
                    return vt(v.unsafe_get<typename n_to_type<Tn>::type>());
                }
				return deduce_visit<Tn - 1, Tvisitor>::visit(n, vt, v);
            }
        };

        template <typename Tvisitor>
        struct deduce_visit<0, Tvisitor> {
            static inline
				typename Tvisitor::ret_type
				visit(size_t n, Tvisitor const& vt, variant& v)
            {
                if (n == 0) {
                    return vt(v.unsafe_get<typename n_to_type<0>::type>());
                }
				throw std::runtime_error("invalid visit");
            }

            static inline
				typename Tvisitor::ret_type
				visit(size_t n, Tvisitor const& vt, variant const& v)
            {
                if (n == 0) {
                    return vt(v.unsafe_get<typename n_to_type<0>::type>());
                }
				throw std::runtime_error("invalid visit");
            }
        };

    public:
        template <typename Tvisitor>
		typename Tvisitor::ret_type
			apply_visitor(Tvisitor const& vt)
        {
            return deduce_visit<mp::tlist_length<tlist__>::value - 1, Tvisitor>::visit(tag_, vt, *this);
        }

        template <typename Tvisitor>
		typename Tvisitor::ret_type
			apply_visitor(Tvisitor const& vt) const
        {
            return deduce_visit<mp::tlist_length<tlist__>::value - 1, Tvisitor>::visit(tag_, vt, *this);
        }

    public:
        template <typename T>
        struct tag {
            enum { value = lyza::mp::find_count<tlist__, type_matcher<T> >::value };
        };

    private:
        template <typename T>
        struct mp_union
        : public
          lyza::mp::union_nth<
                union__,
                lyza::mp::find_count<tlist__, type_matcher<T> >::value
          > {};

    public:
        template <typename T>
        const typename mp_union<T>::type&
        unsafe_get() const
        {
            return mp_union<T>::get(v_);
        }

        template <typename T>
        typename mp_union<T>::type&
        unsafe_get()
        {
            return mp_union<T>::get(v_);
        }

        template <typename T>
        const typename mp_union<T>::type&
        get() const
        {
            if (isa<T>())
                return mp_union<T>::get(v_);
            throw std::bad_cast();
        }

        template <typename T>
        typename mp_union<T>::type&
        get()
        {
            if (isa<T>())
                return mp_union<T>::get(v_);
            throw std::bad_cast();
        }

    protected:
        size_t tag_;
        typename union__::type v_;
};

}}

#endif
<commit_msg>Fix copy in lyza variant<commit_after>#ifndef LYZA_FUNCTIONAL_VARIANT__
# define LYZA_FUNCTIONAL_VARIANT__

# include <exception>
# include <tuple>
# include <typeinfo>
# include <type_traits>
# include <utility>

# include "mp/union.hpp"
# include "mp/tlist.hpp"

namespace lyza { namespace functional {

typedef void dummy;

template <typename ...Targs>
class variant {
    template <size_t Tn, typename Tdummy>
    struct deduce_copy {
        static inline void copy(size_t n, variant& a, variant const& b)
        {
            if (Tn == n) {
				typedef typename n_to_type<Tn>::type type;

				type& afield = a.unsafe_get<type>();
				new (&afield) type();
				afield = b.unsafe_get<type>();
				a.tag_ = b.tag_;
            } else {
                deduce_copy<Tn - 1, Tdummy>::copy(n, a, b);
            }
        }
    };

    template <typename Tdummy>
    struct deduce_copy<0, Tdummy> {
        static inline void copy(size_t n, variant& a, variant const& b)
        {
            if (n == 0) {
				typedef typename n_to_type<0>::type type;

				type& afield = a.unsafe_get<type>();
				new (&afield) type();
				afield = b.unsafe_get<type>();
				a.tag_ = b.tag_;
            }
        }
    };

    public:
        void copy(variant& v)
        {
            deduce_copy<mp::tlist_length<tlist__>::value - 1, dummy>::copy(v.tag_, *this, v);
        }

        void copy(variant const& v)
        {
            deduce_copy<mp::tlist_length<tlist__>::value - 1, dummy>::copy(v.tag_, *this, v);
        }

        template <typename T>
        variant(T&& t)
            : tag_(tag<T>::value), v_()
        {
            unsafe_get<T>() = std::move(t);
        }

        template <typename T>
        variant(T const& t)
            : tag_(tag<T>::value), v_()
        {
            unsafe_get<T>() = t;
        }

        template <typename T>
        variant(T& t)
            : tag_(tag<T>::value), v_()
        {
            unsafe_get<T>() = t;
        }

        variant(variant const& v)
            : tag_(v.tag_), v_()
        {
            copy(v);
        }

        variant(variant& v)
            : tag_(v.tag_), v_()
        {
            copy(v);
        }

        template <typename T>
        variant& operator=(T& t)
        {
			T& field = mp_union<T>::get(v_);
			new (&field) T();

            field = t;
            tag_ = tag<T>::value;
            return *this;
        }

        template <typename T>
        variant& operator=(T&& t)
        {
			T& field = mp_union<T>::get(v_);
			new (&field) T();

            field = std::move(t);
            tag_ = tag<T>::value;
            return *this;
        }

        template <typename T>
        variant& operator=(T const& t)
        {
			T& field = mp_union<T>::get(v_);
			new (&field) T();

            field = t;
            tag_ = tag<T>::value;
            return *this;
        }

        variant& operator=(variant& v)
        {
			copy(v);
            return *this;
        }

        variant& operator=(variant const& v)
        {
			copy(v);
			return *this;
        }

        template <typename T>
        bool isa() const
        {
            return tag<T>::value == tag_;
        }

        template <typename Tret>
        static Tret otherwise(variant& v)
        {
            throw std::runtime_error("type matching failed");
        }

        template <typename T, typename Tret>
        Tret match(
                std::function<Tret (T&)> const& f,
                std::function<Tret (variant&)> const& fo = &variant::otherwise<Tret>
                )
        {
            if (isa<T>())
                return f(unsafe_get<T>());
            return fo(*this);
        }

        template <typename T>
        void match(
                std::function<void (T&)> const& f,
                std::function<void (variant&)> const& fo = &variant::otherwise<void>
                )
        {
            if (isa<T>())
                f(unsafe_get<T>());
            else
                fo(*this);
        }

        size_t get_current_tag() const
        {
            return tag_;
        }

    public:
        typedef
            typename mp::make_tlist<Targs...>::type
            tlist__;

        typedef
            typename mp::make_union<Targs...>::type
            union__;

        template <typename T>
        struct type_matcher {
            template <typename U>
            struct match {
                enum { value = std::is_same<T, U>::value };
            };
        };

        template <size_t Tn>
        struct n_to_type {
            typedef
                typename lyza::mp::union_nth<union__, Tn>::type
                type;
        };

    private:
        template <size_t Tn, typename Tvisitor>
        struct deduce_visit {
            static inline
				typename Tvisitor::ret_type
				visit(size_t n, Tvisitor const& vt, variant& v)
            {
                if (Tn == n) {
                    return vt(v.unsafe_get<typename n_to_type<Tn>::type>());
                }
				return deduce_visit<Tn - 1, Tvisitor>::visit(n, vt, v);
            }

            static inline
				typename Tvisitor::ret_type
				visit(size_t n, Tvisitor const& vt, variant const& v)
            {
                if (Tn == n) {
                    return vt(v.unsafe_get<typename n_to_type<Tn>::type>());
                }
				return deduce_visit<Tn - 1, Tvisitor>::visit(n, vt, v);
            }
        };

        template <typename Tvisitor>
        struct deduce_visit<0, Tvisitor> {
            static inline
				typename Tvisitor::ret_type
				visit(size_t n, Tvisitor const& vt, variant& v)
            {
                if (n == 0) {
                    return vt(v.unsafe_get<typename n_to_type<0>::type>());
                }
				throw std::runtime_error("invalid visit");
            }

            static inline
				typename Tvisitor::ret_type
				visit(size_t n, Tvisitor const& vt, variant const& v)
            {
                if (n == 0) {
                    return vt(v.unsafe_get<typename n_to_type<0>::type>());
                }
				throw std::runtime_error("invalid visit");
            }
        };

    public:
        template <typename Tvisitor>
		typename Tvisitor::ret_type
			apply_visitor(Tvisitor const& vt)
        {
            return deduce_visit<mp::tlist_length<tlist__>::value - 1, Tvisitor>::visit(tag_, vt, *this);
        }

        template <typename Tvisitor>
		typename Tvisitor::ret_type
			apply_visitor(Tvisitor const& vt) const
        {
            return deduce_visit<mp::tlist_length<tlist__>::value - 1, Tvisitor>::visit(tag_, vt, *this);
        }

    public:
        template <typename T>
        struct tag {
            enum { value = lyza::mp::find_count<tlist__, type_matcher<T> >::value };
        };

    private:
        template <typename T>
        struct mp_union
        : public
          lyza::mp::union_nth<
                union__,
                lyza::mp::find_count<tlist__, type_matcher<T> >::value
          > {};

    public:
        template <typename T>
        const typename mp_union<T>::type&
        unsafe_get() const
        {
            return mp_union<T>::get(v_);
        }

        template <typename T>
        typename mp_union<T>::type&
        unsafe_get()
        {
            return mp_union<T>::get(v_);
        }

        template <typename T>
        const typename mp_union<T>::type&
        get() const
        {
            if (isa<T>())
                return mp_union<T>::get(v_);
            throw std::bad_cast();
        }

        template <typename T>
        typename mp_union<T>::type&
        get()
        {
            if (isa<T>())
                return mp_union<T>::get(v_);
            throw std::bad_cast();
        }

    protected:
        size_t tag_;
        typename union__::type v_;
};

}}

#endif
<|endoftext|>


<commit_before>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of libmeegotouch.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include "mviewconstants.h"
#include "mstatusbarview.h"
#include <mstatusbar.h>
#include <mapplication.h>
#include <mscenemanager.h>
#include <mscene.h>
#include <mdebug.h>

#include <QFile>
#include <QDataStream>
#include <QDir>
#include <QGraphicsSceneMouseEvent>

#include <mstatusbarviewdbuswrapper.h>

#ifdef HAVE_XDAMAGE
#include <X11/extensions/Xdamage.h>
#endif //HAVE_XDAMAGE

namespace{
    const qreal SharedPixmapHeight = 30;
}

MStatusBarView::MStatusBarView(MStatusBar *controller) :
    MSceneWindowView(controller),
    controller(controller),
    pressDown(false)
#ifdef Q_WS_X11
    , isOnDisplay(false)
    , isInSwitcher(false)
#endif
{
#ifdef Q_WS_X11
    pixmapDamage = 0;

    MApplication *mApplication = static_cast<MApplication *>(QCoreApplication::instance());
    connect(mApplication, SIGNAL(damageEvent(Qt::HANDLE&, short&, short&, unsigned short&, unsigned short&)),
            SLOT(handlePixmapDamageEvent(Qt::HANDLE&, short&, short&, unsigned short&, unsigned short&)));
    connect(controller, SIGNAL(displayExited()),
            this, SLOT(handleDisplayExited()));
    connect(controller, SIGNAL(displayEntered()),
            this, SLOT(handleDisplayEntered()));
    controller->setProperty("sharedPixmapHeight", SharedPixmapHeight);

    if (controller->scene() && !controller->scene()->views().isEmpty()) {
        MWindow* win = qobject_cast<MWindow*>(controller->scene()->views().at(0));
        if (win) {
            isInSwitcher = win->isInSwitcher();
            connect(win, SIGNAL(switcherEntered()), this, SLOT(handleSwitcherEntered()));
            connect(win, SIGNAL(switcherExited()), this, SLOT(handleSwitcherExited()));
        }
    }

    dbusWrapper = new MStatusBarViewDBusWrapper(this);

    connect(dbusWrapper,
            SIGNAL(sharedPixmapHandleFromProviderReceived(quint32, bool)),
            SLOT(handleSharedPixmapHandleReceived(quint32, bool)));

    if (dbusWrapper->isPixmapProviderServiceRegistered())
        isPixmapProviderOnline = true;
    else
        isPixmapProviderOnline = false;

    connect(dbusWrapper, SIGNAL(pixmapProviderServiceRegistered()),
            this, SLOT(handlePixmapProviderOnline()));
    connect(dbusWrapper, SIGNAL(pixmapProviderServiceUnregistered()),
            this, SLOT(handlePixmapProviderOffline()));

    if (isPixmapProviderOnline)
        dbusWrapper->querySharedPixmapHandleFromProvider();

#endif // Q_WS_X11
}

MStatusBarView::~MStatusBarView()
{
#ifdef Q_WS_X11
    destroyXDamageForSharedPixmap();
#endif // Q_WS_X11
}

void MStatusBarView::drawContents(QPainter *painter, const QStyleOptionGraphicsItem *option) const
{
    Q_UNUSED(option);

#ifdef Q_WS_X11
    if (sharedPixmap.isNull() && !dbusWrapper->havePendingSharedPixmapHandleReply() && isPixmapProviderOnline) {
        dbusWrapper->querySharedPixmapHandleFromProvider();
    }

    if (sharedPixmap.isNull() || (controller->sceneManager() == 0))
        return;

    QRectF sourceRect;

    sourceRect.setX(0);
    sourceRect.setY(controller->sceneManager()->orientation() == M::Landscape ? 0 : SharedPixmapHeight);
    sourceRect.setWidth(size().width());
    sourceRect.setHeight(SharedPixmapHeight);

    // provider can die under mysterious circumstances so sharedPixmap can be invalid
    try {
        painter->drawPixmap(QPointF(0.0, 0.0), sharedPixmap, sourceRect);
    } catch(...) {
        qWarning() << "MStatusBarView::drawContents: Cannot draw sharedPixmap.";
        const_cast<MStatusBarView*>(this)->sharedPixmap = QPixmap();
    }

    if (pressDown && style()->enableStatusIndicatorMenu()) {
        painter->save();
        painter->setOpacity(style()->pressDimFactor());
        painter->fillRect(QRectF(QPointF(0.0, 0.0), sourceRect.size()), Qt::black);
        painter->restore();
    }

#else
    Q_UNUSED(painter);
#endif // Q_WS_X11
}

void MStatusBarView::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    firstPos = event->pos();
    playHapticsFeedback();

    if (pressDown)
        return;

    pressDown = true;
    update();
}

void MStatusBarView::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    if (!style()->useSwipeGesture()) {
        return;
    }

    if(firstPos.y()+ style()->swipeThreshold() < event->pos().y()) {
        showStatusIndicatorMenu();
    }
}

void MStatusBarView::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    if (!pressDown)
        return;

    pressDown = false;
    update();

    if (style()->useSwipeGesture()) {
        return;
    }

    QPointF touch = event->scenePos();
    // shape includes the reactive margins
    QRectF rect = controller->mapToScene(controller->shape()).boundingRect();
    rect.adjust(-M_RELEASE_MISS_DELTA, -M_RELEASE_MISS_DELTA,
                M_RELEASE_MISS_DELTA, M_RELEASE_MISS_DELTA);
    if(rect.contains(touch)) {
        showStatusIndicatorMenu();
    }
}

#ifdef Q_WS_X11
bool MStatusBarView::shouldStayUpToDate()
{
    return isOnDisplay && isPixmapProviderOnline && !isInSwitcher;
}

void MStatusBarView::updateXDamageForSharedPixmap()
{
    if (shouldStayUpToDate()) {
        if (!pixmapDamage) {
            if (!sharedPixmap.isNull()) {
                setupXDamageForSharedPixmap();
            } else {
                mWarning("MStatusBarView")
                    << "Couldn't track XDamage events since there's no shared pixmap.";
            }
        }
    } else {
        destroyXDamageForSharedPixmap();
    }
}

void MStatusBarView::setupXDamageForSharedPixmap()
{
    Q_ASSERT(!sharedPixmap.isNull());
#ifdef HAVE_XDAMAGE
    pixmapDamage = XDamageCreate(QX11Info::display(), sharedPixmap.handle(), XDamageReportNonEmpty);
#endif //HAVE_XDAMAGE
}

void MStatusBarView::destroyXDamageForSharedPixmap()
{
#ifdef HAVE_XDAMAGE
    if (pixmapDamage) {
        XDamageDestroy(QX11Info::display(), pixmapDamage);
        pixmapDamage = 0;
    }
#endif //HAVE_XDAMAGE
}

void MStatusBarView::handlePixmapDamageEvent(Qt::HANDLE &damage, short &x, short &y,
                                             unsigned short &width, unsigned short &height)
{
    if (damage == pixmapDamage) {
        Q_ASSERT(shouldStayUpToDate());
        controller->update(x, y, width, height);
    }
}

void MStatusBarView::handleDisplayEntered()
{
    isOnDisplay = true;
    updateXDamageForSharedPixmap();
}

void MStatusBarView::handleDisplayExited()
{
    isOnDisplay = false;
    updateXDamageForSharedPixmap();
}

void MStatusBarView::handleSharedPixmapHandleReceived(quint32 handle, bool ok)
{
    Q_ASSERT(isPixmapProviderOnline);

    if (!ok) {
        return;
    }

    sharedPixmap = QPixmap::fromX11Pixmap(handle, QPixmap::ExplicitlyShared);

    destroyXDamageForSharedPixmap();
    updateXDamageForSharedPixmap();

    if (shouldStayUpToDate()) {
        update();
    }
}

void MStatusBarView::handlePixmapProviderOnline()
{
    isPixmapProviderOnline = true;

    Q_ASSERT(sharedPixmap.isNull());
    Q_ASSERT(!pixmapDamage);

    dbusWrapper->querySharedPixmapHandleFromProvider();
}

void MStatusBarView::handlePixmapProviderOffline()
{
    isPixmapProviderOnline = false;

    destroyXDamageForSharedPixmap();
    sharedPixmap = QPixmap();
}

void MStatusBarView::handleSwitcherEntered()
{
    isInSwitcher = true;
    updateXDamageForSharedPixmap();
}

void MStatusBarView::handleSwitcherExited()
{
    isInSwitcher = false;
    updateXDamageForSharedPixmap();
}
#endif // Q_WS_X11

void MStatusBarView::showStatusIndicatorMenu()
{
    if (style()->enableStatusIndicatorMenu()) {
        dbusWrapper->openStatusIndicatorMenu();
    }
}

void MStatusBarView::playHapticsFeedback()
{
    if (style()->enableStatusIndicatorMenu()) {
        style()->pressFeedback().play();
    }
}

M_REGISTER_VIEW_NEW(MStatusBarView, MStatusBar)
<commit_msg>Changes: Dimming of status bar when pressed depends only on pressDimFactor css parameter          Patch by pankaj saharan<commit_after>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of libmeegotouch.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include "mviewconstants.h"
#include "mstatusbarview.h"
#include <mstatusbar.h>
#include <mapplication.h>
#include <mscenemanager.h>
#include <mscene.h>
#include <mdebug.h>

#include <QFile>
#include <QDataStream>
#include <QDir>
#include <QGraphicsSceneMouseEvent>

#include <mstatusbarviewdbuswrapper.h>

#ifdef HAVE_XDAMAGE
#include <X11/extensions/Xdamage.h>
#endif //HAVE_XDAMAGE

namespace{
    const qreal SharedPixmapHeight = 30;
}

MStatusBarView::MStatusBarView(MStatusBar *controller) :
    MSceneWindowView(controller),
    controller(controller),
    pressDown(false)
#ifdef Q_WS_X11
    , isOnDisplay(false)
    , isInSwitcher(false)
#endif
{
#ifdef Q_WS_X11
    pixmapDamage = 0;

    MApplication *mApplication = static_cast<MApplication *>(QCoreApplication::instance());
    connect(mApplication, SIGNAL(damageEvent(Qt::HANDLE&, short&, short&, unsigned short&, unsigned short&)),
            SLOT(handlePixmapDamageEvent(Qt::HANDLE&, short&, short&, unsigned short&, unsigned short&)));
    connect(controller, SIGNAL(displayExited()),
            this, SLOT(handleDisplayExited()));
    connect(controller, SIGNAL(displayEntered()),
            this, SLOT(handleDisplayEntered()));
    controller->setProperty("sharedPixmapHeight", SharedPixmapHeight);

    if (controller->scene() && !controller->scene()->views().isEmpty()) {
        MWindow* win = qobject_cast<MWindow*>(controller->scene()->views().at(0));
        if (win) {
            isInSwitcher = win->isInSwitcher();
            connect(win, SIGNAL(switcherEntered()), this, SLOT(handleSwitcherEntered()));
            connect(win, SIGNAL(switcherExited()), this, SLOT(handleSwitcherExited()));
        }
    }

    dbusWrapper = new MStatusBarViewDBusWrapper(this);

    connect(dbusWrapper,
            SIGNAL(sharedPixmapHandleFromProviderReceived(quint32, bool)),
            SLOT(handleSharedPixmapHandleReceived(quint32, bool)));

    if (dbusWrapper->isPixmapProviderServiceRegistered())
        isPixmapProviderOnline = true;
    else
        isPixmapProviderOnline = false;

    connect(dbusWrapper, SIGNAL(pixmapProviderServiceRegistered()),
            this, SLOT(handlePixmapProviderOnline()));
    connect(dbusWrapper, SIGNAL(pixmapProviderServiceUnregistered()),
            this, SLOT(handlePixmapProviderOffline()));

    if (isPixmapProviderOnline)
        dbusWrapper->querySharedPixmapHandleFromProvider();

#endif // Q_WS_X11
}

MStatusBarView::~MStatusBarView()
{
#ifdef Q_WS_X11
    destroyXDamageForSharedPixmap();
#endif // Q_WS_X11
}

void MStatusBarView::drawContents(QPainter *painter, const QStyleOptionGraphicsItem *option) const
{
    Q_UNUSED(option);

#ifdef Q_WS_X11
    if (sharedPixmap.isNull() && !dbusWrapper->havePendingSharedPixmapHandleReply() && isPixmapProviderOnline) {
        dbusWrapper->querySharedPixmapHandleFromProvider();
    }

    if (sharedPixmap.isNull() || (controller->sceneManager() == 0))
        return;

    QRectF sourceRect;

    sourceRect.setX(0);
    sourceRect.setY(controller->sceneManager()->orientation() == M::Landscape ? 0 : SharedPixmapHeight);
    sourceRect.setWidth(size().width());
    sourceRect.setHeight(SharedPixmapHeight);

    // provider can die under mysterious circumstances so sharedPixmap can be invalid
    try {
        painter->drawPixmap(QPointF(0.0, 0.0), sharedPixmap, sourceRect);
    } catch(...) {
        qWarning() << "MStatusBarView::drawContents: Cannot draw sharedPixmap.";
        const_cast<MStatusBarView*>(this)->sharedPixmap = QPixmap();
    }

    if (pressDown) {
        painter->save();
        painter->setOpacity(style()->pressDimFactor());
        painter->fillRect(QRectF(QPointF(0.0, 0.0), sourceRect.size()), Qt::black);
        painter->restore();
    }

#else
    Q_UNUSED(painter);
#endif // Q_WS_X11
}

void MStatusBarView::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    firstPos = event->pos();
    playHapticsFeedback();

    if (pressDown)
        return;

    pressDown = true;
    update();
}

void MStatusBarView::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    if (!style()->useSwipeGesture()) {
        return;
    }

    if(firstPos.y()+ style()->swipeThreshold() < event->pos().y()) {
        showStatusIndicatorMenu();
    }
}

void MStatusBarView::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    if (!pressDown)
        return;

    pressDown = false;
    update();

    if (style()->useSwipeGesture()) {
        return;
    }

    QPointF touch = event->scenePos();
    // shape includes the reactive margins
    QRectF rect = controller->mapToScene(controller->shape()).boundingRect();
    rect.adjust(-M_RELEASE_MISS_DELTA, -M_RELEASE_MISS_DELTA,
                M_RELEASE_MISS_DELTA, M_RELEASE_MISS_DELTA);
    if(rect.contains(touch)) {
        showStatusIndicatorMenu();
    }
}

#ifdef Q_WS_X11
bool MStatusBarView::shouldStayUpToDate()
{
    return isOnDisplay && isPixmapProviderOnline && !isInSwitcher;
}

void MStatusBarView::updateXDamageForSharedPixmap()
{
    if (shouldStayUpToDate()) {
        if (!pixmapDamage) {
            if (!sharedPixmap.isNull()) {
                setupXDamageForSharedPixmap();
            } else {
                mWarning("MStatusBarView")
                    << "Couldn't track XDamage events since there's no shared pixmap.";
            }
        }
    } else {
        destroyXDamageForSharedPixmap();
    }
}

void MStatusBarView::setupXDamageForSharedPixmap()
{
    Q_ASSERT(!sharedPixmap.isNull());
#ifdef HAVE_XDAMAGE
    pixmapDamage = XDamageCreate(QX11Info::display(), sharedPixmap.handle(), XDamageReportNonEmpty);
#endif //HAVE_XDAMAGE
}

void MStatusBarView::destroyXDamageForSharedPixmap()
{
#ifdef HAVE_XDAMAGE
    if (pixmapDamage) {
        XDamageDestroy(QX11Info::display(), pixmapDamage);
        pixmapDamage = 0;
    }
#endif //HAVE_XDAMAGE
}

void MStatusBarView::handlePixmapDamageEvent(Qt::HANDLE &damage, short &x, short &y,
                                             unsigned short &width, unsigned short &height)
{
    if (damage == pixmapDamage) {
        Q_ASSERT(shouldStayUpToDate());
        controller->update(x, y, width, height);
    }
}

void MStatusBarView::handleDisplayEntered()
{
    isOnDisplay = true;
    updateXDamageForSharedPixmap();
}

void MStatusBarView::handleDisplayExited()
{
    isOnDisplay = false;
    updateXDamageForSharedPixmap();
}

void MStatusBarView::handleSharedPixmapHandleReceived(quint32 handle, bool ok)
{
    Q_ASSERT(isPixmapProviderOnline);

    if (!ok) {
        return;
    }

    sharedPixmap = QPixmap::fromX11Pixmap(handle, QPixmap::ExplicitlyShared);

    destroyXDamageForSharedPixmap();
    updateXDamageForSharedPixmap();

    if (shouldStayUpToDate()) {
        update();
    }
}

void MStatusBarView::handlePixmapProviderOnline()
{
    isPixmapProviderOnline = true;

    Q_ASSERT(sharedPixmap.isNull());
    Q_ASSERT(!pixmapDamage);

    dbusWrapper->querySharedPixmapHandleFromProvider();
}

void MStatusBarView::handlePixmapProviderOffline()
{
    isPixmapProviderOnline = false;

    destroyXDamageForSharedPixmap();
    sharedPixmap = QPixmap();
}

void MStatusBarView::handleSwitcherEntered()
{
    isInSwitcher = true;
    updateXDamageForSharedPixmap();
}

void MStatusBarView::handleSwitcherExited()
{
    isInSwitcher = false;
    updateXDamageForSharedPixmap();
}
#endif // Q_WS_X11

void MStatusBarView::showStatusIndicatorMenu()
{
    if (style()->enableStatusIndicatorMenu()) {
        dbusWrapper->openStatusIndicatorMenu();
    }
}

void MStatusBarView::playHapticsFeedback()
{
    if (style()->enableStatusIndicatorMenu()) {
        style()->pressFeedback().play();
    }
}

M_REGISTER_VIEW_NEW(MStatusBarView, MStatusBar)
<|endoftext|>