Search code examples
confluence-rest-apilibcurl.net

Can libcurl.net be used to post a new page to confluence?


I'm just getting started using the REST API to create pages.

I'm trying to configure a basic example and I thought of using libcurl.net to do it.

Does anyone see any reason why that wouldn't work?

UPDATE:

Here is what I have so far adapted from the curllib.net "bookpost" example

 Option Explicit On
Imports System.IO
Imports System.Net
Imports SeasideResearch.LibCurlNet

Public Class CurlOperations

Public Shared Sub CurlPost()

    Try

        Dim credUserName As String = "username"
        Dim credPassword As String = "password"

        Dim response As String = Nothing
        Dim outputStdErr As Stream = Nothing

        Curl.GlobalInit(CURLinitFlag.CURL_GLOBAL_ALL)

        Dim easy As Easy
        easy = New Easy

        ' Set up write delegate
        Dim wf As Easy.WriteFunction
        wf = New Easy.WriteFunction(AddressOf OnWriteData)
        easy.SetOpt(CURLoption.CURLOPT_WRITEFUNCTION, wf)

        'Authentication
        easy.SetOpt(CURLoption.CURLOPT_HTTPAUTH, CURLhttpAuth.CURLAUTH_BASIC)
        easy.SetOpt(CURLoption.CURLOPT_USERPWD, credUserName & ":" & credPassword)

        'disable ssl peer verification
        easy.SetOpt(CURLoption.CURLOPT_SSL_VERIFYPEER, False)

        'Header
        easy.SetOpt(CURLoption.CURLOPT_HTTPHEADER, "Content-Type: application/json; charset=utf-8")

        ' Simple post - with a string
        easy.SetOpt(CURLoption.CURLOPT_POSTFIELDS, WikiTools.CommREST.WebToCF.PostCurl())

        ' and the rest of the cURL options
        easy.SetOpt(CURLoption.CURLOPT_USERAGENT, ".NET Framework Client")
        easy.SetOpt(CURLoption.CURLOPT_URL, "https://domain.com/confluence/rest/api/content/")
        easy.SetOpt(CURLoption.CURLOPT_POST, True)

        response = easy.Perform().ToString
        LoggingAndActivites.WriteLog("Response: " & response, GetFunctionName.GetCallerName, True, True)

    Catch ex As Exception

        LoggingAndActivites.WriteLog("Exception: " & ex.ToString, GetFunctionName.GetCallerName, True, True)

    End Try

    Curl.GlobalCleanup()

End Sub

' Called by libcURL.NET when it has data for your program
Public Shared Function OnWriteData(ByVal buf() As Byte, ByVal size As Int32, ByVal nmemb As Int32, ByVal extraData As Object) As Int32

    LoggingAndActivites.WriteLog(System.Text.Encoding.UTF8.GetString(buf), GetFunctionName.GetCallerName, True, True)

    Return size * nmemb

End Function

 End Class

I am getting connected because if I remove the username and password I get a response through the "onWriteData" function as follows:

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html>
<head>
<title>401 Unauthorized</title>
</head>
<body>
<h1>Unauthorized</h1>
<p>This server could not verify that you
are authorized to access the document
requested.  Either you supplied the wrong
credentials (e.g., bad password), or your
browser doesn't understand how to supply
the credentials required.</p>
<hr>
<address>Apache Server at domain.com Port 7080</address>
</body>
</html>

The problem now is that if I correctly log on I'm not getting any response other than the "CURLE_OK" from the "easy.Perform()" function.

It's good because I know it's working to some degree.


Solution

  • So it does work

    Here is what I added/changed to make the code above work

    'I added an Slist to store the header items (I only had one)            
    Dim slistHeaders As New Slist
    slistHeaders.Append("Content-Type: application/json")
    
    'Then I added the slist to the HTTPHEADER
    easy.SetOpt(CURLoption.CURLOPT_HTTPHEADER, slistHeaders)
    

    THINGS TO WATCH FOR:

    (1) The URL of course is the number one thing

    In my case I was using was https://domain.com/confluence/rest/api/content/ because the Confluence Documentation assumes you would be using the root domain name (as did I)

    However, what I didn't know was that the URL I was given to test was already directing me into the "confluence" folder.

    So my URI needed to be https://domain.com/rest/api/content/ instead

    (2) An indicator that your HTTPHEADER needs to be put into an Slist is this return from the server: 415 Unsupported Media Type

    (3) Be sure NOT to use the CURLOPT_HEADER property. If you are seeing this header in your responses you need to make sure it's not used:

    HTTP/1.1 500 Internal Server Error
    Date: Sun, 22 May 2016 17:50:32 GMT
    Server: Apache
    Content-Location: 500.en-GB.html
    Vary: negotiate,accept-language
    TCN: choice
    Accept-Ranges: bytes
    Content-Length: 7575
    Connection: close
    Content-Type: text/html; charset=UTF-8
    Content-Language: en-gb
    

    Refer to my post here for an explanation of why: CURLOPT_HEADER

    (4) Lastly, when you build your app if you receive this error:

    An unhandled exception of type 'System.AccessViolationException' occurred in libcurl.NET.dll
    
    Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
    

    This is caused by libcurl.net process not being cleaned up.

    The "cleanup()" method isn't available in the DLL, despite being lsited in the examples.

    So instead use EASY.Dispose() at the end of your procedure and this error will stop. (I kept the "GlobalCleanup()" method in just for good measure)

    (5) Ironically I went the way of using CURL because I thought that the Confluence interface might require it.

    But it appears now that it doesn't and that you can simply use the "HttpWebRequest" Class in .NET to get the same results.

    However, the Jury is still out because the "lightweight" test server I was assigned to crashed and I'm waiting for them to fix it so I can verify this.

    Either Way I hope all this helps someone

    M