Skip to content

Instantly share code, notes, and snippets.

@p120ph37
Last active August 29, 2015 14:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save p120ph37/8281362ae9da042f3043 to your computer and use it in GitHub Desktop.
Save p120ph37/8281362ae9da042f3043 to your computer and use it in GitHub Desktop.
libcurl basic-auth soap example
/*
* HTTP POST with authentiction using "basic" method.
* Hybrid of anyauthput.c and postinmemory.c
* Specifically adapted for SOAP use.
*
* Usage:
* cc basicauthsoap.c -lcurl -o basicauthsoap
* ./basicauthsoap
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
struct MemoryStruct {
char *memory;
size_t size;
};
static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *context) {
size_t bytec = size * nmemb;
struct MemoryStruct *mem = (struct MemoryStruct *)context;
mem->memory = realloc(mem->memory, mem->size + bytec + 1);
if(mem->memory == NULL) {
printf("not enough memory (realloc returned NULL)\n");
return 0;
}
memcpy(&(mem->memory[mem->size]), ptr, bytec);
mem->size += bytec;
mem->memory[mem->size] = 0;
return nmemb;
}
static size_t xmlEscape(char *out, char *in) {
int i, j = 0;
for(i = 0; in[i] != 0; i++) {
// encoding non-ASCII characters is beyond the scope of this example...
if((unsigned char)in[i] > 127) abort();
switch(in[i]) {
case '"':
if(out) sprintf(out+j, "&quot;"); j += 6; break;
case '\'':
if(out) sprintf(out+j, "&apos;"); j += 6; break;
case '<':
if(out) sprintf(out+j, "&lt;"); j += 4; break;
case '>':
if(out) sprintf(out+j, "&gt;"); j += 4; break;
case '&':
if(out) sprintf(out+j, "&amp;"); j += 5; break;
default:
if(out) out[j] = in[i]; j++;
}
}
if(out) out[j] = 0;
return j;
}
static size_t compose_soap_frobnicate(char *buf, size_t size, char *foo, char *bar, char *baz) {
char fooEsc[xmlEscape(NULL, foo) + 1]; xmlEscape(fooEsc, foo);
char barEsc[xmlEscape(NULL, bar) + 1]; xmlEscape(barEsc, bar);
char bazEsc[xmlEscape(NULL, baz) + 1]; xmlEscape(bazEsc, baz);
return snprintf(buf, size, "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""
" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"
"<soap:Body>"
"<frobnicate xmlns=\"http://example.com/frobnicate\">"
"<foo>%s</foo>"
"<bar>%s</bar>"
"<baz>%s</baz>"
"</frobnicate>"
"</soap:Body>"
"</soap:Envelope>",
fooEsc, barEsc, bazEsc);
}
int main(void) {
CURL *curl;
CURLcode res;
struct MemoryStruct chunk;
chunk.memory = malloc(1);
chunk.size = 0;
chunk.memory[chunk.size] = 0;
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://ameriwether.com/cgi-bin/info.pl");
size_t soap_size = compose_soap_frobnicate(NULL, 0, "'Ein'", ">Zwei<", "\"Drei\"") + 1;
char soap[soap_size];
compose_soap_frobnicate(soap, soap_size, "'Ein'", ">Zwei<", "\"Drei\"");
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, soap);
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_BASIC);
curl_easy_setopt(curl, CURLOPT_USERNAME, "user");
curl_easy_setopt(curl, CURLOPT_PASSWORD, "password");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Content-Type: text/xml; charset=utf-8");
headers = curl_slist_append(headers, "SOAPAction: \"https://ameriwether.com/cgi-bin/info.pl/frobnicate\"");
headers = curl_slist_append(headers, "Accept: text/plain"); // Example output easier to read as plain text.
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
// Make the example URL work even if your CA bundle is missing.
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
res = curl_easy_perform(curl);
if(res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
} else {
printf("%s\n",chunk.memory);
}
// Remember to call the appropriate "free" functions.
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
free(chunk.memory);
curl_global_cleanup();
}
return 0;
}
@p120ph37
Copy link
Author

This is what it looks like when run:

$ ./basicauthsoap
TCP Connection:
 50.116.0.76:38556 > 50.116.0.76:443
SSL Layer (TLSv1.2):
 CIPHER=DHE-RSA-AES128-SHA
 CIPHER_ALGKEYSIZE=128
 CIPHER_EXPORT=false
 CIPHER_USEKEYSIZE=128
 CLIENT_VERIFY=NONE
 COMPRESS_METHOD=NULL
 SECURE_RENEG=true
 SESSION_ID=07E407495294F4FC38ED49CD8F7F0BDDB6AD59847298C2B0D306ECF30941AA4B
 TLS_SNI=ameriwether.com
HTTP Request:
 POST /cgi-bin/info.pl HTTP/1.1
HTTP Basic Auth:
 Username: user
 Password: password
HTTP Headers:
 Accept: text/plain
 ContentLength: 385
 ContentType: text/xml; charset=utf-8
 Host: ameriwether.com
 Soapaction: "https://ameriwether.com/cgi-bin/info.pl/frobnicate"
CGI Params:
 POSTDATA=<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><frobnicate xmlns="http://example.com/frobnicate'><foo>&apos;Ein&apos;</foo><bar>&gt;Zwei&lt;</bar><baz>&quot;Drei&quot;</baz></frobnicate></soap:Body></soap:Envelope>

The source for the CGI that is generating this response can be found here: https://gist.github.com/p120ph37/3bf6c500e0f00ab635da

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment