/*! Gets whether a portal has an active session.
 *  @param target the target to test for an active session.
 *  @param portal the portal to test for an active connection.
 *  @return true if the is an active connection for the portal; false otherwise. */
Boolean iSCSIDaemonIsPortalActive(iSCSIDaemonHandle handle,
                                  iSCSITargetRef target,
                                  iSCSIPortalRef portal)
{
    if(handle < 0 || !target || !portal)
        return false;

    CFDataRef targetData = iSCSITargetCreateData(target);
    CFDataRef portalData = iSCSIPortalCreateData(portal);

    iSCSIDMsgIsPortalActiveCmd cmd = iSCSIDMsgIsPortalActiveCmdInit;
    cmd.targetLength = (UInt32)CFDataGetLength(targetData);
    cmd.portalLength = (UInt32)CFDataGetLength(portalData);

    errno_t error = iSCSIDaemonSendMsg(handle,(iSCSIDMsgGeneric *)&cmd,
                                       targetData,portalData,NULL);
    CFRelease(targetData);
    CFRelease(portalData);

    if(error)
        return false;

    iSCSIDMsgIsPortalActiveRsp rsp;

    if(recv(handle,&rsp,sizeof(rsp),0) != sizeof(rsp))
        return false;

    if(rsp.funcCode != kiSCSIDIsPortalActive)
        return false;
    
    return rsp.active;
}
/*! Logs into a target using a specific portal or all portals in the database.
 *  If an argument is supplied for portal, login occurs over the specified
 *  portal.  Otherwise, the daemon will attempt to login over all portals.
 *  @param handle a handle to a daemon connection.
 *  @param authorization an authorization for the right kiSCSIAuthModifyLogin
 *  @param target specifies the target and connection parameters to use.
 *  @param portal specifies the portal to use (use NULL for all portals).
 *  @param statusCode iSCSI response code indicating operation status.
 *  @return an error code indicating whether the operation was successful. */
errno_t iSCSIDaemonLogin(iSCSIDaemonHandle handle,
                         AuthorizationRef authorization,
                         iSCSITargetRef target,
                         iSCSIPortalRef portal,
                         enum iSCSILoginStatusCode * statusCode)
{
    if(handle < 0 || !target || !authorization || !statusCode)
        return EINVAL;

    CFDataRef targetData = iSCSITargetCreateData(target);
    CFDataRef portalData = NULL;

    iSCSIDMsgLoginCmd cmd = iSCSIDMsgLoginCmdInit;
    cmd.authLength = kAuthorizationExternalFormLength;
    cmd.targetLength = (UInt32)CFDataGetLength(targetData);
    cmd.portalLength = 0;

    if(portal) {
        portalData = iSCSIPortalCreateData(portal);
        cmd.portalLength = (UInt32)CFDataGetLength(portalData);
    }
    
    AuthorizationExternalForm authExtForm;
    AuthorizationMakeExternalForm(authorization,&authExtForm);
    
    CFDataRef authData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
                                                     (UInt8*)&authExtForm.bytes,
                                                     kAuthorizationExternalFormLength,
                                                     kCFAllocatorDefault);

    errno_t error = iSCSIDaemonSendMsg(handle,(iSCSIDMsgGeneric *)&cmd,
                                       authData,targetData,portalData,NULL);
    
    if(portal)
        CFRelease(portalData);
    CFRelease(targetData);

    if(error)
        return error;

    iSCSIDMsgLoginRsp rsp;

    if(recv(handle,&rsp,sizeof(rsp),0) != sizeof(rsp))
        return EIO;

    if(rsp.funcCode != kiSCSIDLogin)
        return EIO;

    // At this point we have a valid response, process it
    *statusCode = rsp.statusCode;

    return rsp.errorCode;
}
/*! Retrieves a list of targets available from a give portal.
 *  @param handle a handle to a daemon connection.
 *  @param portal the iSCSI portal to look for targets.
 *  @param authMethod the preferred authentication method.
 *  @param statusCode iSCSI response code indicating operation status.
 *  @return an error code indicating whether the operation was successful. */
errno_t iSCSIDaemonQueryTargetForAuthMethod(iSCSIDaemonHandle handle,
                                            iSCSIPortalRef portal,
                                            CFStringRef targetIQN,
                                            enum iSCSIAuthMethods * authMethod,
                                            enum iSCSILoginStatusCode * statusCode)
{
    // Validate inputs
    if(handle < 0 || !portal || !targetIQN || !authMethod || !statusCode)
        return EINVAL;
    
    // Setup a target object with the target name
    iSCSIMutableTargetRef target = iSCSITargetCreateMutable();
    iSCSITargetSetIQN(target,targetIQN);
    
    // Generate data to transmit (no longer need target object after this)
    CFDataRef targetData = iSCSITargetCreateData(target);
    iSCSITargetRelease(target);
    
    CFDataRef portalData = iSCSIPortalCreateData(portal);
    
    // Create command header to transmit
    iSCSIDMsgQueryTargetForAuthMethodCmd cmd = iSCSIDMsgQueryTargetForAuthMethodCmdInit;
    cmd.portalLength = (UInt32)CFDataGetLength(portalData);
    cmd.targetLength = (UInt32)CFDataGetLength(targetData);
    
    if(iSCSIDaemonSendMsg(handle,(iSCSIDMsgGeneric *)&cmd,targetData,portalData,NULL))
    {
        CFRelease(portalData);
        CFRelease(targetData);
        return EIO;
    }

    CFRelease(portalData);
    CFRelease(targetData);
    iSCSIDMsgQueryTargetForAuthMethodRsp rsp;
    
    if(recv(handle,&rsp,sizeof(rsp),0) != sizeof(rsp))
        return EIO;
    
    *authMethod = rsp.authMethod;
    *statusCode = rsp.statusCode;
    
    return rsp.errorCode;
}
/*! Logs out of the target or a specific portal, if specified.
 *  @param handle a handle to a daemon connection.
 *  @param target the target to logout.
 *  @param portal the portal to logout.  If one is not specified,
 *  @param statusCode iSCSI response code indicating operation status. */
errno_t iSCSIDaemonLogout(iSCSIDaemonHandle handle,
                          iSCSITargetRef target,
                          iSCSIPortalRef portal,
                          enum iSCSILogoutStatusCode * statusCode)
{
    if(handle < 0 || !target || !statusCode)
        return EINVAL;

    CFDataRef targetData = iSCSITargetCreateData(target);
    CFDataRef portalData = NULL;

    iSCSIDMsgLogoutCmd cmd = iSCSIDMsgLogoutCmdInit;
    cmd.targetLength = (UInt32)CFDataGetLength(targetData);
    cmd.portalLength = 0;

    if(portal) {
        portalData = iSCSIPortalCreateData(portal);
        cmd.portalLength = (UInt32)CFDataGetLength(portalData);
    }

    errno_t error = iSCSIDaemonSendMsg(handle,(iSCSIDMsgGeneric *)&cmd,
                                       targetData,portalData,NULL);
    if(portal)
        CFRelease(portalData);
    CFRelease(targetData);

    if(error)
        return error;

    iSCSIDMsgLogoutRsp rsp;

    if(recv(handle,&rsp,sizeof(rsp),0) != sizeof(rsp))
        return EIO;

    if(rsp.funcCode != kiSCSIDLogout)
        return EIO;

    // At this point we have a valid response, process it
    *statusCode = rsp.statusCode;
    
    return rsp.errorCode;
}
/*! Creates a dictionary of connection parameters for the connection associated
 *  with the specified target and portal, if one exists.
 *  @param handle a handle to a daemon connection.
 *  @param target the target associated with the the specified portal.
 *  @param portal the portal to check for active connections to generate
 *  a dictionary of connection parameters.
 *  @return a dictionary of connection properties. */
CFDictionaryRef iSCSIDaemonCreateCFPropertiesForConnection(iSCSIDaemonHandle handle,
                                                           iSCSITargetRef target,
                                                           iSCSIPortalRef portal)
{
    // Validate inputs
    if(handle < 0 || !target || !portal)
        return NULL;

    CFDictionaryRef properties = NULL;
    CFDataRef targetData = iSCSITargetCreateData(target);
    CFDataRef portalData = iSCSIPortalCreateData(portal);

    // Send command to daemon
    iSCSIDMsgCreateCFPropertiesForConnectionCmd cmd = iSCSIDMsgCreateCFPropertiesForConnectionCmdInit;

    cmd.targetLength = (UInt32)CFDataGetLength(targetData);
    cmd.portalLength = (UInt32)CFDataGetLength(portalData);

    errno_t error = iSCSIDaemonSendMsg(handle,(iSCSIDMsgGeneric *)&cmd,
                                       targetData,portalData,NULL);
    CFRelease(targetData);
    CFRelease(portalData);

    iSCSIDMsgCreateCFPropertiesForConnectionRsp rsp;
    
    if(!error)
        error = iSCSIDaemonRecvMsg(handle,(iSCSIDMsgGeneric*)&rsp,NULL);
    
    if(!error) {
        CFDataRef data = NULL;
        error = iSCSIDaemonRecvMsg(handle,0,&data,rsp.dataLength,NULL);
        
        if(!error && data) {
            CFPropertyListFormat format;
            properties = CFPropertyListCreateWithData(kCFAllocatorDefault,data,0,&format,NULL);
            CFRelease(data);
        }
    }
    return properties;
}