page.CheckOut() throws SPException: URL is invalid even though page exists - c#

I'm writing an event receiver for a SharePoint site, and I want this receiver to edit the contents of a basic page after it is created. Here is the function that is giving me issues:
public void FillPage(SPSite site, SPItemEventProperties properties, string pageName)
{
using (site)
{
// Wait until the page has been generated
while (!PageExists(properties.BeforeUrl))
{
Thread.Sleep(10000);
}
Thread.Sleep(30000); // Added so I can check that the URL exists in my browser
SPWeb web = site.RootWeb;
SPFile page = web.GetFile(properties.BeforeUrl);
page.CheckOut(); // Throws SPException: 'URL is invalid'.
...
}
}
The PageExists function simply uses an HttpWebRequest pointed to the page that was just generated:
public bool PageExists(string url_ending)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(new Uri((the root site URL) + url_ending));
request.Timeout = 15000;
try
{
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
return true;
}
catch (WebException we)
{
if (we.Message.Contains("Unauthorized"))
{
return true; // If it's an authorization error, the page exists but access was denied
}
return false;
}
}
The CheckOut function returns: "SPException: The URL '...' is invalid. It may refer to a nonexistent file or folder, or refer to a valid file or folder that is not in the current Web." In addition, I added a breakpoint at the line containing 'page.Checkout()' and examined that page variable, and found that all of its members throw a 'System.IO.FileNotFoundException' or a 'System.IndexOutOfRangeException', even though it's pointed to the correct URL. I've also checked that the HttpWebRequest is pointed to the correct URL, which it is, and as mentioned in the comments I check to see that the page exists in my browser before the code can attempt to check it out.
From what searching I did, I found out that this error is often thrown when the database logs are filling up. But from what I found, in that case this error would also occur when attempting to check out documents from the SharePoint site itself, and I have not had that issue; I only get this error when I attempt to check out a page from the event receiver. Any idea what's going on?

I found this article
http://blog.mastykarz.nl/inconvenient-spwebgetfilestring/
which explains that GetFile can have unexpected results.
There is a workaround provided:
using (SPSite site = new SPSite("http://moss"))
{
using (SPWeb web = site.RootWeb)
{
object o = web.GetFileOrFolderObject("/site/subsite1/Pages/default.aspx");
if (o is SPFile)
{
SPFile f = (SPFile)o;
}
}
}
you should give it a try !

Related

Adding the WWW-Authenticate header gives an error

I'm trying to configure basic access authorization on my .NET HttpListener but I keep running into the same error. I've tried all the solutions that can be found on this site and many others but with no success.
I need to use admin/admin as username/password for basic authentication. The wikipedi page shows how the header should look, which I followed.
I keep getting the error "The header WWW-Authenticate must be changed with the correct method, parameter:name" there is however no parameter called "name" that must be added, like shown on the wikipedia page. I've ran out of options unfortunately and hope that somebody can help.
My code is as follows
private void WebRequestCallback(IAsyncResult result)
{
if (httpListener == null)
{
return;
}
HttpListenerContext context = httpListener.EndGetContext(result);
if (basicAccessAuth)
{
HttpListenerRequest Request = context.Request;
HttpListenerResponse Response = context.Response;
httpListener.AuthenticationSchemes = AuthenticationSchemes.Basic;
NameValueCollection nvCol = new NameValueCollection();
nvCol.Add("Authorization", "admin:admin");
httpListener.Realm = "Overflow";
Request.Headers.Add(nvCol); // error gets thrown here, missing "name" parameter
Response.Headers.Add("WWW-Authenticate: Basic YWRtaW46YWRtaW4=");
HttpListenerBasicIdentity identity = (HttpListenerBasicIdentity)context.User.Identity;
MessageBox.Show(identity.Name);
}
httpListener.BeginGetContext(new AsyncCallback(WebRequestCallback), httpListener);
if (ReceiveWebRequest != null)
{
ReceiveWebRequest(context);
}
ProcessRequest(context);
}
I have managed to figure out my issue. I added the header in the wrong way, it should be Response.AddHeader instead of Response.Headers.Add

AWS S3 Presigned URL Get returning ListBucketResult instead

I am trying to get a presigned URL to access an image in my private bucket, by using the GetPreSignedUrlRequest method.
My code is as follows:
public string GetPresignedImageURL(string keyString)
{
string urlString = "";
try
{
GetPreSignedUrlRequest request = new GetPreSignedUrlRequest
{
BucketName = bucket,
Key = keyString,
Expires = DateTime.Now.AddMinutes(5)
};
urlString = _client.GetPreSignedURL(request);
}
catch (AmazonS3Exception e)
{
Console.WriteLine("Error encountered on server. Message:'{0}' when writing an object", e.Message);
}
catch (Exception e)
{
Console.WriteLine("Unknown encountered on server. Message:'{0}' when writing an object", e.Message);
}
return urlString;
}
I passed in the key of the object I am getting e.g. 0BE1137F0F3E4703A0F0689346B49871_0.jpg.
However, this is the response URL I get. It did not append the object's key to the response, only the signature headers.
https://<bucket>.ap-southeast-1.amazonaws.com/?X-Amz-Expires=300&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=<credential>/20190701/ap-southeast-1/s3/aws4_request&X-Amz-Date=20190701T065534Z&X-Amz-SignedHeaders=host&X-Amz-Signature=<signature>
If I paste this link into the browser, it shows that it is a ListBucketResult request.
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Name>bucket</Name>
<Prefix/>
<Marker/>
<MaxKeys>1000</MaxKeys>
<IsTruncated>false</IsTruncated>
<Contents>
<Key>0BE1137F0F3E4703A0F0689346B49871_0.jpg</Key>
<LastModified>2019-07-01T06:52:17.000Z</LastModified>
<ETag>"89db9b468ba0eb45600ed9603fe9f41d"</ETag>
<Size>1621409</Size>
<Owner>...</Owner>
<StorageClass>STANDARD</StorageClass>
</Contents>
<Contents>
<Key>18F6F2B700A747F983DB26EBC8F3E92F_0.jpg</Key>
<LastModified>2019-06-28T08:44:40.000Z</LastModified>
<ETag>"61aa2a6270ec840b185331646ee884a2"</ETag>
<Size>88703</Size>
<Owner>...</Owner>
<StorageClass>STANDARD</StorageClass>
</Contents>
</ListBucketResult>
I am not sure if this is a bug, or if I am missing something here. I would like to know how I can get the presigned URL of the image to either display in the browser or as a direct download link (using content-disposition, which didn't work either) instead of the bucket's keys list. Thank you!
Check your keyString variable. Print it out before sending the request so that you are sure it isn't null. The only difference between the get presigned url for single object vs get presigned url for listing all objects is the Key that you send as a parameter.
See more here: https://docs.aws.amazon.com/sdkfornet1/latest/apidocs/html/T_Amazon_S3_Model_GetPreSignedUrlRequest.htm

How can I use existingResponse="Auto" successfully?

So I am returning detailed 400 error responses from my MVC web app. Setting existingResponse="PassThrough" works, but that's not what I want. I don't want to expose all failures, I only want to expose them when I have custom responses.
Auto, is set by default, but I deliberately set it. However, the documentation says "SetStatus" flag must be set, but I have no idea how to do such a thing. I wrote the following four controller methods in order to test it, and only BadRequestD works. The others set the status code and the status just fine, but the body content is "Bad Request".
public ActionResult BadRequestA()
{
Response.StatusCode = 400;
return Content("weeeeee");
}
public ActionResult BadRequestB()
{
Response.Status = "400 U DUN MESSED UP";
return Content("weeeeee");
}
public ActionResult BadRequestC()
{
Response.Status = "400 U DUN MESSED UP";
Response.StatusCode = 400;
return Content("weeeeee");
}
public ActionResult BadRequestD()
{
Response.StatusCode = 400;
Response.TrySkipIisCustomErrors = true;
return Content("weeeeee");
}
However, the documentation says "SetStatus" flag must be set, but I have no idea how to do such a thing
It's actually talking about the fTrySkipCustomErrors flag/argument to the IHttpResponse::SetStatus method in the IIS C++ SDK (see note I added to bottom of documentation here). But in ASP.NET the flag is exposed as Response.TrySkipIisCustomErrors. So according to:
http://www.iis.net/configreference/system.webserver/httperrors
Auto = Leaves the response untouched only if the SetStatus flag is set
I would expect to see IIS replace the response with its own html error page content (you can configure what that content is) by default unless you set:
Response.TrySkipIisCustomErrors = true;
Which is what you're seeing.
Additional related info, in MVC5 it seems to act as if that flag is true even if it's false for uncaught exceptions which I don't see in WebForms. As a workaround in Global.asax I'm:
protected void Application_Error()
{
var error = Server.GetLastError();
Server.ClearError();
//code to log error here
var httpException = error as HttpException;
Response.StatusCode = httpException != null ? httpException.GetHttpCode() : (int)HttpStatusCode.InternalServerError;
}
If you need to have custom responses with 4xx http statuses and still want to use Custom Error Pages here's what you should do:
set existingResponse="Auto" in web.config;
set TrySkipIisCustomErrors = true in your action (one that returns 4xx status and a content);
clear server error in global.asax (in Application_Error() - Server.ClearError()) and re-set the status code (Reponse.StatusCode = ((HttpException)Server.GetLastError()).GetHttpCode())
It's weird that IIS team didn't implement existingResponse attribute for specific status codes, so it's impossible to use existingResponse="PassThrough" just for one (or few) codes.

Upload file to a Google Site from C# Code

Any idea of how to upload a file to Google site from c#?
I am trying to upload but getting a 403 error. However, I am using the same credentials to connect to the site and get the list of attachments and pages present on the site.
Any help would be greatly appreciated!!
They most likely have an anti-CSRF scheme that stores temporal identifiers in the page and/or cookies, this is specifically to hinder bots.
You are most likely submitting a request without the proper CSRF tokens and get rejected. I would recommend analyzing how they handle CSRF, after this point it will most likely boil down to making a WebRequest to the page and so you can get any cookies they get back, along with having the form so you can scrape out any hidden fields that are relevant. Then move those over to your post request that you're attempting to the send the file to.
I figured out the problem and resolved it. Below is the complete function:
public bool UploadAttachment()
{
try
{
//AsyncSendData data = new AsyncSendData();
string parentUrl = Cabinets["Cabinet1"].ToString();
string parentID = parentUrl.Split('/')[7];
AtomEntry entry = new AtomEntry();
entry.Title.Text = "abc.jpg";
AtomCategory cat = new AtomCategory();
cat.Term = ATTACHMENT_TERM;
cat.Label = "attachment";
cat.Scheme = KIND_SCHEME;
entry.Categories.Add(cat);
AtomLink link = new AtomLink();
link.Rel = PARENT_REL;
link.HRef = parentUrl;
entry.Links.Add(link);
AtomContent content = new AtomContent();
FileInfo info = new FileInfo("C:\\Bluehills.txt");
FileStream stream = info.Open(FileMode.Open,FileAccess.ReadWrite,FileShare.ReadWrite);
this.setUserCredentials(userName, password);
Uri postUri = new Uri(makeFeedUri("content"));
entry.Source = new AtomSource();
//this.EntrySend(postUri, entry, GDataRequestType.Insert);
// Send the request and receive the response:
AtomEntry insertedEntry = this.Insert(postUri, stream, (string)DocumentTypes["TXT"], "bluehills");
return true;
}
catch (Exception ex)
{
return false;
}
}

HTTP Module and Cookies in Sharepoint 2007

I have some proof concept code for a HTTP module. The code checks to see if a cookie exists, if so it retrieves a value, if the cookie does not exist it creates it and sets the value.
Once this is done I write to the screen to see what action has been taken (all nice and simple). So on the first request the cookie is created; subsequent requests retrieve the value from the cookie.
When I test this in a normal asp.net web site everything works correctly – yay! However as soon as I transfer it to SharePoint something weird happens, the cookie is never saved - that is the code always branches into creating the cookie and never takes the branch to retrieve the value - regardless of page refreshes or secondary requests.
Heres the code...
public class SwithcMasterPage : IHttpModule
{
public void Dispose()
{
throw new NotImplementedException();
}
public void Init(HttpApplication context)
{
// register handler
context.PreRequestHandlerExecute += new EventHandler(PreRequestHandlerExecute);
}
void PreRequestHandlerExecute(object sender, EventArgs e)
{
string outputText = string.Empty;
HttpCookie cookie = null;
string cookieName = "MPSetting";
cookie = HttpContext.Current.Request.Cookies[cookieName];
if (cookie == null)
{
// cookie doesn't exist, create
HttpCookie ck = new HttpCookie(cookieName);
ck.Value = GetCorrectMasterPage();
ck.Expires = DateTime.Now.AddMinutes(5);
HttpContext.Current.Response.Cookies.Add(ck);
outputText = "storing master page setting in cookie.";
}
else
{
// get the master page from cookie
outputText = "retrieving master page setting from cookie.";
}
HttpContext.Current.Response.Write(outputText + "<br/>");
}
private string GetCorrectMasterPage()
{
// logic goes here to get the correct master page
return "/_catalogs/masterpage/BlackBand.master";
}
This turned out to be the authentication of the web app. To work correctly you must use a FQDM that has been configured for Forms Authentication.
You can use Fiddler or FireBug (on FireFox) to inspect response to see if your cookie is being sent. If not then perhaps you can try your logic in PostRequestHandlerExecute. This is assuming that Sharepoint or some other piece of code is tinkering with response cookies. This way, you can be the last one adding the cookie.

Categories

Resources