pontarius-xmpp 0.4.0.2 → 0.4.1.0
raw patch · 24 files changed
+1034/−320 lines, 24 filesdep +HUnitdep +configuratordep +exceptionsdep ~basedep ~hsloggerdep ~mtlnew-uploaderPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: HUnit, configurator, exceptions, lens-family, profunctors, x509-system
Dependency ranges changed: base, hslogger, mtl, network, stm, text, tls, transformers, xml-picklers, xml-types
API changes (from Hackage documentation)
+ Network.Xmpp: IQRequestSendError :: XmppFailure -> IQRequestError
+ Network.Xmpp: IQRequestTimeout :: IQRequestError
+ Network.Xmpp: IQRequestUnpickleError :: UnpickleError -> IQRequestError
+ Network.Xmpp: class IQRequestClass a where data family IQResponseType a
+ Network.Xmpp: data IQRequestError
+ Network.Xmpp: enablePresenceTracking :: SessionConfiguration -> Bool
+ Network.Xmpp: iqErrorAttributes :: IQError -> ![ExtendedAttribute]
+ Network.Xmpp: iqRequestAttributes :: IQRequest -> ![ExtendedAttribute]
+ Network.Xmpp: iqResultAttributes :: IQResult -> ![ExtendedAttribute]
+ Network.Xmpp: messageAttributes :: Message -> ![ExtendedAttribute]
+ Network.Xmpp: messageErrorAttributes :: MessageError -> ![ExtendedAttribute]
+ Network.Xmpp: onPresenceChange :: SessionConfiguration -> Maybe (Jid -> PeerStatus -> PeerStatus -> IO ())
+ Network.Xmpp: pickleRequest :: IQRequestClass a => PU Element a
+ Network.Xmpp: pickleResponse :: IQRequestClass a => PU [Element] (IQResponseType a)
+ Network.Xmpp: presenceAttributes :: Presence -> ![ExtendedAttribute]
+ Network.Xmpp: presenceErrorAttributes :: PresenceError -> ![ExtendedAttribute]
+ Network.Xmpp: requestNamespace :: IQRequestClass a => a -> Text
+ Network.Xmpp: requestType :: IQRequestClass a => a -> IQRequestType
+ Network.Xmpp: runIQHandler :: IQRequestClass a => IQRequestHandler a -> Session -> IO ()
+ Network.Xmpp: sendIQRequest :: (IQRequestClass a, MonadError IQRequestError m, MonadIO m) => Maybe Integer -> Maybe Jid -> a -> Session -> m (Either IQError (IQResponseType a))
+ Network.Xmpp: streamState :: Session -> STM ConnectionState
+ Network.Xmpp: type IQRequestHandler a = a -> IO (Either StanzaError (IQResponseType a))
+ Network.Xmpp.IM: PeerAvailable :: (Maybe IMPresence) -> PeerStatus
+ Network.Xmpp.IM: PeerUnavailable :: PeerStatus
+ Network.Xmpp.IM: data PeerStatus
+ Network.Xmpp.IM: getAvailablePeers :: Session -> STM [Jid]
+ Network.Xmpp.IM: getEntityStatus :: Jid -> Session -> STM PeerStatus
+ Network.Xmpp.IM: getPeerEntities :: Jid -> Session -> STM (Map Jid (Maybe IMPresence))
+ Network.Xmpp.IM: getRosterSTM :: Session -> STM Roster
+ Network.Xmpp.IM: isPeerAvailable :: Jid -> Session -> STM Bool
+ Network.Xmpp.Internal: (+++) :: ByteString -> ByteString -> ByteString
+ Network.Xmpp.Internal: (<~) :: Jid -> Jid -> Bool
+ Network.Xmpp.Internal: Auth :: StanzaErrorType
+ Network.Xmpp.Internal: AuthIllegalCredentials :: AuthFailure
+ Network.Xmpp.Internal: AuthNoAcceptableMechanism :: [Text] -> AuthFailure
+ Network.Xmpp.Internal: AuthOtherFailure :: AuthFailure
+ Network.Xmpp.Internal: AuthSaslFailure :: SaslFailure -> AuthFailure
+ Network.Xmpp.Internal: AuthStreamFailure :: XmppFailure -> AuthFailure
+ Network.Xmpp.Internal: Available :: PresenceType
+ Network.Xmpp.Internal: BadRequest :: StanzaErrorCondition
+ Network.Xmpp.Internal: Both :: Subscription
+ Network.Xmpp.Internal: Cancel :: StanzaErrorType
+ Network.Xmpp.Internal: Chat :: MessageType
+ Network.Xmpp.Internal: Closed :: ConnectionState
+ Network.Xmpp.Internal: Conflict :: StanzaErrorCondition
+ Network.Xmpp.Internal: Continue :: StanzaErrorType
+ Network.Xmpp.Internal: EventHandlers :: (XmppFailure -> IO ()) -> EventHandlers
+ Network.Xmpp.Internal: FeatureNotImplemented :: StanzaErrorCondition
+ Network.Xmpp.Internal: Finished :: ConnectionState
+ Network.Xmpp.Internal: Forbidden :: StanzaErrorCondition
+ Network.Xmpp.Internal: From :: Subscription
+ Network.Xmpp.Internal: Get :: IQRequestType
+ Network.Xmpp.Internal: Gone :: (Maybe NonemptyText) -> StanzaErrorCondition
+ Network.Xmpp.Internal: GroupChat :: MessageType
+ Network.Xmpp.Internal: Headline :: MessageType
+ Network.Xmpp.Internal: IMP :: Maybe ShowStatus -> Maybe Text -> Maybe Int -> IMPresence
+ Network.Xmpp.Internal: IQError :: !Text -> !(Maybe Jid) -> !(Maybe Jid) -> !(Maybe LangTag) -> !StanzaError -> !(Maybe Element) -> ![ExtendedAttribute] -> IQError
+ Network.Xmpp.Internal: IQRequest :: !Text -> !(Maybe Jid) -> !(Maybe Jid) -> !(Maybe LangTag) -> !IQRequestType -> !Element -> ![ExtendedAttribute] -> IQRequest
+ Network.Xmpp.Internal: IQRequestSendError :: XmppFailure -> IQRequestError
+ Network.Xmpp.Internal: IQRequestTicket :: (Either StanzaError (Maybe Element) -> [ExtendedAttribute] -> IO (Maybe (Either XmppFailure ()))) -> IQRequest -> [Annotation] -> IQRequestTicket
+ Network.Xmpp.Internal: IQRequestTimeout :: IQRequestError
+ Network.Xmpp.Internal: IQRequestUnpickleError :: UnpickleError -> IQRequestError
+ Network.Xmpp.Internal: IQResponseError :: IQError -> IQResponse
+ Network.Xmpp.Internal: IQResponseResult :: IQResult -> IQResponse
+ Network.Xmpp.Internal: IQResult :: !Text -> !(Maybe Jid) -> !(Maybe Jid) -> !(Maybe LangTag) -> !(Maybe Element) -> ![ExtendedAttribute] -> IQResult
+ Network.Xmpp.Internal: IQSendError :: XmppFailure -> IQSendError
+ Network.Xmpp.Internal: IQTimeOut :: IQSendError
+ Network.Xmpp.Internal: InstantMessage :: Maybe MessageThread -> [MessageSubject] -> [MessageBody] -> InstantMessage
+ Network.Xmpp.Internal: InternalServerError :: StanzaErrorCondition
+ Network.Xmpp.Internal: Interrupt :: (TMVar ()) -> Interrupt
+ Network.Xmpp.Internal: Item :: Bool -> Bool -> Jid -> Maybe Text -> Subscription -> [Text] -> Item
+ Network.Xmpp.Internal: ItemNotFound :: StanzaErrorCondition
+ Network.Xmpp.Internal: Jid :: !(Maybe NonemptyText) -> !NonemptyText -> !(Maybe NonemptyText) -> Jid
+ Network.Xmpp.Internal: JidMalformed :: StanzaErrorCondition
+ Network.Xmpp.Internal: LangTag :: !Text -> ![Text] -> LangTag
+ Network.Xmpp.Internal: Message :: !(Maybe Text) -> !(Maybe Jid) -> !(Maybe Jid) -> !(Maybe LangTag) -> !MessageType -> ![Element] -> ![ExtendedAttribute] -> Message
+ Network.Xmpp.Internal: MessageBody :: Maybe LangTag -> Text -> MessageBody
+ Network.Xmpp.Internal: MessageError :: !(Maybe Text) -> !(Maybe Jid) -> !(Maybe Jid) -> !(Maybe LangTag) -> !StanzaError -> ![Element] -> ![ExtendedAttribute] -> MessageError
+ Network.Xmpp.Internal: MessageSubject :: Maybe LangTag -> Text -> MessageSubject
+ Network.Xmpp.Internal: MessageThread :: Text -> Maybe Text -> MessageThread
+ Network.Xmpp.Internal: Modify :: StanzaErrorType
+ Network.Xmpp.Internal: None :: Subscription
+ Network.Xmpp.Internal: Nonempty :: Text -> NonemptyText
+ Network.Xmpp.Internal: Normal :: MessageType
+ Network.Xmpp.Internal: NotAcceptable :: StanzaErrorCondition
+ Network.Xmpp.Internal: NotAllowed :: StanzaErrorCondition
+ Network.Xmpp.Internal: NotAuthorized :: StanzaErrorCondition
+ Network.Xmpp.Internal: Plain :: ConnectionState
+ Network.Xmpp.Internal: PolicyViolation :: StanzaErrorCondition
+ Network.Xmpp.Internal: Presence :: !(Maybe Text) -> !(Maybe Jid) -> !(Maybe Jid) -> !(Maybe LangTag) -> !PresenceType -> ![Element] -> ![ExtendedAttribute] -> Presence
+ Network.Xmpp.Internal: PresenceError :: !(Maybe Text) -> !(Maybe Jid) -> !(Maybe Jid) -> !(Maybe LangTag) -> !StanzaError -> ![Element] -> ![ExtendedAttribute] -> PresenceError
+ Network.Xmpp.Internal: Probe :: PresenceType
+ Network.Xmpp.Internal: Query :: Maybe Text -> [QueryItem] -> Query
+ Network.Xmpp.Internal: QueryItem :: Maybe Bool -> Bool -> Jid -> Maybe Text -> Maybe Subscription -> [Text] -> QueryItem
+ Network.Xmpp.Internal: RecipientUnavailable :: StanzaErrorCondition
+ Network.Xmpp.Internal: Redirect :: (Maybe NonemptyText) -> StanzaErrorCondition
+ Network.Xmpp.Internal: RegistrationRequired :: StanzaErrorCondition
+ Network.Xmpp.Internal: RemoteServerNotFound :: StanzaErrorCondition
+ Network.Xmpp.Internal: RemoteServerTimeout :: StanzaErrorCondition
+ Network.Xmpp.Internal: Remove :: Subscription
+ Network.Xmpp.Internal: ResourceConstraint :: StanzaErrorCondition
+ Network.Xmpp.Internal: Roster :: Maybe Text -> Map Jid Item -> Roster
+ Network.Xmpp.Internal: SaslAborted :: SaslError
+ Network.Xmpp.Internal: SaslAccountDisabled :: SaslError
+ Network.Xmpp.Internal: SaslChallenge :: (Maybe Text) -> SaslElement
+ Network.Xmpp.Internal: SaslCredentialsExpired :: SaslError
+ Network.Xmpp.Internal: SaslEncryptionRequired :: SaslError
+ Network.Xmpp.Internal: SaslFailure :: SaslError -> Maybe (Maybe LangTag, Text) -> SaslFailure
+ Network.Xmpp.Internal: SaslIncorrectEncoding :: SaslError
+ Network.Xmpp.Internal: SaslInvalidAuthzid :: SaslError
+ Network.Xmpp.Internal: SaslInvalidMechanism :: SaslError
+ Network.Xmpp.Internal: SaslMalformedRequest :: SaslError
+ Network.Xmpp.Internal: SaslMechanismTooWeak :: SaslError
+ Network.Xmpp.Internal: SaslNotAuthorized :: SaslError
+ Network.Xmpp.Internal: SaslSuccess :: (Maybe Text) -> SaslElement
+ Network.Xmpp.Internal: SaslTemporaryAuthFailure :: SaslError
+ Network.Xmpp.Internal: Secured :: ConnectionState
+ Network.Xmpp.Internal: ServiceUnavailable :: StanzaErrorCondition
+ Network.Xmpp.Internal: Session :: TChan (Stanza, [Annotation]) -> TVar IQHandlers -> WriteSemaphore -> ThreadId -> IO Text -> TMVar Stream -> TMVar EventHandlers -> IO () -> TVar Roster -> TVar Peers -> SessionConfiguration -> (Stanza -> IO (Either XmppFailure ())) -> HostName -> Maybe (ConnectionState -> [SaslHandler], Maybe Text) -> TVar Int -> Session
+ Network.Xmpp.Internal: SessionConfiguration :: StreamConfiguration -> (Session -> XmppFailure -> IO ()) -> IO (IO Text) -> [Plugin] -> Bool -> Bool -> Maybe (Jid -> PeerStatus -> PeerStatus -> IO ()) -> SessionConfiguration
+ Network.Xmpp.Internal: Set :: IQRequestType
+ Network.Xmpp.Internal: StanzaError :: StanzaErrorType -> StanzaErrorCondition -> Maybe (Maybe LangTag, NonemptyText) -> Maybe Element -> StanzaError
+ Network.Xmpp.Internal: StatusAway :: ShowStatus
+ Network.Xmpp.Internal: StatusChat :: ShowStatus
+ Network.Xmpp.Internal: StatusDnd :: ShowStatus
+ Network.Xmpp.Internal: StatusXa :: ShowStatus
+ Network.Xmpp.Internal: StreamBadFormat :: StreamErrorCondition
+ Network.Xmpp.Internal: StreamBadNamespacePrefix :: StreamErrorCondition
+ Network.Xmpp.Internal: StreamCloseError :: ([Element], XmppFailure) -> XmppFailure
+ Network.Xmpp.Internal: StreamConflict :: StreamErrorCondition
+ Network.Xmpp.Internal: StreamConnectionTimeout :: StreamErrorCondition
+ Network.Xmpp.Internal: StreamEndFailure :: XmppFailure
+ Network.Xmpp.Internal: StreamErrorFailure :: StreamErrorInfo -> XmppFailure
+ Network.Xmpp.Internal: StreamErrorInfo :: !StreamErrorCondition -> !(Maybe (Maybe LangTag, NonemptyText)) -> !(Maybe Element) -> StreamErrorInfo
+ Network.Xmpp.Internal: StreamHostGone :: StreamErrorCondition
+ Network.Xmpp.Internal: StreamHostUnknown :: StreamErrorCondition
+ Network.Xmpp.Internal: StreamImproperAddressing :: StreamErrorCondition
+ Network.Xmpp.Internal: StreamInternalServerError :: StreamErrorCondition
+ Network.Xmpp.Internal: StreamInvalidFrom :: StreamErrorCondition
+ Network.Xmpp.Internal: StreamInvalidNamespace :: StreamErrorCondition
+ Network.Xmpp.Internal: StreamInvalidXml :: StreamErrorCondition
+ Network.Xmpp.Internal: StreamNotAuthorized :: StreamErrorCondition
+ Network.Xmpp.Internal: StreamNotWellFormed :: StreamErrorCondition
+ Network.Xmpp.Internal: StreamPolicyViolation :: StreamErrorCondition
+ Network.Xmpp.Internal: StreamRemoteConnectionFailed :: StreamErrorCondition
+ Network.Xmpp.Internal: StreamReset :: StreamErrorCondition
+ Network.Xmpp.Internal: StreamResourceConstraint :: StreamErrorCondition
+ Network.Xmpp.Internal: StreamRestrictedXml :: StreamErrorCondition
+ Network.Xmpp.Internal: StreamSeeOtherHost :: StreamErrorCondition
+ Network.Xmpp.Internal: StreamSystemShutdown :: StreamErrorCondition
+ Network.Xmpp.Internal: StreamUndefinedCondition :: StreamErrorCondition
+ Network.Xmpp.Internal: StreamUnsupportedEncoding :: StreamErrorCondition
+ Network.Xmpp.Internal: StreamUnsupportedFeature :: StreamErrorCondition
+ Network.Xmpp.Internal: StreamUnsupportedStanzaType :: StreamErrorCondition
+ Network.Xmpp.Internal: StreamUnsupportedVersion :: StreamErrorCondition
+ Network.Xmpp.Internal: Subscribe :: PresenceType
+ Network.Xmpp.Internal: Subscribed :: PresenceType
+ Network.Xmpp.Internal: SubscriptionRequired :: StanzaErrorCondition
+ Network.Xmpp.Internal: TcpConnectionFailure :: XmppFailure
+ Network.Xmpp.Internal: TlsError :: XmppTlsError -> XmppFailure
+ Network.Xmpp.Internal: TlsNoServerSupport :: XmppFailure
+ Network.Xmpp.Internal: TlsStreamSecured :: XmppFailure
+ Network.Xmpp.Internal: To :: Subscription
+ Network.Xmpp.Internal: Unavailable :: PresenceType
+ Network.Xmpp.Internal: UndefinedCondition :: StanzaErrorCondition
+ Network.Xmpp.Internal: UnexpectedRequest :: StanzaErrorCondition
+ Network.Xmpp.Internal: Unsubscribe :: PresenceType
+ Network.Xmpp.Internal: Unsubscribed :: PresenceType
+ Network.Xmpp.Internal: UseConnection :: (ErrorT XmppFailure IO StreamHandle) -> ConnectionDetails
+ Network.Xmpp.Internal: UseHost :: HostName -> PortID -> ConnectionDetails
+ Network.Xmpp.Internal: UseRealm :: ConnectionDetails
+ Network.Xmpp.Internal: UseSrv :: HostName -> ConnectionDetails
+ Network.Xmpp.Internal: Version :: !Integer -> !Integer -> Version
+ Network.Xmpp.Internal: Wait :: StanzaErrorType
+ Network.Xmpp.Internal: XmppAuthFailure :: AuthFailure -> XmppFailure
+ Network.Xmpp.Internal: XmppIOException :: IOException -> XmppFailure
+ Network.Xmpp.Internal: XmppIllegalTcpDetails :: XmppFailure
+ Network.Xmpp.Internal: XmppInvalidXml :: String -> XmppFailure
+ Network.Xmpp.Internal: XmppNoStream :: XmppFailure
+ Network.Xmpp.Internal: XmppOtherFailure :: XmppFailure
+ Network.Xmpp.Internal: XmppTlsError :: TLSError -> XmppTlsError
+ Network.Xmpp.Internal: XmppTlsException :: TLSException -> XmppTlsError
+ Network.Xmpp.Internal: answerIM :: [MessageBody] -> Message -> Maybe Message
+ Network.Xmpp.Internal: answerIQ :: IQRequestTicket -> Either StanzaError (Maybe Element) -> [ExtendedAttribute] -> IO (Maybe (Either XmppFailure ()))
+ Network.Xmpp.Internal: answerMessage :: Message -> [Element] -> Maybe Message
+ Network.Xmpp.Internal: answerTicket :: IQRequestTicket -> Either StanzaError (Maybe Element) -> [ExtendedAttribute] -> IO (Maybe (Either XmppFailure ()))
+ Network.Xmpp.Internal: bodyContent :: MessageBody -> Text
+ Network.Xmpp.Internal: bodyLang :: MessageBody -> Maybe LangTag
+ Network.Xmpp.Internal: bufferSrc :: Source (ErrorT XmppFailure IO) o -> IO (ConduitM i o (ErrorT XmppFailure IO) ())
+ Network.Xmpp.Internal: catchPush :: IO () -> IO (Either XmppFailure ())
+ Network.Xmpp.Internal: checkHostName :: Text -> Maybe Text
+ Network.Xmpp.Internal: class IQRequestClass a where data family IQResponseType a
+ Network.Xmpp.Internal: client :: (MonadIO m, CPRG rng) => ClientParams -> rng -> Backend -> m Context
+ Network.Xmpp.Internal: closeConnection :: Session -> IO ()
+ Network.Xmpp.Internal: closeStreams :: Stream -> IO ()
+ Network.Xmpp.Internal: closeStreams' :: StateT StreamState IO ()
+ Network.Xmpp.Internal: conf :: Session -> SessionConfiguration
+ Network.Xmpp.Internal: connPersist :: TMVar (ByteString -> IO a) -> IO ()
+ Network.Xmpp.Internal: connect :: HostName -> StreamConfiguration -> ErrorT XmppFailure IO (Maybe StreamHandle)
+ Network.Xmpp.Internal: connectSrv :: ResolvConf -> String -> ErrorT XmppFailure IO (Maybe Handle)
+ Network.Xmpp.Internal: connectTcp :: [(HostName, PortID)] -> IO (Maybe Handle)
+ Network.Xmpp.Internal: connectionClosedHandler :: EventHandlers -> XmppFailure -> IO ()
+ Network.Xmpp.Internal: createStream :: HostName -> StreamConfiguration -> ErrorT XmppFailure IO (Stream)
+ Network.Xmpp.Internal: data AuthFailure
+ Network.Xmpp.Internal: data ConnectionDetails
+ Network.Xmpp.Internal: data ConnectionState
+ Network.Xmpp.Internal: data EventHandlers
+ Network.Xmpp.Internal: data IMPresence
+ Network.Xmpp.Internal: data IQError
+ Network.Xmpp.Internal: data IQRequest
+ Network.Xmpp.Internal: data IQRequestError
+ Network.Xmpp.Internal: data IQRequestTicket
+ Network.Xmpp.Internal: data IQRequestType
+ Network.Xmpp.Internal: data IQResponse
+ Network.Xmpp.Internal: data IQResult
+ Network.Xmpp.Internal: data IQSendError
+ Network.Xmpp.Internal: data InstantMessage
+ Network.Xmpp.Internal: data Interrupt
+ Network.Xmpp.Internal: data Item
+ Network.Xmpp.Internal: data Jid
+ Network.Xmpp.Internal: data LangTag
+ Network.Xmpp.Internal: data Message
+ Network.Xmpp.Internal: data MessageBody
+ Network.Xmpp.Internal: data MessageError
+ Network.Xmpp.Internal: data MessageSubject
+ Network.Xmpp.Internal: data MessageThread
+ Network.Xmpp.Internal: data MessageType
+ Network.Xmpp.Internal: data Presence
+ Network.Xmpp.Internal: data PresenceError
+ Network.Xmpp.Internal: data PresenceType
+ Network.Xmpp.Internal: data Query
+ Network.Xmpp.Internal: data QueryItem
+ Network.Xmpp.Internal: data Roster
+ Network.Xmpp.Internal: data SaslElement
+ Network.Xmpp.Internal: data SaslError
+ Network.Xmpp.Internal: data SaslFailure
+ Network.Xmpp.Internal: data Session
+ Network.Xmpp.Internal: data SessionConfiguration
+ Network.Xmpp.Internal: data ShowStatus
+ Network.Xmpp.Internal: data StanzaError
+ Network.Xmpp.Internal: data StanzaErrorCondition
+ Network.Xmpp.Internal: data StanzaErrorType
+ Network.Xmpp.Internal: data StreamErrorCondition
+ Network.Xmpp.Internal: data StreamErrorInfo
+ Network.Xmpp.Internal: data Subscription
+ Network.Xmpp.Internal: data Version
+ Network.Xmpp.Internal: data XmppFailure
+ Network.Xmpp.Internal: data XmppTlsError
+ Network.Xmpp.Internal: debugConduit :: (Show o, MonadIO m) => ConduitM o o m b
+ Network.Xmpp.Internal: debugOut :: MonadIO m => ByteString -> m ()
+ Network.Xmpp.Internal: digestMd5 :: Username -> Maybe AuthZID -> Password -> SaslHandler
+ Network.Xmpp.Internal: domainpart :: Jid -> Text
+ Network.Xmpp.Internal: domainpart_ :: Jid -> !NonemptyText
+ Network.Xmpp.Internal: dupSession :: Session -> IO Session
+ Network.Xmpp.Internal: elements :: MonadError XmppFailure m => Conduit Event m Element
+ Network.Xmpp.Internal: enablePresenceTracking :: SessionConfiguration -> Bool
+ Network.Xmpp.Internal: enableRoster :: SessionConfiguration -> Bool
+ Network.Xmpp.Internal: endSession :: Session -> IO ()
+ Network.Xmpp.Internal: errorCondition :: StreamErrorInfo -> !StreamErrorCondition
+ Network.Xmpp.Internal: errorText :: StreamErrorInfo -> !(Maybe (Maybe LangTag, NonemptyText))
+ Network.Xmpp.Internal: errorXml :: StreamErrorInfo -> !(Maybe Element)
+ Network.Xmpp.Internal: eventHandlers :: Session -> TMVar EventHandlers
+ Network.Xmpp.Internal: filterMessages :: (MessageError -> Bool) -> (Message -> Bool) -> Session -> IO (Either MessageError Message)
+ Network.Xmpp.Internal: filterMessagesA :: (Annotated MessageError -> Bool) -> (Annotated Message -> Bool) -> Session -> IO (Either (Annotated MessageError) (Annotated Message))
+ Network.Xmpp.Internal: fixDnsResult :: Either e a -> Maybe a
+ Network.Xmpp.Internal: flattenAttrs :: [(Name, [Content])] -> [(Name, Text)]
+ Network.Xmpp.Internal: fromItem :: Item -> QueryItem
+ Network.Xmpp.Internal: fromNonempty :: NonemptyText -> Text
+ Network.Xmpp.Internal: getAnnotation :: Typeable b => Annotated a -> Maybe b
+ Network.Xmpp.Internal: getFeatures :: Session -> IO StreamFeatures
+ Network.Xmpp.Internal: getIM :: Message -> Maybe InstantMessage
+ Network.Xmpp.Internal: getIMPresence :: Presence -> Maybe IMPresence
+ Network.Xmpp.Internal: getJid :: Session -> IO (Maybe Jid)
+ Network.Xmpp.Internal: getMessage :: Session -> IO Message
+ Network.Xmpp.Internal: getMessageA :: Session -> IO (Annotated Message)
+ Network.Xmpp.Internal: getRoster :: Session -> IO Roster
+ Network.Xmpp.Internal: getRosterSTM :: Session -> STM Roster
+ Network.Xmpp.Internal: getStanza :: Session -> IO (Stanza, [Annotation])
+ Network.Xmpp.Internal: getStanzaChan :: Session -> TChan (Stanza, [Annotation])
+ Network.Xmpp.Internal: handleRoster :: Maybe Jid -> TVar Roster -> StanzaHandler
+ Network.Xmpp.Internal: handleToStreamHandle :: Handle -> StreamHandle
+ Network.Xmpp.Internal: hashToken :: Hash ctx hash => hash
+ Network.Xmpp.Internal: idGenerator :: Session -> IO Text
+ Network.Xmpp.Internal: imBody :: InstantMessage -> [MessageBody]
+ Network.Xmpp.Internal: imPresence :: IMPresence
+ Network.Xmpp.Internal: imSubject :: InstantMessage -> [MessageSubject]
+ Network.Xmpp.Internal: imThread :: InstantMessage -> Maybe MessageThread
+ Network.Xmpp.Internal: imToElements :: InstantMessage -> [Element]
+ Network.Xmpp.Internal: initRoster :: Session -> IO ()
+ Network.Xmpp.Internal: instantMessage :: InstantMessage
+ Network.Xmpp.Internal: iqErrorAttributes :: IQError -> ![ExtendedAttribute]
+ Network.Xmpp.Internal: iqErrorFrom :: IQError -> !(Maybe Jid)
+ Network.Xmpp.Internal: iqErrorID :: IQError -> !Text
+ Network.Xmpp.Internal: iqErrorLangTag :: IQError -> !(Maybe LangTag)
+ Network.Xmpp.Internal: iqErrorPayload :: IQError -> !(Maybe Element)
+ Network.Xmpp.Internal: iqErrorStanzaError :: IQError -> !StanzaError
+ Network.Xmpp.Internal: iqErrorTo :: IQError -> !(Maybe Jid)
+ Network.Xmpp.Internal: iqHandlers :: Session -> TVar IQHandlers
+ Network.Xmpp.Internal: iqRequestAnnotations :: IQRequestTicket -> [Annotation]
+ Network.Xmpp.Internal: iqRequestAttributes :: IQRequest -> ![ExtendedAttribute]
+ Network.Xmpp.Internal: iqRequestBody :: IQRequestTicket -> IQRequest
+ Network.Xmpp.Internal: iqRequestFrom :: IQRequest -> !(Maybe Jid)
+ Network.Xmpp.Internal: iqRequestID :: IQRequest -> !Text
+ Network.Xmpp.Internal: iqRequestLangTag :: IQRequest -> !(Maybe LangTag)
+ Network.Xmpp.Internal: iqRequestPayload :: IQRequest -> !Element
+ Network.Xmpp.Internal: iqRequestTo :: IQRequest -> !(Maybe Jid)
+ Network.Xmpp.Internal: iqRequestType :: IQRequest -> !IQRequestType
+ Network.Xmpp.Internal: iqResultAttributes :: IQResult -> ![ExtendedAttribute]
+ Network.Xmpp.Internal: iqResultFrom :: IQResult -> !(Maybe Jid)
+ Network.Xmpp.Internal: iqResultID :: IQResult -> !Text
+ Network.Xmpp.Internal: iqResultLangTag :: IQResult -> !(Maybe LangTag)
+ Network.Xmpp.Internal: iqResultPayload :: IQResult -> !(Maybe Element)
+ Network.Xmpp.Internal: iqResultTo :: IQResult -> !(Maybe Jid)
+ Network.Xmpp.Internal: isBare :: Jid -> Bool
+ Network.Xmpp.Internal: isFull :: Jid -> Bool
+ Network.Xmpp.Internal: items :: Roster -> Map Jid Item
+ Network.Xmpp.Internal: jid :: QuasiQuoter
+ Network.Xmpp.Internal: jidFromText :: Text -> Maybe Jid
+ Network.Xmpp.Internal: jidFromTexts :: Maybe Text -> Text -> Maybe Text -> Maybe Jid
+ Network.Xmpp.Internal: jidQ :: QuasiQuoter
+ Network.Xmpp.Internal: jidToText :: Jid -> Text
+ Network.Xmpp.Internal: jidToTexts :: Jid -> (Maybe Text, Text, Maybe Text)
+ Network.Xmpp.Internal: killStream :: Stream -> IO (Either XmppFailure ())
+ Network.Xmpp.Internal: langTagFromText :: Text -> Maybe LangTag
+ Network.Xmpp.Internal: langTagQ :: QuasiQuoter
+ Network.Xmpp.Internal: langTagToText :: LangTag -> Text
+ Network.Xmpp.Internal: listenIQ :: IQRequestType -> Text -> Session -> IO (Either (STM IQRequestTicket) (STM IQRequestTicket))
+ Network.Xmpp.Internal: lmb :: [t] -> Maybe [t]
+ Network.Xmpp.Internal: localpart :: Jid -> Maybe Text
+ Network.Xmpp.Internal: localpart_ :: Jid -> !(Maybe NonemptyText)
+ Network.Xmpp.Internal: majorVersion :: Version -> !Integer
+ Network.Xmpp.Internal: makeNonce :: IO ByteString
+ Network.Xmpp.Internal: mbl :: Maybe [a] -> [a]
+ Network.Xmpp.Internal: merge :: [ByteString] -> ByteString
+ Network.Xmpp.Internal: message :: Message
+ Network.Xmpp.Internal: messageAttributes :: Message -> ![ExtendedAttribute]
+ Network.Xmpp.Internal: messageError :: MessageError
+ Network.Xmpp.Internal: messageErrorAttributes :: MessageError -> ![ExtendedAttribute]
+ Network.Xmpp.Internal: messageErrorFrom :: MessageError -> !(Maybe Jid)
+ Network.Xmpp.Internal: messageErrorID :: MessageError -> !(Maybe Text)
+ Network.Xmpp.Internal: messageErrorLangTag :: MessageError -> !(Maybe LangTag)
+ Network.Xmpp.Internal: messageErrorPayload :: MessageError -> ![Element]
+ Network.Xmpp.Internal: messageErrorS :: Stanza
+ Network.Xmpp.Internal: messageErrorStanzaError :: MessageError -> !StanzaError
+ Network.Xmpp.Internal: messageErrorTo :: MessageError -> !(Maybe Jid)
+ Network.Xmpp.Internal: messageFrom :: Message -> !(Maybe Jid)
+ Network.Xmpp.Internal: messageID :: Message -> !(Maybe Text)
+ Network.Xmpp.Internal: messageLangTag :: Message -> !(Maybe LangTag)
+ Network.Xmpp.Internal: messagePayload :: Message -> ![Element]
+ Network.Xmpp.Internal: messageS :: Stanza
+ Network.Xmpp.Internal: messageTo :: Message -> !(Maybe Jid)
+ Network.Xmpp.Internal: messageType :: Message -> !MessageType
+ Network.Xmpp.Internal: minorVersion :: Version -> !Integer
+ Network.Xmpp.Internal: mkBackend :: StreamHandle -> Backend
+ Network.Xmpp.Internal: mkReadBuffer :: IO ByteString -> IO (Int -> IO ByteString)
+ Network.Xmpp.Internal: mkStanzaError :: StanzaErrorCondition -> StanzaError
+ Network.Xmpp.Internal: mkStream :: StreamState -> IO Stream
+ Network.Xmpp.Internal: modifyHandlers :: (EventHandlers -> EventHandlers) -> Session -> IO ()
+ Network.Xmpp.Internal: newSession :: Stream -> SessionConfiguration -> HostName -> Maybe (ConnectionState -> [SaslHandler], Maybe Text) -> IO (Either XmppFailure Session)
+ Network.Xmpp.Internal: newStanzaID :: Session -> IO Text
+ Network.Xmpp.Internal: newtype NonemptyText
+ Network.Xmpp.Internal: nodeprepProfile :: StringPrepProfile
+ Network.Xmpp.Internal: nonAsciiSpaces :: Set Char
+ Network.Xmpp.Internal: nonEmpty :: Text -> Maybe NonemptyText
+ Network.Xmpp.Internal: normalizePassword :: Text -> Maybe Text
+ Network.Xmpp.Internal: normalizeUsername :: Text -> Maybe Text
+ Network.Xmpp.Internal: nsHack :: Element -> Element
+ Network.Xmpp.Internal: onConnectionClosed :: SessionConfiguration -> Session -> XmppFailure -> IO ()
+ Network.Xmpp.Internal: onPresenceChange :: SessionConfiguration -> Maybe (Jid -> PeerStatus -> PeerStatus -> IO ())
+ Network.Xmpp.Internal: openElementFromEvents :: StreamSink Element
+ Network.Xmpp.Internal: openElementToEvents :: Element -> [Event]
+ Network.Xmpp.Internal: pairs :: ByteString -> Either String Pairs
+ Network.Xmpp.Internal: parseJid :: String -> Jid
+ Network.Xmpp.Internal: parseLangTag :: String -> LangTag
+ Network.Xmpp.Internal: pickleElem :: PU [Node] a -> a -> Element
+ Network.Xmpp.Internal: pickleRequest :: IQRequestClass a => PU Element a
+ Network.Xmpp.Internal: pickleResponse :: IQRequestClass a => PU [Element] (IQResponseType a)
+ Network.Xmpp.Internal: plain :: Username -> Maybe AuthZID -> Password -> SaslHandler
+ Network.Xmpp.Internal: plugins :: SessionConfiguration -> [Plugin]
+ Network.Xmpp.Internal: prepCredentials :: Text -> Maybe Text -> Text -> ErrorT AuthFailure (StateT StreamState IO) (Text, Maybe Text, Text)
+ Network.Xmpp.Internal: presTo :: Presence -> Jid -> Presence
+ Network.Xmpp.Internal: presence :: Presence
+ Network.Xmpp.Internal: presenceAttributes :: Presence -> ![ExtendedAttribute]
+ Network.Xmpp.Internal: presenceErrorAttributes :: PresenceError -> ![ExtendedAttribute]
+ Network.Xmpp.Internal: presenceErrorFrom :: PresenceError -> !(Maybe Jid)
+ Network.Xmpp.Internal: presenceErrorID :: PresenceError -> !(Maybe Text)
+ Network.Xmpp.Internal: presenceErrorLangTag :: PresenceError -> !(Maybe LangTag)
+ Network.Xmpp.Internal: presenceErrorPayload :: PresenceError -> ![Element]
+ Network.Xmpp.Internal: presenceErrorStanzaError :: PresenceError -> !StanzaError
+ Network.Xmpp.Internal: presenceErrorTo :: PresenceError -> !(Maybe Jid)
+ Network.Xmpp.Internal: presenceFrom :: Presence -> !(Maybe Jid)
+ Network.Xmpp.Internal: presenceID :: Presence -> !(Maybe Text)
+ Network.Xmpp.Internal: presenceLangTag :: Presence -> !(Maybe LangTag)
+ Network.Xmpp.Internal: presenceOffline :: Presence
+ Network.Xmpp.Internal: presenceOnline :: Presence
+ Network.Xmpp.Internal: presencePayload :: Presence -> ![Element]
+ Network.Xmpp.Internal: presenceRef :: Session -> TVar Peers
+ Network.Xmpp.Internal: presenceS :: Stanza
+ Network.Xmpp.Internal: presenceSubscribe :: Jid -> Presence
+ Network.Xmpp.Internal: presenceSubscribed :: Jid -> Presence
+ Network.Xmpp.Internal: presenceTo :: Presence -> !(Maybe Jid)
+ Network.Xmpp.Internal: presenceType :: Presence -> !PresenceType
+ Network.Xmpp.Internal: presenceUnsubscribe :: Jid -> Presence
+ Network.Xmpp.Internal: presenceUnsubscribed :: Jid -> Presence
+ Network.Xmpp.Internal: primaryTag :: LangTag -> !Text
+ Network.Xmpp.Internal: priority :: IMPresence -> Maybe Int
+ Network.Xmpp.Internal: pullChallenge :: ErrorT AuthFailure (StateT StreamState IO) (Maybe ByteString)
+ Network.Xmpp.Internal: pullElement :: StateT StreamState IO (Either XmppFailure Element)
+ Network.Xmpp.Internal: pullFinalMessage :: ErrorT AuthFailure (StateT StreamState IO) (Maybe ByteString)
+ Network.Xmpp.Internal: pullMessage :: Session -> IO (Either MessageError Message)
+ Network.Xmpp.Internal: pullMessageA :: Session -> IO (Either (Annotated MessageError) (Annotated Message))
+ Network.Xmpp.Internal: pullPresence :: Session -> IO (Either PresenceError Presence)
+ Network.Xmpp.Internal: pullPresenceA :: Session -> IO (Either (Annotated PresenceError) (Annotated Presence))
+ Network.Xmpp.Internal: pullSaslElement :: ErrorT AuthFailure (StateT StreamState IO) SaslElement
+ Network.Xmpp.Internal: pullSuccess :: ErrorT AuthFailure (StateT StreamState IO) (Maybe Text)
+ Network.Xmpp.Internal: pullUnpickle :: PU [Node] a -> StateT StreamState IO (Either XmppFailure a)
+ Network.Xmpp.Internal: pushElement :: Element -> StateT StreamState IO (Either XmppFailure ())
+ Network.Xmpp.Internal: pushOpenElement :: Element -> StateT StreamState IO (Either XmppFailure ())
+ Network.Xmpp.Internal: pushXmlDecl :: StateT StreamState IO (Either XmppFailure ())
+ Network.Xmpp.Internal: qiApproved :: QueryItem -> Maybe Bool
+ Network.Xmpp.Internal: qiAsk :: QueryItem -> Bool
+ Network.Xmpp.Internal: qiGroups :: QueryItem -> [Text]
+ Network.Xmpp.Internal: qiJid :: QueryItem -> Jid
+ Network.Xmpp.Internal: qiName :: QueryItem -> Maybe Text
+ Network.Xmpp.Internal: qiSubscription :: QueryItem -> Maybe Subscription
+ Network.Xmpp.Internal: queryItems :: Query -> [QueryItem]
+ Network.Xmpp.Internal: queryVer :: Query -> Maybe Text
+ Network.Xmpp.Internal: quote :: ByteString -> ByteString
+ Network.Xmpp.Internal: readMaybe_ :: Read a => String -> Maybe a
+ Network.Xmpp.Internal: readWorker :: (Stanza -> IO ()) -> (XmppFailure -> IO ()) -> TMVar Stream -> IO a
+ Network.Xmpp.Internal: readerThread :: Session -> ThreadId
+ Network.Xmpp.Internal: reconnect :: Integer -> Session -> IO (Bool, [XmppFailure])
+ Network.Xmpp.Internal: reconnect' :: Session -> IO Integer
+ Network.Xmpp.Internal: reconnectNow :: Session -> IO (Maybe XmppFailure)
+ Network.Xmpp.Internal: reconnectWait :: Session -> TVar Int
+ Network.Xmpp.Internal: renderElement :: Element -> ByteString
+ Network.Xmpp.Internal: renderOpenElement :: Element -> ByteString
+ Network.Xmpp.Internal: requestNamespace :: IQRequestClass a => a -> Text
+ Network.Xmpp.Internal: requestType :: IQRequestClass a => a -> IQRequestType
+ Network.Xmpp.Internal: resolvAndConnectTcp :: ResolvSeed -> Domain -> Int -> IO (Maybe Handle)
+ Network.Xmpp.Internal: resolvSrvsAndConnectTcp :: ResolvSeed -> [(Domain, Int)] -> IO (Maybe Handle)
+ Network.Xmpp.Internal: resourcepart :: Jid -> Maybe Text
+ Network.Xmpp.Internal: resourcepart_ :: Jid -> !(Maybe NonemptyText)
+ Network.Xmpp.Internal: resourceprepProfile :: StringPrepProfile
+ Network.Xmpp.Internal: respond :: Maybe ByteString -> ErrorT AuthFailure (StateT StreamState IO) ()
+ Network.Xmpp.Internal: restartStream :: StateT StreamState IO (Either XmppFailure ())
+ Network.Xmpp.Internal: rethrowErrorCall :: IO a -> IO a
+ Network.Xmpp.Internal: retrieveRoster :: Maybe Roster -> Session -> IO (Maybe Roster)
+ Network.Xmpp.Internal: riApproved :: Item -> Bool
+ Network.Xmpp.Internal: riAsk :: Item -> Bool
+ Network.Xmpp.Internal: riGroups :: Item -> [Text]
+ Network.Xmpp.Internal: riJid :: Item -> Jid
+ Network.Xmpp.Internal: riName :: Item -> Maybe Text
+ Network.Xmpp.Internal: riSubscription :: Item -> Subscription
+ Network.Xmpp.Internal: rosterAdd :: Jid -> Maybe Text -> [Text] -> Session -> IO (Either IQSendError (Annotated IQResponse))
+ Network.Xmpp.Internal: rosterRef :: Session -> TVar Roster
+ Network.Xmpp.Internal: rosterRemove :: Jid -> Session -> IO Bool
+ Network.Xmpp.Internal: rosterSet :: Jid -> Maybe Text -> [Text] -> Session -> IO (Either IQSendError (Annotated IQResponse))
+ Network.Xmpp.Internal: runConnectionClosedHandler :: Session -> XmppFailure -> IO ()
+ Network.Xmpp.Internal: runEventsSink :: Sink Event (ErrorT XmppFailure IO) b -> StateT StreamState IO (Either XmppFailure b)
+ Network.Xmpp.Internal: runHandler :: (EventHandlers -> IO a) -> Session -> IO a
+ Network.Xmpp.Internal: runIQHandler :: IQRequestClass a => IQRequestHandler a -> Session -> IO ()
+ Network.Xmpp.Internal: sRealm :: Session -> HostName
+ Network.Xmpp.Internal: sSaslCredentials :: Session -> Maybe (ConnectionState -> [SaslHandler], Maybe Text)
+ Network.Xmpp.Internal: sanitizeIM :: InstantMessage -> InstantMessage
+ Network.Xmpp.Internal: saslFailureCondition :: SaslFailure -> SaslError
+ Network.Xmpp.Internal: saslFailureText :: SaslFailure -> Maybe (Maybe LangTag, Text)
+ Network.Xmpp.Internal: saslFromJust :: Maybe a -> ErrorT AuthFailure (StateT StreamState IO) a
+ Network.Xmpp.Internal: saslInit :: Text -> Maybe ByteString -> ErrorT AuthFailure (StateT StreamState IO) ()
+ Network.Xmpp.Internal: saslInitE :: Text -> Maybe Text -> Element
+ Network.Xmpp.Internal: saslPrepQuery :: StringPrepProfile
+ Network.Xmpp.Internal: saslPrepStore :: StringPrepProfile
+ Network.Xmpp.Internal: saslResponseE :: Maybe Text -> Element
+ Network.Xmpp.Internal: scram :: Hash ctx hash => hash -> Text -> Maybe Text -> Text -> ErrorT AuthFailure (StateT StreamState IO) ()
+ Network.Xmpp.Internal: scramSha1 :: Username -> Maybe AuthZID -> Password -> SaslHandler
+ Network.Xmpp.Internal: semWrite :: WriteSemaphore -> ByteString -> IO (Either XmppFailure ())
+ Network.Xmpp.Internal: sendIQ :: Maybe Integer -> Maybe Jid -> IQRequestType -> Maybe LangTag -> Element -> [ExtendedAttribute] -> Session -> IO (Either XmppFailure (STM (Maybe (Annotated IQResponse))))
+ Network.Xmpp.Internal: sendIQ' :: Maybe Integer -> Maybe Jid -> IQRequestType -> Maybe LangTag -> Element -> [ExtendedAttribute] -> Session -> IO (Either IQSendError IQResponse)
+ Network.Xmpp.Internal: sendIQA' :: Maybe Integer -> Maybe Jid -> IQRequestType -> Maybe LangTag -> Element -> [ExtendedAttribute] -> Session -> IO (Either IQSendError (Annotated IQResponse))
+ Network.Xmpp.Internal: sendIQRequest :: (IQRequestClass a, MonadError IQRequestError m, MonadIO m) => Maybe Integer -> Maybe Jid -> a -> Session -> m (Either IQError (IQResponseType a))
+ Network.Xmpp.Internal: sendMessage :: Message -> Session -> IO (Either XmppFailure ())
+ Network.Xmpp.Internal: sendPresence :: Presence -> Session -> IO (Either XmppFailure ())
+ Network.Xmpp.Internal: sendRawStanza :: Stanza -> Session -> IO (Either XmppFailure ())
+ Network.Xmpp.Internal: sendStanza :: Stanza -> Session -> IO (Either XmppFailure ())
+ Network.Xmpp.Internal: sendStanza' :: Session -> Stanza -> IO (Either XmppFailure ())
+ Network.Xmpp.Internal: session :: HostName -> AuthData -> SessionConfiguration -> IO (Either XmppFailure Session)
+ Network.Xmpp.Internal: sessionStanzaIDs :: SessionConfiguration -> IO (IO Text)
+ Network.Xmpp.Internal: sessionStreamConfiguration :: SessionConfiguration -> StreamConfiguration
+ Network.Xmpp.Internal: setConnectionClosedHandler :: (XmppFailure -> Session -> IO ()) -> Session -> IO ()
+ Network.Xmpp.Internal: showPort :: PortID -> String
+ Network.Xmpp.Internal: showStatus :: IMPresence -> Maybe ShowStatus
+ Network.Xmpp.Internal: simpleAuth :: Username -> Password -> AuthData
+ Network.Xmpp.Internal: simpleIM :: Jid -> Text -> Message
+ Network.Xmpp.Internal: sourceStreamHandle :: (MonadIO m, MonadError XmppFailure m) => StreamHandle -> ConduitM i ByteString m ()
+ Network.Xmpp.Internal: srvLookup :: Text -> ResolvSeed -> ErrorT XmppFailure IO (Maybe [(Domain, Int)])
+ Network.Xmpp.Internal: stanzaCh :: Session -> TChan (Stanza, [Annotation])
+ Network.Xmpp.Internal: stanzaErrorApplicationSpecificCondition :: StanzaError -> Maybe Element
+ Network.Xmpp.Internal: stanzaErrorCondition :: StanzaError -> StanzaErrorCondition
+ Network.Xmpp.Internal: stanzaErrorText :: StanzaError -> Maybe (Maybe LangTag, NonemptyText)
+ Network.Xmpp.Internal: stanzaErrorType :: StanzaError -> StanzaErrorType
+ Network.Xmpp.Internal: startStream :: StateT StreamState IO (Either XmppFailure ())
+ Network.Xmpp.Internal: startThreadsWith :: TMVar (ByteString -> IO (Either XmppFailure ())) -> (Stanza -> IO ()) -> TMVar EventHandlers -> Stream -> IO (Either XmppFailure (IO (), TMVar Stream, ThreadId))
+ Network.Xmpp.Internal: starttlsE :: Element
+ Network.Xmpp.Internal: status :: IMPresence -> Maybe Text
+ Network.Xmpp.Internal: stopThreads :: Session -> IO ()
+ Network.Xmpp.Internal: streamRef :: Session -> TMVar Stream
+ Network.Xmpp.Internal: streamS :: Maybe Jid -> StreamSink (Either Element (Text, Maybe Jid, Maybe Jid, Maybe Text, Maybe LangTag, StreamFeatures))
+ Network.Xmpp.Internal: streamState :: Session -> STM ConnectionState
+ Network.Xmpp.Internal: streamUnpickleElem :: PU [Node] a -> Element -> StreamSink a
+ Network.Xmpp.Internal: subjectContent :: MessageSubject -> Text
+ Network.Xmpp.Internal: subjectLang :: MessageSubject -> Maybe LangTag
+ Network.Xmpp.Internal: subtags :: LangTag -> ![Text]
+ Network.Xmpp.Internal: text :: NonemptyText -> Text
+ Network.Xmpp.Internal: threadID :: MessageThread -> Text
+ Network.Xmpp.Internal: threadParent :: MessageThread -> Maybe Text
+ Network.Xmpp.Internal: throwOutJunk :: Monad m => ConduitM Event a m ()
+ Network.Xmpp.Internal: timeout :: Maybe Integer
+ Network.Xmpp.Internal: tlsinit :: (MonadIO m, MonadIO m1) => ClientParams -> Backend -> m (Source m1 ByteString, Sink ByteString m1 (), ByteString -> IO (), Int -> m1 ByteString, Context)
+ Network.Xmpp.Internal: toBare :: Jid -> Jid
+ Network.Xmpp.Internal: toItem :: QueryItem -> Item
+ Network.Xmpp.Internal: toPairs :: ByteString -> ErrorT AuthFailure (StateT StreamState IO) Pairs
+ Network.Xmpp.Internal: toSpace :: Char -> Text
+ Network.Xmpp.Internal: tryIOError :: IO a -> IO (Either IOError a)
+ Network.Xmpp.Internal: type Annotated a = (a, [Annotation])
+ Network.Xmpp.Internal: type AuthData = Maybe (ConnectionState -> [SaslHandler], Maybe Resource)
+ Network.Xmpp.Internal: type AuthZID = Text
+ Network.Xmpp.Internal: type ExtendedAttribute = (Name, Text)
+ Network.Xmpp.Internal: type IQHandlers = (Map (IQRequestType, Text) (TChan IQRequestTicket), Map Text (Either (Maybe Jid) Jid, TMVar (Maybe (Annotated IQResponse))))
+ Network.Xmpp.Internal: type IQRequestHandler a = a -> IO (Either StanzaError (IQResponseType a))
+ Network.Xmpp.Internal: type Pairs = [(ByteString, ByteString)]
+ Network.Xmpp.Internal: type Password = Text
+ Network.Xmpp.Internal: type Resource = Text
+ Network.Xmpp.Internal: type StanzaHandler = (Stanza -> IO (Either XmppFailure ())) -> Stanza -> [Annotation] -> IO [(Stanza, [Annotation])]
+ Network.Xmpp.Internal: type StreamSink a = ConduitM Event Void (ErrorT XmppFailure IO) a
+ Network.Xmpp.Internal: type Username = Text
+ Network.Xmpp.Internal: type WriteSemaphore = TMVar (ByteString -> IO (Either XmppFailure ()))
+ Network.Xmpp.Internal: unlistenIQ :: IQRequestType -> Text -> Session -> IO ()
+ Network.Xmpp.Internal: unpickleElem :: PU [Node] a -> Element -> Either UnpickleError a
+ Network.Xmpp.Internal: ver :: Roster -> Maybe Text
+ Network.Xmpp.Internal: versionFromText :: Text -> Maybe Version
+ Network.Xmpp.Internal: waitForMessage :: (Message -> Bool) -> Session -> IO Message
+ Network.Xmpp.Internal: waitForMessageA :: (Annotated Message -> Bool) -> Session -> IO (Annotated Message)
+ Network.Xmpp.Internal: waitForMessageError :: (MessageError -> Bool) -> Session -> IO MessageError
+ Network.Xmpp.Internal: waitForMessageErrorA :: (Annotated MessageError -> Bool) -> Session -> IO (Annotated MessageError)
+ Network.Xmpp.Internal: waitForPresence :: (Presence -> Bool) -> Session -> IO Presence
+ Network.Xmpp.Internal: waitForPresenceA :: (Annotated Presence -> Bool) -> Session -> IO (Annotated Presence)
+ Network.Xmpp.Internal: waitForStream :: Session -> IO ()
+ Network.Xmpp.Internal: withConnection :: (Stream -> IO (b, Stream)) -> Session -> IO (Either XmppFailure b)
+ Network.Xmpp.Internal: withIM :: Message -> InstantMessage -> Message
+ Network.Xmpp.Internal: withIMPresence :: IMPresence -> Presence -> Presence
+ Network.Xmpp.Internal: withStream' :: StateT StreamState IO a -> Stream -> IO a
+ Network.Xmpp.Internal: withTMVar :: TMVar a -> (a -> IO (c, a)) -> IO c
+ Network.Xmpp.Internal: wrapExceptions :: IO a -> IO (Either XmppFailure a)
+ Network.Xmpp.Internal: wrapIOException :: MonadIO m => IO a -> m (Either XmppFailure a)
+ Network.Xmpp.Internal: writeSemaphore :: Session -> WriteSemaphore
+ Network.Xmpp.Internal: xmlLang :: Name
+ Network.Xmpp.Internal: xmppDefaultParams :: ClientParams
+ Network.Xmpp.Internal: xmppDefaultParamsStrong :: ClientParams
+ Network.Xmpp.Internal: xmppNoStream :: StreamState
+ Network.Xmpp.Internal: xmppSasl :: [SaslHandler] -> Stream -> IO (Either XmppFailure (Maybe AuthFailure))
+ Network.Xmpp.Internal: xorBS :: ByteString -> ByteString -> ByteString
+ Network.Xmpp.Internal: xpChallenge :: PU [Node] (Maybe Text)
+ Network.Xmpp.Internal: xpExtendedAttrs :: PU [Attribute] [ExtendedAttribute]
+ Network.Xmpp.Internal: xpFailure :: PU [Node] SaslFailure
+ Network.Xmpp.Internal: xpIM :: PU [Element] InstantMessage
+ Network.Xmpp.Internal: xpIMPresence :: PU [Element] IMPresence
+ Network.Xmpp.Internal: xpIQError :: PU [Node] IQError
+ Network.Xmpp.Internal: xpIQRequest :: PU [Node] IQRequest
+ Network.Xmpp.Internal: xpIQRequestType :: PU Text IQRequestType
+ Network.Xmpp.Internal: xpIQResult :: PU [Node] IQResult
+ Network.Xmpp.Internal: xpItems :: PU [Node] [QueryItem]
+ Network.Xmpp.Internal: xpJid :: PU Text Jid
+ Network.Xmpp.Internal: xpLang :: PU Text LangTag
+ Network.Xmpp.Internal: xpLangTag :: PU [Attribute] (Maybe LangTag)
+ Network.Xmpp.Internal: xpMessage :: PU [Node] (Message)
+ Network.Xmpp.Internal: xpMessageBody :: PU [Element] [MessageBody]
+ Network.Xmpp.Internal: xpMessageError :: PU [Node] (MessageError)
+ Network.Xmpp.Internal: xpMessageSubject :: PU [Element] [MessageSubject]
+ Network.Xmpp.Internal: xpMessageThread :: PU [Element] (Maybe MessageThread)
+ Network.Xmpp.Internal: xpMessageType :: PU Text MessageType
+ Network.Xmpp.Internal: xpNodeElem :: PU [Node] a -> PU Element a
+ Network.Xmpp.Internal: xpNonemptyText :: PU Text NonemptyText
+ Network.Xmpp.Internal: xpPresence :: PU [Node] Presence
+ Network.Xmpp.Internal: xpPresenceError :: PU [Node] PresenceError
+ Network.Xmpp.Internal: xpPresenceType :: PU Text PresenceType
+ Network.Xmpp.Internal: xpQuery :: PU [Node] Query
+ Network.Xmpp.Internal: xpSaslElement :: PU [Node] SaslElement
+ Network.Xmpp.Internal: xpSaslError :: PU Text SaslError
+ Network.Xmpp.Internal: xpShow :: PU Text ShowStatus
+ Network.Xmpp.Internal: xpStanza :: PU [Node] Stanza
+ Network.Xmpp.Internal: xpStanzaError :: PU [Node] StanzaError
+ Network.Xmpp.Internal: xpStanzaErrorCondition :: PU [Node] StanzaErrorCondition
+ Network.Xmpp.Internal: xpStanzaErrorType :: PU Text StanzaErrorType
+ Network.Xmpp.Internal: xpStream :: PU [Node] (Text, Maybe Jid, Maybe Jid, Maybe Text, Maybe LangTag)
+ Network.Xmpp.Internal: xpStreamError :: PU [Node] StreamErrorInfo
+ Network.Xmpp.Internal: xpStreamErrorCondition :: PU Text StreamErrorCondition
+ Network.Xmpp.Internal: xpStreamFeatures :: PU [Node] StreamFeatures
+ Network.Xmpp.Internal: xpStreamStanza :: PU [Node] (Either StreamErrorInfo Stanza)
+ Network.Xmpp.Internal: xpSubscription :: PU Text Subscription
+ Network.Xmpp.Internal: xpSuccess :: PU [Node] (Maybe Text)
+ Network.Xmpp.Internal: zeroHandle :: StreamHandle
+ Network.Xmpp.Internal: zeroSource :: Source (ErrorT XmppFailure IO) a
+ Network.Xmpp.Lens: _IQError :: Prism Stanza IQError
+ Network.Xmpp.Lens: _IQRequest :: Prism Stanza IQRequest
+ Network.Xmpp.Lens: _IQResult :: Prism Stanza IQResult
+ Network.Xmpp.Lens: _JidText :: Prism Text Jid
+ Network.Xmpp.Lens: _Message :: Prism Stanza Message
+ Network.Xmpp.Lens: _MessageError :: Prism Stanza MessageError
+ Network.Xmpp.Lens: _Presence :: Prism Stanza Presence
+ Network.Xmpp.Lens: _PresenceError :: Prism Stanza PresenceError
+ Network.Xmpp.Lens: _isBare :: Prism Jid Jid
+ Network.Xmpp.Lens: _isFull :: Prism Jid Jid
+ Network.Xmpp.Lens: clientServerIdentificationL :: Lens ClientParams (String, ByteString)
+ Network.Xmpp.Lens: clientSupportedL :: Lens ClientParams Supported
+ Network.Xmpp.Lens: clientUseServerNameIndicationL :: Lens ClientParams Bool
+ Network.Xmpp.Lens: mkIso :: (a -> b) -> (b -> a) -> Iso a b
+ Network.Xmpp.Lens: mkLens :: (a -> b) -> (b -> a -> a) -> Lens a b
+ Network.Xmpp.Lens: onPresenceChangeL :: Lens SessionConfiguration (Maybe (Jid -> PeerStatus -> PeerStatus -> IO ()))
+ Network.Xmpp.Lens: prism' :: (b -> s) -> (s -> Maybe b) -> Prism s b
+ Network.Xmpp.Lens: supportedCiphersL :: Lens Supported [Cipher]
+ Network.Xmpp.Lens: supportedVersionsL :: Lens Supported [Version]
+ Network.Xmpp.Lens: tlsServerIdentificationL :: Lens SessionConfiguration (String, ByteString)
+ Network.Xmpp.Lens: tlsSupportedCiphersL :: Lens SessionConfiguration [Cipher]
+ Network.Xmpp.Lens: tlsSupportedVersionsL :: Lens SessionConfiguration [Version]
+ Network.Xmpp.Lens: tlsUseNameIndicationL :: Lens SessionConfiguration Bool
+ Network.Xmpp.Lens: type Iso a b = forall p f. (Profunctor p, Functor f) => p b (f b) -> p a (f a)
- Network.Xmpp: IQError :: !Text -> !(Maybe Jid) -> !(Maybe Jid) -> !(Maybe LangTag) -> !StanzaError -> !(Maybe Element) -> IQError
+ Network.Xmpp: IQError :: !Text -> !(Maybe Jid) -> !(Maybe Jid) -> !(Maybe LangTag) -> !StanzaError -> !(Maybe Element) -> ![ExtendedAttribute] -> IQError
- Network.Xmpp: IQRequest :: !Text -> !(Maybe Jid) -> !(Maybe Jid) -> !(Maybe LangTag) -> !IQRequestType -> !Element -> IQRequest
+ Network.Xmpp: IQRequest :: !Text -> !(Maybe Jid) -> !(Maybe Jid) -> !(Maybe LangTag) -> !IQRequestType -> !Element -> ![ExtendedAttribute] -> IQRequest
- Network.Xmpp: IQResult :: !Text -> !(Maybe Jid) -> !(Maybe Jid) -> !(Maybe LangTag) -> !(Maybe Element) -> IQResult
+ Network.Xmpp: IQResult :: !Text -> !(Maybe Jid) -> !(Maybe Jid) -> !(Maybe LangTag) -> !(Maybe Element) -> ![ExtendedAttribute] -> IQResult
- Network.Xmpp: Message :: !(Maybe Text) -> !(Maybe Jid) -> !(Maybe Jid) -> !(Maybe LangTag) -> !MessageType -> ![Element] -> Message
+ Network.Xmpp: Message :: !(Maybe Text) -> !(Maybe Jid) -> !(Maybe Jid) -> !(Maybe LangTag) -> !MessageType -> ![Element] -> ![ExtendedAttribute] -> Message
- Network.Xmpp: MessageError :: !(Maybe Text) -> !(Maybe Jid) -> !(Maybe Jid) -> !(Maybe LangTag) -> !StanzaError -> ![Element] -> MessageError
+ Network.Xmpp: MessageError :: !(Maybe Text) -> !(Maybe Jid) -> !(Maybe Jid) -> !(Maybe LangTag) -> !StanzaError -> ![Element] -> ![ExtendedAttribute] -> MessageError
- Network.Xmpp: Presence :: !(Maybe Text) -> !(Maybe Jid) -> !(Maybe Jid) -> !(Maybe LangTag) -> !PresenceType -> ![Element] -> Presence
+ Network.Xmpp: Presence :: !(Maybe Text) -> !(Maybe Jid) -> !(Maybe Jid) -> !(Maybe LangTag) -> !PresenceType -> ![Element] -> ![ExtendedAttribute] -> Presence
- Network.Xmpp: PresenceError :: !(Maybe Text) -> !(Maybe Jid) -> !(Maybe Jid) -> !(Maybe LangTag) -> !StanzaError -> ![Element] -> PresenceError
+ Network.Xmpp: PresenceError :: !(Maybe Text) -> !(Maybe Jid) -> !(Maybe Jid) -> !(Maybe LangTag) -> !StanzaError -> ![Element] -> ![ExtendedAttribute] -> PresenceError
- Network.Xmpp: SessionConfiguration :: StreamConfiguration -> (Session -> XmppFailure -> IO ()) -> IO (IO Text) -> [Plugin] -> Bool -> SessionConfiguration
+ Network.Xmpp: SessionConfiguration :: StreamConfiguration -> (Session -> XmppFailure -> IO ()) -> IO (IO Text) -> [Plugin] -> Bool -> Bool -> Maybe (Jid -> PeerStatus -> PeerStatus -> IO ()) -> SessionConfiguration
- Network.Xmpp: answerIQ :: IQRequestTicket -> Either StanzaError (Maybe Element) -> IO (Maybe (Either XmppFailure ()))
+ Network.Xmpp: answerIQ :: IQRequestTicket -> Either StanzaError (Maybe Element) -> [ExtendedAttribute] -> IO (Maybe (Either XmppFailure ()))
- Network.Xmpp: sendIQ :: Maybe Integer -> Maybe Jid -> IQRequestType -> Maybe LangTag -> Element -> Session -> IO (Either XmppFailure (STM (Maybe (Annotated IQResponse))))
+ Network.Xmpp: sendIQ :: Maybe Integer -> Maybe Jid -> IQRequestType -> Maybe LangTag -> Element -> [ExtendedAttribute] -> Session -> IO (Either XmppFailure (STM (Maybe (Annotated IQResponse))))
- Network.Xmpp: sendIQ' :: Maybe Integer -> Maybe Jid -> IQRequestType -> Maybe LangTag -> Element -> Session -> IO (Either IQSendError IQResponse)
+ Network.Xmpp: sendIQ' :: Maybe Integer -> Maybe Jid -> IQRequestType -> Maybe LangTag -> Element -> [ExtendedAttribute] -> Session -> IO (Either IQSendError IQResponse)
- Network.Xmpp.Lens: set :: Lens a b -> b -> a -> a
+ Network.Xmpp.Lens: set :: Setter a a' b b' -> b' -> a -> a'
- Network.Xmpp.Lens: view :: Lens a b -> a -> b
+ Network.Xmpp.Lens: view :: FoldLike b a a' b b' -> a -> b
Files
- pontarius-xmpp.cabal +37/−10
- source/Network/Xmpp.hs +6/−0
- source/Network/Xmpp/Concurrent.hs +25/−14
- source/Network/Xmpp/Concurrent/Basic.hs +7/−0
- source/Network/Xmpp/Concurrent/IQ.hs +91/−13
- source/Network/Xmpp/Concurrent/Presence.hs +13/−3
- source/Network/Xmpp/Concurrent/Threads.hs +3/−8
- source/Network/Xmpp/Concurrent/Types.hs +19/−1
- source/Network/Xmpp/IM.hs +9/−0
- source/Network/Xmpp/IM/Presence.hs +8/−7
- source/Network/Xmpp/IM/PresenceTracker.hs +110/−0
- source/Network/Xmpp/IM/PresenceTracker/Types.hs +20/−0
- source/Network/Xmpp/IM/Roster.hs +72/−46
- source/Network/Xmpp/Internal.hs +47/−32
- source/Network/Xmpp/Lens.hs +179/−27
- source/Network/Xmpp/Marshal.hs +122/−102
- source/Network/Xmpp/Sasl/Common.hs +19/−17
- source/Network/Xmpp/Stanza.hs +3/−2
- source/Network/Xmpp/Stream.hs +23/−13
- source/Network/Xmpp/Tls.hs +21/−11
- source/Network/Xmpp/Types.hs +68/−13
- tests/Run.hs +14/−0
- tests/Run/Payload.hs +117/−0
- tests/Tests/Arbitrary/Xmpp.hs +1/−1
pontarius-xmpp.cabal view
@@ -1,5 +1,5 @@ Name: pontarius-xmpp-Version: 0.4.0.2+Version: 0.4.1.0 Cabal-Version: >= 1.9.2 Build-Type: Custom License: BSD3@@ -36,11 +36,6 @@ Library hs-source-dirs: source Exposed: True-- -- The only different between the below two blocks is that the first one caps- -- the range for the `bytestring' package, and that the second one includes- -- `template-haskell' for GHC 7.6.1 and above.- Build-Depends: attoparsec >=0.10.0.3 , base >4 && <5 , base64-bytestring >=0.1.0.0@@ -53,25 +48,29 @@ , cryptohash-cryptoapi >=0.1 , data-default >=0.2 , dns >=0.3.0+ , exceptions >= 0.6 , hslogger >=1.1.0 , iproute >=1.2.4+ , lens-family , lifted-base >=0.1.0.1 , mtl >=2.0.0.0 , network >=2.3.1.0+ , profunctors >= 4 , pureMD5 >=2.1.2.1- , resourcet >=0.3.0 , random >=1.0.0.0+ , resourcet >=0.3.0 , split >=0.1.2.3- , stm >=2.1.2.1+ , stm >=2.4 , stringprep >=1.0.0 , text >=0.11.1.5 , tls >=1.2- , transformers >=0.2.2.0+ , transformers >=0.3 , unbounded-delays >=0.1 , void >=0.5.5- , xml-types >=0.3.1+ , x509-system >=1.4 , xml-conduit >=1.1.0.7 , xml-picklers >=0.3.3+ , xml-types >=0.3.1 If impl(ghc ==7.0.1) { Build-Depends: bytestring >=0.9.1.9 && <=0.9.2.1@@ -95,8 +94,11 @@ , Network.Xmpp.Concurrent.Types , Network.Xmpp.IM.Message , Network.Xmpp.IM.Presence+ , Network.Xmpp.IM.PresenceTracker , Network.Xmpp.IM.Roster , Network.Xmpp.IM.Roster.Types+ , Network.Xmpp.IM.PresenceTracker+ , Network.Xmpp.IM.PresenceTracker.Types , Network.Xmpp.Marshal , Network.Xmpp.Sasl , Network.Xmpp.Sasl.Common@@ -166,6 +168,31 @@ , QuickCheck , derive , quickcheck-instances++Test-Suite runtests+ Type: exitcode-stdio-1.0+ hs-source-dirs: tests+ main-is: Run.hs+ other-modules: Run.Payload+ GHC-Options: -Wall -threaded+ Build-Depends: base+ , HUnit+ , configurator+ , directory+ , filepath+ , hslogger+ , hspec+ , hspec-expectations+ , mtl+ , network+ , pontarius-xmpp+ , stm+ , text+ , xml-picklers+ , xml-types+ , tasty+ , tasty-hunit+ , tls benchmark benchmarks type: exitcode-stdio-1.0
source/Network/Xmpp.hs view
@@ -58,6 +58,7 @@ , closeConnection , endSession , waitForStream+ , streamState -- ** Authentication handlers -- | The use of 'scramSha1' is /recommended/, but 'digestMd5' might be -- useful for interaction with older implementations.@@ -193,11 +194,15 @@ , IQResult(..) , IQError(..) , IQResponse(..)+ , IQRequestClass(..)+ , IQRequestHandler , sendIQ , sendIQ'+ , sendIQRequest , answerIQ , iqResult , listenIQ+ , runIQHandler , unlistenIQ -- * Errors , StanzaErrorType(..)@@ -207,6 +212,7 @@ , StanzaErrorCondition(..) , SaslFailure(..) , IQSendError(..)+ , IQRequestError(..) -- * Threads , dupSession -- * Lenses
source/Network/Xmpp/Concurrent.hs view
@@ -40,6 +40,8 @@ import Network.Xmpp.Concurrent.Types import Network.Xmpp.IM.Roster import Network.Xmpp.IM.Roster.Types+import Network.Xmpp.IM.PresenceTracker+import Network.Xmpp.IM.PresenceTracker.Types import Network.Xmpp.Sasl import Network.Xmpp.Sasl.Types import Network.Xmpp.Stream@@ -87,14 +89,15 @@ Nothing -> return . Just $ serviceUnavailable iq Just ch -> do sentRef <- newTMVar False- let answerT answer = do- let IQRequest iqid from _to lang _tp bd = iq+ let answerT answer attrs = do+ let IQRequest iqid from _to lang _tp bd _attrs = iq response = case answer of Left er -> IQErrorS $ IQError iqid Nothing from lang er- (Just bd)+ (Just bd) attrs Right res -> IQResultS $ IQResult iqid Nothing from lang res+ attrs Ex.bracketOnError (atomically $ takeTMVar sentRef) (atomically . tryPutTMVar sentRef) $ \wasSent -> do@@ -114,8 +117,8 @@ writeTChan ch $ IQRequestTicket answerT iq as return Nothing maybe (return ()) (void . out) res- serviceUnavailable (IQRequest iqid from _to lang _tp bd) =- IQErrorS $ IQError iqid Nothing from lang err (Just bd)+ serviceUnavailable (IQRequest iqid from _to lang _tp bd _attrs) =+ IQErrorS $ IQError iqid Nothing from lang err (Just bd) [] err = StanzaError Cancel ServiceUnavailable Nothing Nothing handleIQResponse :: TVar IQHandlers -> Either IQError IQResult -> IO ()@@ -164,30 +167,38 @@ iqHands <- lift $ newTVarIO (Map.empty, Map.empty) eh <- lift $ newEmptyTMVarIO ros <- liftIO . newTVarIO $ Roster Nothing Map.empty+ peers <- liftIO . newTVarIO $ Peers Map.empty rew <- lift $ newTVarIO 60 let out = writeStanza writeSem- let rosterH = if (enableRoster config) then [handleRoster ros out]- else []+ boundJid <- liftIO $ withStream' (gets streamJid) stream+ let rosterH = if (enableRoster config)+ then [handleRoster boundJid ros out]+ else []+ let presenceH = if (enablePresenceTracking config)+ then [handlePresence (onPresenceChange config) peers out]+ else [] (sStanza, ps) <- initPlugins out $ plugins config let stanzaHandler = runHandlers $ List.concat [ inHandler <$> ps- , [ toChan stanzaChan out- , handleIQ iqHands out- ]+ , [ toChan stanzaChan sStanza]+ , presenceH , rosterH+ , [ handleIQ iqHands sStanza] ]- (kill, streamState, reader) <- ErrorT $ startThreadsWith writeSem stanzaHandler eh stream+ (kill, sState, reader) <- ErrorT $ startThreadsWith writeSem stanzaHandler+ eh stream idGen <- liftIO $ sessionStanzaIDs config let sess = Session { stanzaCh = stanzaChan , iqHandlers = iqHands , writeSemaphore = writeSem , readerThread = reader , idGenerator = idGen- , streamRef = streamState+ , streamRef = sState , eventHandlers = eh , stopThreads = kill , conf = config , rosterRef = ros+ , presenceRef = peers , sendStanza' = sStanza , sRealm = realm , sSaslCredentials = mbSasl@@ -195,9 +206,11 @@ } liftIO . atomically $ putTMVar eh $ EventHandlers { connectionClosedHandler = onConnectionClosed config sess }+ -- Pass the new session to the plugins so they can "tie the knot" liftIO . forM_ ps $ \p -> onSessionUp p sess return sess where+ -- Pass the stanza out action to each plugin initPlugins out' = go out' [] where go out ps' [] = return (out, ps')@@ -267,8 +280,6 @@ then [plain uname Nothing pwd] else [] , Nothing)-- -- | Reconnect immediately with the stored settings. Returns @Just@ the error -- when the reconnect attempt fails and Nothing when no failure was encountered.
source/Network/Xmpp/Concurrent/Basic.hs view
@@ -1,6 +1,7 @@ {-# OPTIONS_HADDOCK hide #-} module Network.Xmpp.Concurrent.Basic where +import Control.Applicative import Control.Concurrent.STM import qualified Control.Exception as Ex import Control.Monad.State.Strict@@ -19,6 +20,7 @@ writeStanza :: WriteSemaphore -> Stanza -> IO (Either XmppFailure ()) writeStanza sem a = do let outData = renderElement $ nsHack (pickleElem xpStanza a)+ debugOut outData semWrite sem outData @@ -69,3 +71,8 @@ Plain -> return () Secured -> return () _ -> retry++streamState :: Session -> STM ConnectionState+streamState Session{streamRef = sr} = do+ s <- readTMVar sr+ streamConnectionState <$> (readTMVar $ unStream s)
source/Network/Xmpp/Concurrent/IQ.hs view
@@ -1,3 +1,6 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-} {-# OPTIONS_HADDOCK hide #-} module Network.Xmpp.Concurrent.IQ where @@ -6,12 +9,20 @@ import Control.Concurrent.STM import Control.Concurrent.Thread.Delay (delay) import Control.Monad+import Control.Monad.Error+import Control.Monad.Trans import qualified Data.Map as Map+import Data.Maybe import Data.Text (Text)+import Data.XML.Pickle import Data.XML.Types+import Lens.Family2 (toListOf, (&), (^.)) import Network.Xmpp.Concurrent.Basic import Network.Xmpp.Concurrent.Types+import Network.Xmpp.Lens+import Network.Xmpp.Stanza import Network.Xmpp.Types+import System.Log.Logger -- | Sends an IQ, returns an STM action that returns the first inbound IQ with a -- matching ID that has type @result@ or @error@ or Nothing if the timeout was@@ -27,11 +38,12 @@ -> Maybe LangTag -- ^ Language tag of the payload (@Nothing@ for -- default) -> Element -- ^ The IQ body (there has to be exactly one)+ -> [ExtendedAttribute] -- ^ Additional stanza attributes -> Session -> IO (Either XmppFailure (STM (Maybe (Annotated IQResponse))))-sendIQ timeOut to tp lang body session = do+sendIQ timeOut t tp lang body attrs session = do newId <- idGenerator session- j <- case to of+ j <- case t of Just t -> return $ Right t Nothing -> Left <$> getJid session ref <- atomically $ do@@ -40,7 +52,8 @@ (byNS, byId) <- readTVar (iqHandlers session) writeTVar (iqHandlers session) (byNS, Map.insert newId value byId) return resRef- res <- sendStanza (IQRequestS $ IQRequest newId Nothing to lang tp body) session+ res <- sendStanza (IQRequestS $ IQRequest newId Nothing t lang tp body attrs)+ session case res of Right () -> do case timeOut of@@ -60,14 +73,15 @@ -- | Like 'sendIQ', but waits for the answer IQ. sendIQA' :: Maybe Integer- -> Maybe Jid- -> IQRequestType- -> Maybe LangTag- -> Element- -> Session- -> IO (Either IQSendError (Annotated IQResponse))-sendIQA' timeout to tp lang body session = do- ref <- sendIQ timeout to tp lang body session+ -> Maybe Jid+ -> IQRequestType+ -> Maybe LangTag+ -> Element+ -> [ExtendedAttribute]+ -> Session+ -> IO (Either IQSendError (Annotated IQResponse))+sendIQA' timeout to tp lang body attrs session = do+ ref <- sendIQ timeout to tp lang body attrs session either (return . Left . IQSendError) (fmap (maybe (Left IQTimeOut) Right) . atomically) ref @@ -77,9 +91,11 @@ -> IQRequestType -> Maybe LangTag -> Element+ -> [ExtendedAttribute] -> Session -> IO (Either IQSendError IQResponse)-sendIQ' timeout to tp lang body session = fmap fst <$> sendIQA' timeout to tp lang body session+sendIQ' timeout to tp lang body attrs session =+ fmap fst <$> sendIQA' timeout to tp lang body attrs session -- | Register your interest in inbound IQ stanzas of a specific type and -- namespace. The returned STM action yields the received, matching IQ stanzas.@@ -130,5 +146,67 @@ -- (False is returned in that case) answerIQ :: IQRequestTicket -> Either StanzaError (Maybe Element)+ -> [ExtendedAttribute] -> IO (Maybe (Either XmppFailure ()))-answerIQ ticket = answerTicket ticket+answerIQ = answerTicket+++-- Class++class IQRequestClass a where+ data IQResponseType a+ pickleRequest :: PU Element a+ pickleResponse :: PU [Element] (IQResponseType a)+ requestType :: a -> IQRequestType+ requestNamespace :: a -> Text++data IQRequestError = IQRequestSendError XmppFailure+ | IQRequestTimeout+ | IQRequestUnpickleError UnpickleError+ deriving Show++-- | Send an IQ request. May throw IQSendError, UnpickleError,++sendIQRequest :: (IQRequestClass a, MonadError IQRequestError m, MonadIO m) =>+ Maybe Integer+ -> Maybe Jid+ -> a+ -> Session+ -> m (Either IQError (IQResponseType a))+sendIQRequest timeout t req con = do+ mbRes <- liftIO $ sendIQ' timeout t (requestType req) Nothing+ (pickle pickleRequest req) [] con+ case mbRes of+ Left (IQTimeOut) -> throwError IQRequestTimeout+ Left (IQSendError e) -> throwError $ IQRequestSendError e+ Right (IQResponseError e) -> return $ Left e+ Right (IQResponseResult res) ->+ case unpickle pickleResponse (res & toListOf payloadT) of+ Left e -> throwError $ IQRequestUnpickleError e+ Right r -> return $ Right r++type IQRequestHandler a = a -> IO (Either StanzaError (IQResponseType a))++runIQHandler :: IQRequestClass a =>+ IQRequestHandler a+ -> Session+ -> IO ()+runIQHandler (handler :: a -> IO (Either StanzaError (IQResponseType a)))+ sess = do+ let prx = undefined :: a+ ns = (requestNamespace prx)+ mbChan <- listenIQ (requestType prx) ns sess+ case mbChan of+ Left _ -> warningM "Pontarius.Xmpp" $ "IQ namespace " ++ show ns+ ++ " is already handled"+ Right getNext -> forever $ do+ ticket <- atomically getNext+ case unpickle pickleRequest (iqRequestBody ticket ^. payload) of+ Left _ -> answerIQ ticket (Left $ mkStanzaError BadRequest) []+ Right req -> do+ res <- handler req+ case res of+ Left e -> answerIQ ticket (Left e) []+ Right r -> do+ let answer = (pickle pickleResponse r)+ answerIQ ticket (Right $ listToMaybe answer ) []
source/Network/Xmpp/Concurrent/Presence.hs view
@@ -3,9 +3,12 @@ import Control.Applicative ((<$>)) import Control.Concurrent.STM-import Network.Xmpp.Types-import Network.Xmpp.Concurrent.Types+import Lens.Family2 hiding (to)+import Lens.Family2.Stock import Network.Xmpp.Concurrent.Basic+import Network.Xmpp.Concurrent.Types+import Network.Xmpp.Lens+import Network.Xmpp.Types -- | Read a presence stanza from the inbound stanza channel, discards any other -- stanzas. Returns the presence stanza with annotations.@@ -40,4 +43,11 @@ -- | Send a presence stanza. sendPresence :: Presence -> Session -> IO (Either XmppFailure ())-sendPresence p session = sendStanza (PresenceS p) session+sendPresence p session = sendStanza (PresenceS checkedP) session+ where+ -- | RFC 6121 §3.1.1: When a user sends a presence subscription request to a+ -- potential instant messaging and presence contact, the value of the 'to'+ -- attribute MUST be a bare JID rather than a full JID+ checkedP = case presenceType p of+ Subscribe -> p & to . _Just %~ toBare+ _ -> p
source/Network/Xmpp/Concurrent/Threads.hs view
@@ -26,8 +26,6 @@ readWorker onStanza onCClosed stateRef = forever . Ex.mask_ $ do s' <- Ex.catches ( do- -- we don't know whether pull will- -- necessarily be interruptible atomically $ do s@(Stream con) <- readTMVar stateRef scs <- streamConnectionState <$> readTMVar con@@ -44,6 +42,8 @@ Nothing -> return () Just s -> do res <- Ex.catches (do+ -- we don't know whether pull will+ -- necessarily be interruptible allowInterrupt res <- pullStanza s case res of@@ -70,9 +70,7 @@ allowInterrupt = unsafeUnmask $ return () -- While waiting for the first semaphore(s) to flip we might receive another -- interrupt. When that happens we add it's semaphore to the list and retry- -- waiting. We do this because we might receive another- -- interrupt while we're waiting for a mutex to unlock; if that happens, the- -- new interrupt is added to the list and is waited for as well.+ -- waiting. handleInterrupts :: [TMVar ()] -> IO [()] handleInterrupts ts = Ex.catch (atomically $ forM ts takeTMVar)@@ -81,9 +79,6 @@ stateIsClosed Finished = True stateIsClosed _ = False ---- Two streams: input and output. Threads read from input stream and write to--- output stream. -- | Runs thread in XmppState monad. Returns channel of incoming and outgoing -- stances, respectively, and an Action to stop the Threads and close the -- connection.
source/Network/Xmpp/Concurrent/Types.hs view
@@ -17,6 +17,7 @@ import Data.XML.Types (Element) import Network import Network.Xmpp.IM.Roster.Types+import Network.Xmpp.IM.PresenceTracker.Types import Network.Xmpp.Sasl.Types import Network.Xmpp.Types @@ -84,6 +85,17 @@ -- | Enable roster handling according to rfc 6121. See 'getRoster' to -- acquire the current roster , enableRoster :: Bool+ -- | Track incomming presence stancas.+ , enablePresenceTracking :: Bool+ -- | Callback that is invoked when the presence status of a peer changes,+ -- i.e. it comes online, goes offline or its IM presence changes. The+ -- arguments are the (full) JID of the peer, the old state and the new+ -- state. The function is called in a new thread to avoid blocking+ -- handling stanzas+ , onPresenceChange :: Maybe ( Jid+ -> PeerStatus+ -> PeerStatus+ -> IO ()) } instance Default SessionConfiguration where@@ -97,6 +109,8 @@ return . Text.pack . show $ curId , plugins = [] , enableRoster = True+ , enablePresenceTracking = True+ , onPresenceChange = Nothing } -- | Handlers to be run when the Xmpp session ends and when the Xmpp connection is@@ -130,6 +144,7 @@ , eventHandlers :: TMVar EventHandlers , stopThreads :: IO () , rosterRef :: TVar Roster+ , presenceRef :: TVar Peers , conf :: SessionConfiguration , sendStanza' :: Stanza -> IO (Either XmppFailure ()) , sRealm :: HostName@@ -151,6 +166,7 @@ { -- | Send an answer to an IQ request once. Subsequent calls will do -- nothing and return Nothing answerTicket :: Either StanzaError (Maybe Element)+ -> [ExtendedAttribute] -> IO (Maybe (Either XmppFailure ())) -- | The actual IQ request that created this ticket. , iqRequestBody :: IQRequest@@ -162,4 +178,6 @@ data IQSendError = IQSendError XmppFailure -- There was an error sending the IQ -- stanza | IQTimeOut -- No answer was received during the allotted time- deriving (Show, Eq)+ deriving (Show, Eq, Typeable)++instance Ex.Exception IQSendError
source/Network/Xmpp/IM.hs view
@@ -22,11 +22,20 @@ , Roster(..) , Item(..) , getRoster+ , getRosterSTM , rosterAdd , rosterRemove+ -- * presenceTracker+ , PeerStatus(..)+ , isPeerAvailable+ , getEntityStatus+ , getAvailablePeers+ , getPeerEntities ) where import Network.Xmpp.IM.Message import Network.Xmpp.IM.Presence import Network.Xmpp.IM.Roster import Network.Xmpp.IM.Roster.Types+import Network.Xmpp.IM.PresenceTracker+import Network.Xmpp.IM.PresenceTracker.Types
source/Network/Xmpp/IM/Presence.hs view
@@ -4,21 +4,21 @@ module Network.Xmpp.IM.Presence where -import Data.Default-import Data.Text (Text)-import Data.XML.Pickle-import Data.XML.Types-import Network.Xmpp.Types+import Data.Default+import Data.Text (Text)+import Data.XML.Pickle+import Data.XML.Types+import Network.Xmpp.Types data ShowStatus = StatusAway | StatusChat | StatusDnd- | StatusXa deriving (Read, Show)+ | StatusXa deriving (Read, Show, Eq) data IMPresence = IMP { showStatus :: Maybe ShowStatus , status :: Maybe Text , priority :: Maybe Int- } deriving Show+ } deriving (Show, Eq) imPresence :: IMPresence imPresence = IMP { showStatus = Nothing@@ -53,6 +53,7 @@ xp3Tuple (xpOption $ xpElemNodes "{jabber:client}show" (xpContent xpShow))+ -- TODO: Multiple status elements with different lang tags (xpOption $ xpElemNodes "{jabber:client}status" (xpContent xpText)) (xpOption $ xpElemNodes "{jabber:client}priority"
+ source/Network/Xmpp/IM/PresenceTracker.hs view
@@ -0,0 +1,110 @@+{-# LANGUAGE RankNTypes #-}+module Network.Xmpp.IM.PresenceTracker where++import Control.Applicative+import Control.Concurrent+import Control.Concurrent.STM+import Control.Monad+import qualified Data.Foldable as Foldable+import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map+import Data.Maybe+import Lens.Family2+import Lens.Family2.Stock+import Network.Xmpp.Concurrent.Types+import Network.Xmpp.IM.Presence+import Network.Xmpp.Lens hiding (Lens, Traversal)+import Network.Xmpp.Types+import Prelude hiding (mapM)++import Network.Xmpp.IM.PresenceTracker.Types++_peers :: Iso Peers (Map Jid (Map Jid (Maybe IMPresence)))+_peers = mkIso unPeers Peers++_PeerAvailable :: Prism PeerStatus (Maybe IMPresence)+_PeerAvailable = prism' PeerAvailable fromPeerAvailable+ where+ fromPeerAvailable (PeerAvailable pa) = Just pa+ fromPeerAvailable _ = Nothing++_PeerUnavailable :: Prism PeerStatus ()+_PeerUnavailable = prism' (const PeerUnavailable) fromPeerUnavailable+ where+ fromPeerUnavailable PeerUnavailable = Just ()+ fromPeerUnavailable _ = Nothing++_PeerStatus :: Iso (Maybe (Maybe IMPresence)) PeerStatus+_PeerStatus = mkIso toPeerStatus fromPeerStatus+ where+ toPeerStatus (Nothing) = PeerUnavailable+ toPeerStatus (Just imp) = PeerAvailable imp+ fromPeerStatus PeerUnavailable = Nothing+ fromPeerStatus (PeerAvailable imp) = Just imp++maybeMap :: Iso (Maybe (Map a b)) (Map a b)+maybeMap = mkIso maybeToMap mapToMaybe+ where+ maybeToMap Nothing = Map.empty+ maybeToMap (Just m) = m+ mapToMaybe m | Map.null m = Nothing+ | otherwise = Just m+++-- | Status of give full JID+peerStatusL :: Jid -> Lens' Peers PeerStatus+peerStatusL j = _peers . at (toBare j) . maybeMap . at j . _PeerStatus++peerMapPeerAvailable :: Jid -> Peers -> Bool+peerMapPeerAvailable j | isFull j = not . nullOf (peerStatusL j . _PeerAvailable)+ | otherwise = not . nullOf (_peers . at j . _Just)++handlePresence :: Maybe (Jid -> PeerStatus -> PeerStatus -> IO ())+ -> TVar Peers+ -> StanzaHandler+handlePresence onChange peers _ st _ = do+ let mbPr = do+ pr <- st ^? _Presence -- Only act on presence stanzas+ fr <- pr ^? from . _Just . _isFull -- Only act on full JIDs+ return (pr, fr)+ Foldable.forM_ mbPr $ \(pr, fr) ->+ case presenceType pr of+ Available -> setStatus fr (PeerAvailable (getIMPresence pr))+ Unavailable -> setStatus fr PeerUnavailable+ _ -> return ()+ return [(st, [])]+ where+ setStatus fr newStatus = do+ os <- atomically $ do+ ps <- readTVar peers+ let oldStatus = ps ^. peerStatusL fr+ writeTVar peers $ ps & set (peerStatusL fr) newStatus+ return oldStatus+ unless (os == newStatus) $ case onChange of+ Nothing -> return ()+ Just oc -> void . forkIO $ oc fr os newStatus+ return ()++-- | Check whether a given jid is available+isPeerAvailable :: Jid -> Session -> STM Bool+isPeerAvailable j sess = peerMapPeerAvailable j <$> readTVar (presenceRef sess)++-- | Get status of given full JID+getEntityStatus :: Jid -> Session -> STM PeerStatus+getEntityStatus j sess = do+ peers <- readTVar (presenceRef sess)+ return $ peers ^. peerStatusL j++-- | Get list of (bare) Jids with available entities+getAvailablePeers :: Session -> STM [Jid]+getAvailablePeers sess = do+ Peers peers <- readTVar (presenceRef sess)+ return $ Map.keys peers++-- | Get all available full JIDs to the given JID+getPeerEntities :: Jid -> Session -> STM (Map Jid (Maybe IMPresence))+getPeerEntities j sess = do+ Peers peers <- readTVar (presenceRef sess)+ case Map.lookup (toBare j) peers of+ Nothing -> return Map.empty+ Just js -> return js
+ source/Network/Xmpp/IM/PresenceTracker/Types.hs view
@@ -0,0 +1,20 @@+module Network.Xmpp.IM.PresenceTracker.Types where++import Data.Map (Map)++import Network.Xmpp.Types+import Network.Xmpp.IM.Presence++-- Map from bare JIDs to a map of full JIDs to show maybe status.+--+-- Invariants:+-- * The outer map should not have entries for bare JIDs that have no+-- available resource, i.e. the inner map should never be empty+--+-- * The inner map keys' local and domain part coincide with the outer keys'+newtype Peers = Peers { unPeers :: Map Jid (Map Jid (Maybe IMPresence))}+ deriving (Show)++data PeerStatus = PeerAvailable (Maybe IMPresence)+ | PeerUnavailable+ deriving (Show, Eq)
source/Network/Xmpp/IM/Roster.hs view
@@ -31,22 +31,15 @@ timeout :: Maybe Integer timeout = Just 3000000 -- 3 seconds --- | Push a roster item to the server. The values for approved and ask are--- ignored and all values for subsciption except "remove" are ignored.-rosterPush :: Item -> Session -> IO (Either IQSendError (Annotated IQResponse))-rosterPush item session = do- let el = pickleElem xpQuery (Query Nothing [fromItem item])- sendIQA' timeout Nothing Set Nothing el session- -- | Add or update an item to the roster. -- -- To update the item just send the complete set of new data.-rosterAdd :: Jid -- ^ JID of the item+rosterSet :: Jid -- ^ JID of the item -> Maybe Text -- ^ Name alias -> [Text] -- ^ Groups (duplicates will be removed) -> Session -> IO (Either IQSendError (Annotated IQResponse))-rosterAdd j n gs session = do+rosterSet j n gs session = do let el = pickleElem xpQuery (Query Nothing [QueryItem { qiApproved = Nothing , qiAsk = False@@ -55,8 +48,16 @@ , qiSubscription = Nothing , qiGroups = nub gs }])- sendIQA' timeout Nothing Set Nothing el session+ sendIQA' timeout Nothing Set Nothing el [] session +-- | Synonym to rosterSet+rosterAdd :: Jid+ -> Maybe Text+ -> [Text]+ -> Session+ -> IO (Either IQSendError (Annotated IQResponse))+rosterAdd = rosterSet+ -- | Remove an item from the roster. Return 'True' when the item is sucessfully -- removed or if it wasn't in the roster to begin with. rosterRemove :: Jid -> Session -> IO Bool@@ -69,10 +70,21 @@ case res of Right (IQResponseResult IQResult{}, _) -> return True _ -> return False+ where+ rosterPush :: Item+ -> Session+ -> IO (Either IQSendError (Annotated IQResponse))+ rosterPush item session = do+ let el = pickleElem xpQuery (Query Nothing [fromItem item])+ sendIQA' timeout Nothing Set Nothing el [] session +-- | Retrieve the current Roster state (STM version)+getRosterSTM :: Session -> STM Roster+getRosterSTM session = readTVar (rosterRef session)+ -- | Retrieve the current Roster state getRoster :: Session -> IO Roster-getRoster session = atomically $ readTVar (rosterRef session)+getRoster session = atomically $ getRosterSTM session -- | Get the initial roster or refresh the roster. You don't need to call this -- on your own.@@ -86,37 +98,48 @@ "Server did not return a roster: " Just roster -> atomically $ writeTVar (rosterRef session) roster -handleRoster :: TVar Roster -> StanzaHandler-handleRoster ref out sta _ = case sta of- IQRequestS (iqr@IQRequest{iqRequestPayload =- iqb@Element{elementName = en}})- | nameNamespace en == Just "jabber:iq:roster" -> do- case iqRequestFrom iqr of- Just _from -> return [(sta, [])] -- Don't handle roster pushes- -- from unauthorized sources- Nothing -> case unpickleElem xpQuery iqb of- Right Query{ queryVer = v- , queryItems = [update]- } -> do- handleUpdate v update- _ <- out $ result iqr- return []- _ -> do- errorM "Pontarius.Xmpp" "Invalid roster query"- _ <- out $ badRequest iqr- return []- _ -> return [(sta, [])]+handleRoster :: Maybe Jid -> TVar Roster -> StanzaHandler+handleRoster mbBoundJid ref out sta _ = do+ case sta of+ IQRequestS (iqr@IQRequest{iqRequestPayload =+ iqb@Element{elementName = en}})+ | nameNamespace en == Just "jabber:iq:roster" -> do+ let doHandle = case (iqRequestFrom iqr, mbBoundJid) of+ -- We don't need to check our own JID when the IQ+ -- request was sent without a from address+ (Nothing, _) -> True+ -- We don't have a Jid bound, so we can't verify that+ -- the from address matches our bare jid+ (Just _fr, Nothing) -> False+ -- Check that the from address matches our bare jid+ (Just fr, Just boundJid) | fr == toBare boundJid -> True+ | otherwise -> False+ if doHandle+ then case unpickleElem xpQuery iqb of+ Right Query{ queryVer = v+ , queryItems = [update]+ } -> do+ handleUpdate v update+ _ <- out $ result iqr+ return []+ _ -> do+ errorM "Pontarius.Xmpp" "Invalid roster query"+ _ <- out $ badRequest iqr+ return []+ -- Don't handle roster pushes from unauthorized sources+ else return [(sta, [])]+ _ -> return [(sta, [])] where handleUpdate v' update = atomically $ modifyTVar ref $ \(Roster v is) -> Roster (v' `mplus` v) $ case qiSubscription update of Just Remove -> Map.delete (qiJid update) is _ -> Map.insert (qiJid update) (toItem update) is - badRequest (IQRequest iqid from _to lang _tp bd) =- IQErrorS $ IQError iqid Nothing from lang errBR (Just bd)+ badRequest (IQRequest iqid from _to lang _tp bd _attrs) =+ IQErrorS $ IQError iqid Nothing from lang errBR (Just bd) [] errBR = StanzaError Cancel BadRequest Nothing Nothing- result (IQRequest iqid from _to lang _tp _bd) =- IQResultS $ IQResult iqid Nothing from lang Nothing+ result (IQRequest iqid from _to lang _tp _bd _attrs) =+ IQResultS $ IQResult iqid Nothing from lang Nothing [] retrieveRoster :: Maybe Roster -> Session -> IO (Maybe Roster) retrieveRoster mbOldRoster sess = do@@ -128,6 +151,7 @@ else Nothing res <- sendIQ' timeout Nothing Get Nothing (pickleElem xpQuery (Query version []))+ [] sess case res of Left e -> do@@ -156,9 +180,13 @@ , riAsk = qiAsk qi , riJid = qiJid qi , riName = qiName qi- , riSubscription = fromMaybe None (qiSubscription qi)+ , riSubscription = fromSubscription (qiSubscription qi) , riGroups = nub $ qiGroups qi }+ where+ fromSubscription Nothing = None+ fromSubscription (Just s) | s `elem` [None, To, From, Both] = s+ | otherwise = None fromItem :: Item -> QueryItem fromItem i = QueryItem { qiApproved = Nothing@@ -197,17 +225,15 @@ xpSubscription :: PU Text Subscription xpSubscription = ("xpSubscription", "") <?>- xpPartial ( \input -> case subscriptionFromText input of- Nothing -> Left "Could not parse subscription."- Just j -> Right j)- subscriptionToText+ xpIso subscriptionFromText+ subscriptionToText where- subscriptionFromText "none" = Just None- subscriptionFromText "to" = Just To- subscriptionFromText "from" = Just From- subscriptionFromText "both" = Just Both- subscriptionFromText "remove" = Just Remove- subscriptionFromText _ = Nothing+ subscriptionFromText "none" = None+ subscriptionFromText "to" = To+ subscriptionFromText "from" = From+ subscriptionFromText "both" = Both+ subscriptionFromText "remove" = Remove+ subscriptionFromText _ = None subscriptionToText None = "none" subscriptionToText To = "to" subscriptionToText From = "from"
source/Network/Xmpp/Internal.hs view
@@ -17,43 +17,58 @@ -- top of this API. module Network.Xmpp.Internal- ( -- * Stream- Stream(..)- , StreamConfiguration(..)- , StreamState(..)- , StreamHandle(..)- , StreamFeatures(..)- , openStream- , withStream- -- * TLS- , tls- , TlsBehaviour(..)- -- * Auth- , SaslHandler- , auth- -- * Stanzas- , Stanza(..)- , pushStanza- , pullStanza- , writeStanza- -- ** IQ- , pushIQ- , iqError- , iqResult- , associatedErrorType- -- * Plugins- , Plugin- , Plugin'(..)- , Annotation(..)- , connectTls- )- where+ ( module Network.Xmpp.Concurrent+ , module Network.Xmpp.Concurrent.Basic+ , module Network.Xmpp.Concurrent.IQ+ , module Network.Xmpp.Concurrent.Message+ , module Network.Xmpp.Concurrent.Monad+ , module Network.Xmpp.Concurrent.Presence+ , module Network.Xmpp.Concurrent.Threads+ , module Network.Xmpp.Concurrent.Types+ , module Network.Xmpp.IM.Message+ , module Network.Xmpp.IM.Presence+ , module Network.Xmpp.IM.Roster+ , module Network.Xmpp.IM.Roster.Types+ , module Network.Xmpp.Marshal+ , module Network.Xmpp.Sasl+ , module Network.Xmpp.Sasl.Common+ , module Network.Xmpp.Sasl.Mechanisms+ , module Network.Xmpp.Sasl.Mechanisms.DigestMd5+ , module Network.Xmpp.Sasl.Mechanisms.Plain+ , module Network.Xmpp.Sasl.Mechanisms.Scram+ , module Network.Xmpp.Sasl.StringPrep+ , module Network.Xmpp.Sasl.Types+ , module Network.Xmpp.Stanza+ , module Network.Xmpp.Stream+ , module Network.Xmpp.Tls+ , module Network.Xmpp.Types+ , module Network.Xmpp.Utilities+ ) where ++import Network.Xmpp.Concurrent import Network.Xmpp.Concurrent.Basic+import Network.Xmpp.Concurrent.IQ+import Network.Xmpp.Concurrent.Message+import Network.Xmpp.Concurrent.Monad+import Network.Xmpp.Concurrent.Presence+import Network.Xmpp.Concurrent.Threads import Network.Xmpp.Concurrent.Types+import Network.Xmpp.IM.Message+import Network.Xmpp.IM.Presence+import Network.Xmpp.IM.Roster+import Network.Xmpp.IM.Roster.Types+import Network.Xmpp.Marshal import Network.Xmpp.Sasl+import Network.Xmpp.Sasl.Common+import Network.Xmpp.Sasl.Mechanisms+import Network.Xmpp.Sasl.Mechanisms.DigestMd5+import Network.Xmpp.Sasl.Mechanisms.Plain+import Network.Xmpp.Sasl.Mechanisms.Scram+import Network.Xmpp.Sasl.StringPrep import Network.Xmpp.Sasl.Types import Network.Xmpp.Stanza-import Network.Xmpp.Stream+import Network.Xmpp.Stream hiding (mbl, lmb) import Network.Xmpp.Tls import Network.Xmpp.Types+import Network.Xmpp.Utilities
source/Network/Xmpp/Lens.hs view
@@ -5,30 +5,60 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FunctionalDependencies #-} --- | Van Laarhoven lenses for XMPP types. The lenses are designed to work with--- the lens library. This module also provides a few simple accessors ('view',--- 'modify', 'set' and 'getAll') so you don't need to pull in the--- lens library to get some use out of them.+-- | (More than just) Van Laarhoven lenses for XMPP types. The accessors in here+-- are designed to work with an optics library like lens or lens-family. This+-- module also provides a few simple functions ('view', 'modify', 'set' and+-- 'getAll') so you don't need to pull in another library to get some use out+-- of them. ----- The name of the lenses corresponds to the field name of the data types with+-- * The name of the lenses corresponds to the field name of the data types with -- an upper-case L appended. For documentation of the fields refer to the documentation of the data types (linked in the section header)+--+-- * Same goes for Traversals, except they are suffixed with a \'T\'+--+-- * Prism generally start with an underscore+--+-- /NB/ you do not need to import this module to get access to the optics+-- defined herein. They are also exported from Network.Xmpp. You only need to+-- import this module if you want to use the complementary accessor functions+-- without using an optics library like lens or lens-family module Network.Xmpp.Lens ( Lens , Traversal+ , Prism+ , Iso -- * Accessors -- | Reimplementation of the basic lens functions so you don't have to- -- bring in all of lens library in to use the lenses+ -- bring in a lens library to use the optics -- ** Lenses- , view+ , LF.view , modify- , set+ , LF.set -- * Traversals , getAll+ -- * Prisms++ -- ** Construction+ , prism'+ , mkLens+ , mkIso -- * Lenses + -- ** JID+ , _JidText+ , _isFull+ , _isBare+ -- ** Stanzas+ , _IQRequest+ , _IQResult+ , _IQError+ , _Message+ , _MessageError+ , _Presence+ , _PresenceError , IsStanza(..) , HasStanzaPayload(..) , IsErrorStanza(..)@@ -48,12 +78,23 @@ , establishSessionL , tlsBehaviourL , tlsParamsL+ -- **** TLS parameters+ , clientServerIdentificationL+ , tlsServerIdentificationL+ , clientSupportedL+ , supportedCiphersL+ , supportedVersionsL+ , tlsSupportedCiphersL+ , tlsSupportedVersionsL+ , clientUseServerNameIndicationL+ , tlsUseNameIndicationL -- *** 'SessionConfiguration' , streamConfigurationL , onConnectionClosedL , sessionStanzaIDsL , ensableRosterL , pluginsL+ , onPresenceChangeL -- ** IM -- *** Roster -- **** 'Roster'@@ -99,40 +140,43 @@ where import Control.Applicative-import Data.Functor.Identity(Identity(..))+import qualified Data.ByteString as BS+import Data.Functor.Identity (Identity(..)) import qualified Data.Map as Map+import Data.Profunctor+import Data.Text (Text) import qualified Data.Text as Text-import Data.Text(Text)-import Data.XML.Types(Element)-import Network.DNS(ResolvConf)-import Network.TLS (ClientParams)+import Data.XML.Types (Element)+import qualified Lens.Family2 as LF+import Network.DNS (ResolvConf)+import Network.TLS as TLS import Network.Xmpp.Concurrent.Types-import Network.Xmpp.IM.Roster.Types import Network.Xmpp.IM.Message import Network.Xmpp.IM.Presence+import Network.Xmpp.IM.PresenceTracker.Types+import Network.Xmpp.IM.Roster.Types import Network.Xmpp.Types -- | Van-Laarhoven lenses.+{-# DEPRECATED Lens "Use Lens' from lens-family or lens" #-} type Lens a b = Functor f => (b -> f b) -> a -> f a +{-# DEPRECATED Traversal "Use Traversal' from lens-family or lens" #-} type Traversal a b = Applicative f => (b -> f b) -> a -> f a ---- Accessors----------------+type Prism a b = forall p f. (Choice p, Applicative f) => p b (f b) -> p a (f a) +type Iso a b = forall p f. (Profunctor p, Functor f) => p b (f b) -> p a (f a) --- | Read the value the lens is pointing to-view :: Lens a b -> a -> b-view l x = getConst $ l Const x+prism' :: (b -> s) -> (s -> Maybe b) -> Prism s b+prism' bs sma = dimap (\s -> maybe (Left s) Right (sma s))+ (either pure (fmap bs)) . right' --- | Replace the value the lens is pointing to-set :: Lens a b -> b -> a -> a-set l b x = modify l (const b) x+mkLens :: (a -> b) -> (b -> a -> a) -> Lens a b+mkLens get set = \inj x -> fmap (flip set x) (inj $ get x) --- | modify the Value(s) a Lens or Traversal is pointing to-modify :: Traversal a b -> (b -> b) -> a -> a-modify t f = runIdentity . t (Identity . f)+mkIso :: (a -> b) -> (b -> a) -> Iso a b+mkIso t f = dimap t (fmap f) newtype Collect a b = Collect {getCollection :: [a]} deriving Functor @@ -140,14 +184,27 @@ pure _ = Collect [] Collect xs <*> Collect ys = Collect $ xs ++ ys +{-# DEPRECATED getAll "use toListOf (lens-family), partsOf (lens) or similar" #-} -- | Return all the values a Traversal is pointing to in a list getAll :: Traversal a b -> a -> [b] getAll t = getCollection . t (Collect . pure) +{-# DEPRECATED modify "use over (lens-family, lens)" #-}+modify :: Traversal a b -> (b -> b) -> a -> a+modify t f = runIdentity . t (Identity . f) -- Xmpp Lenses -------------------- +_JidText :: Prism Text Jid+_JidText = prism' jidToText jidFromText++_isFull :: Prism Jid Jid+_isFull = prism' id (\j -> if isFull j then Just j else Nothing)++_isBare :: Prism Jid Jid+_isBare = prism' toBare (\j -> if isBare j then Just j else Nothing)+ class IsStanza s where -- | From-attribute of the stanza from :: Lens s (Maybe Jid)@@ -278,6 +335,48 @@ <$> inj (if Text.null x then Nothing else Just x) +_IQRequest :: Prism Stanza IQRequest+_IQRequest = prism' IQRequestS fromIQRequestS+ where+ fromIQRequestS (IQRequestS s) = Just s+ fromIQRequestS _ = Nothing++_IQResult :: Prism Stanza IQResult+_IQResult = prism' IQResultS fromIQResultS+ where+ fromIQResultS (IQResultS s) = Just s+ fromIQResultS _ = Nothing++_IQError :: Prism Stanza IQError+_IQError = prism' IQErrorS fromIQErrorS+ where+ fromIQErrorS (IQErrorS s) = Just s+ fromIQErrorS _ = Nothing++_Message :: Prism Stanza Message+_Message = prism' MessageS fromMessageS+ where+ fromMessageS (MessageS s) = Just s+ fromMessageS _ = Nothing++_MessageError :: Prism Stanza MessageError+_MessageError = prism' MessageErrorS fromMessageErrorS+ where+ fromMessageErrorS (MessageErrorS s) = Just s+ fromMessageErrorS _ = Nothing++_Presence :: Prism Stanza Presence+_Presence = prism' PresenceS fromPresenceS+ where+ fromPresenceS (PresenceS s) = Just s+ fromPresenceS _ = Nothing++_PresenceError :: Prism Stanza PresenceError+_PresenceError = prism' PresenceErrorS fromPresenceErrorS+ where+ fromPresenceErrorS (PresenceErrorS s) = Just s+ fromPresenceErrorS _ = Nothing+ class IsErrorStanza s where -- | Error element of the stanza stanzaError :: Lens s StanzaError@@ -390,11 +489,36 @@ tlsBehaviourL inj sc@StreamConfiguration{tlsBehaviour = x} = (\x' -> sc{tlsBehaviour = x'}) <$> inj x + tlsParamsL :: Lens StreamConfiguration ClientParams tlsParamsL inj sc@StreamConfiguration{tlsParams = x} = (\x' -> sc{tlsParams = x'}) <$> inj x --- SessioConfiguration+-- TLS parameters+-----------------++clientServerIdentificationL :: Lens ClientParams (String, BS.ByteString)+clientServerIdentificationL inj cp@ClientParams{clientServerIdentification = x}+ = (\x' -> cp{clientServerIdentification = x'}) <$> inj x++clientSupportedL :: Lens ClientParams Supported+clientSupportedL inj cp@ClientParams{clientSupported = x}+ = (\x' -> cp{clientSupported = x'}) <$> inj x++clientUseServerNameIndicationL :: Lens ClientParams Bool+clientUseServerNameIndicationL inj+ cp@ClientParams{clientUseServerNameIndication = x}+ = (\x' -> cp{clientUseServerNameIndication = x'}) <$> inj x++supportedCiphersL :: Lens Supported [Cipher]+supportedCiphersL inj s@Supported{supportedCiphers = x}+ = (\x' -> s{supportedCiphers = x'}) <$> inj x++supportedVersionsL :: Lens Supported [TLS.Version]+supportedVersionsL inj s@Supported{supportedVersions = x}+ = (\x' -> s{supportedVersions = x'}) <$> inj x++-- SessionConfiguration ----------------------- streamConfigurationL :: Lens SessionConfiguration StreamConfiguration streamConfigurationL inj sc@SessionConfiguration{sessionStreamConfiguration = x}@@ -415,6 +539,34 @@ pluginsL :: Lens SessionConfiguration [Plugin] pluginsL inj sc@SessionConfiguration{plugins = x} = (\x' -> sc{plugins = x'}) <$> inj x++onPresenceChangeL :: Lens SessionConfiguration (Maybe ( Jid -> PeerStatus+ -> PeerStatus -> IO ()))+onPresenceChangeL inj sc@SessionConfiguration{onPresenceChange = x}+ = (\x' -> sc{onPresenceChange = x'}) <$> inj x++-- | Access clientServerIdentification inside tlsParams inside streamConfiguration+tlsServerIdentificationL :: Lens SessionConfiguration (String, BS.ByteString)+tlsServerIdentificationL = streamConfigurationL+ . tlsParamsL+ . clientServerIdentificationL++-- | Access clientUseServerNameIndication inside tlsParams+tlsUseNameIndicationL :: Lens SessionConfiguration Bool+tlsUseNameIndicationL = streamConfigurationL+ . tlsParamsL+ . clientUseServerNameIndicationL++-- | Access supportedCiphers inside clientSupported inside tlsParams+tlsSupportedCiphersL :: Lens SessionConfiguration [Cipher]+tlsSupportedCiphersL = streamConfigurationL+ . tlsParamsL . clientSupportedL . supportedCiphersL++-- | Access supportedVersions inside clientSupported inside tlsParams+tlsSupportedVersionsL :: Lens SessionConfiguration [TLS.Version]+tlsSupportedVersionsL = streamConfigurationL+ . tlsParamsL . clientSupportedL . supportedVersionsL+ -- Roster ------------------
source/Network/Xmpp/Marshal.hs view
@@ -10,12 +10,14 @@ module Network.Xmpp.Marshal where -import Data.XML.Pickle-import Data.XML.Types+import Data.XML.Pickle+import Data.XML.Types -import Data.Text+import qualified Control.Exception as Ex+import Data.Text (Text)+import qualified Data.Text as Text -import Network.Xmpp.Types+import Network.Xmpp.Types xpNonemptyText :: PU Text NonemptyText xpNonemptyText = ("xpNonemptyText" , "") <?+> xpWrap Nonempty fromNonempty xpText@@ -23,6 +25,16 @@ xpStreamStanza :: PU [Node] (Either StreamErrorInfo Stanza) xpStreamStanza = xpEither xpStreamError xpStanza +xpExtendedAttrs :: PU [Attribute] [ExtendedAttribute]+xpExtendedAttrs = ("xpAttrVerbatim" , "") <?+>+ xpIso (map (\(name, cs) -> (name, flattenContents cs)))+ (map (\(name, c) -> (name, [ContentText c])))+ where+ flattenContents = Text.concat . filterContentText+ filterContentText = map (\c -> case c of+ ContentText t -> t+ ContentEntity{} -> Ex.throw UnresolvedEntityException )+ xpStanza :: PU [Node] Stanza xpStanza = ("xpStanza" , "") <?+> xpAlt stanzaSel [ xpWrap IQRequestS (\(IQRequestS x) -> x) xpIQRequest@@ -46,15 +58,16 @@ xpMessage :: PU [Node] (Message) xpMessage = ("xpMessage" , "") <?+> xpWrap- (\((tp, qid, from, to, lang), ext) -> Message qid from to lang tp ext)- (\(Message qid from to lang tp ext) -> ((tp, qid, from, to, lang), ext))+ (\((tp, qid, from, to, lang, attrs), ext) -> Message qid from to lang tp ext attrs)+ (\(Message qid from to lang tp ext attrs) -> ((tp, qid, from, to, lang, attrs), ext)) (xpElem "{jabber:client}message"- (xp5Tuple+ (xp6Tuple (xpDefault Normal $ xpAttr "type" xpMessageType) (xpAttrImplied "id" xpId) (xpAttrImplied "from" xpJid) (xpAttrImplied "to" xpJid) xpLangTag+ xpExtendedAttrs -- TODO: NS? ) (xpAll xpElemVerbatim)@@ -62,45 +75,54 @@ xpPresence :: PU [Node] Presence xpPresence = ("xpPresence" , "") <?+> xpWrap- (\((qid, from, to, lang, tp), ext) -> Presence qid from to lang tp ext)- (\(Presence qid from to lang tp ext) -> ((qid, from, to, lang, tp), ext))+ (\((qid, from, to, lang, tp, attr), ext)+ -> Presence qid from to lang tp ext attr)+ (\(Presence qid from to lang tp ext attr)+ -> ((qid, from, to, lang, tp, attr), ext)) (xpElem "{jabber:client}presence"- (xp5Tuple+ (xp6Tuple (xpAttrImplied "id" xpId) (xpAttrImplied "from" xpJid) (xpAttrImplied "to" xpJid) xpLangTag (xpDefault Available $ xpAttr "type" xpPresenceType)+ xpExtendedAttrs ) (xpAll xpElemVerbatim) ) xpIQRequest :: PU [Node] IQRequest xpIQRequest = ("xpIQRequest" , "") <?+> xpWrap- (\((qid, from, to, lang, tp),body) -> IQRequest qid from to lang tp body)- (\(IQRequest qid from to lang tp body) -> ((qid, from, to, lang, tp), body))+ (\((qid, from, to, lang, tp, attr),body)+ -> IQRequest qid from to lang tp body attr)+ (\(IQRequest qid from to lang tp body attr)+ -> ((qid, from, to, lang, tp, attr), body)) (xpElem "{jabber:client}iq"- (xp5Tuple+ (xp6Tuple (xpAttr "id" xpId) (xpAttrImplied "from" xpJid) (xpAttrImplied "to" xpJid) xpLangTag ((xpAttr "type" xpIQRequestType))+ xpExtendedAttrs ) xpElemVerbatim ) xpIQResult :: PU [Node] IQResult xpIQResult = ("xpIQResult" , "") <?+> xpWrap- (\((qid, from, to, lang, _tp),body) -> IQResult qid from to lang body)- (\(IQResult qid from to lang body) -> ((qid, from, to, lang, ()), body))+ (\((qid, from, to, lang, _tp, attr),body)+ -> IQResult qid from to lang body attr)+ (\(IQResult qid from to lang body attr)+ -> ((qid, from, to, lang, (), attr ), body)) (xpElem "{jabber:client}iq"- (xp5Tuple+ (xp6Tuple (xpAttr "id" xpId) (xpAttrImplied "from" xpJid) (xpAttrImplied "to" xpJid) xpLangTag ((xpAttrFixed "type" "result"))+ xpExtendedAttrs ) (xpOption xpElemVerbatim) )@@ -134,10 +156,8 @@ -- the optional field is left empty and must be filled in by the caller xpStanzaErrorConditionShape :: PU Text StanzaErrorCondition xpStanzaErrorConditionShape = ("xpStanzaErrorCondition", "") <?>- xpPartial ( \input -> case stanzaErrorConditionFromText input of- Nothing -> Left "Could not parse stanza error condition."- Just j -> Right j)- stanzaErrorConditionToText+ xpIso stanzaErrorConditionFromText+ stanzaErrorConditionToText stanzaErrorConditionToText BadRequest = "bad-request" stanzaErrorConditionToText Conflict = "conflict" stanzaErrorConditionToText FeatureNotImplemented = "feature-not-implemented"@@ -160,41 +180,43 @@ stanzaErrorConditionToText SubscriptionRequired = "subscription-required" stanzaErrorConditionToText UndefinedCondition = "undefined-condition" stanzaErrorConditionToText UnexpectedRequest = "unexpected-request"- stanzaErrorConditionFromText "bad-request" = Just BadRequest- stanzaErrorConditionFromText "conflict" = Just Conflict- stanzaErrorConditionFromText "feature-not-implemented" = Just FeatureNotImplemented- stanzaErrorConditionFromText "forbidden" = Just Forbidden- stanzaErrorConditionFromText "gone" = Just $ Gone Nothing- stanzaErrorConditionFromText "internal-server-error" = Just InternalServerError- stanzaErrorConditionFromText "item-not-found" = Just ItemNotFound- stanzaErrorConditionFromText "jid-malformed" = Just JidMalformed- stanzaErrorConditionFromText "not-acceptable" = Just NotAcceptable- stanzaErrorConditionFromText "not-allowed" = Just NotAllowed- stanzaErrorConditionFromText "not-authorized" = Just NotAuthorized- stanzaErrorConditionFromText "policy-violation" = Just PolicyViolation- stanzaErrorConditionFromText "recipient-unavailable" = Just RecipientUnavailable- stanzaErrorConditionFromText "redirect" = Just $ Redirect Nothing- stanzaErrorConditionFromText "registration-required" = Just RegistrationRequired- stanzaErrorConditionFromText "remote-server-not-found" = Just RemoteServerNotFound- stanzaErrorConditionFromText "remote-server-timeout" = Just RemoteServerTimeout- stanzaErrorConditionFromText "resource-constraint" = Just ResourceConstraint- stanzaErrorConditionFromText "service-unavailable" = Just ServiceUnavailable- stanzaErrorConditionFromText "subscription-required" = Just SubscriptionRequired- stanzaErrorConditionFromText "undefined-condition" = Just UndefinedCondition- stanzaErrorConditionFromText "unexpected-request" = Just UnexpectedRequest- stanzaErrorConditionFromText _ = Nothing+ stanzaErrorConditionFromText "bad-request" = BadRequest+ stanzaErrorConditionFromText "conflict" = Conflict+ stanzaErrorConditionFromText "feature-not-implemented" = FeatureNotImplemented+ stanzaErrorConditionFromText "forbidden" = Forbidden+ stanzaErrorConditionFromText "gone" = Gone Nothing+ stanzaErrorConditionFromText "internal-server-error" = InternalServerError+ stanzaErrorConditionFromText "item-not-found" = ItemNotFound+ stanzaErrorConditionFromText "jid-malformed" = JidMalformed+ stanzaErrorConditionFromText "not-acceptable" = NotAcceptable+ stanzaErrorConditionFromText "not-allowed" = NotAllowed+ stanzaErrorConditionFromText "not-authorized" = NotAuthorized+ stanzaErrorConditionFromText "policy-violation" = PolicyViolation+ stanzaErrorConditionFromText "recipient-unavailable" = RecipientUnavailable+ stanzaErrorConditionFromText "redirect" = Redirect Nothing+ stanzaErrorConditionFromText "registration-required" = RegistrationRequired+ stanzaErrorConditionFromText "remote-server-not-found" = RemoteServerNotFound+ stanzaErrorConditionFromText "remote-server-timeout" = RemoteServerTimeout+ stanzaErrorConditionFromText "resource-constraint" = ResourceConstraint+ stanzaErrorConditionFromText "service-unavailable" = ServiceUnavailable+ stanzaErrorConditionFromText "subscription-required" = SubscriptionRequired+ stanzaErrorConditionFromText "undefined-condition" = UndefinedCondition+ stanzaErrorConditionFromText "unexpected-request" = UnexpectedRequest+ stanzaErrorConditionFromText _ = UndefinedCondition xpStanzaError :: PU [Node] StanzaError xpStanzaError = ("xpStanzaError" , "") <?+> xpWrap- (\(tp, (cond, txt, ext)) -> StanzaError tp cond txt ext)- (\(StanzaError tp cond txt ext) -> (tp, (cond, txt, ext)))+ (\((tp, _code), (cond, txt, ext)) -> StanzaError tp cond txt ext)+ (\(StanzaError tp cond txt ext) -> ((tp, Nothing), (cond, txt, ext))) (xpElem "{jabber:client}error"- (xpAttr "type" xpStanzaErrorType)+ (xp2Tuple+ (xpAttr "type" xpStanzaErrorType)+ (xpAttribute' "code" xpId)) (xp3Tuple xpStanzaErrorCondition- (xpOption $ xpElem "{jabber:client}text"+ (xpOption $ xpElem "{urn:ietf:params:xml:ns:xmpp-stanzas}text" (xpAttrImplied xmlLang xpLang) (xpContent xpNonemptyText) )@@ -204,52 +226,54 @@ xpMessageError :: PU [Node] (MessageError) xpMessageError = ("xpMessageError" , "") <?+> xpWrap- (\((_, qid, from, to, lang), (err, ext)) ->- MessageError qid from to lang err ext)- (\(MessageError qid from to lang err ext) ->- (((), qid, from, to, lang), (err, ext)))+ (\((_, qid, from, to, lang, attr), (err, ext)) ->+ MessageError qid from to lang err ext attr)+ (\(MessageError qid from to lang err ext attr) ->+ (((), qid, from, to, lang, attr), (err, ext))) (xpElem "{jabber:client}message"- (xp5Tuple+ (xp6Tuple (xpAttrFixed "type" "error") (xpAttrImplied "id" xpId) (xpAttrImplied "from" xpJid) (xpAttrImplied "to" xpJid) (xpAttrImplied xmlLang xpLang)- -- TODO: NS?+ xpExtendedAttrs ) (xp2Tuple xpStanzaError (xpAll xpElemVerbatim)) ) xpPresenceError :: PU [Node] PresenceError xpPresenceError = ("xpPresenceError" , "") <?+> xpWrap- (\((qid, from, to, lang, _),(err, ext)) ->- PresenceError qid from to lang err ext)- (\(PresenceError qid from to lang err ext) ->- ((qid, from, to, lang, ()), (err, ext)))+ (\((qid, from, to, lang, _, attr),(err, ext)) ->+ PresenceError qid from to lang err ext attr)+ (\(PresenceError qid from to lang err ext attr) ->+ ((qid, from, to, lang, (), attr), (err, ext))) (xpElem "{jabber:client}presence"- (xp5Tuple+ (xp6Tuple (xpAttrImplied "id" xpId) (xpAttrImplied "from" xpJid) (xpAttrImplied "to" xpJid) xpLangTag (xpAttrFixed "type" "error")+ xpExtendedAttrs ) (xp2Tuple xpStanzaError (xpAll xpElemVerbatim)) ) xpIQError :: PU [Node] IQError xpIQError = ("xpIQError" , "") <?+> xpWrap- (\((qid, from, to, lang, _tp),(err, body)) ->- IQError qid from to lang err body)- (\(IQError qid from to lang err body) ->- ((qid, from, to, lang, ()), (err, body)))+ (\((qid, from, to, lang, _tp, attr),(err, body)) ->+ IQError qid from to lang err body attr)+ (\(IQError qid from to lang err body attr) ->+ ((qid, from, to, lang, (), attr), (err, body))) (xpElem "{jabber:client}iq"- (xp5Tuple+ (xp6Tuple (xpAttr "id" xpId) (xpAttrImplied "from" xpJid) (xpAttrImplied "to" xpJid) xpLangTag ((xpAttrFixed "type" "error"))+ xpExtendedAttrs ) (xp2Tuple xpStanzaError (xpOption xpElemVerbatim)) )@@ -376,16 +400,14 @@ xpMessageType :: PU Text MessageType xpMessageType = ("xpMessageType", "") <?>- xpPartial ( \input -> case messageTypeFromText input of- Nothing -> Left "Could not parse message type."- Just j -> Right j)- messageTypeToText+ xpIso messageTypeFromText+ messageTypeToText where- messageTypeFromText "chat" = Just Chat- messageTypeFromText "groupchat" = Just GroupChat- messageTypeFromText "headline" = Just Headline- messageTypeFromText "normal" = Just Normal- messageTypeFromText _ = Just Normal+ messageTypeFromText "chat" = Chat+ messageTypeFromText "groupchat" = GroupChat+ messageTypeFromText "headline" = Headline+ messageTypeFromText "normal" = Normal+ messageTypeFromText _ = Normal messageTypeToText Chat = "chat" messageTypeToText GroupChat = "groupchat" messageTypeToText Headline = "headline"@@ -437,10 +459,8 @@ xpStreamErrorCondition :: PU Text StreamErrorCondition xpStreamErrorCondition = ("xpStreamErrorCondition", "") <?>- xpPartial ( \input -> case streamErrorConditionFromText input of- Nothing -> Left "Could not parse stream error condition."- Just j -> Right j)- streamErrorConditionToText+ xpIso streamErrorConditionFromText+ streamErrorConditionToText where streamErrorConditionToText StreamBadFormat = "bad-format" streamErrorConditionToText StreamBadNamespacePrefix = "bad-namespace-prefix"@@ -467,29 +487,29 @@ streamErrorConditionToText StreamUnsupportedFeature = "unsupported-feature" streamErrorConditionToText StreamUnsupportedStanzaType = "unsupported-stanza-type" streamErrorConditionToText StreamUnsupportedVersion = "unsupported-version"- streamErrorConditionFromText "bad-format" = Just StreamBadFormat- streamErrorConditionFromText "bad-namespace-prefix" = Just StreamBadNamespacePrefix- streamErrorConditionFromText "conflict" = Just StreamConflict- streamErrorConditionFromText "connection-timeout" = Just StreamConnectionTimeout- streamErrorConditionFromText "host-gone" = Just StreamHostGone- streamErrorConditionFromText "host-unknown" = Just StreamHostUnknown- streamErrorConditionFromText "improper-addressing" = Just StreamImproperAddressing- streamErrorConditionFromText "internal-server-error" = Just StreamInternalServerError- streamErrorConditionFromText "invalid-from" = Just StreamInvalidFrom- streamErrorConditionFromText "invalid-namespace" = Just StreamInvalidNamespace- streamErrorConditionFromText "invalid-xml" = Just StreamInvalidXml- streamErrorConditionFromText "not-authorized" = Just StreamNotAuthorized- streamErrorConditionFromText "not-well-formed" = Just StreamNotWellFormed- streamErrorConditionFromText "policy-violation" = Just StreamPolicyViolation- streamErrorConditionFromText "remote-connection-failed" = Just StreamRemoteConnectionFailed- streamErrorConditionFromText "reset" = Just StreamReset- streamErrorConditionFromText "resource-constraint" = Just StreamResourceConstraint- streamErrorConditionFromText "restricted-xml" = Just StreamRestrictedXml- streamErrorConditionFromText "see-other-host" = Just StreamSeeOtherHost- streamErrorConditionFromText "system-shutdown" = Just StreamSystemShutdown- streamErrorConditionFromText "undefined-condition" = Just StreamUndefinedCondition- streamErrorConditionFromText "unsupported-encoding" = Just StreamUnsupportedEncoding- streamErrorConditionFromText "unsupported-feature" = Just StreamUnsupportedFeature- streamErrorConditionFromText "unsupported-stanza-type" = Just StreamUnsupportedStanzaType- streamErrorConditionFromText "unsupported-version" = Just StreamUnsupportedVersion- streamErrorConditionFromText _ = Nothing+ streamErrorConditionFromText "bad-format" = StreamBadFormat+ streamErrorConditionFromText "bad-namespace-prefix" = StreamBadNamespacePrefix+ streamErrorConditionFromText "conflict" = StreamConflict+ streamErrorConditionFromText "connection-timeout" = StreamConnectionTimeout+ streamErrorConditionFromText "host-gone" = StreamHostGone+ streamErrorConditionFromText "host-unknown" = StreamHostUnknown+ streamErrorConditionFromText "improper-addressing" = StreamImproperAddressing+ streamErrorConditionFromText "internal-server-error" = StreamInternalServerError+ streamErrorConditionFromText "invalid-from" = StreamInvalidFrom+ streamErrorConditionFromText "invalid-namespace" = StreamInvalidNamespace+ streamErrorConditionFromText "invalid-xml" = StreamInvalidXml+ streamErrorConditionFromText "not-authorized" = StreamNotAuthorized+ streamErrorConditionFromText "not-well-formed" = StreamNotWellFormed+ streamErrorConditionFromText "policy-violation" = StreamPolicyViolation+ streamErrorConditionFromText "remote-connection-failed" = StreamRemoteConnectionFailed+ streamErrorConditionFromText "reset" = StreamReset+ streamErrorConditionFromText "resource-constraint" = StreamResourceConstraint+ streamErrorConditionFromText "restricted-xml" = StreamRestrictedXml+ streamErrorConditionFromText "see-other-host" = StreamSeeOtherHost+ streamErrorConditionFromText "system-shutdown" = StreamSystemShutdown+ streamErrorConditionFromText "undefined-condition" = StreamUndefinedCondition+ streamErrorConditionFromText "unsupported-encoding" = StreamUnsupportedEncoding+ streamErrorConditionFromText "unsupported-feature" = StreamUnsupportedFeature+ streamErrorConditionFromText "unsupported-stanza-type" = StreamUnsupportedStanzaType+ streamErrorConditionFromText "unsupported-version" = StreamUnsupportedVersion+ streamErrorConditionFromText _ = StreamUndefinedCondition -- §4.9.2
source/Network/Xmpp/Sasl/Common.hs view
@@ -1,6 +1,7 @@ {-# OPTIONS_HADDOCK hide #-} {-# LANGUAGE PatternGuards #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE FlexibleContexts #-} module Network.Xmpp.Sasl.Common where @@ -86,10 +87,7 @@ xpSaslError :: PU Text.Text SaslError xpSaslError = ("xpSaslError", "") <?>- xpPartial ( \input -> case saslErrorFromText input of- Nothing -> Left "Could not parse SASL error."- Just j -> Right j)- saslErrorToText+ xpIso saslErrorFromText saslErrorToText where saslErrorToText SaslAborted = "aborted" saslErrorToText SaslAccountDisabled = "account-disabled"@@ -102,18 +100,18 @@ saslErrorToText SaslMechanismTooWeak = "mechanism-too-weak" saslErrorToText SaslNotAuthorized = "not-authorized" saslErrorToText SaslTemporaryAuthFailure = "temporary-auth-failure"- saslErrorFromText "aborted" = Just SaslAborted- saslErrorFromText "account-disabled" = Just SaslAccountDisabled- saslErrorFromText "credentials-expired" = Just SaslCredentialsExpired- saslErrorFromText "encryption-required" = Just SaslEncryptionRequired- saslErrorFromText "incorrect-encoding" = Just SaslIncorrectEncoding- saslErrorFromText "invalid-authzid" = Just SaslInvalidAuthzid- saslErrorFromText "invalid-mechanism" = Just SaslInvalidMechanism- saslErrorFromText "malformed-request" = Just SaslMalformedRequest- saslErrorFromText "mechanism-too-weak" = Just SaslMechanismTooWeak- saslErrorFromText "not-authorized" = Just SaslNotAuthorized- saslErrorFromText "temporary-auth-failure" = Just SaslTemporaryAuthFailure- saslErrorFromText _ = Nothing+ saslErrorFromText "aborted" = SaslAborted+ saslErrorFromText "account-disabled" = SaslAccountDisabled+ saslErrorFromText "credentials-expired" = SaslCredentialsExpired+ saslErrorFromText "encryption-required" = SaslEncryptionRequired+ saslErrorFromText "incorrect-encoding" = SaslIncorrectEncoding+ saslErrorFromText "invalid-authzid" = SaslInvalidAuthzid+ saslErrorFromText "invalid-mechanism" = SaslInvalidMechanism+ saslErrorFromText "malformed-request" = SaslMalformedRequest+ saslErrorFromText "mechanism-too-weak" = SaslMechanismTooWeak+ saslErrorFromText "not-authorized" = SaslNotAuthorized+ saslErrorFromText "temporary-auth-failure" = SaslTemporaryAuthFailure+ saslErrorFromText _ = SaslNotAuthorized -- Challenge element pickler. xpChallenge :: PU [Node] (Maybe Text.Text)@@ -137,10 +135,14 @@ saslInit :: Text.Text -> Maybe BS.ByteString -> ErrorT AuthFailure (StateT StreamState IO) () saslInit mechanism payload = do r <- lift . pushElement . saslInitE mechanism $- Text.decodeUtf8 . B64.encode <$> payload+ Text.decodeUtf8 . encodeEmpty . B64.encode <$> payload case r of Right () -> return () Left e -> throwError $ AuthStreamFailure e+ where+ -- §6.4.2+ encodeEmpty "" = "="+ encodeEmpty x = x -- | Pull the next element. pullSaslElement :: ErrorT AuthFailure (StateT StreamState IO) SaslElement
source/Network/Xmpp/Stanza.hs view
@@ -74,8 +74,8 @@ -- error type is derived from the condition using 'associatedErrorType' and -- both text and the application specific condition are left empty iqError :: StanzaErrorCondition -> IQRequest -> IQError-iqError condition (IQRequest iqid from' _to lang' _tp _bd) =- IQError iqid Nothing from' lang' (mkStanzaError condition) Nothing+iqError condition (IQRequest iqid from' _to lang' _tp _bd _attr) =+ IQError iqid Nothing from' lang' (mkStanzaError condition) Nothing [] -- | Create an IQ Result matching an IQ request@@ -86,6 +86,7 @@ , iqResultTo = view from iqr , iqResultLangTag = view lang iqr , iqResultPayload = pl+ , iqResultAttributes = [] } -- | The RECOMMENDED error type associated with an error condition. The
source/Network/Xmpp/Stream.hs view
@@ -44,8 +44,10 @@ import System.Log.Logger import System.Random (randomRIO) import Text.XML.Stream.Parse as XP+import Lens.Family2 (over) import Network.Xmpp.Utilities+import qualified Network.Xmpp.Lens as L -- "readMaybe" definition, as readMaybe is not introduced in the `base' package -- until version 4.6.@@ -133,10 +135,13 @@ response <- ErrorT $ runEventsSink $ streamS expectedTo case response of Right (ver, from, to, sid, lt, features)- | (Text.unpack ver) /= "1.0" ->+ | versionFromText ver == Nothing -> closeStreamWithError+ StreamUnsupportedVersion Nothing+ "Unspecified version"+ | let v = versionFromText ver+ in isJust v && majorVersion (fromJust v) >= 2 -> closeStreamWithError StreamUnsupportedVersion Nothing- "Unknown version"-+ "Non-1.x version" -- HACK: We ignore MUST-strength requirement (section 4.7.4. of RFC -- 6120) for the sake of compatibility with jabber.org -- | lt == Nothing ->@@ -464,7 +469,7 @@ Right (Right r) -> return $ Right r -- Performs the given IO operation, catches any errors and re-throws everything--- except 'ResourceVanished' and IllegalOperation, in which case it will return False instead+-- except 'ResourceVanished' and IllegalOperation, which it will return. catchPush :: IO () -> IO (Either XmppFailure ()) catchPush p = ExL.catch (p >> return (Right ()))@@ -533,7 +538,7 @@ , streamId = Nothing , streamLang = Nothing , streamJid = Nothing- , streamConfiguration = config+ , streamConfiguration = maybeSetTlsHost realm config } stream' <- mkStream stream return $ Right stream'@@ -546,12 +551,12 @@ liftIO . debugM "Pontarius.Xmpp" $ "In: " ++ (BSC8.unpack d) ++ "." return d+ tlsIdentL = L.tlsParamsL . L.clientServerIdentificationL+ updateHost host ("", _) = (host, "")+ updateHost _ hst = hst+ maybeSetTlsHost host = over tlsIdentL (updateHost host) --- Connects to the provided hostname or IP address. If a hostname is provided, a--- DNS-SRV lookup is performed (unless `sockAddr' has been specified, in which--- case that address is used instead). If an A(AAA) record results are--- encountered, all IP addresses will be tried until a successful connection--- attempt has been made. Will return the Handle acquired, if any.+-- Connects using the specified method. Returns the Handle acquired, if any. connect :: HostName -> StreamConfiguration -> ErrorT XmppFailure IO (Maybe StreamHandle) connect realm config = do@@ -593,6 +598,10 @@ "No SRV records, using fallback process." lift $ resolvAndConnectTcp resolvSeed (BSC8.pack $ host) 5222+ Just [(".", _)] -> do+ liftIO $ infoM "Pontarius.Xmpp"+ "SRV lookup returned \".\"; service not available"+ throwError TcpConnectionFailure Just srvRecords' -> do lift $ debugM "Pontarius.Xmpp" "SRV records found, performing A/AAAA lookups."@@ -624,10 +633,11 @@ connectTo address port) :: IO (Either Ex.IOException Handle) case result of Right handle -> do- debugM "Pontarius.Xmpp" "Successfully connected to HostName."+ debugM "Pontarius.Xmpp" $ "Successfully connected to " ++ show address return $ Just handle Left _ -> do- debugM "Pontarius.Xmpp" "Connection to HostName could not be established."+ debugM "Pontarius.Xmpp" $+ "Connection to " ++ show address ++ " could not be established." connectTcp remainder #if MIN_VERSION_dns(1, 0, 0)@@ -775,7 +785,7 @@ -> IO (Either XmppFailure (Either IQError IQResult)) pushIQ iqID to tp lang body stream = runErrorT $ do ErrorT $ pushStanza- (IQRequestS $ IQRequest iqID Nothing to lang tp body) stream+ (IQRequestS $ IQRequest iqID Nothing to lang tp body []) stream res <- lift $ pullStanza stream case res of Left e -> throwError e
source/Network/Xmpp/Tls.hs view
@@ -2,6 +2,7 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PackageImports #-}+{-# LANGUAGE FlexibleContexts #-} module Network.Xmpp.Tls where @@ -16,12 +17,14 @@ import qualified Data.ByteString.Lazy as BL import Data.Conduit import Data.IORef+import Data.Monoid import Data.XML.Types import Network.DNS.Resolver (ResolvConf) import Network.TLS import Network.Xmpp.Stream import Network.Xmpp.Types import System.Log.Logger (debugM, errorM, infoM)+import System.X509 mkBackend :: StreamHandle -> Backend mkBackend con = Backend { backendSend = \bs -> void (streamSend con bs)@@ -54,7 +57,7 @@ . wrapExceptions . flip withStream con . runErrorT $ do- conf <- gets $ streamConfiguration+ conf <- gets streamConfiguration sState <- gets streamConnectionState case sState of Plain -> return ()@@ -123,7 +126,11 @@ tlsinit params backend = do liftIO $ debugM "Pontarius.Xmpp.Tls" "TLS with debug mode enabled." gen <- liftIO (cprgCreate <$> createEntropyPool :: IO SystemRNG)- con <- client params gen backend+ sysCStore <- liftIO getSystemCertificateStore+ let params' = params{clientShared =+ (clientShared params){ sharedCAStore =+ sysCStore <> sharedCAStore (clientShared params)}}+ con <- client params' gen backend handshake con let src = forever $ do dt <- liftIO $ recvData con@@ -173,20 +180,23 @@ Nothing -> throwError TcpConnectionFailure Just h'' -> return h'' let hand = handleToStreamHandle h- (_raw, _snk, psh, recv, ctx) <- tlsinit params $ mkBackend hand- return $ StreamHandle { streamSend = catchPush . psh- , streamReceive = wrapExceptions . recv- , streamFlush = contextFlush ctx- , streamClose = bye ctx >> streamClose hand- }+ let params' = params{clientServerIdentification+ = case clientServerIdentification params of+ ("", _) -> (host, "")+ csi -> csi+ }+ (_raw, _snk, psh, recv, ctx) <- tlsinit params' $ mkBackend hand+ return StreamHandle{ streamSend = catchPush . psh+ , streamReceive = wrapExceptions . recv+ , streamFlush = contextFlush ctx+ , streamClose = bye ctx >> streamClose hand+ } wrapExceptions :: IO a -> IO (Either XmppFailure a) wrapExceptions f = Ex.catches (liftM Right $ f) [ Ex.Handler $ return . Left . XmppIOException , Ex.Handler $ wrap . XmppTlsError- , Ex.Handler $ wrap . XmppTlsConnectionNotEstablished- , Ex.Handler $ wrap . XmppTlsTerminated- , Ex.Handler $ wrap . XmppTlsHandshakeFailed+ , Ex.Handler $ wrap . XmppTlsException , Ex.Handler $ return . Left ] where
source/Network/Xmpp/Types.hs view
@@ -22,12 +22,15 @@ , IQResponse(..) , IQResult(..) , LangTag (..)+ , langTagQ , langTagFromText , langTagToText , parseLangTag+ , ExtendedAttribute , Message(..) , message , MessageError(..)+ , messageError , MessageType(..) , Presence(..) , presence@@ -37,6 +40,9 @@ , SaslFailure(..) , StreamFeatures(..) , Stanza(..)+ , messageS+ , messageErrorS+ , presenceS , StanzaError(..) , StanzaErrorCondition(..) , StanzaErrorType(..)@@ -44,6 +50,7 @@ , XmppTlsError(..) , StreamErrorCondition(..) , Version(..)+ , versionFromText , StreamHandle(..) , Stream(..) , StreamState(..)@@ -52,6 +59,7 @@ , ConnectionDetails(..) , StreamConfiguration(..) , xmppDefaultParams+ , xmppDefaultParamsStrong , Jid(..) #if WITH_TEMPLATE_HASKELL , jidQ@@ -89,7 +97,8 @@ import Data.Text (Text) import qualified Data.Text as Text import Data.Typeable(Typeable)-import Data.XML.Types+import Data.XML.Types as XML+import qualified Data.Text.Encoding as Text #if WITH_TEMPLATE_HASKELL import Language.Haskell.TH import Language.Haskell.TH.Quote@@ -138,6 +147,8 @@ | PresenceErrorS !PresenceError deriving (Eq, Show) +type ExtendedAttribute = (XML.Name, Text)+ -- | A "request" Info/Query (IQ) stanza is one with either "get" or "set" as -- type. It always contains an xml payload. data IQRequest = IQRequest { iqRequestID :: !Text@@ -146,6 +157,7 @@ , iqRequestLangTag :: !(Maybe LangTag) , iqRequestType :: !IQRequestType , iqRequestPayload :: !Element+ , iqRequestAttributes :: ![ExtendedAttribute] } deriving (Eq, Show) -- | The type of IQ request that is made.@@ -163,6 +175,7 @@ , iqResultTo :: !(Maybe Jid) , iqResultLangTag :: !(Maybe LangTag) , iqResultPayload :: !(Maybe Element)+ , iqResultAttributes :: ![ExtendedAttribute] } deriving (Eq, Show) -- | The answer to an IQ request that generated an error.@@ -172,6 +185,7 @@ , iqErrorLangTag :: !(Maybe LangTag) , iqErrorStanzaError :: !StanzaError , iqErrorPayload :: !(Maybe Element) -- should this be []?+ , iqErrorAttributes :: ![ExtendedAttribute] } deriving (Eq, Show) -- | The message stanza. Used for /push/ type communication.@@ -181,6 +195,7 @@ , messageLangTag :: !(Maybe LangTag) , messageType :: !MessageType , messagePayload :: ![Element]+ , messageAttributes :: ![ExtendedAttribute] } deriving (Eq, Show) -- | An empty message@@ -201,6 +216,7 @@ , messageLangTag = Nothing , messageType = Normal , messagePayload = []+ , messageAttributes = [] } -- | Empty message stanza@@ -219,9 +235,31 @@ , messageErrorLangTag :: !(Maybe LangTag) , messageErrorStanzaError :: !StanzaError , messageErrorPayload :: ![Element]+ , messageErrorAttributes :: ![ExtendedAttribute] } deriving (Eq, Show) +messageError :: MessageError+messageError = MessageError { messageErrorID = Nothing+ , messageErrorFrom = Nothing+ , messageErrorTo = Nothing+ , messageErrorLangTag = Nothing+ , messageErrorStanzaError =+ StanzaError { stanzaErrorType = Cancel+ , stanzaErrorCondition =+ ServiceUnavailable+ , stanzaErrorText = Nothing+ , stanzaErrorApplicationSpecificCondition = Nothing+ }+ , messageErrorPayload = []+ , messageErrorAttributes = []+ } +instance Default MessageError where+ def = messageError++messageErrorS :: Stanza+messageErrorS = MessageErrorS def+ -- | The type of a Message being sent -- (<http://xmpp.org/rfcs/rfc6121.html#message-syntax-type>) data MessageType = -- | The message is sent in the context of a one-to-one chat@@ -266,6 +304,7 @@ , presenceLangTag :: !(Maybe LangTag) , presenceType :: !PresenceType , presencePayload :: ![Element]+ , presenceAttributes :: ![ExtendedAttribute] } deriving (Eq, Show) -- | An empty presence.@@ -276,6 +315,7 @@ , presenceLangTag = Nothing , presenceType = Available , presencePayload = []+ , presenceAttributes = [] } -- | Empty presence stanza@@ -292,6 +332,7 @@ , presenceErrorLangTag :: !(Maybe LangTag) , presenceErrorStanzaError :: !StanzaError , presenceErrorPayload :: ![Element]+ , presenceErrorAttributes :: ![ExtendedAttribute] } deriving (Eq, Show) -- | @PresenceType@ holds Xmpp presence types. The "error" message type is left@@ -532,9 +573,7 @@ } deriving (Show, Eq) data XmppTlsError = XmppTlsError TLSError- | XmppTlsConnectionNotEstablished TLSException- | XmppTlsTerminated TLSException- | XmppTlsHandshakeFailed TLSException+ | XmppTlsException TLSException deriving (Show, Eq, Typeable) -- | Signals an XMPP stream error or another unpredicted stream-related@@ -692,10 +731,10 @@ -- | Signals the state of the stream connection. data ConnectionState- = Closed -- ^ No stream has been established+ = Closed -- ^ Stream has not been established yet | Plain -- ^ Stream established, but not secured via TLS | Secured -- ^ Stream established and secured via TLS- | Finished -- ^ Stream is closed+ | Finished -- ^ Stream was closed deriving (Show, Eq, Typeable) -- | Defines operations for sending, receiving, flushing, and closing on a@@ -1009,8 +1048,9 @@ guard $ Text.all (`Set.notMember` prohibMap) l'' l''' <- nonEmpty l'' return $ Just l'''- domainPart' <- SP.runStringPrep (SP.namePrepProfile False) d+ domainPart' <- SP.runStringPrep (SP.namePrepProfile False) (stripSuffix d) guard $ validDomainPart domainPart'+ guard $ validPartLength domainPart' domainPart <- nonEmpty domainPart' resourcePart <- case r of Nothing -> return Nothing@@ -1026,7 +1066,10 @@ -- checks validPartLength :: Text -> Bool- validPartLength p = Text.length p > 0 && Text.length p < 1024+ validPartLength p = Text.length p > 0+ && BS.length (Text.encodeUtf8 p) < 1024+ -- RFC6122 §2.2+ stripSuffix t = if Text.last t == '.' then Text.init t else t -- | Returns 'True' if the JID is /bare/, that is, it doesn't have a resource -- part, and 'False' otherwise.@@ -1149,11 +1192,14 @@ | UseSrv HostName -- ^ Use this hostname for a SRV lookup | UseHost HostName PortID -- ^ Use specified host | UseConnection (ErrorT XmppFailure IO StreamHandle)- -- ^ Use custom method to create a StreamHandle. This+ -- ^ Use a custom method to create a StreamHandle. This -- will also be used by reconnect. For example, to -- establish TLS before starting the stream as done by -- GCM, see 'connectTls'. You can also return an- -- already established connection.+ -- already established connection. This method should+ -- also return a hostname that is used for TLS+ -- signature verification. If startTLS is not used it+ -- can be left empty -- | Configuration settings related to the stream. data StreamConfiguration =@@ -1182,9 +1228,9 @@ , tlsParams :: ClientParams } --- | Default parameters for TLS. Those are the default client parameters from the tls package with the ciphers set to ciphersuite_strong-xmppDefaultParams :: ClientParams-xmppDefaultParams = (defaultParamsClient "" BS.empty)+-- | Default parameters for TLS restricted to strong ciphers+xmppDefaultParamsStrong :: ClientParams+xmppDefaultParamsStrong = (defaultParamsClient "" BS.empty) { clientSupported = def { supportedCiphers = ciphersuite_strong ++ [ cipher_AES256_SHA1@@ -1192,6 +1238,15 @@ ] } }++-- | Default parameters for TLS+xmppDefaultParams :: ClientParams+xmppDefaultParams = (defaultParamsClient "" BS.empty)+ { clientSupported = def+ { supportedCiphers = ciphersuite_all+ }+ }+ instance Default StreamConfiguration where def = StreamConfiguration { preferredLang = Nothing
+ tests/Run.hs view
@@ -0,0 +1,14 @@+module Main where++import Test.Tasty.HUnit+import Test.Tasty++import qualified Run.SendReceive as SendReceive+import qualified Run.Google as Google++sendReceiveTest = testCase "send and receive" SendReceive.run+googleTest = testCase "connect to google service" Google.connectGoogle++main = defaultMain $ testGroup "connection tests" [ sendReceiveTest+ , googleTest+ ]
+ tests/Run/Payload.hs view
@@ -0,0 +1,117 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE PatternGuards #-}+{-# LANGUAGE LambdaCase #-}+++module Run.Payload where++import Control.Monad+import Control.Monad.STM+import qualified Data.Text as Text+import Data.XML.Pickle+import Data.XML.Types+import Network.Xmpp+import Network.Xmpp.Internal+import System.Log.Logger+import Test.HUnit hiding (Node)+import Test.Hspec.Expectations++data Payload = Payload+ { payloadCounter :: !Int+ , ignoreFlag :: !Bool+ , errorFlag :: !Bool+ , payloadText :: !Text.Text+ } deriving (Eq, Show)++testNS :: Text.Text+testNS = "xmpp:library:test"++payloadP :: PU [Node] Payload+payloadP = xpWrap (\((counter,iFlag, eFlag) , message)+ -> Payload counter iFlag eFlag message)+ (\(Payload counter iFlag eFlag message)+ ->((counter,iFlag, eFlag) , message)) $+ xpElem (Name "request" (Just testNS) Nothing)+ (xp3Tuple+ (xpAttr "counter" xpPrim)+ (xpAttr "ignoreFlag" xpPrim)+ (xpAttr "errorFlag" xpPrim)+ )+ (xpElemNodes (Name "message" (Just testNS) Nothing)+ (xpContent xpId))++invertPayload :: Payload -> Payload+invertPayload (Payload count _iFlag _eFlag message) =+ Payload (count + 1) False False (Text.reverse message)++iqResponder :: Session -> IO ()+iqResponder context = do+ chan' <- listenIQ Set testNS context+ chan <- case chan' of+ Left _ -> do+ assertFailure "Channel was already taken"+ undefined+ Right c -> return c+ forever $ do+ next <- atomically $ chan+ let Right payload = unpickleElem payloadP . iqRequestPayload $+ iqRequestBody next+ let answerPayload = invertPayload payload+ let answerBody = pickleElem payloadP answerPayload+ unless (ignoreFlag payload) . void $+ case errorFlag payload of+ False -> answerIQ next (Right $ Just answerBody) []+ True -> answerIQ next (Left $ mkStanzaError NotAcceptable) []++testString :: Text.Text+testString = "abc ÄÖ>"++testPayload :: Jid -> Session -> IO ()+testPayload them session = do+ infoM "Pontarius.Xmpp" "Testing IQ send/receive"+ let pl1 = Payload 1 False False testString+ body1 = pickleElem payloadP pl1+ resp <- sendIQ' (Just 3000000) (Just them) Set Nothing body1 [] session++ case resp of+ Left e -> assertFailure $ "Could not send pl1" ++ show e+ Right (IQResponseError e) ->+ assertFailure $ "Unexpected IQ error" ++ show e+ Right (IQResponseResult IQResult{iqResultPayload = Just pl}) -> do+ case unpickleElem payloadP pl of+ Left e -> assertFailure $ "Error unpickling response p1"+ ++ ppUnpickleError e+ Right r -> do+ payloadCounter r `shouldBe` 2+ payloadText r `shouldBe` Text.reverse testString+ Right (IQResponseResult _) ->+ assertFailure "IQ result didn't contain payload"+ infoM "Pontarius.Xmpp" "Done testing IQ send/receive"+ ----------------------+ -- Timeout test+ ----------------------+ let pl2 = Payload 2 True False testString+ body2 = pickleElem payloadP pl2+ infoM "Pontarius.Xmpp" "Testing timeout"+ resp <- sendIQ' (Just 1000000) (Just them) Set Nothing body2 [] session+ case resp of+ Left IQTimeOut -> return ()+ Left e -> assertFailure $ "Unexpected send error" ++ show e+ Right r -> assertFailure $ "Unexpected IQ answer" ++ show r+ infoM "Pontarius.Xmpp" "IQ timed out (as expected)"+ ----------------------+ -- Error test+ ----------------------+ infoM "Pontarius.Xmpp" "Testing IQ error"+ let pl3 = Payload 3 False True testString+ body3 = pickleElem payloadP pl3+ resp <- sendIQ' (Just 3000000) (Just them) Set Nothing body3 [] session+ case resp of+ Left e -> assertFailure $ "Unexpected send error" ++ show e+ Right (IQResponseError e) ->+ stanzaErrorCondition (iqErrorStanzaError e) `shouldBe` NotAcceptable++ Right r -> assertFailure $ "Received unexpected IQ response" ++ show r+ infoM "Pontarius.Xmpp" "Received expected error"
tests/Tests/Arbitrary/Xmpp.hs view
@@ -5,7 +5,7 @@ import Data.Char import Data.Maybe import qualified Data.Text as Text-import Network.Xmpp.Types+import Network.Xmpp.Internal hiding (elements) import Test.QuickCheck import Test.QuickCheck.Instances() import qualified Text.CharRanges as Ranges