+
+ return(curl);
+}
+
+
+struct xrds {
+ StrBuf *CharData;
+ int nesting_level;
+ int in_xrd;
+ int current_service_priority;
+ int selected_service_priority;
+ StrBuf *current_service_uri;
+ StrBuf *selected_service_uri;
+ int current_service_is_oid2auth;
+};
+
+
+void xrds_xml_start(void *data, const char *supplied_el, const char **attr) {
+ struct xrds *xrds = (struct xrds *) data;
+ int i;
+
+ ++xrds->nesting_level;
+
+ if (!strcasecmp(supplied_el, "XRD")) {
+ ++xrds->in_xrd;
+ }
+
+ else if (!strcasecmp(supplied_el, "service")) {
+ xrds->current_service_priority = 0;
+ xrds->current_service_is_oid2auth = 0;
+ for (i=0; attr[i] != NULL; i+=2) {
+ if (!strcasecmp(attr[i], "priority")) {
+ xrds->current_service_priority = atoi(attr[i+1]);
+ }
+ }
+ }
+
+ FlushStrBuf(xrds->CharData);
+}
+
+
+void xrds_xml_end(void *data, const char *supplied_el) {
+ struct xrds *xrds = (struct xrds *) data;
+
+ --xrds->nesting_level;
+
+ if (!strcasecmp(supplied_el, "XRD")) {
+ --xrds->in_xrd;
+ }
+
+ else if (!strcasecmp(supplied_el, "type")) {
+ if ( (xrds->in_xrd)
+ && (!strcasecmp(ChrPtr(xrds->CharData), "http://specs.openid.net/auth/2.0/server"))
+ ) {
+ xrds->current_service_is_oid2auth = 1;
+ }
+ if ( (xrds->in_xrd)
+ && (!strcasecmp(ChrPtr(xrds->CharData), "http://specs.openid.net/auth/2.0/signon"))
+ ) {
+ xrds->current_service_is_oid2auth = 1;
+ /* FIXME in this case, the Claimed ID should be considered immutable */
+ }
+ }
+
+ else if (!strcasecmp(supplied_el, "uri")) {
+ if (xrds->in_xrd) {
+ FlushStrBuf(xrds->current_service_uri);
+ StrBufAppendBuf(xrds->current_service_uri, xrds->CharData, 0);
+ }
+ }
+
+ else if (!strcasecmp(supplied_el, "service")) {
+ if ( (xrds->in_xrd)
+ && (xrds->current_service_priority < xrds->selected_service_priority)
+ && (xrds->current_service_is_oid2auth)
+ ) {
+ xrds->selected_service_priority = xrds->current_service_priority;
+ FlushStrBuf(xrds->selected_service_uri);
+ StrBufAppendBuf(xrds->selected_service_uri, xrds->current_service_uri, 0);
+ }
+
+ }
+
+ FlushStrBuf(xrds->CharData);
+}
+
+
+void xrds_xml_chardata(void *data, const XML_Char *s, int len) {
+ struct xrds *xrds = (struct xrds *) data;
+
+ StrBufAppendBufPlain (xrds->CharData, s, len, 0);
+}
+
+
+/*
+ * Parse an XRDS document.
+ * If an OpenID Provider URL is discovered, op_url to that value and return nonzero.
+ * If nothing useful happened, return 0.
+ */
+int parse_xrds_document(StrBuf *ReplyBuf) {
+ ctdl_openid *oiddata = (ctdl_openid *) CC->openid_data;
+ struct xrds xrds;
+ int return_value = 0;
+
+ memset(&xrds, 0, sizeof (struct xrds));
+ xrds.selected_service_priority = INT_MAX;
+ xrds.CharData = NewStrBuf();
+ xrds.current_service_uri = NewStrBuf();
+ xrds.selected_service_uri = NewStrBuf();
+ XML_Parser xp = XML_ParserCreate(NULL);
+ if (xp) {
+ XML_SetUserData(xp, &xrds);
+ XML_SetElementHandler(xp, xrds_xml_start, xrds_xml_end);
+ XML_SetCharacterDataHandler(xp, xrds_xml_chardata);
+ XML_Parse(xp, ChrPtr(ReplyBuf), StrLength(ReplyBuf), 0);
+ XML_Parse(xp, "", 0, 1);
+ XML_ParserFree(xp);