<commit_before>#include "s3propparser.hpp"
#include <utils/davix_s3_utils.hpp>

#include <stack>
#include <utils/davix_logger_internal.hpp>

namespace Davix{

const std::string col_prop = "Name";
const std::string delimiter_prop ="Contents";
const std::string name_prop = "Key";
const std::string size_prop = "Size";

const std::string prefix_prop = "Prefix";
const std::string com_prefix_prop = "CommonPrefixes";
const std::string listbucketresult_prop = "ListBucketResult";
const std::string last_modified_prop = "LastModified";

struct S3PropParser::Internal{
    std::string current;
    std::string prefix;
    std::string prefix_to_remove;
    bool inside_com_prefix;
    int prop_count;
    std::stack<std::string> stack_status;
    std::deque<FileProperties> props;

    FileProperties property;
    S3ListingMode::S3ListingMode _s3_listing_mode;

    int start_elem(const std::string &elem){
        // new tag, clean content;
        current.clear();

        // check XML nested level, security protection
        if(stack_status.size() < 200){
            stack_status.push(elem);
        }else{
            throw DavixException(davix_scope_xml_parser(), StatusCode::ParsingError, "Impossible to parse S3 content, corrupted XML");
        }

        // check element, if collection name add first entry
        if( StrUtil::compare_ncase(col_prop, elem) ==0){
            DAVIX_SLOG(DAVIX_LOG_TRACE, DAVIX_LOG_XML, "collection found", elem.c_str());
            property.clear();
            prop_count = 0;
        }

        // check element, if new entry clear current entry
        if( StrUtil::compare_ncase(delimiter_prop, elem) ==0){
            DAVIX_SLOG(DAVIX_LOG_TRACE, DAVIX_LOG_XML, "new element found", elem.c_str());
            property.clear();
        }

        // check element, if common prefixes set flag
        if( (_s3_listing_mode == S3ListingMode::Hierarchical) && StrUtil::compare_ncase(com_prefix_prop, elem) ==0){
            DAVIX_SLOG(DAVIX_LOG_TRACE, DAVIX_LOG_XML, "common prefixes found", elem.c_str());
            inside_com_prefix = true;
        }

        // check element, if prefix clear current entry
        if( (_s3_listing_mode == S3ListingMode::Hierarchical) && StrUtil::compare_ncase(prefix_prop, elem) ==0){
            DAVIX_SLOG(DAVIX_LOG_TRACE, DAVIX_LOG_XML, "prefix found", elem.c_str());
            property.clear();
        }

        return 1;
    }

    int add_chunk(const std::string & chunk){
        current.append(chunk);
        return 0;
    }

    int end_elem(const std::string &elem){
        StrUtil::trim(current);

        // found prefix
        if( (_s3_listing_mode == S3ListingMode::Hierarchical) &&
                StrUtil::compare_ncase(prefix_prop, elem) ==0 &&
                !current.empty()){
            DAVIX_SLOG(DAVIX_LOG_TRACE, DAVIX_LOG_XML, "new prefix {}", current.c_str());
            prefix = current;
            if(inside_com_prefix){  // all keys would have been processed by now, just common prefixes left, use as DIRs
                DAVIX_SLOG(DAVIX_LOG_TRACE, DAVIX_LOG_XML, "push new common prefix {}", current.c_str());
                current = current.erase(current.size()-1,1);
                property.filename = current.erase(0, prefix_to_remove.size());
                property.info.mode =  0755 | S_IFDIR;
                property.info.mode &= ~(S_IFREG);
                props.push_back(property);
                prop_count++;
            }
        }

        // new name new fileprop
        if( StrUtil::compare_ncase(name_prop, elem) ==0){

            if((_s3_listing_mode == S3ListingMode::Flat)){  // flat mode
                property.filename = current.erase(0,prefix.size());
            }
            else if(prefix.empty()){    // at root level
                property.filename = current;
            }
            else if(!prefix.empty()){
                if(prefix.compare((prefix.size()-1),1,"/")){ // prefix doesn't end with '/', file
                    property.filename = current;
                }
                else if(!(StrUtil::compare_ncase(prefix, current) ==0)){ // folder
                    property.filename = current.erase(0, prefix_to_remove.size());

                }
            }

            if(!property.filename.empty())
                property.info.mode = 0755;
        }

        if( StrUtil::compare_ncase(size_prop, elem) ==0){
            try{
                dav_size_t size = toType<dav_size_t, std::string>()(current);
                DAVIX_SLOG(DAVIX_LOG_TRACE, DAVIX_LOG_XML, "element size {}", size);
                property.info.size = size;
            }catch(...){
                DAVIX_SLOG(DAVIX_LOG_TRACE, DAVIX_LOG_XML, "Unable to parse element size");
            }
        }

        if( StrUtil::compare_ncase(last_modified_prop, elem) ==0){
            try{
                time_t mtime = S3::s3TimeConverter(current);
                DAVIX_SLOG(DAVIX_LOG_TRACE, DAVIX_LOG_XML, "element LastModified {}", current);
                property.info.mtime = mtime;
                property.info.ctime = mtime;
            }catch(...){
                DAVIX_SLOG(DAVIX_LOG_TRACE, DAVIX_LOG_XML, "Unable to parse element LastModified");
            }
        }

        // found bucket name
        // push it as first item to identify bucket
        if( StrUtil::compare_ncase(col_prop, elem) ==0){
            DAVIX_SLOG(DAVIX_LOG_TRACE, DAVIX_LOG_XML, "push collection", elem.c_str());
            property.filename = current;
            property.info.mode |= S_IFDIR;
            property.info.mode &= ~(S_IFREG);
            props.push_back(property);
        }

        // check element, if end entry push new entry
        if( StrUtil::compare_ncase(delimiter_prop, elem) ==0){
            DAVIX_SLOG(DAVIX_LOG_TRACE, DAVIX_LOG_XML, "push new element {}", elem.c_str());
            props.push_back(property);
            prop_count++;
        }

        // check element, if end common prefix reset flag
        if( (_s3_listing_mode == S3ListingMode::Hierarchical) && StrUtil::compare_ncase(com_prefix_prop, elem) ==0){
            inside_com_prefix = false;
        }

        // end of xml respond and still no property, requested key exists but isn't a directory
        if( (_s3_listing_mode == S3ListingMode::Hierarchical) && (StrUtil::compare_ncase(listbucketresult_prop, elem) ==0) && (prop_count ==0) ){
            throw DavixException(davix_scope_directory_listing_str(), StatusCode::IsNotADirectory, "Not a S3 directory");
        }

        // reduce stack size
        if(stack_status.size() > 0)
            stack_status.pop();
        current.clear();
        return 0;
    }

};

S3PropParser::S3PropParser() : d_ptr(new Internal())
{
    S3PropParser(S3ListingMode::Hierarchical, "");
}

S3PropParser::S3PropParser(S3ListingMode::S3ListingMode s3_listing_mode = S3ListingMode::Hierarchical) : d_ptr(new Internal())
{
    S3PropParser(s3_listing_mode, "");
}

S3PropParser::S3PropParser(S3ListingMode::S3ListingMode s3_listing_mode = S3ListingMode::Hierarchical, std::string s3_prefix = "") : d_ptr(new Internal())
{
    d_ptr->_s3_listing_mode = s3_listing_mode;

    if(!s3_prefix.empty()){
        if(s3_prefix.compare(s3_prefix.size()-1,1,"/")==0)
            d_ptr->prefix_to_remove = s3_prefix.erase(0,1);
        else
            d_ptr->prefix_to_remove = s3_prefix;
    }
}

S3PropParser::~S3PropParser(){
}

int S3PropParser::parserStartElemCb(int parent, const char *nspace, const char *name, const char **atts){
    (void) parent;
    (void) nspace;
    (void) atts;
    return d_ptr->start_elem(std::string(name));

}

int S3PropParser::parserCdataCb(int state, const char *cdata, size_t len){
    (void) state;
    (void) len;
    return d_ptr->add_chunk(std::string(cdata, len));
}

int S3PropParser::parserEndElemCb(int state, const char *nspace, const char *name){
    (void) state;
    (void) nspace;
    return d_ptr->end_elem(std::string(name));
}

std::deque<FileProperties> & S3PropParser::getProperties(){
    return d_ptr->props;
}

}
<commit_msg>Remove defaults inside s3propparser.cpp<commit_after>#include "s3propparser.hpp"
#include <utils/davix_s3_utils.hpp>

#include <stack>
#include <utils/davix_logger_internal.hpp>

namespace Davix{

const std::string col_prop = "Name";
const std::string delimiter_prop ="Contents";
const std::string name_prop = "Key";
const std::string size_prop = "Size";

const std::string prefix_prop = "Prefix";
const std::string com_prefix_prop = "CommonPrefixes";
const std::string listbucketresult_prop = "ListBucketResult";
const std::string last_modified_prop = "LastModified";

struct S3PropParser::Internal{
    std::string current;
    std::string prefix;
    std::string prefix_to_remove;
    bool inside_com_prefix;
    int prop_count;
    std::stack<std::string> stack_status;
    std::deque<FileProperties> props;

    FileProperties property;
    S3ListingMode::S3ListingMode _s3_listing_mode;

    int start_elem(const std::string &elem){
        // new tag, clean content;
        current.clear();

        // check XML nested level, security protection
        if(stack_status.size() < 200){
            stack_status.push(elem);
        }else{
            throw DavixException(davix_scope_xml_parser(), StatusCode::ParsingError, "Impossible to parse S3 content, corrupted XML");
        }

        // check element, if collection name add first entry
        if( StrUtil::compare_ncase(col_prop, elem) ==0){
            DAVIX_SLOG(DAVIX_LOG_TRACE, DAVIX_LOG_XML, "collection found", elem.c_str());
            property.clear();
            prop_count = 0;
        }

        // check element, if new entry clear current entry
        if( StrUtil::compare_ncase(delimiter_prop, elem) ==0){
            DAVIX_SLOG(DAVIX_LOG_TRACE, DAVIX_LOG_XML, "new element found", elem.c_str());
            property.clear();
        }

        // check element, if common prefixes set flag
        if( (_s3_listing_mode == S3ListingMode::Hierarchical) && StrUtil::compare_ncase(com_prefix_prop, elem) ==0){
            DAVIX_SLOG(DAVIX_LOG_TRACE, DAVIX_LOG_XML, "common prefixes found", elem.c_str());
            inside_com_prefix = true;
        }

        // check element, if prefix clear current entry
        if( (_s3_listing_mode == S3ListingMode::Hierarchical) && StrUtil::compare_ncase(prefix_prop, elem) ==0){
            DAVIX_SLOG(DAVIX_LOG_TRACE, DAVIX_LOG_XML, "prefix found", elem.c_str());
            property.clear();
        }

        return 1;
    }

    int add_chunk(const std::string & chunk){
        current.append(chunk);
        return 0;
    }

    int end_elem(const std::string &elem){
        StrUtil::trim(current);

        // found prefix
        if( (_s3_listing_mode == S3ListingMode::Hierarchical) &&
                StrUtil::compare_ncase(prefix_prop, elem) ==0 &&
                !current.empty()){
            DAVIX_SLOG(DAVIX_LOG_TRACE, DAVIX_LOG_XML, "new prefix {}", current.c_str());
            prefix = current;
            if(inside_com_prefix){  // all keys would have been processed by now, just common prefixes left, use as DIRs
                DAVIX_SLOG(DAVIX_LOG_TRACE, DAVIX_LOG_XML, "push new common prefix {}", current.c_str());
                current = current.erase(current.size()-1,1);
                property.filename = current.erase(0, prefix_to_remove.size());
                property.info.mode =  0755 | S_IFDIR;
                property.info.mode &= ~(S_IFREG);
                props.push_back(property);
                prop_count++;
            }
        }

        // new name new fileprop
        if( StrUtil::compare_ncase(name_prop, elem) ==0){

            if((_s3_listing_mode == S3ListingMode::Flat)){  // flat mode
                property.filename = current.erase(0,prefix.size());
            }
            else if(prefix.empty()){    // at root level
                property.filename = current;
            }
            else if(!prefix.empty()){
                if(prefix.compare((prefix.size()-1),1,"/")){ // prefix doesn't end with '/', file
                    property.filename = current;
                }
                else if(!(StrUtil::compare_ncase(prefix, current) ==0)){ // folder
                    property.filename = current.erase(0, prefix_to_remove.size());

                }
            }

            if(!property.filename.empty())
                property.info.mode = 0755;
        }

        if( StrUtil::compare_ncase(size_prop, elem) ==0){
            try{
                dav_size_t size = toType<dav_size_t, std::string>()(current);
                DAVIX_SLOG(DAVIX_LOG_TRACE, DAVIX_LOG_XML, "element size {}", size);
                property.info.size = size;
            }catch(...){
                DAVIX_SLOG(DAVIX_LOG_TRACE, DAVIX_LOG_XML, "Unable to parse element size");
            }
        }

        if( StrUtil::compare_ncase(last_modified_prop, elem) ==0){
            try{
                time_t mtime = S3::s3TimeConverter(current);
                DAVIX_SLOG(DAVIX_LOG_TRACE, DAVIX_LOG_XML, "element LastModified {}", current);
                property.info.mtime = mtime;
                property.info.ctime = mtime;
            }catch(...){
                DAVIX_SLOG(DAVIX_LOG_TRACE, DAVIX_LOG_XML, "Unable to parse element LastModified");
            }
        }

        // found bucket name
        // push it as first item to identify bucket
        if( StrUtil::compare_ncase(col_prop, elem) ==0){
            DAVIX_SLOG(DAVIX_LOG_TRACE, DAVIX_LOG_XML, "push collection", elem.c_str());
            property.filename = current;
            property.info.mode |= S_IFDIR;
            property.info.mode &= ~(S_IFREG);
            props.push_back(property);
        }

        // check element, if end entry push new entry
        if( StrUtil::compare_ncase(delimiter_prop, elem) ==0){
            DAVIX_SLOG(DAVIX_LOG_TRACE, DAVIX_LOG_XML, "push new element {}", elem.c_str());
            props.push_back(property);
            prop_count++;
        }

        // check element, if end common prefix reset flag
        if( (_s3_listing_mode == S3ListingMode::Hierarchical) && StrUtil::compare_ncase(com_prefix_prop, elem) ==0){
            inside_com_prefix = false;
        }

        // end of xml respond and still no property, requested key exists but isn't a directory
        if( (_s3_listing_mode == S3ListingMode::Hierarchical) && (StrUtil::compare_ncase(listbucketresult_prop, elem) ==0) && (prop_count ==0) ){
            throw DavixException(davix_scope_directory_listing_str(), StatusCode::IsNotADirectory, "Not a S3 directory");
        }

        // reduce stack size
        if(stack_status.size() > 0)
            stack_status.pop();
        current.clear();
        return 0;
    }

};

S3PropParser::S3PropParser() : d_ptr(new Internal())
{
    S3PropParser(S3ListingMode::Hierarchical, "");
}

S3PropParser::S3PropParser(S3ListingMode::S3ListingMode s3_listing_mode) : d_ptr(new Internal())
{
    S3PropParser(s3_listing_mode, "");
}

S3PropParser::S3PropParser(S3ListingMode::S3ListingMode s3_listing_mode, std::string s3_prefix) : d_ptr(new Internal())
{
    d_ptr->_s3_listing_mode = s3_listing_mode;

    if(!s3_prefix.empty()){
        if(s3_prefix.compare(s3_prefix.size()-1,1,"/")==0)
            d_ptr->prefix_to_remove = s3_prefix.erase(0,1);
        else
            d_ptr->prefix_to_remove = s3_prefix;
    }
}

S3PropParser::~S3PropParser(){
}

int S3PropParser::parserStartElemCb(int parent, const char *nspace, const char *name, const char **atts){
    (void) parent;
    (void) nspace;
    (void) atts;
    return d_ptr->start_elem(std::string(name));

}

int S3PropParser::parserCdataCb(int state, const char *cdata, size_t len){
    (void) state;
    (void) len;
    return d_ptr->add_chunk(std::string(cdata, len));
}

int S3PropParser::parserEndElemCb(int state, const char *nspace, const char *name){
    (void) state;
    (void) nspace;
    return d_ptr->end_elem(std::string(name));
}

std::deque<FileProperties> & S3PropParser::getProperties(){
    return d_ptr->props;
}

}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2010  Timo Savola
 */

#ifndef CIO_CHANNEL_HPP
#define CIO_CHANNEL_HPP

#include "channel.h"

#include <cassert>
#include <cstring>
#include <stdexcept>

namespace cio {

/**
 * @defgroup channel_cpp Channel (C++)
 * @code #include <cio/channel.hpp> @endcode
 * @{
 */

template <typename T>
class channel
{
	struct cio_channel *c;

public:
	/**
	 * Create a new channel.
	 */
	channel() throw (std::bad_alloc) : c(cio_channel_create(sizeof (T)))
	{
		if (!c)
			throw std::bad_alloc();
	}

	/**
	 * Steal a reference to a C channel object.
	 *
	 * @pre Item size of @p c_ptr must match @b T
	 */
	explicit channel(struct cio_channel *c_ptr) throw () : c(c_ptr)
	{
		assert(cio_channel_item_size(c_ptr) == sizeof (T));
	}

	/**
	 * Reference an existing channel.
	 */
	channel(channel &other) throw () : c(other.c)
	{
		cio_channel_ref(c);
	}

	/**
	 * Unreference the channel.
	 */
	~channel() throw ()
	{
		cio_channel_unref(c);
	}

	/**
	 * Drop the existing reference and reference another channel.
	 */
	channel &operator=(channel &other) throw ()
	{
		cio_channel_unref(c);
		c = other.c;
		cio_channel_ref(c);
		return *this;
	}

	/**
	 * @see cio_channel_close
	 */
	void close() throw ()
	{
		cio_channel_close(c);
	}

	/**
	 * @see cio_read
	 */
	bool read(T &item)
	{
		int ret = cio_channel_read(c, reinterpret_cast<void *> (&item), sizeof (T));
		if (ret < 0)
			throw std::runtime_error(std::strerror(errno));
		return ret != 0;
	}

	/**
	 * @see cio_write
	 */
	bool write(const T &item)
	{
		int ret = cio_channel_write(c, reinterpret_cast<const void *> (&item), sizeof (T));
		if (ret < 0)
			throw std::runtime_error(std::strerror(errno));
		return ret != 0;
	}

	/**
	 * @return a shared reference to the C channel object
	 */
	struct cio_channel *c_ptr() throw ()
	{
		return c;
	}

	class iterator
	{
		friend class channel;

		channel *c;
		mutable T item;

		explicit iterator(channel *c_) : c(c_)
		{
			operator++();
		}

		iterator() throw () : c(0)
		{
		}

	public:
		T operator*() const throw ()
		{
			return item;
		}

		T &operator->() const throw ()
		{
			return item;
		}

		T &operator++()
		{
			if (!c->read(item))
				c = 0;
			return item;
		}

		T operator++(int)
		{
			T copy = item;
			if (!c->read(item))
				c = 0;
			return copy;
		}

		bool operator==(const iterator &other) const throw ()
		{
			return !c && !other.c;
		}

		bool operator!=(const iterator &other) const throw ()
		{
			return !operator==(other);
		}
	};

	iterator begin()
	{
		return iterator(this);
	}

	iterator end() throw ()
	{
		return iterator();
	}
};

/** @} */

} // namespace cio

#endif
<commit_msg>fix channel C++ iterator<commit_after>/*
 * Copyright (c) 2010  Timo Savola
 */

#ifndef CIO_CHANNEL_HPP
#define CIO_CHANNEL_HPP

#include "channel.h"

#include <cassert>
#include <cstring>
#include <stdexcept>

namespace cio {

/**
 * @defgroup channel_cpp Channel (C++)
 * @code #include <cio/channel.hpp> @endcode
 * @{
 */

template <typename T>
class channel
{
	struct cio_channel *c;

public:
	/**
	 * Create a new channel.
	 */
	channel() throw (std::bad_alloc) : c(cio_channel_create(sizeof (T)))
	{
		if (!c)
			throw std::bad_alloc();
	}

	/**
	 * Steal a reference to a C channel object.
	 *
	 * @pre Item size of @p c_ptr must match @b T
	 */
	explicit channel(struct cio_channel *c_ptr) throw () : c(c_ptr)
	{
		assert(cio_channel_item_size(c_ptr) == sizeof (T));
	}

	/**
	 * Reference an existing channel.
	 */
	channel(channel &other) throw () : c(other.c)
	{
		cio_channel_ref(c);
	}

	/**
	 * Unreference the channel.
	 */
	~channel() throw ()
	{
		cio_channel_unref(c);
	}

	/**
	 * Drop the existing reference and reference another channel.
	 */
	channel &operator=(channel &other) throw ()
	{
		cio_channel_unref(c);
		c = other.c;
		cio_channel_ref(c);
		return *this;
	}

	/**
	 * @see cio_channel_close
	 */
	void close() throw ()
	{
		cio_channel_close(c);
	}

	/**
	 * @see cio_read
	 */
	bool read(T &item)
	{
		int ret = cio_channel_read(c, reinterpret_cast<void *> (&item), sizeof (T));
		if (ret < 0)
			throw std::runtime_error(std::strerror(errno));
		return ret != 0;
	}

	/**
	 * @see cio_write
	 */
	bool write(const T &item)
	{
		int ret = cio_channel_write(c, reinterpret_cast<const void *> (&item), sizeof (T));
		if (ret < 0)
			throw std::runtime_error(std::strerror(errno));
		return ret != 0;
	}

	/**
	 * @return a shared reference to the C channel object
	 */
	struct cio_channel *c_ptr() throw ()
	{
		return c;
	}

	class iterator
	{
		friend class channel;

		channel *c;
		mutable T item;

		explicit iterator(channel *c_) : c(c_)
		{
			operator++();
		}

		iterator() throw () : c(0)
		{
		}

	public:
		T operator*() const throw ()
		{
			return item;
		}

		T *operator->() const throw ()
		{
			return &item;
		}

		T &operator++()
		{
			if (!c->read(item))
				c = 0;
			return item;
		}

		T operator++(int)
		{
			T copy = item;
			if (!c->read(item))
				c = 0;
			return copy;
		}

		bool operator==(const iterator &other) const throw ()
		{
			return !c && !other.c;
		}

		bool operator!=(const iterator &other) const throw ()
		{
			return !operator==(other);
		}
	};

	iterator begin()
	{
		return iterator(this);
	}

	iterator end() throw ()
	{
		return iterator();
	}
};

/** @} */

} // namespace cio

#endif
<|endoftext|>


<commit_before>/*
    This file is part of KitchenSync.

    Copyright (c) 2004 Cornelius Schumacher <schumacher@kde.org>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.
*/

#include "remotekonnector.h"

#include "remotekonnectorconfig.h"

#include <calendarsyncee.h>
#include <addressbooksyncee.h>
#include <bookmarksyncee.h>

#include <kabc/vcardconverter.h>
#include <libkcal/icalformat.h>

#include <konnectorinfo.h>
#include <kapabilities.h>

#include <kconfig.h>
#include <kgenericfactory.h>

using namespace KSync;
using namespace KABC;
using namespace KCal;

extern "C"
{
  void *init_libremotekonnector()
  {
    return new KRES::PluginFactory<RemoteKonnector,RemoteKonnectorConfig>();
  }
}

RemoteKonnector::RemoteKonnector( const KConfig *config )
    : Konnector( config ), mConfigWidget( 0 )
{
  if ( config ) {
    mCalendarUrl = config->readPathEntry( "CalendarUrl" );
    mAddressBookUrl = config->readPathEntry( "AddressBookUrl" );
    mBookmarkUrl = config->readPathEntry( "BookmarkUrl" );
  }

  mAddressBookSyncee =	new AddressBookSyncee( &mAddressBook );
  mAddressBookSyncee->setSource( i18n( "Remote" ) );
  mCalendarSyncee = new CalendarSyncee( &mCalendar );
  mCalendarSyncee->setSource( i18n( "Remote" ) );

  mSyncees.append( mCalendarSyncee );
  mSyncees.append( mAddressBookSyncee );
  mSyncees.append( new BookmarkSyncee( &mBookmarkManager ) );
}

RemoteKonnector::~RemoteKonnector()
{
}

void RemoteKonnector::writeConfig( KConfig *config )
{
  Konnector::writeConfig( config );

  config->writePathEntry( "CalendarUrl", mCalendarUrl );
  config->writeEntry( "AddressBookUrl", mAddressBookUrl );
  config->writeEntry( "BookmarkFile", mAddressBookUrl );
}

KSync::Kapabilities RemoteKonnector::capabilities()
{
  KSync::Kapabilities caps;

  caps.setSupportMetaSyncing( false ); // we can meta sync
  caps.setSupportsPushSync( false ); // we can initialize the sync from here
  caps.setNeedsConnection( false ); // we need to have pppd running
  caps.setSupportsListDir( false ); // we will support that once there is API for it...
  caps.setNeedsIPs( false ); // we need the IP
  caps.setNeedsSrcIP( false ); // we do not bind to any address...
  caps.setNeedsDestIP( false ); // we need to know where to connect
  caps.setAutoHandle( false ); // we currently do not support auto handling
  caps.setNeedAuthentication( false ); // HennevL says we do not need that
  caps.setNeedsModelName( false ); // we need a name for our meta path!

  return caps;
}

void RemoteKonnector::setCapabilities( const KSync::Kapabilities& )
{
}

bool RemoteKonnector::readSyncees()
{
  kdDebug() << "RemoteKonnector::readSyncees()" << endl;

  mSynceeReadCount = 0;

  if ( !mCalendarUrl.isEmpty() ) {
    kdDebug() << "RemoteKonnector::readSyncees(): calendar: " << mCalendarUrl
              << endl;

    mCalendarData = "";

    KIO::TransferJob *job = KIO::get( KURL( mCalendarUrl ) );
    connect( job, SIGNAL( result( KIO::Job * ) ),
             SLOT( slotCalendarReadResult( KIO::Job * ) ) );
    connect( job, SIGNAL( data( KIO::Job *, const QByteArray & ) ),
             SLOT( slotCalendarData( KIO::Job *, const QByteArray & ) ) );

    ++mSynceeReadCount;
  }

  if ( !mAddressBookUrl.isEmpty() ) {
    kdDebug() << "RemoteKonnector::readSyncees(): AddressBook: "
              << mAddressBookUrl << endl;

    mAddressBookData = "";

    KIO::TransferJob *job = KIO::get( KURL( mAddressBookUrl ) );
    connect( job, SIGNAL( result( KIO::Job * ) ),
             SLOT( slotAddressBookReadResult( KIO::Job * ) ) );
    connect( job, SIGNAL( data( KIO::Job *, const QByteArray & ) ),
             SLOT( slotAddressBookData( KIO::Job *, const QByteArray & ) ) );

    ++mSynceeReadCount;
  }

  // TODO: Read Bookmarks

  return true;
}

void RemoteKonnector::slotCalendarData( KIO::Job *, const QByteArray &d )
{
  mCalendarData += QString::fromUtf8( d );
}

void RemoteKonnector::slotCalendarReadResult( KIO::Job *job )
{
  --mSynceeReadCount;

  if ( job->error() ) {
    job->showErrorDialog( 0 );
    emit synceeReadError( this );
  } else {
    mCalendar.close();
    ICalFormat ical;
    if ( ical.fromString( &mCalendar, mCalendarData ) ) {
      kdDebug() << "Read succeeded." << endl;
      mCalendarSyncee->reset();
      mCalendarSyncee->setIdentifier( mCalendarUrl );
      kdDebug() << "IDENTIFIER: " << mCalendarSyncee->identifier() << endl;
    } else {
      kdDebug() << "Read failed." << endl;
      emit synceeReadError( this );
    }
  }

  finishRead();
}

void RemoteKonnector::slotAddressBookData( KIO::Job *, const QByteArray &d )
{
  mAddressBookData += QString::fromUtf8( d );
}

void RemoteKonnector::slotAddressBookReadResult( KIO::Job *job )
{
  --mSynceeReadCount;

  if ( job->error() ) {
    job->showErrorDialog( 0 );
    emit synceeReadError( this );
  } else {
    mAddressBook.clear();
    VCardConverter v;
    Addressee::List a = v.parseVCards( mAddressBookData );
    Addressee::List::ConstIterator it;
    for( it = a.begin(); it != a.end(); ++it ) {
      mAddressBook.insertAddressee( *it );
      KSync::AddressBookSyncEntry entry( *it, mAddressBookSyncee );
      mAddressBookSyncee->addEntry( &entry );
    }
  }

  finishRead();
}

void RemoteKonnector::finishRead()
{
  if ( mSynceeReadCount > 0 ) return;

  emit synceesRead( this );
}

bool RemoteKonnector::connectDevice()
{
  return true;
}

bool RemoteKonnector::disconnectDevice()
{
  return true;
}

KSync::KonnectorInfo RemoteKonnector::info() const
{
  return KonnectorInfo( i18n("Remote Konnector"),
                        QIconSet(),
                        QString::fromLatin1("RemoteKonnector"),  // same as the .desktop file
                        "Remote Konnector",
                        "agenda", // icon name
                        false );
}

bool RemoteKonnector::writeSyncees()
{
  kdDebug() << "RemoteKonnector::writeSyncees()" << endl;

  mSynceeWriteCount = 0;

  if ( !mCalendarUrl.isEmpty() ) {
    kdDebug() << "RemoteKonnector::writeSyncees(): calendar: " << mCalendarUrl
              << endl;

    ICalFormat ical;
    mCalendarData = ical.toString( &mCalendar );
    if ( !mCalendarData.isEmpty() ) {
      KIO::TransferJob *job = KIO::put( KURL( mCalendarUrl ), -1, true, false );
      connect( job, SIGNAL( result( KIO::Job * ) ),
               SLOT( slotCalendarWriteResult( KIO::Job * ) ) );
      connect( job, SIGNAL( dataReq( KIO::Job *, QByteArray & ) ),
               SLOT( slotCalendarDataReq( KIO::Job *, QByteArray & ) ) );

      ++mSynceeWriteCount;
    }
  }

  if ( !mAddressBookUrl.isEmpty() ) {
    kdDebug() << "RemoteKonnector::writeSyncees(): AddressBook: "
              << mAddressBookUrl << endl;

    mAddressBookData = "";

    VCardConverter v;
    AddressBook::ConstIterator it;
    for( mAddressBook.begin(); it != mAddressBook.end(); ++it ) {
      mAddressBookData.append( v.createVCard( *it ) );
    }

    if ( !mAddressBookData.isEmpty() ) {
      KIO::TransferJob *job = KIO::put( KURL( mAddressBookUrl ), -1, true,
                                        false );
      connect( job, SIGNAL( result( KIO::Job * ) ),
               SLOT( slotAddressBookWriteResult( KIO::Job * ) ) );
      connect( job, SIGNAL( dataReq( KIO::Job *, QByteArray & ) ),
               SLOT( slotAddressBookDataReq( KIO::Job *, QByteArray & ) ) );

      ++mSynceeWriteCount;
    }
  }

  // TODO: Write Bookmarks

  return true;
}

void RemoteKonnector::slotCalendarDataReq( KIO::Job *, QByteArray &d )
{
  if ( !mCalendarData.isEmpty() ) {
    d = mCalendarData.utf8();
    mCalendarData = QString::null;
  }
}

void RemoteKonnector::slotCalendarWriteResult( KIO::Job *job )
{
  --mSynceeWriteCount;

  if ( job->error() ) {
    job->showErrorDialog( 0 );
    emit synceeWriteError( this );
  }

  finishWrite();
}

void RemoteKonnector::slotAddressBookDataReq( KIO::Job *, QByteArray &d )
{
  if ( !mAddressBookData.isEmpty() ) {
    d = mAddressBookData.utf8();
    mAddressBookData = QString::null;
  }
}

void RemoteKonnector::slotAddressBookWriteResult( KIO::Job *job )
{
  --mSynceeWriteCount;

  if ( job->error() ) {
    job->showErrorDialog( 0 );
    emit synceeWriteError( this );
  }

  finishWrite();
}

void RemoteKonnector::finishWrite()
{
  if ( mSynceeWriteCount > 0 ) return;

  emit synceesWritten( this );
}

#include "remotekonnector.moc"
<commit_msg>The addressbook needs at least one resource, otherwise it'll crash. And initialize the iterator is also a good idea IMHO...<commit_after>/*
    This file is part of KitchenSync.

    Copyright (c) 2004 Cornelius Schumacher <schumacher@kde.org>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.
*/

#include "remotekonnector.h"

#include "remotekonnectorconfig.h"

#include <calendarsyncee.h>
#include <addressbooksyncee.h>
#include <bookmarksyncee.h>

#include <kabc/vcardconverter.h>
#include <libkcal/icalformat.h>
#include <libkdepim/kabcresourcenull.h>

#include <konnectorinfo.h>
#include <kapabilities.h>

#include <kconfig.h>
#include <kgenericfactory.h>

using namespace KSync;
using namespace KABC;
using namespace KCal;

extern "C"
{
  void *init_libremotekonnector()
  {
    return new KRES::PluginFactory<RemoteKonnector,RemoteKonnectorConfig>();
  }
}

RemoteKonnector::RemoteKonnector( const KConfig *config )
    : Konnector( config ), mConfigWidget( 0 )
{
  if ( config ) {
    mCalendarUrl = config->readPathEntry( "CalendarUrl" );
    mAddressBookUrl = config->readPathEntry( "AddressBookUrl" );
    mBookmarkUrl = config->readPathEntry( "BookmarkUrl" );
  }

  mAddressBook.addResource( new KABC::ResourceNull() );

  mAddressBookSyncee =	new AddressBookSyncee( &mAddressBook );
  mAddressBookSyncee->setSource( i18n( "Remote" ) );
  mCalendarSyncee = new CalendarSyncee( &mCalendar );
  mCalendarSyncee->setSource( i18n( "Remote" ) );

  mSyncees.append( mCalendarSyncee );
  mSyncees.append( mAddressBookSyncee );
  mSyncees.append( new BookmarkSyncee( &mBookmarkManager ) );
}

RemoteKonnector::~RemoteKonnector()
{
}

void RemoteKonnector::writeConfig( KConfig *config )
{
  Konnector::writeConfig( config );

  config->writePathEntry( "CalendarUrl", mCalendarUrl );
  config->writeEntry( "AddressBookUrl", mAddressBookUrl );
  config->writeEntry( "BookmarkFile", mAddressBookUrl );
}

KSync::Kapabilities RemoteKonnector::capabilities()
{
  KSync::Kapabilities caps;

  caps.setSupportMetaSyncing( false ); // we can meta sync
  caps.setSupportsPushSync( false ); // we can initialize the sync from here
  caps.setNeedsConnection( false ); // we need to have pppd running
  caps.setSupportsListDir( false ); // we will support that once there is API for it...
  caps.setNeedsIPs( false ); // we need the IP
  caps.setNeedsSrcIP( false ); // we do not bind to any address...
  caps.setNeedsDestIP( false ); // we need to know where to connect
  caps.setAutoHandle( false ); // we currently do not support auto handling
  caps.setNeedAuthentication( false ); // HennevL says we do not need that
  caps.setNeedsModelName( false ); // we need a name for our meta path!

  return caps;
}

void RemoteKonnector::setCapabilities( const KSync::Kapabilities& )
{
}

bool RemoteKonnector::readSyncees()
{
  kdDebug() << "RemoteKonnector::readSyncees()" << endl;

  mSynceeReadCount = 0;

  if ( !mCalendarUrl.isEmpty() ) {
    kdDebug() << "RemoteKonnector::readSyncees(): calendar: " << mCalendarUrl
              << endl;

    mCalendarData = "";

    KIO::TransferJob *job = KIO::get( KURL( mCalendarUrl ) );
    connect( job, SIGNAL( result( KIO::Job * ) ),
             SLOT( slotCalendarReadResult( KIO::Job * ) ) );
    connect( job, SIGNAL( data( KIO::Job *, const QByteArray & ) ),
             SLOT( slotCalendarData( KIO::Job *, const QByteArray & ) ) );

    ++mSynceeReadCount;
  }

  if ( !mAddressBookUrl.isEmpty() ) {
    kdDebug() << "RemoteKonnector::readSyncees(): AddressBook: "
              << mAddressBookUrl << endl;

    mAddressBookData = "";

    KIO::TransferJob *job = KIO::get( KURL( mAddressBookUrl ) );
    connect( job, SIGNAL( result( KIO::Job * ) ),
             SLOT( slotAddressBookReadResult( KIO::Job * ) ) );
    connect( job, SIGNAL( data( KIO::Job *, const QByteArray & ) ),
             SLOT( slotAddressBookData( KIO::Job *, const QByteArray & ) ) );

    ++mSynceeReadCount;
  }

  // TODO: Read Bookmarks

  return true;
}

void RemoteKonnector::slotCalendarData( KIO::Job *, const QByteArray &d )
{
  mCalendarData += QString::fromUtf8( d );
}

void RemoteKonnector::slotCalendarReadResult( KIO::Job *job )
{
  --mSynceeReadCount;

  if ( job->error() ) {
    job->showErrorDialog( 0 );
    emit synceeReadError( this );
  } else {
    mCalendar.close();
    ICalFormat ical;
    if ( ical.fromString( &mCalendar, mCalendarData ) ) {
      kdDebug() << "Read succeeded." << endl;
      mCalendarSyncee->reset();
      mCalendarSyncee->setIdentifier( mCalendarUrl );
      kdDebug() << "IDENTIFIER: " << mCalendarSyncee->identifier() << endl;
    } else {
      kdDebug() << "Read failed." << endl;
      emit synceeReadError( this );
    }
  }

  finishRead();
}

void RemoteKonnector::slotAddressBookData( KIO::Job *, const QByteArray &d )
{
  mAddressBookData += QString::fromUtf8( d );
}

void RemoteKonnector::slotAddressBookReadResult( KIO::Job *job )
{
  --mSynceeReadCount;

  if ( job->error() ) {
    job->showErrorDialog( 0 );
    emit synceeReadError( this );
  } else {
    mAddressBook.clear();
    VCardConverter v;
    Addressee::List a = v.parseVCards( mAddressBookData );
    Addressee::List::ConstIterator it;
    for( it = a.begin(); it != a.end(); ++it ) {
      mAddressBook.insertAddressee( *it );
      KSync::AddressBookSyncEntry entry( *it, mAddressBookSyncee );
      mAddressBookSyncee->addEntry( &entry );
    }
  }

  finishRead();
}

void RemoteKonnector::finishRead()
{
  if ( mSynceeReadCount > 0 ) return;

  emit synceesRead( this );
}

bool RemoteKonnector::connectDevice()
{
  return true;
}

bool RemoteKonnector::disconnectDevice()
{
  return true;
}

KSync::KonnectorInfo RemoteKonnector::info() const
{
  return KonnectorInfo( i18n("Remote Konnector"),
                        QIconSet(),
                        QString::fromLatin1("RemoteKonnector"),  // same as the .desktop file
                        "Remote Konnector",
                        "agenda", // icon name
                        false );
}

bool RemoteKonnector::writeSyncees()
{
  kdDebug() << "RemoteKonnector::writeSyncees()" << endl;

  mSynceeWriteCount = 0;

  if ( !mCalendarUrl.isEmpty() ) {
    kdDebug() << "RemoteKonnector::writeSyncees(): calendar: " << mCalendarUrl
              << endl;

    ICalFormat ical;
    mCalendarData = ical.toString( &mCalendar );
    if ( !mCalendarData.isEmpty() ) {
      KIO::TransferJob *job = KIO::put( KURL( mCalendarUrl ), -1, true, false );
      connect( job, SIGNAL( result( KIO::Job * ) ),
               SLOT( slotCalendarWriteResult( KIO::Job * ) ) );
      connect( job, SIGNAL( dataReq( KIO::Job *, QByteArray & ) ),
               SLOT( slotCalendarDataReq( KIO::Job *, QByteArray & ) ) );

      ++mSynceeWriteCount;
    }
  }

  if ( !mAddressBookUrl.isEmpty() ) {
    kdDebug() << "RemoteKonnector::writeSyncees(): AddressBook: "
              << mAddressBookUrl << endl;

    mAddressBookData = "";

    VCardConverter v;
    AddressBook::ConstIterator it;
    for ( it = mAddressBook.begin(); it != mAddressBook.end(); ++it ) {
      mAddressBookData.append( v.createVCard( *it ) );
    }

    if ( !mAddressBookData.isEmpty() ) {
      KIO::TransferJob *job = KIO::put( KURL( mAddressBookUrl ), -1, true,
                                        false );
      connect( job, SIGNAL( result( KIO::Job * ) ),
               SLOT( slotAddressBookWriteResult( KIO::Job * ) ) );
      connect( job, SIGNAL( dataReq( KIO::Job *, QByteArray & ) ),
               SLOT( slotAddressBookDataReq( KIO::Job *, QByteArray & ) ) );

      ++mSynceeWriteCount;
    }
  }

  // TODO: Write Bookmarks

  return true;
}

void RemoteKonnector::slotCalendarDataReq( KIO::Job *, QByteArray &d )
{
  if ( !mCalendarData.isEmpty() ) {
    d = mCalendarData.utf8();
    mCalendarData = QString::null;
  }
}

void RemoteKonnector::slotCalendarWriteResult( KIO::Job *job )
{
  --mSynceeWriteCount;

  if ( job->error() ) {
    job->showErrorDialog( 0 );
    emit synceeWriteError( this );
  }

  finishWrite();
}

void RemoteKonnector::slotAddressBookDataReq( KIO::Job *, QByteArray &d )
{
  if ( !mAddressBookData.isEmpty() ) {
    d = mAddressBookData.utf8();
    mAddressBookData = QString::null;
  }
}

void RemoteKonnector::slotAddressBookWriteResult( KIO::Job *job )
{
  --mSynceeWriteCount;

  if ( job->error() ) {
    job->showErrorDialog( 0 );
    emit synceeWriteError( this );
  }

  finishWrite();
}

void RemoteKonnector::finishWrite()
{
  if ( mSynceeWriteCount > 0 ) return;

  emit synceesWritten( this );
}

#include "remotekonnector.moc"
<|endoftext|>


<commit_before>//===-- RNBSocket.cpp -------------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//  Created by Greg Clayton on 12/12/07.
//
//===----------------------------------------------------------------------===//

#include "RNBSocket.h"
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <termios.h>
#include "DNBLog.h"
#include "DNBError.h"

#ifdef WITH_LOCKDOWN
#include "lockdown.h"
#endif

/* Once we have a RNBSocket object with a port # specified,
   this function is called to wait for an incoming connection.
   This function blocks while waiting for that connection.  */

bool
ResolveIPV4HostName (const char *hostname, in_addr_t &addr)
{
    if (hostname == NULL ||
        hostname[0] == '\0' ||
        strcmp(hostname, "localhost") == 0 ||
        strcmp(hostname, "127.0.0.1") == 0)
    {
        addr = htonl (INADDR_LOOPBACK);
        return true;
    }
    else if (strcmp(hostname, "*") == 0)
    {
        addr = htonl (INADDR_ANY);
        return true;
    }
    else
    {
        // See if an IP address was specified as numbers
        int inet_pton_result = ::inet_pton (AF_INET, hostname, &addr);

        if (inet_pton_result == 1)
            return true;

        struct hostent *host_entry = gethostbyname (hostname);
        if (host_entry)
        {
            std::string ip_str (::inet_ntoa (*(struct in_addr *)*host_entry->h_addr_list));
            inet_pton_result = ::inet_pton (AF_INET, ip_str.c_str(), &addr);
            if (inet_pton_result == 1)
                return true;
        }
    }
    return false;
}

rnb_err_t
RNBSocket::Listen (const char *listen_host, in_port_t port, PortBoundCallback callback, const void *callback_baton)
{
    //DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s called", (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__);
    // Disconnect without saving errno
    Disconnect (false);

    // Now figure out the hostname that will be attaching and palce it into
    struct sockaddr_in listen_addr;
    ::memset (&listen_addr, 0, sizeof listen_addr);
    listen_addr.sin_len = sizeof listen_addr;
    listen_addr.sin_family = AF_INET;
    listen_addr.sin_port = htons (port);
    listen_addr.sin_addr.s_addr = INADDR_ANY;

    if (!ResolveIPV4HostName(listen_host, listen_addr.sin_addr.s_addr))
    {
        DNBLogThreaded("error: failed to resolve connecting host '%s'", listen_host);
        return rnb_err;
    }

    DNBError err;
    int listen_fd = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (listen_fd == -1)
        err.SetError(errno, DNBError::POSIX);

    if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
        err.LogThreaded("::socket ( domain = AF_INET, type = SOCK_STREAM, protocol = IPPROTO_TCP ) => socket = %i", listen_fd);

    if (err.Fail())
        return rnb_err;

    // enable local address reuse
    SetSocketOption (listen_fd, SOL_SOCKET, SO_REUSEADDR, 1);

    struct sockaddr_in sa;
    ::memset (&sa, 0, sizeof sa);
    sa.sin_len = sizeof sa;
    sa.sin_family = AF_INET;
    sa.sin_port = htons (port);
    sa.sin_addr.s_addr = INADDR_ANY; // Let incoming connections bind to any host network interface (this is NOT who can connect to us)
    int error = ::bind (listen_fd, (struct sockaddr *) &sa, sizeof(sa));
    if (error == -1)
        err.SetError(errno, DNBError::POSIX);

    if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
        err.LogThreaded("::bind ( socket = %i, (struct sockaddr *) &sa, sizeof(sa)) )", listen_fd);

    if (err.Fail())
    {
        ClosePort (listen_fd, false);
        return rnb_err;
    }

    if (callback && port == 0)
    {
        // We were asked to listen on port zero which means we
        // must now read the actual port that was given to us
        // as port zero is a special code for "find an open port
        // for me".
        socklen_t sa_len = sizeof (sa);
        if (getsockname(listen_fd, (struct sockaddr *)&sa, &sa_len) == 0)
        {
            port = ntohs (sa.sin_port);
            callback (callback_baton, port);
        }
    }

    error = ::listen (listen_fd, 5);
    if (error == -1)
        err.SetError(errno, DNBError::POSIX);

    if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
        err.LogThreaded("::listen ( socket = %i, backlog = 1 )", listen_fd);

    if (err.Fail())
    {
        ClosePort (listen_fd, false);
        return rnb_err;
    }

    struct sockaddr_in accept_addr;
    ::memset (&accept_addr, 0, sizeof accept_addr);
    accept_addr.sin_len = sizeof accept_addr;

    bool accept_connection = false;

    // Loop until we are happy with our connection
    while (!accept_connection)
    {
        socklen_t accept_addr_len = sizeof accept_addr;
        m_fd = ::accept (listen_fd, (struct sockaddr *)&accept_addr, &accept_addr_len);

        if (m_fd == -1)
            err.SetError(errno, DNBError::POSIX);

        if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
            err.LogThreaded("::accept ( socket = %i, address = %p, address_len = %u )", listen_fd, &accept_addr, accept_addr_len);

        if (err.Fail())
            break;

        if (listen_addr.sin_addr.s_addr == INADDR_ANY)
            accept_connection = true;
        else
        {
            if (accept_addr_len == listen_addr.sin_len &&
                accept_addr.sin_addr.s_addr == listen_addr.sin_addr.s_addr)
            {
                accept_connection = true;
            }
            else
            {
                ::close (m_fd);
                m_fd = -1;
                const uint8_t *accept_ip = (const uint8_t *)&accept_addr.sin_addr.s_addr;
                const uint8_t *listen_ip = (const uint8_t *)&listen_addr.sin_addr.s_addr;
                ::fprintf (stderr,
                           "error: rejecting incoming connection from %u.%u.%u.%u (expecting %u.%u.%u.%u)\n",
                           accept_ip[0], accept_ip[1], accept_ip[2], accept_ip[3],
                           listen_ip[0], listen_ip[1], listen_ip[2], listen_ip[3]);
                DNBLogThreaded ("error: rejecting connection from %u.%u.%u.%u (expecting %u.%u.%u.%u)",
                                accept_ip[0], accept_ip[1], accept_ip[2], accept_ip[3],
                                listen_ip[0], listen_ip[1], listen_ip[2], listen_ip[3]);
            }
        }
    }

    ClosePort (listen_fd, false);

    if (err.Fail())
    {
        return rnb_err;
    }
    else
    {
        // Keep our TCP packets coming without any delays.
        SetSocketOption (m_fd, IPPROTO_TCP, TCP_NODELAY, 1);
    }

    return rnb_success;
}

rnb_err_t
RNBSocket::Connect (const char *host, uint16_t port)
{
    Disconnect (false);

    // Create the socket
    m_fd = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (m_fd == -1)
        return rnb_err;

    // Enable local address reuse
    SetSocketOption (m_fd, SOL_SOCKET, SO_REUSEADDR, 1);

    struct sockaddr_in sa;
    ::memset (&sa, 0, sizeof (sa));
    sa.sin_family = AF_INET;
    sa.sin_port = htons (port);

    if (!ResolveIPV4HostName(host, sa.sin_addr.s_addr))
    {
        DNBLogThreaded("error: failed to resolve host '%s'", host);
        Disconnect (false);
        return rnb_err;
    }

    if (-1 == ::connect (m_fd, (const struct sockaddr *)&sa, sizeof(sa)))
    {
        Disconnect (false);
        return rnb_err;
    }

    // Keep our TCP packets coming without any delays.
    SetSocketOption (m_fd, IPPROTO_TCP, TCP_NODELAY, 1);
    return rnb_success;
}

rnb_err_t
RNBSocket::useFD(int fd)
{
       if (fd < 0) {
               DNBLogThreadedIf(LOG_RNB_COMM, "Bad file descriptor passed in.");
               return rnb_err;
       }

       m_fd = fd;
       return rnb_success;
}

#ifdef WITH_LOCKDOWN
rnb_err_t
RNBSocket::ConnectToService()
{
    DNBLog("Connecting to com.apple.%s service...", DEBUGSERVER_PROGRAM_NAME);
    // Disconnect from any previous connections
    Disconnect(false);
    if (::secure_lockdown_checkin (&m_ld_conn, NULL, NULL) != kLDESuccess)
    {
        DNBLogThreadedIf(LOG_RNB_COMM, "::secure_lockdown_checkin(&m_fd, NULL, NULL) failed");
        m_fd = -1;
        return rnb_not_connected;
    }
    m_fd = ::lockdown_get_socket (m_ld_conn);
    if (m_fd == -1)
    {
        DNBLogThreadedIf(LOG_RNB_COMM, "::lockdown_get_socket() failed");
        return rnb_not_connected;
    }
    m_fd_from_lockdown = true;
    return rnb_success;
}
#endif

rnb_err_t
RNBSocket::OpenFile (const char *path)
{
    DNBError err;
    m_fd = open (path, O_RDWR);
    if (m_fd == -1)
    {
        err.SetError(errno, DNBError::POSIX);
        err.LogThreaded ("can't open file '%s'", path);
        return rnb_not_connected;
    }
    else
    {
        struct termios stdin_termios;

        if (::tcgetattr (m_fd, &stdin_termios) == 0)
        {
            stdin_termios.c_lflag &= ~ECHO;     // Turn off echoing
            stdin_termios.c_lflag &= ~ICANON;   // Get one char at a time
            ::tcsetattr (m_fd, TCSANOW, &stdin_termios);
        }
    }
    return rnb_success;
}

int
RNBSocket::SetSocketOption(int fd, int level, int option_name, int option_value)
{
    return ::setsockopt(fd, level, option_name, &option_value, sizeof(option_value));
}

rnb_err_t
RNBSocket::Disconnect (bool save_errno)
{
#ifdef WITH_LOCKDOWN
    if (m_fd_from_lockdown)
    {
        m_fd_from_lockdown = false;
        m_fd = -1;
        if (lockdown_deactivate (m_ld_conn) == 0)
            return rnb_success;
        else
            return rnb_err;
    }
#endif
    return ClosePort (m_fd, save_errno);
}

rnb_err_t
RNBSocket::Read (std::string &p)
{
    char buf[1024];
    p.clear();

    // Note that BUF is on the stack so we must be careful to keep any
    // writes to BUF from overflowing or we'll have security issues.

    if (m_fd == -1)
        return rnb_err;

    //DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s calling read()", (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__);
    DNBError err;
    int bytesread = read (m_fd, buf, sizeof (buf));
    if (bytesread <= 0)
        err.SetError(errno, DNBError::POSIX);
    else
        p.append(buf, bytesread);

    if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
        err.LogThreaded("::read ( %i, %p, %llu ) => %i", m_fd, buf, sizeof (buf), (uint64_t)bytesread);

    // Our port went away - we have to mark this so IsConnected will return the truth.
    if (bytesread == 0)
    {
        m_fd = -1;
        return rnb_not_connected;
    }
    else if (bytesread == -1)
    {
        m_fd = -1;
        return rnb_err;
    }
    // Strip spaces from the end of the buffer
    while (!p.empty() && isspace (p[p.size() - 1]))
        p.erase (p.size () - 1);

    // Most data in the debugserver packets valid printable characters...
    DNBLogThreadedIf(LOG_RNB_COMM, "read: %s", p.c_str());
    return rnb_success;
}

rnb_err_t
RNBSocket::Write (const void *buffer, size_t length)
{
    if (m_fd == -1)
        return rnb_err;

    DNBError err;
    int bytessent = write (m_fd, buffer, length);
    if (bytessent < 0)
        err.SetError(errno, DNBError::POSIX);

    if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
        err.LogThreaded("::write ( socket = %i, buffer = %p, length = %llu) => %i", m_fd, buffer, length, (uint64_t)bytessent);

    if (bytessent < 0)
        return rnb_err;

    if (bytessent != length)
        return rnb_err;

    DNBLogThreadedIf(LOG_RNB_PACKETS, "putpkt: %*s", (int)length, (char *)buffer);   // All data is string based in debugserver, so this is safe
    DNBLogThreadedIf(LOG_RNB_COMM, "sent: %*s", (int)length, (char *)buffer);

    return rnb_success;
}

rnb_err_t
RNBSocket::ClosePort (int& fd, bool save_errno)
{
    int close_err = 0;
    if (fd > 0)
    {
        errno = 0;
        close_err = close (fd);
        fd = -1;
    }
    return close_err != 0 ? rnb_err : rnb_success;
}

<commit_msg>Use the correct call to close down the lockdown connection.<commit_after>//===-- RNBSocket.cpp -------------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//  Created by Greg Clayton on 12/12/07.
//
//===----------------------------------------------------------------------===//

#include "RNBSocket.h"
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <termios.h>
#include "DNBLog.h"
#include "DNBError.h"

#ifdef WITH_LOCKDOWN
#include "lockdown.h"
#endif

/* Once we have a RNBSocket object with a port # specified,
   this function is called to wait for an incoming connection.
   This function blocks while waiting for that connection.  */

bool
ResolveIPV4HostName (const char *hostname, in_addr_t &addr)
{
    if (hostname == NULL ||
        hostname[0] == '\0' ||
        strcmp(hostname, "localhost") == 0 ||
        strcmp(hostname, "127.0.0.1") == 0)
    {
        addr = htonl (INADDR_LOOPBACK);
        return true;
    }
    else if (strcmp(hostname, "*") == 0)
    {
        addr = htonl (INADDR_ANY);
        return true;
    }
    else
    {
        // See if an IP address was specified as numbers
        int inet_pton_result = ::inet_pton (AF_INET, hostname, &addr);

        if (inet_pton_result == 1)
            return true;

        struct hostent *host_entry = gethostbyname (hostname);
        if (host_entry)
        {
            std::string ip_str (::inet_ntoa (*(struct in_addr *)*host_entry->h_addr_list));
            inet_pton_result = ::inet_pton (AF_INET, ip_str.c_str(), &addr);
            if (inet_pton_result == 1)
                return true;
        }
    }
    return false;
}

rnb_err_t
RNBSocket::Listen (const char *listen_host, in_port_t port, PortBoundCallback callback, const void *callback_baton)
{
    //DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s called", (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__);
    // Disconnect without saving errno
    Disconnect (false);

    // Now figure out the hostname that will be attaching and palce it into
    struct sockaddr_in listen_addr;
    ::memset (&listen_addr, 0, sizeof listen_addr);
    listen_addr.sin_len = sizeof listen_addr;
    listen_addr.sin_family = AF_INET;
    listen_addr.sin_port = htons (port);
    listen_addr.sin_addr.s_addr = INADDR_ANY;

    if (!ResolveIPV4HostName(listen_host, listen_addr.sin_addr.s_addr))
    {
        DNBLogThreaded("error: failed to resolve connecting host '%s'", listen_host);
        return rnb_err;
    }

    DNBError err;
    int listen_fd = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (listen_fd == -1)
        err.SetError(errno, DNBError::POSIX);

    if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
        err.LogThreaded("::socket ( domain = AF_INET, type = SOCK_STREAM, protocol = IPPROTO_TCP ) => socket = %i", listen_fd);

    if (err.Fail())
        return rnb_err;

    // enable local address reuse
    SetSocketOption (listen_fd, SOL_SOCKET, SO_REUSEADDR, 1);

    struct sockaddr_in sa;
    ::memset (&sa, 0, sizeof sa);
    sa.sin_len = sizeof sa;
    sa.sin_family = AF_INET;
    sa.sin_port = htons (port);
    sa.sin_addr.s_addr = INADDR_ANY; // Let incoming connections bind to any host network interface (this is NOT who can connect to us)
    int error = ::bind (listen_fd, (struct sockaddr *) &sa, sizeof(sa));
    if (error == -1)
        err.SetError(errno, DNBError::POSIX);

    if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
        err.LogThreaded("::bind ( socket = %i, (struct sockaddr *) &sa, sizeof(sa)) )", listen_fd);

    if (err.Fail())
    {
        ClosePort (listen_fd, false);
        return rnb_err;
    }

    if (callback && port == 0)
    {
        // We were asked to listen on port zero which means we
        // must now read the actual port that was given to us
        // as port zero is a special code for "find an open port
        // for me".
        socklen_t sa_len = sizeof (sa);
        if (getsockname(listen_fd, (struct sockaddr *)&sa, &sa_len) == 0)
        {
            port = ntohs (sa.sin_port);
            callback (callback_baton, port);
        }
    }

    error = ::listen (listen_fd, 5);
    if (error == -1)
        err.SetError(errno, DNBError::POSIX);

    if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
        err.LogThreaded("::listen ( socket = %i, backlog = 1 )", listen_fd);

    if (err.Fail())
    {
        ClosePort (listen_fd, false);
        return rnb_err;
    }

    struct sockaddr_in accept_addr;
    ::memset (&accept_addr, 0, sizeof accept_addr);
    accept_addr.sin_len = sizeof accept_addr;

    bool accept_connection = false;

    // Loop until we are happy with our connection
    while (!accept_connection)
    {
        socklen_t accept_addr_len = sizeof accept_addr;
        m_fd = ::accept (listen_fd, (struct sockaddr *)&accept_addr, &accept_addr_len);

        if (m_fd == -1)
            err.SetError(errno, DNBError::POSIX);

        if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
            err.LogThreaded("::accept ( socket = %i, address = %p, address_len = %u )", listen_fd, &accept_addr, accept_addr_len);

        if (err.Fail())
            break;

        if (listen_addr.sin_addr.s_addr == INADDR_ANY)
            accept_connection = true;
        else
        {
            if (accept_addr_len == listen_addr.sin_len &&
                accept_addr.sin_addr.s_addr == listen_addr.sin_addr.s_addr)
            {
                accept_connection = true;
            }
            else
            {
                ::close (m_fd);
                m_fd = -1;
                const uint8_t *accept_ip = (const uint8_t *)&accept_addr.sin_addr.s_addr;
                const uint8_t *listen_ip = (const uint8_t *)&listen_addr.sin_addr.s_addr;
                ::fprintf (stderr,
                           "error: rejecting incoming connection from %u.%u.%u.%u (expecting %u.%u.%u.%u)\n",
                           accept_ip[0], accept_ip[1], accept_ip[2], accept_ip[3],
                           listen_ip[0], listen_ip[1], listen_ip[2], listen_ip[3]);
                DNBLogThreaded ("error: rejecting connection from %u.%u.%u.%u (expecting %u.%u.%u.%u)",
                                accept_ip[0], accept_ip[1], accept_ip[2], accept_ip[3],
                                listen_ip[0], listen_ip[1], listen_ip[2], listen_ip[3]);
            }
        }
    }

    ClosePort (listen_fd, false);

    if (err.Fail())
    {
        return rnb_err;
    }
    else
    {
        // Keep our TCP packets coming without any delays.
        SetSocketOption (m_fd, IPPROTO_TCP, TCP_NODELAY, 1);
    }

    return rnb_success;
}

rnb_err_t
RNBSocket::Connect (const char *host, uint16_t port)
{
    Disconnect (false);

    // Create the socket
    m_fd = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (m_fd == -1)
        return rnb_err;

    // Enable local address reuse
    SetSocketOption (m_fd, SOL_SOCKET, SO_REUSEADDR, 1);

    struct sockaddr_in sa;
    ::memset (&sa, 0, sizeof (sa));
    sa.sin_family = AF_INET;
    sa.sin_port = htons (port);

    if (!ResolveIPV4HostName(host, sa.sin_addr.s_addr))
    {
        DNBLogThreaded("error: failed to resolve host '%s'", host);
        Disconnect (false);
        return rnb_err;
    }

    if (-1 == ::connect (m_fd, (const struct sockaddr *)&sa, sizeof(sa)))
    {
        Disconnect (false);
        return rnb_err;
    }

    // Keep our TCP packets coming without any delays.
    SetSocketOption (m_fd, IPPROTO_TCP, TCP_NODELAY, 1);
    return rnb_success;
}

rnb_err_t
RNBSocket::useFD(int fd)
{
       if (fd < 0) {
               DNBLogThreadedIf(LOG_RNB_COMM, "Bad file descriptor passed in.");
               return rnb_err;
       }

       m_fd = fd;
       return rnb_success;
}

#ifdef WITH_LOCKDOWN
rnb_err_t
RNBSocket::ConnectToService()
{
    DNBLog("Connecting to com.apple.%s service...", DEBUGSERVER_PROGRAM_NAME);
    // Disconnect from any previous connections
    Disconnect(false);
    if (::secure_lockdown_checkin (&m_ld_conn, NULL, NULL) != kLDESuccess)
    {
        DNBLogThreadedIf(LOG_RNB_COMM, "::secure_lockdown_checkin(&m_fd, NULL, NULL) failed");
        m_fd = -1;
        return rnb_not_connected;
    }
    m_fd = ::lockdown_get_socket (m_ld_conn);
    if (m_fd == -1)
    {
        DNBLogThreadedIf(LOG_RNB_COMM, "::lockdown_get_socket() failed");
        return rnb_not_connected;
    }
    m_fd_from_lockdown = true;
    return rnb_success;
}
#endif

rnb_err_t
RNBSocket::OpenFile (const char *path)
{
    DNBError err;
    m_fd = open (path, O_RDWR);
    if (m_fd == -1)
    {
        err.SetError(errno, DNBError::POSIX);
        err.LogThreaded ("can't open file '%s'", path);
        return rnb_not_connected;
    }
    else
    {
        struct termios stdin_termios;

        if (::tcgetattr (m_fd, &stdin_termios) == 0)
        {
            stdin_termios.c_lflag &= ~ECHO;     // Turn off echoing
            stdin_termios.c_lflag &= ~ICANON;   // Get one char at a time
            ::tcsetattr (m_fd, TCSANOW, &stdin_termios);
        }
    }
    return rnb_success;
}

int
RNBSocket::SetSocketOption(int fd, int level, int option_name, int option_value)
{
    return ::setsockopt(fd, level, option_name, &option_value, sizeof(option_value));
}

rnb_err_t
RNBSocket::Disconnect (bool save_errno)
{
#ifdef WITH_LOCKDOWN
    if (m_fd_from_lockdown)
    {
        m_fd_from_lockdown = false;
        m_fd = -1;
        lockdown_disconnect (m_ld_conn);
        return rnb_success;
    }
#endif
    return ClosePort (m_fd, save_errno);
}

rnb_err_t
RNBSocket::Read (std::string &p)
{
    char buf[1024];
    p.clear();

    // Note that BUF is on the stack so we must be careful to keep any
    // writes to BUF from overflowing or we'll have security issues.

    if (m_fd == -1)
        return rnb_err;

    //DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s calling read()", (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__);
    DNBError err;
    int bytesread = read (m_fd, buf, sizeof (buf));
    if (bytesread <= 0)
        err.SetError(errno, DNBError::POSIX);
    else
        p.append(buf, bytesread);

    if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
        err.LogThreaded("::read ( %i, %p, %llu ) => %i", m_fd, buf, sizeof (buf), (uint64_t)bytesread);

    // Our port went away - we have to mark this so IsConnected will return the truth.
    if (bytesread == 0)
    {
        m_fd = -1;
        return rnb_not_connected;
    }
    else if (bytesread == -1)
    {
        m_fd = -1;
        return rnb_err;
    }
    // Strip spaces from the end of the buffer
    while (!p.empty() && isspace (p[p.size() - 1]))
        p.erase (p.size () - 1);

    // Most data in the debugserver packets valid printable characters...
    DNBLogThreadedIf(LOG_RNB_COMM, "read: %s", p.c_str());
    return rnb_success;
}

rnb_err_t
RNBSocket::Write (const void *buffer, size_t length)
{
    if (m_fd == -1)
        return rnb_err;

    DNBError err;
    int bytessent = write (m_fd, buffer, length);
    if (bytessent < 0)
        err.SetError(errno, DNBError::POSIX);

    if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
        err.LogThreaded("::write ( socket = %i, buffer = %p, length = %llu) => %i", m_fd, buffer, length, (uint64_t)bytessent);

    if (bytessent < 0)
        return rnb_err;

    if (bytessent != length)
        return rnb_err;

    DNBLogThreadedIf(LOG_RNB_PACKETS, "putpkt: %*s", (int)length, (char *)buffer);   // All data is string based in debugserver, so this is safe
    DNBLogThreadedIf(LOG_RNB_COMM, "sent: %*s", (int)length, (char *)buffer);

    return rnb_success;
}

rnb_err_t
RNBSocket::ClosePort (int& fd, bool save_errno)
{
    int close_err = 0;
    if (fd > 0)
    {
        errno = 0;
        close_err = close (fd);
        fd = -1;
    }
    return close_err != 0 ? rnb_err : rnb_success;
}

<|endoftext|>


<commit_before>/**
framenotifier_app.cpp

  Created on: 13 Oct 2015
      Author: up45
*/
#include <signal.h>
#include <string>
#include <cstring>
#include <iostream>
#include <fstream>
using namespace std;

#include <log4cxx/logger.h>
#include <log4cxx/basicconfigurator.h>
#include <log4cxx/propertyconfigurator.h>
#include <log4cxx/helpers/exception.h>
using namespace log4cxx;
using namespace log4cxx::helpers;

#include <boost/foreach.hpp>
#include <boost/program_options.hpp>
#include "boost/date_time/posix_time/posix_time.hpp"
namespace po = boost::program_options;

#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/stringbuffer.h"
using namespace rapidjson;

#include "zmq/zmq.hpp"

#include "framenotifier_data.h"
#include "FileWriter.h"

void parse_arguments(int argc, char** argv, po::variables_map& vm, LoggerPtr& logger)
{
    try
    {
        std::string config_file;

        // Declare a group of options that will allowed only on the command line
        po::options_description generic("Generic options");
        generic.add_options()
                ("help,h",
                    "Print this help message")
                ("config,c",    po::value<string>(&config_file),
                    "Specify program configuration file")
                ;

        // Declare a group of options that will be allowed both on the command line
        // and in the configuration file
        po::options_description config("Configuration options");
        config.add_options()
                ("logconfig,l",  po::value<string>(),
                    "Set the log4cxx logging configuration file")
                ("ready,r",       po::value<std::string>()->default_value("tcp://127.0.0.1:5001"),
                    "Ready ZMQ endpoint from frameReceiver")
                ("release",       po::value<std::string>()->default_value("tcp://127.0.0.1:5002"),
                        "Release frame ZMQ endpoint from frameReceiver")
                ("frames,f",     po::value<unsigned int>()->default_value(1),
                    "Set the number of frames to be notified about before terminating")
                ("sharedbuf",    po::value<std::string>()->default_value("FrameReceiverBuffer"),
                    "Set the name of the shared memory frame buffer")
                ;

        // Group the variables for parsing at the command line and/or from the configuration file
        po::options_description cmdline_options;
        cmdline_options.add(generic).add(config);
        po::options_description config_file_options;
        config_file_options.add(config);

        // Parse the command line options
        po::store(po::parse_command_line(argc, argv, cmdline_options), vm);
        po::notify(vm);

        // If the command-line help option was given, print help and exit
        if (vm.count("help"))
        {
            std::cout << "usage: frameReceiver [options]" << std::endl << std::endl;
            std::cout << cmdline_options << std::endl;
            exit(1);
        }

        // If the command line config option was given, parse the specified configuration
        // file for additional options. Note that boost::program_options gives precedence
        // to the first instance occurring. In this case, that implies that command-line
        // options have precedence over equivalent configuration file entries.
        if (vm.count("config"))
        {
            std::ifstream config_ifs(config_file.c_str());
            if (config_ifs)
            {
                LOG4CXX_DEBUG(logger, "Parsing configuration file " << config_file);
                po::store(po::parse_config_file(config_ifs, config_file_options, true), vm);
                po::notify(vm);
            }
            else
            {
                LOG4CXX_ERROR(logger, "Unable to open configuration file " << config_file << " for parsing");
                exit(1);
            }
        }

        if (vm.count("logconfig"))
        {
            PropertyConfigurator::configure(vm["logconfig"].as<string>());
            LOG4CXX_DEBUG(logger, "log4cxx config file is set to " << vm["logconfig"].as<string>());
        }

        if (vm.count("ready"))
        {
            LOG4CXX_DEBUG(logger, "Setting frame ready notification ZMQ address to " << vm["ready"].as<string>());
        }

        if (vm.count("release"))
        {
            LOG4CXX_DEBUG(logger, "Setting frame release notification ZMQ address to " << vm["release"].as<string>());
        }

        if (vm.count("frames"))
        {
            LOG4CXX_DEBUG(logger, "Setting number of frames to receive to " << vm["frames"].as<unsigned int>());
        }

    }
    catch (po::unknown_option &e)
    {
        LOG4CXX_WARN(logger, "CLI parsing error: " << e.what() << ". Will carry on...");
    }
    catch (Exception &e)
    {
        LOG4CXX_FATAL(logger, "Got Log4CXX exception: " << e.what());
        throw;
    }
    catch (exception &e)
    {
        LOG4CXX_ERROR(logger, "Got exception:" << e.what());
        throw;
    }
    catch (...)
    {
        LOG4CXX_FATAL(logger, "Exception of unknown type!");
        throw;
    }
}

int main(int argc, char** argv)
{
    int rc = 0;

    // Create a default basic logger configuration, which can be overridden by command-line option later
    BasicConfigurator::configure();

    LoggerPtr logger(Logger::getLogger("FrameNotifier"));
    po::variables_map vm;
    parse_arguments(argc, argv, vm, logger);

    SharedMemParser smp(vm["sharedbuf"].as<string>());

    // Assuming this is a P2M, our image dimensions are:
    size_t bytes_per_pixel = 2;
    dimensions_t p2m_dims(2); p2m_dims[0] = 1484; p2m_dims[1] = 1404;

    zmq::context_t zmq_context; // Global ZMQ context

    // The "release" ZMQ PUB socket, used to release frames back to the frameReceiver
    zmq::socket_t zsocket_release(zmq_context, ZMQ_PUB);
    zsocket_release.connect(vm["release"].as<string>().c_str());

    // The "ready" ZMQ SUB socket, used to get notifications that new frames are ready to be processed
    zmq::socket_t zsocket_ready(zmq_context, ZMQ_SUB);
    zsocket_ready.connect(vm["ready"].as<string>().c_str());
    zsocket_ready.setsockopt(ZMQ_SUBSCRIBE, "", strlen(""));
    zmq::pollitem_t poll_item;
    poll_item.socket = zsocket_ready;
    poll_item.events = ZMQ_POLLIN;
    poll_item.fd = 0;
    poll_item.revents = 0;

    // The file writer object
    FileWriter hdfwr;
    hdfwr.createFile("data.h5");
    FileWriter::DatasetDefinition dset_def;
    dset_def.name = "data";
    dset_def.frame_dimensions = p2m_dims;
    dset_def.pixel = FileWriter::pixel_raw_16bit;
    dset_def.num_frames = vm["frames"].as<unsigned int>();
    hdfwr.createDataset(dset_def);

    // The polling loop. Polls on all elements in poll_item
    // Stop the loop by setting keep_running=false
    // Loop automatically ends if notification_count > user option "frames"
    unsigned long notification_count = 0;
    bool keep_running = true;
    LOG4CXX_DEBUG(logger, "Entering ZMQ polling loop (" << vm["ready"].as<string>().c_str() << ")");
    while (keep_running)
    {
        // Always reset the last raw events before polling
        poll_item.revents = 0;

        // Do the poll with a 10ms timeout (i.e. 10Hz poll)
        zmq::poll(&poll_item, 1, 10);

        // Check for poll error and exit the loop is one is found
        if (poll_item.revents & ZMQ_POLLERR)
        {
            LOG4CXX_ERROR(logger, "Got ZMQ error in polling. Quitting polling loop.");
            keep_running = false;
        }
        // Do work if a message is received
        else if (poll_item.revents & ZMQ_POLLIN)
        {
            LOG4CXX_DEBUG(logger, "Reading data from ZMQ socket");
            notification_count++;
            zmq::message_t msg;
            zsocket_ready.recv(&msg); // Read the message from the ready socket

            string msg_str(reinterpret_cast<char*>(msg.data()), msg.size()-1);
            LOG4CXX_DEBUG(logger, "Parsing JSON msg string: " << msg_str);
            Document msg_doc;
            msg_doc.Parse(msg_str.c_str()); // Parse the JSON string into a Document DOM object

            // Setup a string buffer and a writer so we can stringify the Document DOM object again
            StringBuffer buffer;
            PrettyWriter<StringBuffer> writer(buffer);
            msg_doc.Accept(writer);
            LOG4CXX_DEBUG(logger, "Parsed json: " << buffer.GetString());

            // Copy the data out into a Frame object
            LOG4CXX_DEBUG(logger, "Creating Frame object");
            Frame frame(bytes_per_pixel, p2m_dims);
            LOG4CXX_DEBUG(logger, "Copying buffer ID: " << msg_doc["params"]["buffer_id"].GetInt64());
            smp.get_frame(frame, msg_doc["params"]["buffer_id"].GetInt64());
            LOG4CXX_DEBUG(logger, "Frame completeness: " << frame.get_header()->packets_received << " packets received");

            // Clear the json string buffer and reset the writer so we can re-use them
            // after modifying the Document DOM object
            buffer.Clear();
            writer.Reset(buffer);

            // We want to return the exact same JSON message back to the frameReceiver
            // so we just modify the relevant bits: msg_val: from frame_ready to frame_release
            // and update the timestamp
            msg_doc["msg_val"].SetString("frame_release");

            // Create and update the timestamp in the JSON message
            boost::posix_time::ptime msg_timestamp = boost::posix_time::microsec_clock::local_time();
            string msg_timestamp_str = boost::posix_time::to_iso_extended_string(msg_timestamp);
            msg_doc["timestamp"].SetString(StringRef(msg_timestamp_str.c_str()));

            // Encode the JSON Document DOM object using the writer
            msg_doc.Accept(writer);
            LOG4CXX_DEBUG(logger, "Changing msg_val: " << msg_doc["msg_val"].GetString());
            string release_msg(buffer.GetString()); // Get a copy of the JSON string
            LOG4CXX_DEBUG(logger, "New json: " << release_msg);

            LOG4CXX_DEBUG(logger, "Sending release response");
            // Send the new JSON object (string) back to the frameReceiver on the "release" ZMQ socket
            size_t nbytes = zsocket_release.send(release_msg.c_str(), release_msg.size() + 1);
            LOG4CXX_DEBUG(logger, "Sent " << nbytes << " bytes");

            // Write the frame to disk
            hdfwr.writeFrame(frame);

        } else
        {
            // No new data
        }

        // Quit the loop if we have received the desired number of frames
        if (notification_count >= vm["frames"].as<unsigned int>()) keep_running=false;
    }
    hdfwr.closeFile();
    return rc;
}

<commit_msg>filewriter: adding CLI option for output filename<commit_after>/**
framenotifier_app.cpp

  Created on: 13 Oct 2015
      Author: up45
*/
#include <signal.h>
#include <string>
#include <cstring>
#include <iostream>
#include <fstream>
using namespace std;

#include <log4cxx/logger.h>
#include <log4cxx/basicconfigurator.h>
#include <log4cxx/propertyconfigurator.h>
#include <log4cxx/helpers/exception.h>
using namespace log4cxx;
using namespace log4cxx::helpers;

#include <boost/foreach.hpp>
#include <boost/program_options.hpp>
#include "boost/date_time/posix_time/posix_time.hpp"
namespace po = boost::program_options;

#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/stringbuffer.h"
using namespace rapidjson;

#include "zmq/zmq.hpp"

#include "framenotifier_data.h"
#include "FileWriter.h"

void parse_arguments(int argc, char** argv, po::variables_map& vm, LoggerPtr& logger)
{
    try
    {
        std::string config_file;

        // Declare a group of options that will allowed only on the command line
        po::options_description generic("Generic options");
        generic.add_options()
                ("help,h",
                    "Print this help message")
                ("config,c",    po::value<string>(&config_file),
                    "Specify program configuration file")
                ;

        // Declare a group of options that will be allowed both on the command line
        // and in the configuration file
        po::options_description config("Configuration options");
        config.add_options()
                ("logconfig,l",  po::value<string>(),
                    "Set the log4cxx logging configuration file")
                ("ready,r",       po::value<std::string>()->default_value("tcp://127.0.0.1:5001"),
                    "Ready ZMQ endpoint from frameReceiver")
                ("release",       po::value<std::string>()->default_value("tcp://127.0.0.1:5002"),
                        "Release frame ZMQ endpoint from frameReceiver")
                ("frames,f",     po::value<unsigned int>()->default_value(1),
                    "Set the number of frames to be notified about before terminating")
                ("sharedbuf",    po::value<std::string>()->default_value("FrameReceiverBuffer"),
                    "Set the name of the shared memory frame buffer")
                ("output,o",     po::value<std::string>(),
                    "Name of HDF5 file to write frames to (default: no file writing)")
                ;

        // Group the variables for parsing at the command line and/or from the configuration file
        po::options_description cmdline_options;
        cmdline_options.add(generic).add(config);
        po::options_description config_file_options;
        config_file_options.add(config);

        // Parse the command line options
        po::store(po::parse_command_line(argc, argv, cmdline_options), vm);
        po::notify(vm);

        // If the command-line help option was given, print help and exit
        if (vm.count("help"))
        {
            std::cout << "usage: frameReceiver [options]" << std::endl << std::endl;
            std::cout << cmdline_options << std::endl;
            exit(1);
        }

        // If the command line config option was given, parse the specified configuration
        // file for additional options. Note that boost::program_options gives precedence
        // to the first instance occurring. In this case, that implies that command-line
        // options have precedence over equivalent configuration file entries.
        if (vm.count("config"))
        {
            std::ifstream config_ifs(config_file.c_str());
            if (config_ifs)
            {
                LOG4CXX_DEBUG(logger, "Parsing configuration file " << config_file);
                po::store(po::parse_config_file(config_ifs, config_file_options, true), vm);
                po::notify(vm);
            }
            else
            {
                LOG4CXX_ERROR(logger, "Unable to open configuration file " << config_file << " for parsing");
                exit(1);
            }
        }

        if (vm.count("logconfig"))
        {
            PropertyConfigurator::configure(vm["logconfig"].as<string>());
            LOG4CXX_DEBUG(logger, "log4cxx config file is set to " << vm["logconfig"].as<string>());
        }

        if (vm.count("ready"))
        {
            LOG4CXX_DEBUG(logger, "Setting frame ready notification ZMQ address to " << vm["ready"].as<string>());
        }

        if (vm.count("release"))
        {
            LOG4CXX_DEBUG(logger, "Setting frame release notification ZMQ address to " << vm["release"].as<string>());
        }

        if (vm.count("frames"))
        {
            LOG4CXX_DEBUG(logger, "Setting number of frames to receive to " << vm["frames"].as<unsigned int>());
        }

        if (vm.count("output"))
        {
            LOG4CXX_DEBUG(logger, "Writing frames to file: " << vm["output"].as<string>());
        }

    }
    catch (po::unknown_option &e)
    {
        LOG4CXX_WARN(logger, "CLI parsing error: " << e.what() << ". Will carry on...");
    }
    catch (Exception &e)
    {
        LOG4CXX_FATAL(logger, "Got Log4CXX exception: " << e.what());
        throw;
    }
    catch (exception &e)
    {
        LOG4CXX_ERROR(logger, "Got exception:" << e.what());
        throw;
    }
    catch (...)
    {
        LOG4CXX_FATAL(logger, "Exception of unknown type!");
        throw;
    }
}

int main(int argc, char** argv)
{
    int rc = 0;

    // Create a default basic logger configuration, which can be overridden by command-line option later
    BasicConfigurator::configure();

    LoggerPtr logger(Logger::getLogger("FrameNotifier"));
    po::variables_map vm;
    parse_arguments(argc, argv, vm, logger);

    SharedMemParser smp(vm["sharedbuf"].as<string>());

    // Assuming this is a P2M, our image dimensions are:
    size_t bytes_per_pixel = 2;
    dimensions_t p2m_dims(2); p2m_dims[0] = 1484; p2m_dims[1] = 1404;

    zmq::context_t zmq_context; // Global ZMQ context

    // The "release" ZMQ PUB socket, used to release frames back to the frameReceiver
    zmq::socket_t zsocket_release(zmq_context, ZMQ_PUB);
    zsocket_release.connect(vm["release"].as<string>().c_str());

    // The "ready" ZMQ SUB socket, used to get notifications that new frames are ready to be processed
    zmq::socket_t zsocket_ready(zmq_context, ZMQ_SUB);
    zsocket_ready.connect(vm["ready"].as<string>().c_str());
    zsocket_ready.setsockopt(ZMQ_SUBSCRIBE, "", strlen(""));
    zmq::pollitem_t poll_item;
    poll_item.socket = zsocket_ready;
    poll_item.events = ZMQ_POLLIN;
    poll_item.fd = 0;
    poll_item.revents = 0;

    // The file writer object
    FileWriter hdfwr;
    bool write_file = false;
    if (vm.count("output")) {
        write_file = true;
        hdfwr.createFile(vm["output"].as<string>());
        FileWriter::DatasetDefinition dset_def;
        dset_def.name = "data";
        dset_def.frame_dimensions = p2m_dims;
        dset_def.pixel = FileWriter::pixel_raw_16bit;
        dset_def.num_frames = vm["frames"].as<unsigned int>();
        hdfwr.createDataset(dset_def);
    }

    // The polling loop. Polls on all elements in poll_item
    // Stop the loop by setting keep_running=false
    // Loop automatically ends if notification_count > user option "frames"
    unsigned long notification_count = 0;
    bool keep_running = true;
    LOG4CXX_DEBUG(logger, "Entering ZMQ polling loop (" << vm["ready"].as<string>().c_str() << ")");
    while (keep_running)
    {
        // Always reset the last raw events before polling
        poll_item.revents = 0;

        // Do the poll with a 10ms timeout (i.e. 10Hz poll)
        zmq::poll(&poll_item, 1, 10);

        // Check for poll error and exit the loop is one is found
        if (poll_item.revents & ZMQ_POLLERR)
        {
            LOG4CXX_ERROR(logger, "Got ZMQ error in polling. Quitting polling loop.");
            keep_running = false;
        }
        // Do work if a message is received
        else if (poll_item.revents & ZMQ_POLLIN)
        {
            LOG4CXX_DEBUG(logger, "Reading data from ZMQ socket");
            notification_count++;
            zmq::message_t msg;
            zsocket_ready.recv(&msg); // Read the message from the ready socket

            string msg_str(reinterpret_cast<char*>(msg.data()), msg.size()-1);
            LOG4CXX_DEBUG(logger, "Parsing JSON msg string: " << msg_str);
            Document msg_doc;
            msg_doc.Parse(msg_str.c_str()); // Parse the JSON string into a Document DOM object

            // Setup a string buffer and a writer so we can stringify the Document DOM object again
            StringBuffer buffer;
            PrettyWriter<StringBuffer> writer(buffer);
            msg_doc.Accept(writer);
            LOG4CXX_DEBUG(logger, "Parsed json: " << buffer.GetString());

            // Copy the data out into a Frame object
            LOG4CXX_DEBUG(logger, "Creating Frame object");
            Frame frame(bytes_per_pixel, p2m_dims);
            LOG4CXX_DEBUG(logger, "Copying buffer ID: " << msg_doc["params"]["buffer_id"].GetInt64());
            smp.get_frame(frame, msg_doc["params"]["buffer_id"].GetInt64());
            LOG4CXX_DEBUG(logger, "Frame completeness: " << frame.get_header()->packets_received << " packets received");

            // Clear the json string buffer and reset the writer so we can re-use them
            // after modifying the Document DOM object
            buffer.Clear();
            writer.Reset(buffer);

            // We want to return the exact same JSON message back to the frameReceiver
            // so we just modify the relevant bits: msg_val: from frame_ready to frame_release
            // and update the timestamp
            msg_doc["msg_val"].SetString("frame_release");

            // Create and update the timestamp in the JSON message
            boost::posix_time::ptime msg_timestamp = boost::posix_time::microsec_clock::local_time();
            string msg_timestamp_str = boost::posix_time::to_iso_extended_string(msg_timestamp);
            msg_doc["timestamp"].SetString(StringRef(msg_timestamp_str.c_str()));

            // Encode the JSON Document DOM object using the writer
            msg_doc.Accept(writer);
            LOG4CXX_DEBUG(logger, "Changing msg_val: " << msg_doc["msg_val"].GetString());
            string release_msg(buffer.GetString()); // Get a copy of the JSON string
            LOG4CXX_DEBUG(logger, "New json: " << release_msg);

            LOG4CXX_DEBUG(logger, "Sending release response");
            // Send the new JSON object (string) back to the frameReceiver on the "release" ZMQ socket
            size_t nbytes = zsocket_release.send(release_msg.c_str(), release_msg.size() + 1);
            LOG4CXX_DEBUG(logger, "Sent " << nbytes << " bytes");

            // Write the frame to disk
            if (write_file) hdfwr.writeFrame(frame);

        } else
        {
            // No new data
        }

        // Quit the loop if we have received the desired number of frames
        if (notification_count >= vm["frames"].as<unsigned int>()) keep_running=false;
    }
    if (write_file) hdfwr.closeFile();
    return rc;
}

<|endoftext|>