SAML Idp Creation taking too much time - c#

I am using "Kentor.AuthServices.dll" and "Kentor.AuthServices.Mvc.dll" in my code to allowing Single sign on with ADFS server and it is working fine but the problem is that it is taking around more than 1 min show the adfs login screen.
I have debugged the code and record the timing and found the all the code working fine but identity provider creating code is taking more than 1 min.
I am not able to understand why it is taking too much time.
I am putting my code below can anyone please help?
thanks in advance.
try
{
CommonUtility.LogMessage("Start at:" + DateTime.Now);
string adfsUrl = System.Configuration.ConfigurationManager.AppSettings["ADServer"] ?? "";
if(string.IsNullOrEmpty(adfsUrl))
{
CommonUtility.LogMessage("no adfs server found in config");
return RedirectToAction("Login", "Account", string.Empty);
}
string requestUrlScheme = System.Configuration.ConfigurationManager.AppSettings["ADInstance"] ?? "https";
string federationUrl = System.Configuration.ConfigurationManager.AppSettings["ADFSMetaData"] ?? "";
CommonUtility.LogMessage("metdaDataUrl=" + federationUrl);
string trustUrl = string.Format("{0}/adfs/services/trust", adfsUrl);
CommonUtility.LogMessage("trustURL=" + trustUrl);
var idps = Kentor.AuthServices.Mvc.AuthServicesController.Options.IdentityProviders.KnownIdentityProviders;
foreach (var idpItem in idps)
{
CommonUtility.LogMessage("existing ENtity ID=" + idpItem.EntityId.Id);
if (idpItem.EntityId.Id.Equals(trustUrl))
{
Kentor.AuthServices.Mvc.AuthServicesController.Options.IdentityProviders.Remove(idpItem.EntityId);
CommonUtility.LogMessage("removed existing entity at:" + DateTime.Now);
}
}
var spOptions = CreateSPOptions(requestUrlScheme);
CommonUtility.LogMessage("SP option created at:" + DateTime.Now);
Kentor.AuthServices.IdentityProvider idp = null;
**idp = new Kentor.AuthServices.IdentityProvider(new EntityId(trustUrl), spOptions)
{
AllowUnsolicitedAuthnResponse = true,
LoadMetadata = true,
MetadataLocation = federationUrl,
};**
CommonUtility.LogMessage("idp added at:" + DateTime.Now);
if (Kentor.AuthServices.Mvc.AuthServicesController.Options.SPOptions.EntityId == null)
Kentor.AuthServices.Mvc.AuthServicesController.Options.SPOptions.EntityId = new EntityId(string.Concat(string.Format("{0}://{1}{2}", requestUrlScheme, Request.Url.Authority, Url.Content("~")), "AuthServices"));
else
Kentor.AuthServices.Mvc.AuthServicesController.Options.SPOptions.EntityId.Id =
string.Concat(string.Format("{0}://{1}{2}", requestUrlScheme, Request.Url.Authority, Url.Content("~")), "AuthServices");
CommonUtility.LogMessage("AuthServicesURL=" + string.Concat(string.Format("{0}://{1}{2}", requestUrlScheme, Request.Url.Authority, Url.Content("~")), "AuthServices"));
Kentor.AuthServices.Mvc.AuthServicesController.Options.SPOptions.ReturnUrl =
new Uri(string.Concat(string.Format("{0}://{1}{2}", requestUrlScheme, Request.Url.Authority, Url.Content("~")), "SAMLAuthentication/SAMLResponse"));
CommonUtility.LogMessage("SAMLResponseURL=" + string.Concat(string.Format("{0}://{1}{2}", requestUrlScheme, Request.Url.Authority, Url.Content("~")), "SAMLAuthentication/SAMLResponse"));
Kentor.AuthServices.Mvc.AuthServicesController.Options.IdentityProviders.Add(idp);
CommonUtility.LogMessage("redirect times:" + DateTime.Now);
return RedirectToAction("SignIn", "AuthServices", new { idp = trustUrl });
}
catch (Exception ex)
{
CommonUtility.LogException(ex);
throw ex;
}

When you use "LoadMetadata", the IdentityProvider object will load the metadata from the remote address at construction time. If I remember correctly, that's done synchronously to be able to report errors back as an exception. Does it take time (or give a timeout) to download the metadata?

Related

Need help to find where does API takes data from in ASP.NET MVC

I am a begginer and i work in a MVC project which I cant understand it well yet.
I can't understand where does the API takes data from when I try to connect in Login Screen.
It doesn't use Entity Framework and there isn't a json with the data.
When I enter Id and Pass it calls an API (GetAPIResponse) which somehow finds that is correct.
Need help to understand the code and the logic behind it.
LoginBL class contains:
public bool IsAuthenticated(LoginEntity user)
{
string url = string.Empty;
string callType = string.Empty;
string server = string.Empty;
try
{
// get URL, Call type, Server from config file
url = ConfigurationManager.AppSettings["login_url"].ToString();
callType = ConfigurationManager.AppSettings["calltype"].ToString();
server = ConfigurationManager.AppSettings["server"].ToString();
// Encrypt password
string password = Scrambler.GenerateMD5Hash(user.Password);
// Prepare content for the POST request
string content = #"calltype=" + callType + "&server=" + server + "&user=" + user.UserName + "&pass=" + password + "";
Debug.WriteLine("Callcenter login url: " + content);
HttpResponseMessage json_list = ApiCallBL.GetAPIResponse(url, content);
LoginResponseEntity obj = new LoginResponseEntity();
obj = JsonConvert.DeserializeObject<LoginResponseEntity>(json_list.Content.ReadAsStringAsync().Result);
Debug.WriteLine(callType + " Response: " + json_list.Content.ReadAsStringAsync().Result);
//if API resultCode return 0 then user details and token save in session for further use
if (obj.ResultCode == 0)
{
int restrict = obj.UserInfo.RestrictCallType.HasValue ?
obj.UserInfo.RestrictCallType.Value : 0;
HttpContext.Current.Session["user_id"] = obj.UserInfo.usr_id;
HttpContext.Current.Session["user_name"] = obj.UserInfo.usr_username;
HttpContext.Current.Session["user_group_id"] = obj.UserInfo.UserGroupID;
HttpContext.Current.Session["groupid"] = obj.UserInfo.groupid;
HttpContext.Current.Session["token"] = obj.Token;
HttpContext.Current.Session["web_server_url"] = obj.ServerInfo.web_server_url;
HttpContext.Current.Session["centerX"] = obj.ServerInfo.DefaultGeoX;
HttpContext.Current.Session["centerY"] = obj.ServerInfo.DefaultGeoY;
HttpContext.Current.Session["dateFormat"] = obj.ServerInfo.dateFormat;
HttpContext.Current.Session["currency"] = obj.ServerInfo.currency;
HttpContext.Current.Session["customer_img"] = obj.ServerInfo.customer_img;
HttpContext.Current.Session["groups"] = obj.groups;
HttpContext.Current.Session["restrict_call_type"] = restrict ;
Debug.WriteLine("obj.UserInfo.UserGroupID " + obj.UserInfo.UserGroupID);
Debug.WriteLine("obj.UserInfo.groups " + obj.groups);
//HttpContext.Current.Session["defaultLanguage"] = obj.ServerInfo.defaultLanguage;
HttpCookie cookie = new HttpCookie("Login");
// if remember me checked then user name and password stored in cookie else cookes is expired
if (user.RememberMe)
{
cookie.Values.Add("user_name", obj.UserInfo.usr_username);
cookie.Values.Add("pwd", user.Password);
cookie.Expires = DateTime.Now.AddDays(15);
HttpContext.Current.Response.Cookies.Add(cookie);
}
else
{
cookie.Expires = DateTime.Now.AddDays(-1);
HttpContext.Current.Response.Cookies.Add(cookie);
}
return true;
}
else
{
//ResultCode -5 :Invalid Login ,-1:Database Error ,-2:Server Error ,-3:Invalid Parameter specified ,-4:Invalid Token
return false;
}
}
catch
{
throw;
}
finally
{
url = string.Empty;
callType = string.Empty;
server = string.Empty;
}
}
Okay here after converts pass to MD5 creates a "string content" with the information given.
Then in next line (HttpResponseMessage json_list = ApiCallBL.GetAPIResponse(url, content);) calls the API with the url and content as parameters where it finds if the data exists.
API code:
public static HttpResponseMessage GetAPIResponse(string url, string content)
{
StringBuilder traceLog = null;
HttpContent httpContent = null;
try
{
traceLog = new StringBuilder();
traceLog.AppendLine("Start: BusinessLayer getAPIResponse() Request Data:- " + DateTime.Now + "URL = " + url + "&content = " + httpContent);
using (HttpClient client = new HttpClient())
{
httpContent = new StringContent(content);
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
var resp = client.PostAsync(url, httpContent).Result;
Debug.WriteLine("resp: " + resp.Content.ReadAsStringAsync().Result);
traceLog.AppendLine("End: BusinessLayer getAPIResponse() call completed HttpResponseMessage received");
return resp;
}
}
catch
{
throw;
}
finally
{
traceLog = null;
httpContent.Dispose();
url = string.Empty;
content = string.Empty;
}
}
In the following line, console prints the result that I cant understand where it cames from (Debug.WriteLine("resp: " + resp.Content.ReadAsStringAsync().Result);)
Sorry for the confusion , I am in my first job with zero work experience and I am called to learn how this works alone without proper education on ASP.NET from them.
You will not go very far without debbugger. Learn how to debug in Visual Studio (YouTube tutorials might be fastest way). Place debug points along critical points in code (for example moment when client sends and receives response is line var resp = client.PostAsync...) and check variables.
Url for API server is actually defined in the line
url = ConfigurationManager.AppSettings["login_url"].ToString();
ConfigurationManager means Web.config file, check it's appSettings section for login_url entry, there is your url.
Btw, using (HttpClient client = new HttpClient()) is not a good way to use a HttpClient and will lead to port exhaustion. It's ok for small number of requests, but for larger ones you must reuse it, or use HttpClientFactory (for .NET Core).

How to close SQLite Connection in Xamarin Forms?

I have a code the gets data from my server. Every data will be inserted in my local database (SQLite Database) one by one. Every once in a whole I am getting these two errors. My codes below is where the exception are always appearing.
SQLite.SQLiteException: Busy
SQLite.SQLiteException: database is locked
What is/are the cause(s) why I always get these exceptions?
Update:
I added await conn.CloseAsync(); in every end of the code is that correct?
here is my first code, it will inquire to the server if there were updates and if there are updates it will insert or replace(update) the data in my local database:
var db = DependencyService.Get<ISQLiteDB>();
var conn = db.GetConnection();
string apifile = "sync-retailer-outlet-server-update-api.php";
var lastchecked = Preferences.Get("retaileroutletchangelastcheck", String.Empty, "private_prefs");
int count = 0;
var uri = new Uri(string.Format("http://" + domain + "/TBSApp/app_api/" + apifile + "?Host=" + host + "&Database=" + database + "&ContactID=" + contact + "&LastChecked=" + lastchecked, string.Empty));
try
{
SyncStatus("Getting retailer outlet data from server");
var response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
if (!string.IsNullOrEmpty(content))
{
var dataresult = JsonConvert.DeserializeObject<List<RetailerGroupData>>(content, settings);
var datacount = dataresult.Count;
for (int i = 0; i < datacount; i++)
{
SyncStatus("Saving retailer outlet server update to local database (" + (count + 1) + " out of " + dataresult.Count + ")");
var item = dataresult[i];
var retailerCode = item.RetailerCode;
var insertdata = new RetailerGroupTable
{
RetailerCode = retailerCode
};
await conn.InsertOrReplaceAsync(insertdata);
count++;
}
}
}
}
catch (Exception ex)
{
Crashes.TrackError(ex);
}
await conn.CloseAsync();
Here is the other code that creates the local database table everytime the StartPage is loaded.
public async void CreateTableAsync()
{
try
{
var db = DependencyService.Get<ISQLiteDB>();
var conn = db.GetConnection();
if (conn != null)
{
try
{
await conn.CreateTableAsync<UserTable>();
await conn.CreateTableAsync<ContactsTable>();
await conn.CreateTableAsync<ActivityTable>();
await conn.CreateTableAsync<CAFTable>();
await conn.CreateTableAsync<RetailerGroupTable>();
await conn.CreateTableAsync<UserEmailTable>();
await conn.CreateTableAsync<UserLogsTable>();
await conn.CreateTableAsync<SubscriptionTable>();
await conn.CreateTableAsync<ProvinceTable>();
await conn.CreateTableAsync<TownTable>();
}
catch (Exception ex)
{
Console.Write("Creating table error " + ex.Message);
}
}
}
catch (Exception ex)
{
Crashes.TrackError(ex);
await DisplayAlert("Application Error", "Error:\n\n" + ex.Message.ToString() + "\n\n Please contact your administrator", "Ok");
}
}
await conn.CloseAsync();
using (SQLiteConnection connection = db.GetConnection())
{
// Do whatever you want to do with your active connection
}
Note that you cannot reuse that connection outside the scope of the using block and have to get a new connection the same way the next time you want to access your database.
According to your description and code, I assume you use async-style inserts and are on different threads and thus an insert is timing out waiting for the lock of a different insert to complete. You can use synchronous inserts to avoid this condition.

C# LDAP: Authenticationtype.Secure makes dn-syntax wrong

I need to add a new user to a group with my ldap-system.
public bool AddMemberToGroup(string user, string group)
{
user = "cn=" + user + ",ou=People" + uri;
group = "cn=" + group + ",ou=groups";
this.ldapConnection = new DirectoryEntry("LDAP://" + ip +"/" + "cn=testgroup,ou=groups,dc=my,dc=own,dc=path-to-ldap,dc=de", "admin", "adminPW");
this.ldapConnection.AuthenticationType = AuthenticationTypes.Secure;
try
{
this.ldapConnection.Properties["member"].Add(user);
this.ldapConnection.CommitChanges();
this.ldapConnection.Close();
return true;
}
catch (Exception ex)
{
Debug.WriteLine(ex);
return false;
}
}
In the above code snippet, I get the error, that the dn-syntax is invalid. When I change the AuthenticationTypes to Anonymous, I get the error, that CommitChanges needs authentication.
So I've got two alternatives:
No authentication, but then: How can I add a User to a group
Authenticate, but then: How is my syntax wrong? Could it be, that the server is unavailable?

How to process a DynamoDBEvent in a C# Lambda Function?

I created a C# Lambda function that is being triggered with a DynamoDB stream. It gets excecuted just fine. However, the StreamRecord of the NewImage value returns no values. The count is 0. What am I doing wrong? I have checked all the AWS documention but this seems more and more like a bug. My below lambda function should work and it should return at least 1 StreamRecord in my example. attributeMap.Count always returns 0 but it should return 1.
public void FunctionHandler(DynamoDBEvent dynamoDbEvent, ILambdaContext context)
{
Console.WriteLine($"Beginning to process {dynamoDbEvent.Records.Count} records...");
foreach (var record in dynamoDbEvent.Records)
{
Console.WriteLine($"Event ID: {record.EventID}");
Console.WriteLine($"Event Name: {record.EventName}");
var attributeMap = record.Dynamodb.NewImage;
if (attributeMap.Count > 0) // If item does not exist, attributeMap.Count will be 0
{
Console.WriteLine(attributeMap["AccountId"].S);
}
}
Console.WriteLine("Stream processing complete.");
}
UPDATE: October, 4th, 2018. I no longer use an admin app. I now use CloudFormation exclusively to create and maintain everyting including full CI/CD pipelines with CodePipelines. This includes all Serverless lamba functions in .NET Core 2.1.
I figured it out. The sucky thing is that AWS Docs do not say anything about this. This was a pain in the ass to find out. For anyone else who might need this information here it is: You have to set the stream view type when you create the DynamoDB stream for the table. Here is a picture for the AWS Console:
However, since I setup all tables via an admin console (in C# Core 2.0), here is how I setup the setup the table including the stream specification and the event source mapping request to the lambda function:
var request = new CreateTableRequest
{
TableName = TABLE_CREATE_ACCOUNT,
AttributeDefinitions = new List<AttributeDefinition>()
{
new AttributeDefinition
{
AttributeName = "CommandId",
AttributeType = ScalarAttributeType.S
}
},
KeySchema = new List<KeySchemaElement>()
{
new KeySchemaElement
{
AttributeName = "CommandId",
KeyType = KeyType.HASH
}
},
ProvisionedThroughput = new ProvisionedThroughput
{
ReadCapacityUnits = 1,
WriteCapacityUnits = 1
},
StreamSpecification = new StreamSpecification
{
StreamEnabled = true,
StreamViewType = StreamViewType.NEW_IMAGE
}
};
try
{
var response = _db.CreateTableAsync(request);
var tableDescription = response.Result.TableDescription;
Console.WriteLine("{1}: {0} ReadCapacityUnits: {2} WriteCapacityUnits: {3}",
tableDescription.TableStatus,
tableDescription.TableName,
tableDescription.ProvisionedThroughput.ReadCapacityUnits,
tableDescription.ProvisionedThroughput.WriteCapacityUnits);
string status = tableDescription.TableStatus;
Console.WriteLine(TABLE_CREATE_ACCOUNT + " - " + status);
WaitUntilTableReady(TABLE_CREATE_ACCOUNT);
// This connects the DynamoDB stream to a lambda function
Console.WriteLine("Creating event source mapping between table stream '"+ TABLE_CREATE_ACCOUNT + "' and lambda 'ProcessCreateAccount'");
var req = new CreateEventSourceMappingRequest
{
BatchSize = 100,
Enabled = true,
EventSourceArn = tableDescription.LatestStreamArn,
FunctionName = "ProcessCreateAccount",
StartingPosition = EventSourcePosition.LATEST
};
var reqResponse =_lambda.CreateEventSourceMappingAsync(req);
Console.WriteLine("Event source mapping state: " + reqResponse.Result.State);
}
catch (AmazonDynamoDBException e)
{
Console.WriteLine("Error creating table '" + TABLE_CREATE_ACCOUNT + "'");
Console.WriteLine("Amazon error code: {0}", string.IsNullOrEmpty(e.ErrorCode) ? "None" : e.ErrorCode);
Console.WriteLine("Exception message: {0}", e.Message);
}
catch (Exception e)
{
Console.WriteLine("Error creating table '" + TABLE_CREATE_ACCOUNT + "'");
Console.WriteLine("Exception message: {0}", e.Message);
}
The key is
StreamViewType = StreamViewType.NEW_IMAGE
That was it.

Programmatically unsubscribe a YouTube user C#

Here is my code for a mass unsubscriber i am making, currently everything works - other than the unsubscribe feature.. (Typical huh)
public void UnSubUsers()
{
string feedUrl = "http://gdata.youtube.com/feeds/api/users/" + username.Text + "/subscriptions";
YouTubeQuery query = new YouTubeQuery(feedUrl);
subFeed = service.GetSubscriptions(query);
YouTubeRequestSettings yts = new YouTubeRequestSettings("Unsubscriber", DEVKEY, username.Text, password.Text);
YouTubeRequest request = new YouTubeRequest(yts);
int i = 0;
int x = 0;
x = (listBox1.Items.Count);
for (i=0;i<x ;i++ )
{
string uname = listBox1.Items[i].ToString();
uname=uname.Substring(42);
uname = uname.Remove(uname.LastIndexOf("/"));
Subscription s = new Subscription();
s.Type = SubscriptionEntry.SubscriptionType.channel;
s.UserName = uname;
//MessageBox.Show(uname); //Displays the username so that we know if it is correct
try
{
s.AtomEntry.EditUri = "http://gdata.youtube.com/feeds/api/users/" + username.Text + "/subscriptions";
s.SubscriptionEntry.EditUri = "http://gdata.youtube.com/feeds/api/users/" + username.Text + "/subscriptions";
request.Delete(s);
}
catch (ArgumentNullException e)
{
MessageBox.Show(e.ToString(), "Error");
}
catch (GDataRequestException e)
{
MessageBox.Show(e.ToString(), "Error");
}
}
}
(Also available at http://pastebin.com/LnKMYCJp)
When the code "reaches" request.Delete(s) it gives me this error:
Google.GData.Client.GDataRequestException: Execution of request failed: http://gdata.youtube.com/feeds/api/users/iWinterHD/subscriptions --->System.Net.WebException: The remote server returned an error: (400) Bad Request.
at System.Net.HttpWebRequest.GetResponse()
at Google.GData.Client.GDataRequest.Execute()
--- End of inner exception stack trace ---
at Google.GData.Client.GDataRequest.Execute()
at Google.GData.Client.GDataGAuthRequest.Execute(Int32 retryCounter)
at Google.GData.Client.GDataGAuthRequest.Execute()
at Google.GData.Client.Service.Delete(Uri uriTarget, String eTag)
at Google.GData.Client.FeedRequest1.Delete[Y](Y entry)
at Unsubscriber.SubForm.UnSubUsers() in C:\Users\iWinterHD\documents\visual studio 2010\Projects\Unsubscriber\Unsubscriber\SubForm.cs:line 112
Does anybody know how to fix this, I have been trying to get this working for around 2 hours and I am still getting this error, no matter what I try
When i used fiddler to find out info about the connection this was the header:
DELETE /feeds/api/users/iWinterHD/subscriptions HTTP/1.1
Content-Type: application/atom+xml; charset=UTF-8
User-Agent: G-Unsubscriber/GDataGAuthRequestFactory-CS-Version=2.1.0.0--IEnumerable
X-GData-Key: key=DEVELOPER_KEY
Authorization: GoogleLogin auth=DQAAAMgAAAAfAWmos6z7rpaY8JrK2RNK4Urf7Riu_putKeGgV1KFH5OEmAYA2t5w0DWXbVQJnizQiPmLSl-4D0eCozYn5jVp4DWs4Rpao3udc3eTIC9wibBGRe640m7zZjl96UnFMyf-fJDk0VrTIcAw74S7_WhwBaRDjLS77EOWfERw066NmcYO-2QB_6WZ4Y0o3Y4haVn_pRokm8ckyuTRWJf6cES1yVlZ4fP5diUySVsH7EaHLiUcAquUl7GWCMdF_JbjRVVxvgeMW1zV757JW8l841uk
GData-Version: 2.0
Host: gdata.youtube.com
Connection: Keep-Alive
However the Google Developers example is this:
DELETE /feeds/api/users/default/subscriptions/SUBSCRIPTION_ID HTTP/1.1
Host: gdata.youtube.com
Content-Type: application/atom+xml
Authorization: Bearer ACCESS_TOKEN
GData-Version: 2
X-GData-Key: key=DEVELOPER_KEY
Hopefully that gives a little heads up :)
After a bit of playing around with the API I think I've found the solution.
The AtomEntry.EditUri needs to be the same as the URI to the individual subscription. As it happens this is already stored in the SubscriptionEntry object (which you're overwriting).
Your code should look something like:
itemToRemove.AtomEntry.EditUri = itemToRemove.SubscriptionEntry.EditUri;
Here's the code I used to test this:
var subscriptionsUrl =
"http://gdata.youtube.com/feeds/api/users/warmthonthesoul/subscriptions";
var settings = new YouTubeRequestSettings([...]);
var request = new YouTubeRequest(settings);
var query = new YouTubeQuery(subscriptionsUrl);
var feed = request.GetSubscriptionsFeed("warmthonthesoul").Entries;
var itemToRemove = feed.SingleOrDefault(x =>
x
.SubscriptionEntry
.Title.Text.Contains("Triforcefilms"));
if(itemToRemove != null)
{
itemToRemove.AtomEntry.EditUri = itemToRemove
.SubscriptionEntry
.EditUri;
request.Delete(itemToRemove);
Console.WriteLine("Item removed");
}
Console.ReadLine();
}
After searching through all the variables for around 4 hours i ended up stumbling upon the ID variable, which i later discovered needed to be passed to the final URL in order to remove the subscription WITH that ID, I tested it and it worked perfectly!
public void ListSubs()
{
string feedUrl = "http://gdata.youtube.com/feeds/api/users/" + username.Text + "/subscriptions";
YouTubeQuery query = new YouTubeQuery(feedUrl);
try
{
subFeed = service.GetSubscriptions(query);
foreach (SubscriptionEntry entry in subFeed.Entries)
{
string id = entry.Id.AbsoluteUri;
id = id.Substring(id.LastIndexOf(":")+1);
listBox1.Items.Add(id);
string usrname = entry.Content.Src.Content;
usrname = usrname.Substring(42);
usrname = usrname.Remove(usrname.LastIndexOf("/"));
listBox2.Items.Add(usrname);
}
}
catch(GDataRequestException e)
{
MessageBox.Show(e.ToString(), "Error:");
}
}
public void UnSubUsers()
{
YouTubeRequestSettings yts = new YouTubeRequestSettings("Unsubscriber", DEVELOPER_KEY, username.Text, password.Text);
YouTubeRequest request = new YouTubeRequest(yts);
int i = 0;
int x = 0;
x = (listBox1.Items.Count);
for (i=0;i<x ;i++ )
{
string uname = listBox1.Items[i].ToString();
yts = new YouTubeRequestSettings("Unsubscriber", DEVELOPER_KEY, username.Text, password.Text);
request = new YouTubeRequest(yts);
Subscription s = new Subscription();
s.Type = SubscriptionEntry.SubscriptionType.channel;
s.UserName = uname;
s.Id = listBox1.Items[i].ToString()
try
{
s.AtomEntry.EditUri = "http://gdata.youtube.com/feeds/api/users/" + username.Text + "/subscriptions" + "/" + listBox1.Items[i].ToString();
request.Delete(s);
}
catch (ArgumentNullException e)
{
}
catch (GDataRequestException e)
{
}
}
}
I had to add the subscription ID to the URL i was using to delete the subscription, here is my code to add the subscription ID to the listbox i originally used to store usernames, it turns out you can't pass usernames to the Delete method, but this works just as well because i added a second listbox to find the usernames of the subscription IDs
string id = entry.Id.AbsoluteUri;
id = id.Substring(id.LastIndexOf(":")+1);
listBox1.Items.Add(id);
This code gets the subscription ID from the entry variable, you then add the subscription ID to the EditUri variable:
s.AtomEntry.EditUri = "http://gdata.youtube.com/feeds/api/users/" + username.Text + "/subscriptions" + "/" + listBox1.Items[i].ToString();
request.Delete(s);
My mass unsubscriber is now complete!
Many thanks to #JamieDixon for all his wonderful help!

Categories