create user with xmppframework - ios

I'm currently trying to setup a chat functionality by using ejabberd
I'm using the xmppframework to communicate with my ejabberd server.
What I'm trying to do is the following.
#IBAction func registerUser(_ sender: Any) {
var username: String = ""
var password: String = ""
let elements: NSMutableArray = []
if let user = tf_user.text {
username = "\(user)#192.168.1.19"
elements.add(XMLElement(name: "username", stringValue: username))
}
if let pass = tf_password.text {
password = pass
elements.add(XMLElement(name: "password", stringValue: password))
}
if username != "" && password != "" {
if stream.supportsInBandRegistration() {
do {
try stream.register(withElements: elements as! [Any])
} catch let error {
print(error)
}
}
}
}
However when I make the register call I get the following error
<iq xmlns="jabber:client" from="192.168.1.19" type="error">
<query xmlns="jabber:iq:register">
<username>user1#192.168.1.19</username>
<password>pass</password>
</query><error code="400" type="modify">
<bad-request xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>
<text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">Malformed username</text></error></iq>
I'm not sure why this isn't working. Does anyone know where I'm going wrong? I feel like it has something to do with adding my element.

Related

I tried to connect and fetch mails using mailcore2 in iOS. But I got error

Error Domain=MCOErrorDomain Code=5 "Unable to authenticate with the current session's credentials." UserInfo={NSLocalizedDescription=Unable to authenticate with the current session's credentials.}
I put this code in my project.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
prepareImapSession()
}
var imapsession:MCOIMAPSession = MCOIMAPSession()
var error : Error? = nil
func prepareImapSession()
{
// CONFIGURE THAT DEPENDING OF YOUR NEEDS
imapsession.hostname = "imap.gmail.com" // String
imapsession.username = my email // String
imapsession.password = password // String
imapsession.port = 993 // UInt32 number
imapsession.authType = MCOAuthType.saslLogin
imapsession.connectionType = MCOConnectionType.TLS
DispatchQueue.main.asyncAfter(deadline: .now() + 8) { [self] in
self.useImapWithUIDS()
}
imapsession.connectOperation()
}
func useImapWithUIDS() {
// There is more than one option here, explore depending of your needs
// let kind = MCOIMAPMessagesRequestKind()
// let headers = kind.union(MCOIMAPMessagesRequestKind.headers)
// let request = headers.union(MCOIMAPMessagesRequestKind.flags)
let requestKind: MCOIMAPMessagesRequestKind = [.headers, .flags]
let folder : String = "INBOX"
// HERE ALSO EXPLORE DEPENDING OF YOUR NEEDS, RANGE IT IS THE RANGE OF THE UIDS THAT YOU WANT TO FETCH, I SUGGEST TO YOU TO CHANGE THE // NUMBER ONE IF YOU HAVE A LOWER BOUND TO FETCH EMAIL
let uids : MCOIndexSet = MCOIndexSet(range: MCORangeMake(1, UINT64_MAX))
let fetchOperation = imapsession.fetchMessagesOperation(withFolder: folder, requestKind: requestKind, uids: uids)
fetchOperation?.start
{ [self] (err, msg, vanished) -> Void in
if (err != nil)
{
self.error = err
NSLog((err?.localizedDescription)!)
}
else
{
guard let msgs = msg as? [MCOIMAPMessage]
else
{
print("ERROR GETTING THE MAILS")
return
}
for i in 0..<msgs.count
{
// THE SUBJECT
let subject = msgs[i].header.subject
// THE uid for this email. The uid is unique for one email
let uid = msgs[i].uid
self.useImapFetchContent(uidToFetch: uid)
// The sequenceNumber like the nomber say it is the sequence for the emails in the INBOX from the first one // (sequenceNumber = 1) to the last one , it not represent always the same email. Because if you delete one email then //next one will get the sequence number of that email that was deleted
let sequenceNumber = msgs[i].sequenceNumber
}
}
}
}
// MARK: - EXTRACT THE CONTENT OF ONE EMAIL, IN THIS FUNCTION YOU NEED THE uid, THE UNIQUE NUMBER FOR ONE EMAIL
func useImapFetchContent(uidToFetch uid: UInt32) {
let operation: MCOIMAPFetchContentOperation = imapsession.fetchMessageOperation(withFolder: "INBOX", uid: uid)
operation.start { (Error, data) in
if (Error != nil)
{
NSLog("ERROR")
return
}
let messageParser: MCOMessageParser = MCOMessageParser(data: data)
// IF YOU HAVE ATTACHMENTS USE THIS
let attachments = messageParser.attachments() as? [MCOAttachment]
// THEN YOU NEED THIS PROPERTIE, IN THIS EXAMPLE I TAKE THI FIRST, USE WHAT EVER YOU WANT
let attachData = attachments?.first?.data
// FOR THE MESSAGEPARSER YOU CAN EPLORE MORE THAN ONE OPTION TO OBTAIN THE TEXT
let msgPlainBody = messageParser.plainTextBodyRendering()
}
}
}
I using the mailcore2 framework. I got error description Unable to authenticate with the current session's credentials.
It can be related with 2nd factor authentication on your account. It was described on Apple forum.

Login credentials missing in swift UserDefaults

I have an application which uses a rest api for authentication. The problem I am facing now is that I save user's token in my UserDefaults and username too because those are the two main parameters needed to get user details. so if the application is closed by the user he should still be able to view the view his profile when he opens the application back but instead the profile returns empty details. this is the UserDefaults codes that I have
let defaults = UserDefaults.standard
var isLoggedIn : Bool {
get {
return defaults.bool(forKey: LOGGED_IN_KEY)
}
set {
defaults.set(newValue, forKey: LOGGED_IN_KEY)
}
}
//Auth Token
var authToken: String {
get {
return defaults.value(forKey: TOKEN_KEY) as? String ?? ""
}
set {
defaults.set(newValue, forKey: TOKEN_KEY)
}
}
var userUsername: String {
get {
return defaults.value(forKey: USERNAME_KEY) as? String ?? ""
}
set {
defaults.set(newValue, forKey: USERNAME_KEY)
}
}
I have no idea why it isnt retrieving the user data.
My second question is when I logout the user, all the users details are cleared as expected but the moment I try loging in with a different user, the new user's authToken and details gets printed in the console but the user profile returns the profile of the previous person. which is not supposed to be. my code is shown below
func logoutUser() -> Void {
pk = 0
username = ""
email = ""
firstName = ""
lastName = ""
AuthService.instance.isLoggedIn = false
AuthService.instance.authToken = ""
AuthService.instance.userUsername = ""
}
#IBAction func logoutPressed(_ sender: Any) {
UserDataService.instance.logoutUser()
dismiss(animated: true, completion: nil)
}
I would also like to add that when i run the api using postman i get a response that "detail": "Signature has expired." so i had to input the new token in the header so it displays the user details again
enum SettingKeys: String {
case authToken
//...
}
struct Settings {
static var authToken: String? {
get { return UserDefaults.standard.string(forKey: SettingKeys.authToken.rawValue) }
set(value) { UserDefaults.standard.set(value, forKey: SettingKeys.authToken.rawValue) }
}
static func deleteAll(exclude: [SettingKeys] = []) {
let saveKeys = exclude.map({ $0.rawValue })
for key in UserDefaults.standard.dictionaryRepresentation().keys {
if !saveKeys.contains(key) {
UserDefaults.standard.removeObject(forKey: key)
}
}
}
}
I recommend storing keys as Enum, because then u can use it like that:
//Read
if let token = Settings.authToken {
//do something
}
//Write
Settings.authToken = "123456"
//Delete settings
Settings.deleteAll()
//or choose what to leave
Settings.deleteAll(exclude: [.authToken])
And it's worth to mention that defaults.synchronize() is deprecated.

Accessing database in a command

I want to create a command in which you can create a user (like database seeds).
However, I cannot access a database in a command, my code is like the following:
import Command
import Crypto
struct CreateUserCommand: Command {
var arguments: [CommandArgument] {
return [.argument(name: "email")]
}
var options: [CommandOption] {
return [
.value(name: "password", short: "p", default: "", help: ["Password of a user"]),
]
}
var help: [String] {
return ["Create a user with provided identities."]
}
func run(using context: CommandContext) throws -> Future<Void> {
let email = try context.argument("email")
let password = try context.requireOption("password")
let passwordHash = try BCrypt.hash(password)
let user = User(email: email, password: password)
return user.save(on: context.container).map(to: Future<Void>) { user in
return .done(on: context.container)
}
}
}
Like the above, I want to save users by executing a query on context.container, but I got argument type 'Container' does not conform to expected type 'DatabaseConnectable' Error.
How to access to the database in a command?
It seems like this might be the way to go:
func run(using context: CommandContext) throws -> EventLoopFuture<Void> {
let email = try context.argument("email")
let password = try context.requireOption("password")
let passwordHash = try BCrypt.hash(password)
let user = User(email: email, password: password)
return context.container.withNewConnection(to: .psql) { db in
return user.save(on: db).transform(to: ())
}
}

Moya-ObjectMapper response handling

We are using Alamofire with Moya and Moya-ObjectMapper, to handle the API calls.
Response from server is as below:
{
"userid" : "1",
"token" : "abc"
}
ObjectMapper is unable to handle this and always throws exception.
Error info: JSONMapping(Status Code: 200, Data Length: 313)
At the same time, it is able to handle response in this format:
[
{
"email":["validation.unique"]
}
]
I verified that the JSON response is in proper format. My code snippet is here:
struct SignupNLoginResponse: Mappable {
var token: String?
var userID: String?
init?(_ map: Map){
}
mutating func mapping(map: Map) {
token <- map["token"]
userID <- map["user_id"]
}
}
#IBAction func processSignUp () {
SnLInput.name = "Celine Peter"
SnLInput.email = "celine.peter#domain.in"
SnLInput.password = "testing#123"
provider.request(AppTarget.signUp, completion: { result in
var success = true
var message = "Unable to signup"
switch result {
case let .Success(response):
do {
let outputString:NSString! = NSString(data:response.data, encoding:NSUTF8StringEncoding)
print(outputString)
if let repos: [SignupNLoginResponse]? = try response.mapObjectMapper() {
print(repos)
} else {
success = false
}
} catch {
success = false
print("Error info: \(error)")
}
case let .Failure(error):
guard let error = error as? CustomStringConvertible else {
break
}
message = error.description
}
print(message)
print(success)
})
}
This is our first project using these libraries and we couldn't understand what we are missing. We appreciate any help, pointing us in the right direction. Please let know if any information found missing to figure out the issue.
There are two types of function did you proper method like below, maybe you're missing that.

Retrieving Archived messages from XMPP server in ios

I am integrating XMPP functionality in my ios app and i came across a problem i cannot solve.
The problem is i cannot get archived messages from the server. My client is able to log in and i have tested several service calls (send, receive messages, getting info about a user) with success.
Upon sending
<iq type='get' id='pref1'>
<pref xmlns='urn:xmpp:archive'/>
</iq>
The response is
SEND: <iq type="get"><pref xmlns="urn:xmpp:archive"/></iq>
RECV: <iq xmlns="jabber:client" type="error" to="1#iis2/ae76edc"><error code="501"
type="cancel"><feature-not-implemented xmlns="urn:ietf:params:xml:ns:xmpp-
stanzas"/</error></iq>
The server administrator is able to see the archived messages, as he activated archiving.
Must something be done server or client side in order to achieve this functionality? Could it be that seeing old messages and the server actually implementing and supporting XEP-0136, are two different things?
If you want fetch from server means use this code
internal var xmppMAM: XMPPMessageArchiveManagement?
func setupXMPPMam(){
xmppMAM = XMPPMessageArchiveManagement.init()
xmppMAM?.addDelegate(self, delegateQueue: .global(qos: .background))
// stream is XMPPStream
xmppMAM?.activate(stream)
}
call the setupMam function once XMPP connect
func retrieveArchiveMessage(){
let set = XMPPResultSet(max: totalCount)
xmppMAM?.retrieveMessageArchive(at: XMPPJID(string: user), withFields: nil, with: set)
}
func xmppStream(_ sender: XMPPStream, willReceive message: XMPPMessage) -> XMPPMessage? {
if let forwardedMessage = message.mamResult?.forwardedMessage{
debugPrint(forwardedMessage)
return message
}
}
if you using Robbiehanson framework above code is working perfectly for fetch value from server.
I hope this article is useful for you #Akash Thakkar
an example to get archived messages in Swift 4
declares and initializes the variables XMPPMessageArchivingCoreDataStorage where I initialize the XMPPStream
var xmppMessageStorage: XMPPMessageArchivingCoreDataStorage?
var xmppMessageArchiving: XMPPMessageArchiving?
xmppMessageStorage = XMPPMessageArchivingCoreDataStorage.sharedInstance()
xmppMessageArchiving = XMPPMessageArchiving(messageArchivingStorage: xmppMessageStorage)
xmppMessageArchiving?.clientSideMessageArchivingOnly = true
xmppMessageArchiving?.activate(stream)
xmppMessageArchiving?.addDelegate(self, delegateQueue: DispatchQueue.main)
doing this, whenever a message arrives, this will cause it to be archived without needing to do anything else.
then, to retrieve the archived message
func RecibedMessageArchiving(idFriend: String) {
let JabberIDFriend = idFriend //id friend chat, example test1#example.com
let moc = xmppMessageStorage?.mainThreadManagedObjectContext
let entityDescription = NSEntityDescription.entity(forEntityName: "XMPPMessageArchiving_Message_CoreDataObject", in: moc!)
let request = NSFetchRequest<NSFetchRequestResult>()
let predicateFormat = "bareJidStr like %# "
let predicate = NSPredicate(format: predicateFormat, JabberIDFriend)
request.predicate = predicate
request.entity = entityDescription
//jabberID id del usuario, cliente
var jabberIDCliente = ""
if let jabberj = globalChat.value(forKey: "jabberID"){
jabberIDCliente = jabberj as! String
}
do {
let results = try moc?.fetch(request)
for message: XMPPMessageArchiving_Message_CoreDataObject? in results as? [XMPPMessageArchiving_Message_CoreDataObject?] ?? [] {
var element: DDXMLElement!
do {
element = try DDXMLElement(xmlString: (message as AnyObject).messageStr)
} catch _ {
element = nil
}
let body: String
let sender: String
let date: NSDate
let isIncomings: Bool
if message?.body != nil {
body = (message?.body)!
} else {
body = ""
}
if element.attributeStringValue(forName: "to") == JabberIDFriend {
sender = jabberIDCliente
isIncomings = false
} else {
sender = "test2#example.com"
isIncomings = true
}
var m: [AnyHashable : Any] = [:]
m["msg"] = message?.body
print("body", message?.body)
print("test", element.attributeStringValue(forName: "to"))
print("test2", element.attributeStringValue(forName: "body"))
}
} catch _ {
//catch fetch error here
}
}

Resources