multi_friend_selector.php help

Jul 17, 2008 at 1:44 AM

I've been having a pretty hard time getting my application to work with http://www.facebook.com/multi_friend_selector.php so hopefully someone here can help me.  I tried to keep my code as simple as possible.

I created a MultiFriendSelector and GenerateSignature method based mainly on code I found floating around Google.

I call the method like this...

#########################################################################

InviteFriends.NavigateUrl = MultiFriendSelector("http://apps.facebook.com/myapp", "Hello", "Test", "123", "request", "true");

#########################################################################

The MultiFriendSelector receives in the values that were passed to it and puts them all together to create a URL.  It also calls the GenerateSignature method in order to append the md5 hash at the end of the URL.

#########################################################################

public string MultiFriendSelector(string action, string content, string actiontext, string excludeList, string type, string invite)
 {
        String api_key = "###My API KEY Here###";
        String url = "
http://www.facebook.com/multi_friend_selector.php?";
        url += "action=" + action;
  url += "&actiontext=" + actiontext;
        url += "&api_key=" + api_key;
  url += "&content=" + content;
  url += "&exclude_ids=" + excludeList;
        url += "&invite=" + invite;
        url += "&type=" + type;
        url += "&sig=" + GenerateSignature(action, content, actiontext, excludeList, type, invite, api_key);

        return url;
 }

#########################################################################


The GenerateSignature method receives the values it needs, adds them to a SortedList to sort them by key.  It then appends all of the keys=values pairs together, md5 encrypts it, and then appends that on to the end.
#########################################################################

internal string GenerateSignature(string action, string content, string actiontext, string excludeList, string type, string invite, string api_key)
 {
        SortedList signatureArray = new SortedList();
        signatureArray.Add("action", action);
        signatureArray.Add("actiontext", actiontext);
        signatureArray.Add("api_key", api_key);
        signatureArray.Add("content", content);
        signatureArray.Add("excludeList", excludeList);
        signatureArray.Add("invite", invite);
        signatureArray.Add("type", type);

 

        String appendedValues = "";

        IDictionaryEnumerator ide = signatureArray.GetEnumerator();

        while (ide.MoveNext()) {
            appendedValues += (ide.Key.ToString() + "=" + ide.Value.ToString());
        }

        appendedValues += "###My SECRET KEY Here###";

        System.Security.Cryptography.MD5CryptoServiceProvider x = new System.Security.Cryptography.MD5CryptoServiceProvider();
        byte[] bs = System.Text.Encoding.UTF8.GetBytes(appendedValues);
        bs = x.ComputeHash(bs);
        System.Text.StringBuilder s = new System.Text.StringBuilder();
        foreach (byte b in bs) {
            s.Append(b.ToString("x2").ToLower());
        }
        String sig = s.ToString();

        return sig;
 }

#########################################################################

I'm sure the GenerateSignature method is working properly, because I have tested the hash it returns with ones returned from other MD5 hash generators such as http://www.xs4all.nl/~jlpoutre/BoT/Javascript/Utils/md5_hashing.html

The problem I am having is that the URL MultiFriendSelector returns does not seem to be working right.  It is not a syntax error, it is a logical error, which is why I need help.  It returns a link such as:

http://www.facebook.com/multi_friend_selector.php?action=http://apps.facebook.com/myapp&actiontext=Test&api_key=e533b4b0e99ca241dd60b0878ca02dd8&content=Hello&exclude_ids=123&invite=true&type=request&sig=6b08bb1a6d58ab3def163ec9ab70c680

When I go to that link, I get the following error:

Error with multi friend selector:
The request was not properly signed - received signature "6b08bb1a6d58ab3def163ec9ab70c680".
(You are only seeing this because you are the developer of the application.)

If I go to that link while not signed in on facebook, it simply redirects back to http://www.facebook.com/home.php

 

Jul 17, 2008 at 3:27 PM
Hi dreamshake,

I tried this exact same thing yesterday (asked at bottom of another thread http://www.codeplex.com/FacebookNET/Thread/View.aspx?ThreadId=22834),
I reused the FB.net security code to build my MD5, but I think my problem is I am not exactly sure what to pass in (read thread for details).

I think sig is failing like mine as missing a param, and you get redirected to home in 2nd case as this is default behaviour for this page if you are not developer of an application (i.e. a user or logged out).

When this is sorted we can release 'simple' code for reuse by others.

Hope someone can help,

Regards,
Matt
Jul 18, 2008 at 12:06 AM
Edited Jul 18, 2008 at 12:09 AM
My co-worker, who did the multi-friend-selector work on our app, had this to say:

This link has an example on how to do it within VB.Net

http://wiki.developers.facebook.com/index.php/Talk:Multi_friend_selector

I've done the same thing in C# if anyone wants to see the code.

This technique use POST data rather than GET data.  The author explains why in his article.

However, I think it is possible that you're not URL encoding your values in your querystring (GET data).  I don't think you'd have to URL encode the values when generating the signature, because the receiving server should be url decoding the values then MD5 hashing that to compare to your signature.

public static string GetMd5Hash(string input)
{
            // Create a new instance of the MD5CryptoServiceProvider object.
            System.Security.Cryptography.MD5 md5Hasher = System.Security.Cryptography.MD5.Create();
            // Convert the input string to a byte array and compute the hash.
            byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input));
            // Create a new Stringbuilder to collect the bytes and create a string.
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < data.Length; i++)
            {
                sb.Append(data[i].ToString("x2"));
            }
            return sb.ToString();
}

Then in the relevant page, use the following code to handle the submission:

string strConcat = String.Format("action={0}actiontext={1}api_key={2}content={3}exclude_ids={4}invite={5}max={6}type={7}{8}", action, action_text, AppKey, content, exclude_ids, invite, max, type, Secret);

string sig = Utilities.GetMd5Hash(strConcat);

// build a table for the javascript to work with
Response.Write("<form id=\"postForm\" target=\"_top\" action=\"http://www.facebook.com/multi_friend_selector.php\" method=\"post\">");
Response.Write("</form>");

StringBuilder sb = new StringBuilder();
// build javascript to insert hidden elements into form
sb.Append("<script language=javascript>");
sb.Append("function setElem(form, name, value) {");
sb.Append("var el = document.createElement(\"input\");");
sb.Append("el.type = \"hidden\";");
sb.Append("el.name = name;");
sb.Append("el.value = value;");
sb.Append("form.appendChild(el);");
sb.Append("}");

// insert parameters into form and submit
sb.Append("var myForm = document.forms['postForm'];");
sb.AppendFormat("setElem(myForm, 'action', '{0}');", action);
sb.AppendFormat("setElem(myForm, 'actiontext', '{0}');", action_text);
sb.AppendFormat("setElem(myForm, 'api_key', '{0}');", AppKey);
sb.AppendFormat("setElem(myForm, 'content', '{0}');", content);
sb.AppendFormat("setElem(myForm, 'exclude_ids', '{0}');", exclude_ids);
sb.AppendFormat("setElem(myForm, 'invite', '{0}');", invite);
sb.AppendFormat("setElem(myForm, 'max', '{0}');", max);
sb.AppendFormat("setElem(myForm, 'type', '{0}');", type);
sb.AppendFormat("setElem(myForm, 'sig', '{0}');", sig);
sb.Append("myForm.submit();");
sb.Append("</script>");

ClientScript.RegisterClientScriptBlock(Type.GetType("System.String"), "addScript", sb.ToString());

If you have any further questions, let me know and I'll forward them along to him.

Bill
Jul 18, 2008 at 6:47 AM
Converting it from GET to POST solved the problem.

Thank you very much, wcbrown.
Jul 20, 2008 at 2:01 PM
Thanks again Bill, simply adding URLENCODE did the trick for me on get, will switch to post as required.
Jul 22, 2008 at 4:49 PM
You're welcome. My co-worker is glad that his work bore fruit. I'm thinking that we should probably amend the framework so that this sort of thing is built-in and easier to use, but I'll have to ponder the proper integration point.

Bill
Jul 24, 2008 at 6:32 PM
I've been trying to do the same thing and i've copied the code from here just changing the content to meet my needs, however whenever I try to run it with facebook I get a blank page.

Is it possible for you to post the complete code files for the aspx and aspx.cs here so that I can try them.

Rgds,
Chris
Jul 24, 2008 at 10:37 PM
I fixed my problem, it was a problem with the way the content variable was being parsed into the javascript. Here is the updated line of code im using

sb.AppendFormat(

"setElem(myForm, 'content', \"{0}\");", content);

 

Aug 14, 2008 at 9:30 AM
Hi All,

Thought this was sorted :( but NO, never ever simple

Any ideas why I do not get my return page params back from MFS when I select a person and send? (the msg goes thru fine!)

page.aspx?mfs_typeahead_req_form_48a3fa6d53301=w&ids%5B0%5D=1271745342

I do get them back fine on GET when I press SKIP?

Thanks for help!

Matt
Jun 8, 2009 at 7:34 AM

Bill, Thanks! It worked!

 

My only thing is, I do not know how to get exclude Id's.

 

here's the VB code from http://wiki.developers.facebook.com/index.php/Talk:Multi_friend_selector

 

<code>

Private Function getMembers(ByVal api_key As String, ByVal secret As String) As String
Dim strExcludeIds As String = ""
Dim _fbService As New Facebook.Components.FacebookService()
_fbService.ApplicationKey = api_key
_fbService.Secret = secret
_fbService.IsDesktopApplication = False
_fbService.SessionKey = Session("facebook_session_key")
_fbService.UserId = Session("facebook_userId")
Try
Dim otherFriends As System.Collections.ObjectModel.Collection(Of Facebook.Entity.User) = _fbService.GetFriendsAppUsers
For Each myFriend As Facebook.Entity.User In otherFriends
strExcludeIds &= myFriend.UserId & ","
Next
If strExcludeIds.Length > 0 Then
strExcludeIds.TrimEnd(",")
End If
Catch ex As Exception
End Try
Return strExcludeIds
End Function

</code>

 

I cannot get Facebook.Components.FacebookService() working? Am I missing an API?