0
votes

Of the very few ways I've seen to upload an attachment in infopath to a sharepoint library, none of them seemed very simplistic, and all involved server side code. What I've heard from all these is that the file is stored base64 encrypted inside the xml template of the form.

I have full javascript/jQuery access to the DOM of the form on a sharepoint page, but I'm having difficulty finding the exact location of this base64 string.

Note, I am trying to get the base64 of an attachment BEFORE the form is submitted, I'm assuming it is somewhere in the DOM, but I can't figure out where. Any help is appreciated.

2

2 Answers

0
votes

First of all - make sure you are not mixing XML DOM of an InfoPath document with HTML DOM of HTML page.

Second - when speaking about InfoPath: "XML template" is XML that is stored inside XSN and used as intial (blank) document for this XSN form. Since you are saying "before the form submitted" I'm assuming you actually talking about XML document (form) created with this XSN.

It is possible to add attachment to InfoPath XML document stored in document library using JavaScript directly (open XML from server, replace value of the node, save it back). Code will be slightly more complex than server side code in C#/VB.Net so... Finding node with XPath or JQuery should not be too hard, but it is form specific.

Also I think you are trying to change XML opened at the moment with Forms Server by hacking HTML rendering of the page. I would not do that:

  • there is no XML in the browser when form renderd by Forms Server
  • attachments are not sent to HTML page at all
  • there are not enough people who can help you with that
  • it is obviously not supported
0
votes

I found a solution that uses jQuery and C# code behind pages. Here is the jQuery part:

$.getJSON('_vti_bin/listdata.svc/<NAME OF LIST (FORMATTED FOR LISTDATA.SVC)>', function(data){
  $.each(data.d.results, function(index, value){
    $.get('<NAME OF LIST>/'+value.Title+"?noredirect=true", function(info){
      $.post('<PATH TO filecreator.aspx>, {file: $(info).find('[nodeName="my:memo"]').text()}, function(data){
        $('#response').html(data);
      });
    });
  });
});

The html is a simple enough <div id="response"></div> just meant to receive data.

Here is the filecreator.aspx file:

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Web" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Text" %>
<script runat="server">
  public void Page_Load(object sender, EventArgs e){
    int fileSize, nameLength;
    string name = "";
    byte[] decodedData;
    HttpContext c = HttpContext.Current;
    string data64 = c.Request["file"];
    if(data64==""){
      return;
    }
    byte[] data = Convert.FromBase64String(data64);
    using(MemoryStream ms = new MemoryStream(data)){
      BinaryReader reader = new BinaryReader(ms);
      byte[] header = new byte[16];
      header = reader.ReadBytes(header.Length);

      fileSize = (int)reader.ReadUInt32();
      nameLength = (int)reader.ReadUInt32() * 2;

      byte[] fileName = reader.ReadBytes(nameLength);
      Encoding enc = Encoding.Unicode;
      name = enc.GetString(fileName, 0, nameLength -2);
      decodedData = reader.ReadBytes(fileSize);
    }
    string filePath = Server.MapPath("/temp/"+name);
    if(File.Exists(filePath)){
        File.Delete(filePath);
    }
    FileStream fs = new FileStream(filePath, FileMode.CreateNew);
    BinaryWriter writer = new BinaryWriter(fs);
    writer.Write(decodedData);

    writer.Close();
    fs.Close();
    Response.Write("<a href=\"http://sharept03sb1/services/?download.aspx?file="+name+"\">"+name+"</a><br />");
  }
</script>

And finally, this is download.aspx file that starts the download, it's vulnerable right now to lfi, and I'll mark where this should be fixed:

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Web" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Text" %>
<script runat="server">
  public void Page_Load(object sender, EventArgs e){
    string name = Request.QueryString["file"]; // HERE: Make sure to prevent lfi via directory traversal :)
    FileStream fs = File.Open(Server.MapPath("/temp/"+name), FileMode.Open);
    byte[] file = new byte[fs.Length];
    fs.Read(file, 0, Convert.ToInt32(fs.Length));
    fs.Close();
    Response.AddHeader("Content-disposition", "attachment; filename=" + name);
    Response.ContentType = "application/octet-stream";
    Response.BinaryWrite(file);
    Response.End();
  }
</script>

A couple tips:

  • If you get something talking about code blocks not working, make sure to do some research on PageParserPaths in sharepoint's web.config file. You'll need to change that.
  • This only provides donwload links for every attachment in a document library containing infopath forms. You could of course use parameterized queries to filter certain data.