static void
list_keys_retract(OrmConn conn, int sockfd, const char *datastore)
{
	#define LOG_AND_RETURN(errmsg)\
		do{ods_log_error_and_printf(sockfd,module_str,errmsg);return;}while(0)
	
	// List the keys with retract flags.
    ods_printf(sockfd,
			   "Database set to: %s\n"
			   "Retract Keys:\n"
			   "Zone:                           "
			   "Key role:     "
			   "Id:                                      "
			   "\n"
			   ,datastore
			   );
	
	OrmTransaction transaction(conn);
	if (!transaction.started())
		LOG_AND_RETURN("transaction not started");
	
	{	OrmResultRef rows;
		::ods::keystate::EnforcerZone enfzone;
		if (!OrmMessageEnum(conn,enfzone.descriptor(),rows))
			LOG_AND_RETURN("zone enumeration failed");

		for (bool next=OrmFirst(rows); next; next=OrmNext(rows)) {
			
			if (!OrmGetMessage(rows, enfzone, /*zones + keys*/true))
				LOG_AND_RETURN("retrieving zone from database failed");
			
			for (int k=0; k<enfzone.keys_size(); ++k) {
				const ::ods::keystate::KeyData &key = enfzone.keys(k);
				
				// Don't suggest ZSKs can be retracted, don't show them
				if (key.role() == ::ods::keystate::ZSK)
					continue;
				
				// Only show keys that have the retract flag set.
				if (key.ds_at_parent()!=::ods::keystate::retract)
					continue;
				
				std::string keyrole = keyrole_Name(key.role());
				ods_printf(sockfd,
						   "%-31s %-13s %-40s\n",
						   enfzone.name().c_str(),
						   keyrole.c_str(),
						   key.locator().c_str()
						   );
			}
		}
    }
	
	#undef LOG_AND_RETURN
}
bool HsmKeyFactoryPB::GetHsmKeyByLocator(const std::string loc, HsmKey **ppKey)
{
    // First try to match one of the existing HsmKeyPB objects
    std::map<std::string,HsmKeyPB>::iterator lk = _keys.find(loc);
    if (lk != _keys.end()) {
        *ppKey = &lk->second;
        return true;
    }
    
    // Now enumerate keys in the document try to find a key that matches the
    // parameters exactly and is not yet present in the _keys vector field.
	OrmResultRef rows;
	if (OrmMessageEnum(_conn, ::ods::hsmkey::HsmKey::descriptor(), rows)) {

		::ods::hsmkey::HsmKey *pbkey = NULL;

		for (bool next=OrmFirst(rows); next; next=OrmNext(rows)) {
			
			if (!pbkey)
				pbkey = new ::ods::hsmkey::HsmKey;
			
			if (OrmGetMessage(rows, *pbkey, true)) {
				if (pbkey->locator() == loc) {
					std::pair<std::map<std::string,HsmKeyPB>::iterator,bool> ret;
					ret = _keys.insert(
						std::pair<std::string,HsmKeyPB>(loc,HsmKeyPB(pbkey)) );
					pbkey = NULL;
					*ppKey = &ret.first->second;
					return true;
				}
			}
		}

		if (pbkey)
			delete pbkey;
    }
    return false;
}
bool HsmKeyFactoryPB::UseSharedKey(int bits, const std::string &repository,
                                   const std::string &policy, int algorithm, 
                                   KeyRole role, const std::string &zone, 
                                   HsmKey **ppKey)
{
    // First try to match one of the existing HsmKeyPB objects
    std::map<std::string,HsmKeyPB>::iterator k;
    for (k = _keys.begin(); k != _keys.end(); ++k) {
        if (k->second.bits() == bits 
            && k->second.policy() == policy 
            && k->second.algorithm() == algorithm
            && k->second.keyRole() == role
            && !k->second.usedByZone(zone)
            && k->second.candidateForSharing()
            )
        {
            *ppKey = &k->second;
            (*ppKey)->setUsedByZone(zone,true);
            return true;
        }
    }


#if 0
	// All the database access has to be done within a single transaction
	OrmTransactionRW trans(_conn);
#endif
	
    // Now enumerate keys in the document try to find a key that matches the
    // parameters exactly and is not yet present in the _keys vector field.
	OrmResultRef rows;
	if (OrmMessageEnum(_conn, ::ods::hsmkey::HsmKey::descriptor(), rows)) {

		::ods::hsmkey::HsmKey *pbkey = NULL;
		
		for (bool next=OrmFirst(rows); next; next=OrmNext(rows)) {

			if (!pbkey)
				pbkey = new ::ods::hsmkey::HsmKey;
			
			OrmContextRef context;
			if (OrmGetMessage(rows, *pbkey, true, context)) {
				if (pbkey->has_inception()
					&& pbkey->bits() == bits
					&& pbkey->repository() == repository
					&& pbkey->policy() == policy
					&& pbkey->algorithm() == algorithm
					&& pbkey->role() == (::ods::hsmkey::keyrole)role
					)
				{
					pbkey->set_inception(time_now());
					HsmKeyPB pbkey_ref(pbkey);
					
					
					// Fixate unset attributes that returned their default value.
					// Otherwise when we list the keys those values will show 
					// up as 'not set'
					if (!pbkey->has_policy())
						pbkey_ref.setPolicy(policy);
					if (!pbkey->has_algorithm())
						pbkey_ref.setAlgorithm(algorithm);
					if (!pbkey->has_role())
						pbkey_ref.setKeyRole(role);

					pbkey = NULL;
					
					// We have modified the key and need to update it.
					if (OrmMessageUpdate(context)) {
						
						// we won't be needing the result anymore, so release it
						rows.release();
#if 0
						// now more active queries, so commit should work.
						if (trans.commit()) {
#endif
							std::pair<std::map<std::string,HsmKeyPB>::iterator,bool> ret;
							ret = _keys.insert(std::pair<std::string,HsmKeyPB>(
												pbkey_ref.locator(),pbkey_ref));
							*ppKey = &ret.first->second;
							(*ppKey)->setUsedByZone(zone,true);
							return true;
#if 0
						}
#endif
					}
				}
			}
		}

		if (pbkey)
			delete pbkey;

		rows.release();
		
    }

#if 0	
	// transaction rolback is default, but we make it explicit here.
	trans.rollback();
#endif
	
    return false;
}
static void
retract_keys(OrmConn conn,
			int sockfd,
			const char *zone,
			const char *id,
			const char *datastore,
			const char *ds_retract_command)
{
	#define LOG_AND_RETURN(errmsg)\
		do{ods_log_error_and_printf(sockfd,module_str,errmsg);return;}while(0)
	#define LOG_AND_RETURN_1(errmsg,p)\
		do{ods_log_error_and_printf(sockfd,module_str,errmsg,p);return;}while(0)
	
	OrmTransactionRW transaction(conn);
	if (!transaction.started())
		LOG_AND_RETURN("transaction not started");
	
	{	OrmResultRef rows;
		::ods::keystate::EnforcerZone enfzone;
		if (zone) {
			std::string qzone;
			if (!OrmQuoteStringValue(conn, std::string(zone), qzone))
				LOG_AND_RETURN("quoting string value failed");
			
			if (!OrmMessageEnumWhere(conn,enfzone.descriptor(),
									 rows,"name = %s",qzone.c_str()))
				LOG_AND_RETURN("zone enumeration failed");
		} else {
			if (!OrmMessageEnum(conn,enfzone.descriptor(),rows))
				LOG_AND_RETURN("zone enumeration failed");
		}
		
		bool bZonesModified = false;
		
		if (!OrmFirst(rows)) {
			if (zone)
				LOG_AND_RETURN_1("zone %s not found",zone);
		} else {
			
			for (bool next=true; next; next=OrmNext(rows)) {
				
				OrmContextRef context;
				if (!OrmGetMessage(rows, enfzone, /*zones + keys*/true, context))
					LOG_AND_RETURN("retrieving zone from database failed");
				
				// Try to change the state of a specific 'retract' key to 'retracted'.
				bool bKeyModified = false;
				for (int k=0; k<enfzone.keys_size(); ++k) {
					const ::ods::keystate::KeyData &key = enfzone.keys(k);
					
					// Don't retract ZSKs from the parent.
					if (key.role()==::ods::keystate::ZSK)
						continue;
					
					// Only retract KSKs that have the retract flag set.
					if (key.ds_at_parent()!=::ods::keystate::retract)
						continue;

					if (id) {
						// --id <id>
						//     Force retract key to the parent for specific key id.
						if (key.locator()==id) {
							// retract key with this id from the parent
							if (retract_dnskey_by_id(sockfd,ds_retract_command,
													 key.locator().c_str(),
													 key.role(),
													 enfzone.name().c_str(),
													 key.algorithm()))
							{
								::ods::keystate::KeyData *kd =
									enfzone.mutable_keys(k);
								kd->set_ds_at_parent(::ods::keystate::retracted);
								bKeyModified = true;
							}
						}
					} else {
						if (zone) {
							// --zone <zone>
							//     Force retract key from the parent for specific zone.
							if (enfzone.name()==zone) {
								// retract key for this zone from the parent
								if (retract_dnskey_by_id(sockfd,ds_retract_command,
														 key.locator().c_str(),
														 key.role(),
														 enfzone.name().c_str(),
														 key.algorithm()))
								{
									::ods::keystate::KeyData *kd = 
									enfzone.mutable_keys(k);
									kd->set_ds_at_parent(::ods::keystate::retracted);
									bKeyModified = true;
								}
							}
						} else {
							// --auto
							//     Retract all keys from the parent that have
							//     the retract flag set.
							if (retract_dnskey_by_id(sockfd,ds_retract_command,
													 key.locator().c_str(),
													 key.role(),
													 enfzone.name().c_str(),
													 key.algorithm()))
							{
								::ods::keystate::KeyData *kd = 
									enfzone.mutable_keys(k);
								kd->set_ds_at_parent(::ods::keystate::retracted);
								bKeyModified = true;
							}
						}
					}
				}
				
				if (bKeyModified) {
					if (!OrmMessageUpdate(context))
						LOG_AND_RETURN_1("failed to update zone %s in the database", enfzone.name().c_str());
					
					bZonesModified = true;
				}
			}
			
			// we no longer need the query result, so release it.
			rows.release();
			
		}
		
		// Report back the status of the operation.
		if (bZonesModified) {
			// Commit updated records to the database.
			if (!transaction.commit())
				LOG_AND_RETURN_1("unable to commit updated zone %s to the database",zone);
			
			ods_log_debug("[%s] key states have been updated",module_str);
			ods_printf(sockfd,"update of key states completed.\n");
		} else {
			ods_log_debug("[%s] key states are unchanged",module_str);
			if (id)
				ods_printf(sockfd,
						   "No key state changes for id \"%s\"\n",
						   id);
			else
				if (zone)
					ods_printf(sockfd,
							   "No key state changes for zone \"%s\"\n",
							   zone);
				else
					ods_printf(sockfd,"key states are unchanged\n");
		}
	}
	
	#undef LOG_AND_RETURN
	#undef LOG_AND_RETURN_1
}
void 
perform_zone_del(int sockfd, engineconfig_type *config, const char *zone, int need_write_xml, bool quiet)
{
	GOOGLE_PROTOBUF_VERIFY_VERSION;

	OrmConnRef conn;
	if (!ods_orm_connect(sockfd, config, conn))
		return; // error already reported.

	std::string qzone;
    bool is_del_succeed = false;
    if (strlen(zone) > 0) {
        if (!OrmQuoteStringValue(conn, std::string(zone), qzone)) {
            const char *emsg = "quoting zone value failed";
            ods_log_error_and_printf(sockfd,module_str,emsg);
            return;
        }
    }
	
	{	OrmTransactionRW transaction(conn);
		if (!transaction.started()) {
			const char *emsg = "could not start database transaction";
			ods_log_error_and_printf(sockfd,module_str,emsg);
			return;
		}
		
        if (qzone.empty()) {
            OrmResultRef rows;
            ::ods::keystate::EnforcerZone enfzone;
            std::vector<std::string> del_zones;
            bool ok = OrmMessageEnum(conn, enfzone.descriptor(), rows);
            if (!ok) {
                transaction.rollback();
                ods_log_error("[%s] enum enforcer zone failed", module_str);
                return;
            }

            for (bool next=OrmFirst(rows); next; next = OrmNext(rows)) {
                OrmContextRef context;
                if (!OrmGetMessage(rows, enfzone, true, context)) {
                    rows.release();
                    transaction.rollback();
                    ods_log_error("[%s] retrieving zone from database failed");
                    return;
                }

                del_zones.push_back(enfzone.name());
            }
            rows.release();

            for (std::vector<std::string>::iterator it = del_zones.begin(); 
                    it != del_zones.end(); ++it) {
	            std::string del_zone;
                if (!OrmQuoteStringValue(conn, std::string(*it), del_zone)) {
                    transaction.rollback();
                    const char *emsg = "quoting zone value failed";
                    ods_log_error_and_printf(sockfd,module_str,emsg);
                    return;
                }
                if (!OrmMessageDeleteWhere(conn,
                            ::ods::keystate::EnforcerZone::descriptor(),
                            "name = %s",
                            del_zone.c_str())) {
                    transaction.rollback();
                    const char *emsg = "unable to delete zone %s";
                    ods_log_error_and_printf(sockfd,module_str,emsg, it->c_str());
                    return;
                }

                is_del_succeed = true;
            }
        }
        else {
            //find the zone
            OrmResultRef rows;
            if (!OrmMessageEnumWhere(conn, 
                        ::ods::keystate::EnforcerZone::descriptor(),
                        rows,
                        "name = %s",
                        qzone.c_str())) {
                transaction.rollback();
                ods_log_error_and_printf(sockfd, module_str, 
                        "unable to find zone %s", qzone.c_str());
                return;
            }

            if (!OrmFirst(rows)) {
                rows.release();
                transaction.rollback();
                ods_log_error_and_printf(sockfd, module_str, 
                        "Couldn't find zone %s", qzone.c_str());
                return;
            }

            rows.release();

            if (!OrmMessageDeleteWhere(conn,
                        ::ods::keystate::EnforcerZone::descriptor(),
                        "name = %s",
                        qzone.c_str()))
            {
                transaction.rollback();
                const char *emsg = "unable to delete zone %s";
                ods_log_error_and_printf(sockfd,module_str,emsg,qzone.c_str());
                return;
            }

            is_del_succeed = true;
        }
		
		if (!transaction.commit()) {
			const char *emsg = "committing delete of zone %s to database failed";
			ods_log_error_and_printf(sockfd,module_str,emsg,qzone.c_str());
			return;
		}
    }


	// Now lets write out the required files - the internal list and optionally the zonelist.xml
	// Note at the moment we re-export the whole file in zonelist.xml format here but this should be optimised....
    if (is_del_succeed) {
		if (!perform_write_signzone_file(sockfd, config)) {
        	ods_log_error_and_printf(sockfd, module_str, 
                "failed to write internal zonelist");
		}

 	   if (need_write_xml) {
			if (!perform_zonelist_export_to_file(config->zonelist_filename,config)) {
	        	ods_log_error_and_printf(sockfd, module_str, 
	                	"failed to write zonelist.xml");
			}
			if (!quiet) {
				if (qzone.empty()) {
					ods_printf(sockfd, "Deleted all zones in database and zonelist.xml updated.\n");
				} else {
					ods_printf(sockfd, "Deleted zone: %s in database and zonelist.xml updated.\n", zone);
				}
			}
		} else if (!quiet) {
			if (qzone.empty()) {
				ods_printf(sockfd, "Deleted all zones in database only. Use the --xml flag or run \"ods-enforcer zonelist export\" if an update of zonelist.xml is required.\n", zone);
			} else {
				ods_printf(sockfd, "Deleted zone: %s in database only. Use the --xml flag or run \"ods-enforcer zonelist export\" if an update of zonelist.xml is required.\n", zone);
			}
		}
	}


}
void 
perform_keystate_list(int sockfd, engineconfig_type *config, int bverbose)
{
	GOOGLE_PROTOBUF_VERIFY_VERSION;

	OrmConnRef conn;
	if (!ods_orm_connect(sockfd, config, conn))
		return; // error already reported.
	
	{	OrmTransaction transaction(conn);
		if (!transaction.started()) {
			ods_log_error("[%s] Could not start database transaction", module_str);
			ods_printf(sockfd, "error: Could not start database transaction\n");
			return;
		}
		
		::ods::keystate::EnforcerZone zone;
		
		{	OrmResultRef rows;
			if (!OrmMessageEnum(conn, zone.descriptor(),rows)) {
				ods_log_error("[%s] error enumerating zones", module_str);
				ods_printf(sockfd, "error enumerating zones\n");
				return;
			}
			
			ods_printf(sockfd,
					   "Database set to: %s\n"
					   "Keys:\n"
					   "Zone:                           "
					   "Key role:     "
					   "DS:          "
					   "DNSKEY:      "
					   "RRSIGDNSKEY: "
					   "RRSIG:       "
					   "Pub: "
					   "Act: "
					   "Id:"
					   "\n"
					   ,config->datastore
					   );

			for (bool next=OrmFirst(rows); next; next=OrmNext(rows)) {
				
				if (!OrmGetMessage(rows, zone, true)) {
					ods_log_error("[%s] error reading zone", module_str);
					ods_printf(sockfd, "error reading zone\n");
					return;
				}
					
				for (int k=0; k<zone.keys_size(); ++k) {
					const ::ods::keystate::KeyData &key = zone.keys(k);
					std::string keyrole = keyrole_Name(key.role());
					std::string ds_rrstate = rrstate_Name(key.ds().state());
					std::string dnskey_rrstate = rrstate_Name(key.dnskey().state());
					std::string rrsigdnskey_rrstate = rrstate_Name(key.rrsigdnskey().state());
					std::string rrsig_rrstate = rrstate_Name(key.rrsig().state());
					ods_printf(sockfd, 
							   "%-31s %-13s %-12s %-12s %-12s %-12s %d %4d    %s\n",
							   zone.name().c_str(),
							   keyrole.c_str(),
							   ds_rrstate.c_str(),
							   dnskey_rrstate.c_str(),
							   rrsigdnskey_rrstate.c_str(),
							   rrsig_rrstate.c_str(),
							   key.publish(),
							   key.active_ksk()||key.active_zsk(),
							   key.locator().c_str()
							   );
				}
			}
		}
    }
}