pgstath / Sharp.Xmpp

Sharp.Xmpp is looking for a maintainer! Unfortunatelly I do not have currently the time needed to maintain the library. Luckily a small but vibrant community has evolved around Sharp.Xmpp. If you would like to be the project's maintainer please sent an email to pgstath@gmail.com. This should include issues, tickets and commits that you have done for Sharp.Xmpp or other similar project. Sharp.Xmpp is a multiplatform, Windows and Android, .NET XMPP client assembly.Sharp.Xmpp supports IM functionality & a variety of XMPP extensions, is simple and is extensively tested against Android Xamarin. It is a fork of the currently frozen excellent S22.Xmpp project. Sharp.Xmpp will be at the FOSSDEM 2016 Real time DevRoom!
Other
84 stars 51 forks source link

Registration error #57

Closed akoepe closed 7 years ago

akoepe commented 7 years ago

Hy! Can somebody help me out, because I can't figure out the built in registration function. How should I create the registrationcallback? A detaild example would also help, I coulnd't find enough information about these functons.

XmppClient client = new XmppClient("hostname"); client.Message += (s, e) => { Console.WriteLine(e.Message); }; client.Register(registrationcallback); client.Connect();

Thanks!

cimframe commented 7 years ago

If you can find the S22.XMPP (of which the Sharp code originated) source, there is a help file that contains some helpful examples. Here is what the help file has on account registration that I used as an example to get my code up and running:

Create an instance of the XmppClient class using the constructor that does not expect username and password parameters, connect to the XMPP server on which you wish to register a new XMPP account and call the Register method, passing it a delegate of type RegistrationCallback.

using (XmppClient client = new XmppClient("jabber.ccc.de")) {
    // Setup any event handlers here, before connecting to the server.
    // In this example, we don't need to install any.
    client.Connect();

    // Try to initiate the in-band registration process with the server.
    client.Register(RegistrationCallback);

    [...]

The RegistrationCallback is a method that is invoked during the registration process to let the user enter any data that the server requires in order to create a new account. Many servers merely require the user to enter their designated username and password, but some ask the user to provide additional information such as his or her real name or ask the the user to enter a CAPTCHA.

To facilitate the different needs of server administrators, XMPP employs a flexible request/submit mechanism similar to HTML forms: The server sends a form with various fields and asks the client to fill it out. What kind of fields the form contains is entirely up to the server administrator and differs from server to server.

A shortended version of the RegistrationCallback method is given below.

static SubmitForm RegistrationCallback(RequestForm form) {
    // The RequestForm contains all fields the user is required to fill out
    // in order to complete account registration with the XMPP server.
    if(!String.IsNullOrEmpty(form.Instructions))
        Console.WriteLine(form.Instructions);

    SubmitForm submitForm = new SubmitForm();
    // Go through each field of the form and gather the data from the user.
    foreach (var field in form.Fields) {
        // Gather the input data; XMPP dataforms support different field-types
        // allowing server administrators to flexibly gather whatever data
        // is required to register an account;
        DataField f = null;

        // The field is a text-field, similar to a WinForms TextControl or HTML input field.
        if (field is TextField)
            f = ReadTextField(field as TextField);
        // The field is a password field, similar to an HTML input field of type "password".
        else if (field is PasswordField)
            f = ReadPasswordField(field as PasswordField);
        // The field is a boolean field, which can be either true or false.
        else if (field is BooleanField)
            f = ReadBoolField(field as BooleanField);
        // The field is an input field expecting a Jabber ID (JID).
        else if (field is JidField)
            f = ReadJidField(field as JidField);
        // Hidden fields should just be returned to the server, usually without
        // modification. They serve the same purpose as hidden fields in HTML
        // forms.
        else if (field is HiddenField)
            f = field;
        else if (field is FixedField) {
            // Fixed fields are static labels of text that should be rendered to the user much
            // like a WinForms Label. They don't require any input.
            var fixedField = field as FixedField;
            Console.WriteLine("Static label: " + fixedField.Description);
        }

        if (f != null)
            submitForm.Fields.Add(f);
    }
    // Return the filled-out form to the server.
    return submitForm;
}

After your callback finishes, you then do an Authenticate to complete the client initialization. Hope this helps.

cimframe commented 7 years ago

Forgot to add:

private TextField ReadTextField (DataField textField) {

    switch (textField.Name) {
        case "email":
            return new TextField (textField.Name, textField.Required, textField.Label, textField.Description, _emailAddress);
        case "name":
            return new TextField (textField.Name, textField.Required, textField.Label, textField.Description, _fullName);
        case "password":
            return new TextField (textField.Name, textField.Required, textField.Label, textField.Description, _password);
        case "username":
            return new TextField (textField.Name, textField.Required, textField.Label, textField.Description, _username);
    }

    return null;
}
akoepe commented 7 years ago

Thanks for your answer. I've also found these tutorials, but it was this actually, which I didn't understand.

What exactly should I pass as registrationcallback parameter? Is this a template receveid from the server(in this case how do I get it in my client app and how do I link it into the Register funtion) or is it a template which I add to my client side project, like an xml file or is it a formula, which is already filled out with the user credentials?

I don't need anything fancy just add a user(usrname, password), if my code with some modification from the first message runs in a ConsoleApplication I can figure out the rest.

Thanks again!

cimframe commented 7 years ago

The callback takes the RequestForm parameter like shown above. This object is defined in the extension XEP-0004/Dataforms folder. If you take the code from above and paste it verbatim into your class, it will do everything you need. The request form is passed into the client by the xmpp server so you don't have to do anything special other than process all of the data fields and answer, at a minimum, the required fields which are usually just the username/password.

cimframe commented 7 years ago

Here is a complete example (this works for other situation where the registration callback is used such as MUC configuration):

    internal class Program {

        private const string Hostname = "YOUR_XMPP_SERVER_NAME";
        private const string Password = "PW";
        private const string Username = "UN";

        private static void Main () {

            using (var client = new XmppClient (Hostname)) {

                client.Connect ();

                try {
                    client.Register (RegisterCallback);
                    client.Authenticate (Username, Password);
                } catch (Exception ex) {
                    Console.WriteLine (ex);
                    Console.ReadLine ();
                }

                while (true) {
                    Console.Write ("> ");

                    var command = Console.ReadLine ();

                    if (command == "quit") {
                        return;
                    }
                }
            }
        }

        private static SubmitForm RegisterCallback (RequestForm form) {

            var submitForm = new SubmitForm ();

            // Go through each field of the form and gather the data from the user.
            foreach (var field in form.Fields) {
                // Gather the input data; XMPP dataforms support different field-types
                // allowing server administrators to flexibly gather whatever data
                // is required to register an account;
                DataField f = null;

                // The field is a text-field, similar to a WinForms TextControl or HTML input field.
                if (field is TextField) {
                    f = ReadTextField (field as TextField);
                } else if (field is ListField) {
                    f = ReadListField (field as ListField);
                } else if (field is ListMultiField) {
                    f = ReadListMultiField (field as ListMultiField);
                } else if (field is BooleanField) {
                    f = ReadBooleanField (field as BooleanField);
                } else if (field is PasswordField) {
                    f = ReadPasswordField (field as PasswordField);
                }

                if (f != null) {
                    submitForm.Fields.Add (f);
                }
            }

            // Return the filled-out form to the server.
            return submitForm;
        }

        private static TextField ReadTextField (DataField field) {

            switch (field.Name) {
                case "email":
                    return new TextField (field.Name, Email);
                case "name":
                    return new TextField (field.Name, Name);
                case "password":
                    return new TextField (field.Name, Password);
                case "username":
                    return new TextField (field.Name, Username);

                case "muc#roomconfig_roomdesc":
                    return new TextField (field.Name, "Testing");
                case "muc#roomconfig_roomname":
                    return new TextField (field.Name, "Testing");
            }

            return null;
        }

        private static ListField ReadListField (DataField field) {

            if (field.Name == "allow_private_messages_from_visitors") {
                return new ListField (field.Name, "anyone");
            }

            if (field.Name == "muc#roomconfig_maxusers") {
                return new ListField (field.Name, "200");
            }

            return null;
        }

        private static ListMultiField ReadListMultiField (DataField field) {

            return field.Name == "muc#roomconfig_presencebroadcast"
                       ? new ListMultiField (field.Name, "moderator", "participant")
                       : null;
        }

        private static BooleanField ReadBooleanField (DataField field) {

            switch (field.Name) {
                case "allow_private_messages":
                    return new BooleanField (field.Name, true);
                case "allow_query_users":
                    return new BooleanField (field.Name, true);
                case "allow_subscription":
                    return new BooleanField (field.Name, true);
                case "allow_visitor_nickchange":
                    return new BooleanField (field.Name, true);
                case "allow_visitor_status":
                    return new BooleanField (field.Name, true);
                case "allow_voice_requests":
                    return new BooleanField (field.Name, true);
                case "mam":
                    return new BooleanField (field.Name, true);
                case "members_by_default":
                    return new BooleanField (field.Name, true);
                case "muc#roomconfig_allowinvites":
                    return new BooleanField (field.Name, true);
                case "muc#roomconfig_changesubject":
                    return new BooleanField (field.Name, true);
                case "muc#roomconfig_membersonly":
                    return new BooleanField (field.Name, true);
                case "muc#roomconfig_moderatedroom":
                    return new BooleanField (field.Name, false);
                case "muc#roomconfig_passwordprotectedroom":
                    return new BooleanField (field.Name, false);
                case "muc#roomconfig_persistentroom":
                    return new BooleanField (field.Name, true);
                case "muc#roomconfig_publicroom":
                    return new BooleanField (field.Name, true);
                case "muc#roomconfig_whois":
                    return new BooleanField (field.Name, true);
                case "public_list":
                    return new BooleanField (field.Name, true);
            }

            return null;
        }

        private static PasswordField ReadPasswordField (DataField field) {

            return field.Name == "password"
                       ? new PasswordField (field.Name, Password)
                       : null;
        }
    }
akoepe commented 7 years ago

OK. Now I understand it, I was confused and thought RegistrationCallback is an object and tried to create it.

Anyway thanks a lot, I really appreciate your help and effort.