adam@1: #include adam@1: adam@0: #include adam@0: #include adam@1: #include adam@0: adam@1: #include adam@0: adam@3: #define BUF_MAX 10240 adam@3: #define BUF_INIT 1024 adam@3: adam@2: struct uw_OpenidFfi_discovery { adam@2: uw_Basis_string endpoint, localId; adam@2: }; adam@2: adam@2: uw_Basis_string uw_OpenidFfi_endpoint(uw_context ctx, uw_OpenidFfi_discovery d) { adam@2: return d->endpoint; adam@2: } adam@2: adam@2: uw_Basis_string uw_OpenidFfi_localId(uw_context ctx, uw_OpenidFfi_discovery d) { adam@2: return d->localId; adam@2: } adam@2: adam@0: uw_unit uw_OpenidFfi_init(uw_context ctx) { adam@4: adam@4: adam@0: curl_global_init(CURL_GLOBAL_ALL); adam@0: adam@0: return uw_unit_v; adam@0: } adam@1: adam@1: static CURL *curl(uw_context ctx) { adam@1: CURL *r; adam@1: adam@1: if (!(r = uw_get_global(ctx, "curl"))) { adam@1: r = curl_easy_init(); adam@1: if (r) adam@1: uw_set_global(ctx, "curl", r, curl_easy_cleanup); adam@1: } adam@1: adam@1: return r; adam@1: } adam@1: adam@1: typedef struct { adam@1: uw_context ctx; adam@2: uw_OpenidFfi_discovery d; adam@1: } endpoint; adam@1: adam@1: static void XMLCALL startElement(void *userData, const XML_Char *name, const XML_Char **atts) { adam@1: endpoint *ep = userData; adam@1: adam@1: if (!strcmp(name, "link")) { adam@1: const XML_Char **attp; adam@1: int found = 0; adam@1: adam@1: for (attp = atts; *attp; attp += 2) { adam@1: if (!strcmp(attp[0], "rel") && !strcmp(attp[1], "openid2.provider")) { adam@1: found = 1; adam@1: break; adam@1: } adam@1: } adam@1: adam@1: if (found) { adam@1: for (attp = atts; *attp; attp += 2) { adam@1: if (!strcmp(attp[0], "href")) { adam@2: ep->d->endpoint = uw_strdup(ep->ctx, attp[1]); adam@1: return; adam@1: } adam@1: } adam@1: } adam@1: } adam@1: } adam@1: adam@1: static void XMLCALL endElement(void *userData, const XML_Char *name) { adam@1: } adam@1: adam@1: typedef struct { adam@1: XML_Parser parser; adam@1: int any_errors; adam@3: } curl_discovery_data; adam@1: adam@3: static size_t write_discovery_data(void *buffer, size_t size, size_t nmemb, void *userp) { adam@3: curl_discovery_data *d = userp; adam@1: adam@1: if (!XML_Parse(d->parser, buffer, size * nmemb, 0)) adam@1: d->any_errors = 1; adam@1: adam@1: return size * nmemb; adam@1: } adam@1: adam@2: uw_OpenidFfi_discovery *uw_OpenidFfi_discover(uw_context ctx, uw_Basis_string id) { adam@1: char *s; adam@1: CURL *c = curl(ctx); adam@3: curl_discovery_data cd = {}; adam@2: uw_OpenidFfi_discovery dy = uw_malloc(ctx, sizeof(struct uw_OpenidFfi_discovery)); adam@2: endpoint ep = {ctx, dy}; adam@1: CURLcode code; adam@1: adam@2: dy->endpoint = dy->localId = NULL; adam@2: adam@1: if (!strchr(id, ':')) { adam@1: id = uw_Basis_strcat(ctx, "http://", id); adam@1: if ((s = strchr(id, '#')) != NULL) adam@1: *s = 0; adam@1: } else if ((s = strchr(id, '#')) != NULL) { adam@1: char *id2 = uw_malloc(ctx, s - id + 1); adam@1: memcpy(id2, s, s - id); adam@1: id2[s - id] = 0; adam@1: id = id2; adam@1: } adam@1: adam@1: cd.parser = XML_ParserCreate(NULL); adam@1: XML_SetUserData(cd.parser, &ep); adam@1: uw_push_cleanup(ctx, (void (*)(void *))XML_ParserFree, cd.parser); adam@1: XML_SetElementHandler(cd.parser, startElement, endElement); adam@1: adam@1: curl_easy_setopt(c, CURLOPT_URL, id); adam@3: curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, write_discovery_data); adam@1: curl_easy_setopt(c, CURLOPT_WRITEDATA, &cd); adam@1: adam@1: code = curl_easy_perform(c); adam@1: uw_pop_cleanup(ctx); adam@1: adam@2: if (code || !ep.d->endpoint) adam@1: return NULL; adam@2: else { adam@2: uw_OpenidFfi_discovery *dyp = malloc(sizeof(uw_OpenidFfi_discovery)); adam@2: *dyp = ep.d; adam@2: return dyp; adam@2: } adam@1: } adam@3: adam@3: uw_OpenidFfi_inputs uw_OpenidFfi_createInputs(uw_context ctx) { adam@3: uw_buffer *r = uw_malloc(ctx, sizeof(uw_buffer)); adam@3: uw_buffer_init(BUF_MAX, r, BUF_INIT); adam@3: return r; adam@3: } adam@3: adam@3: static int okForPost(const char *s) { adam@3: for (; *s; ++s) adam@3: if (*s == '=' || *s == '&') adam@3: return 0; adam@3: return 1; adam@3: } adam@3: adam@3: uw_unit uw_OpenidFfi_addInput(uw_context ctx, uw_OpenidFfi_inputs buf, uw_Basis_string key, uw_Basis_string value) { adam@3: if (!okForPost(key)) adam@3: uw_error(ctx, FATAL, "Invalid key for OpenID inputs"); adam@3: if (!okForPost(value)) adam@3: uw_error(ctx, FATAL, "Invalid value for OpenID inputs"); adam@3: adam@3: if (uw_buffer_used(buf) > 0) adam@3: uw_buffer_append(buf, "&", 1); adam@3: adam@3: uw_buffer_append(buf, key, strlen(key)); adam@3: uw_buffer_append(buf, "=", 1); adam@3: uw_buffer_append(buf, value, strlen(value)); adam@3: adam@3: return uw_unit_v; adam@3: } adam@3: adam@3: uw_Basis_string uw_OpenidFfi_getOutput(uw_context ctx, uw_OpenidFfi_outputs buf, uw_Basis_string key) { adam@3: char *s = buf->start; adam@3: adam@3: for (; *s; s = strchr(strchr(s, 0)+1, 0)+1) adam@3: if (!strcmp(key, s)) adam@3: return strchr(s, 0)+1; adam@3: adam@3: return NULL; adam@3: } adam@3: adam@3: static size_t write_buffer_data(void *buffer, size_t size, size_t nmemb, void *userp) { adam@3: uw_buffer *buf = userp; adam@3: adam@3: uw_buffer_append(buf, buffer, size * nmemb); adam@3: adam@3: return size * nmemb; adam@3: } adam@3: adam@3: const char curl_failure[] = "error\0Error fetching URL"; adam@3: adam@4: uw_OpenidFfi_outputs uw_OpenidFfi_direct(uw_context ctx, uw_Basis_string url, uw_OpenidFfi_inputs inps) { adam@3: uw_buffer *buf = uw_malloc(ctx, sizeof(uw_buffer)); adam@3: CURL *c = curl(ctx); adam@3: CURLcode code; adam@3: adam@3: uw_buffer_init(BUF_MAX, buf, BUF_INIT); adam@3: adam@3: uw_buffer_append(inps, "", 1); adam@3: adam@3: curl_easy_setopt(c, CURLOPT_URL, url); adam@3: curl_easy_setopt(c, CURLOPT_POSTFIELDS, inps->start); adam@3: curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, write_buffer_data); adam@3: curl_easy_setopt(c, CURLOPT_WRITEDATA, buf); adam@3: adam@3: code = curl_easy_perform(c); adam@3: adam@3: uw_buffer_append(buf, "", 1); adam@3: adam@3: if (code) { adam@3: uw_buffer_reset(buf); adam@3: uw_buffer_append(buf, curl_failure, sizeof curl_failure); adam@3: } else { adam@3: char *s; adam@3: adam@3: s = buf->start; adam@3: while (*s) { adam@3: char *colon = strchr(s, ':'), *newline; adam@3: adam@3: if (!colon) { adam@3: *s = 0; adam@3: break; adam@3: } adam@3: adam@3: newline = strchr(colon+1, '\n'); adam@3: adam@3: if (!newline) { adam@3: *s = 0; adam@3: break; adam@3: } adam@3: adam@3: *colon = 0; adam@3: *newline = 0; adam@3: s = newline+1; adam@3: } adam@3: } adam@3: adam@3: return buf; adam@3: } adam@4: adam@4: static uw_Basis_string deurl(uw_context ctx, uw_Basis_string s) { adam@4: uw_Basis_string r = uw_malloc(ctx, strlen(s)), s2 = r; adam@4: adam@4: for (; *s; ++s) { adam@4: if (s[0] == '%' && s[1] && s[2]) { adam@4: unsigned u; adam@4: adam@4: sscanf(s+1, "%02x", &u); adam@4: *s2++ = u; adam@4: s += 2; adam@4: } else adam@4: *s2++ = *s; adam@4: } adam@4: adam@4: *s2 = 0; adam@4: return r; adam@4: } adam@4: adam@4: uw_OpenidFfi_outputs uw_OpenidFfi_indirect(uw_context ctx, uw_Basis_string fields) { adam@4: uw_OpenidFfi_outputs b = malloc(sizeof(uw_buffer)); adam@4: adam@4: uw_buffer_init(BUF_MAX, b, BUF_INIT); adam@4: adam@4: while (*fields) { adam@4: char *equal = strchr(fields, '='), *and, *s; adam@4: adam@4: if (!equal) adam@4: break; adam@4: adam@4: *equal = 0; adam@4: s = deurl(ctx, fields); adam@4: uw_buffer_append(b, s, strlen(s)); adam@4: uw_buffer_append(b, "", 1); adam@4: adam@4: and = strchr(equal+1, '&'); adam@4: if (and) { adam@4: *and = 0; adam@4: fields = and+1; adam@4: } else adam@4: fields = and = strchr(equal+1, 0); adam@4: s = deurl(ctx, equal+1); adam@4: uw_buffer_append(b, s, strlen(s)); adam@4: uw_buffer_append(b, "", 1); adam@4: } adam@4: adam@4: uw_buffer_append(b, "", 1); adam@4: return b; adam@4: }