summaryrefslogtreecommitdiffabout
path: root/lib/sreg.cc
blob: 0bd4d2e2f0d92a2d8e8604c58c3048ad7f304143 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#include <opkele/exception.h>
#include <opkele/sreg.h>
#include <opkele/uris.h>
#include <algorithm>

namespace opkele {
    using std::find;

    static const struct _sreg_field {
	const char *fieldname;
	sreg_t::fieldbit_t fieldbit;
    }	fields[] = {
	{ "nickname", sreg_t::field_nickname },
	{ "email", sreg_t::field_email },
	{ "fullname", sreg_t::field_fullname },
	{ "dob", sreg_t::field_dob },
	{ "gender", sreg_t::field_gender },
	{ "postcode", sreg_t::field_postcode },
	{ "country", sreg_t::field_country },
	{ "language", sreg_t::field_language },
	{ "timezone", sreg_t::field_timezone }
    };
#   define fields_BEGIN	fields
#   define fields_END &fields[sizeof(fields)/sizeof(*fields)]
    typedef const struct _sreg_field *fields_iterator;

    bool operator==(const struct _sreg_field& fd,const string& fn) {
	return fd.fieldname==fn;
    }

    void sreg_t::rp_checkid_hook(basic_openid_message& om) {
	string fr, fo;
	for(fields_iterator f=fields_BEGIN;f<fields_END;++f) {
	    if(f->fieldbit&fields_required) {
		if(!fr.empty()) fr+=",";
		fr += f->fieldname;
	    }
	    if(f->fieldbit&fields_optional) {
		if(!fo.empty()) fo+=",";
		fo += f->fieldname;
	    }
	}
	string pfx = om.allocate_ns(OIURI_SREG11,"sreg");
	if(!fr.empty()) om.set_field(pfx+".required",fr);
	if(!fo.empty()) om.set_field(pfx+".optional",fo);
	if(!policy_url.empty()) om.set_field(pfx+".policy_url",policy_url);
    }

    void sreg_t::checkid_hook(basic_openid_message& om) {
	rp_checkid_hook(om); }

    void sreg_t::rp_id_res_hook(const basic_openid_message& om,
	    const basic_openid_message& sp) {
	clear();
	string pfx;
	try {
	    pfx = om.find_ns(OIURI_SREG11,"sreg");
	}catch(failed_lookup&) {
	    try {
		pfx = om.find_ns(OIURI_SREG10,"sreg");
	    }catch(failed_lookup&) { return; }
	}
	pfx += '.';
	for(fields_iterator f=fields_BEGIN;f<fields_END;++f) {
	    string fn = pfx; fn+=f->fieldname;
	    if(!sp.has_field(fn)) continue;
	    has_fields |= f->fieldbit;
	    response[f->fieldbit]=sp.get_field(fn);
	}
    }

    void sreg_t::id_res_hook(const basic_openid_message& om,
	    const basic_openid_message& sp) {
	rp_id_res_hook(om,sp); }

    const string& sreg_t::get_field(fieldbit_t fb) const {
	response_t::const_iterator i = response.find(fb);
	if(i==response.end())
	    throw failed_lookup(OPKELE_CP_ "no field data available");
	return i->second;
    }

    void sreg_t::set_field(fieldbit_t fb,const string& fv) {
	response[fb] = fv;
	has_fields |= fb;
    }

    void sreg_t::reset_field(fieldbit_t fb) {
	has_fields &= ~fb;
	response.erase(fb);
    }

    void sreg_t::clear() {
	has_fields = 0; response.clear();
    }

    static long fields_list_to_bitmask(string& fl) {
	long rv = 0;
	while(!fl.empty()) {
	    string::size_type co = fl.find(',');
	    string fn;
	    if(co==string::npos) {
		fn = fl; fl.erase();
	    }else{
		fn = fl.substr(0,co); fl.erase(0,co+1);
	    }
	    fields_iterator f = find(fields_BEGIN,fields_END,fn);
	    if(f!=fields_END)
		rv |= f->fieldbit;
	}
	return rv;
    }

    void sreg_t::op_checkid_hook(const basic_openid_message& inm) {
	string ins = inm.find_ns(OIURI_SREG11,"sreg");
	fields_optional = 0; fields_required = 0; policy_url.erase();
	fields_response = 0;
	try {
	    string fl = inm.get_field(ins+".required");
	    fields_required = fields_list_to_bitmask(fl);
	}catch(failed_lookup&) { }
	try {
	    string fl = inm.get_field(ins+".optional");
	    fields_optional = fields_list_to_bitmask(fl);
	}catch(failed_lookup&) { }
	try {
	    policy_url = inm.get_field(ins+".policy_url");
	}catch(failed_lookup&) { }
    }

    void sreg_t::op_id_res_hook(basic_openid_message& oum) {
	string ons = oum.allocate_ns(OIURI_SREG11,"sreg");
	fields_response &= has_fields;
	string signeds = "ns."+ons;
	for(fields_iterator f=fields_BEGIN;f<fields_END;++f) {
	    if(!(f->fieldbit&fields_response)) continue;
	    signeds +=',';
	    string pn = ons; pn += '.'; pn += f->fieldname;
	    signeds += pn;
	    oum.set_field(pn,get_field(f->fieldbit));
	}
	oum.add_to_signed(signeds);
    }

    void sreg_t::checkid_hook(const basic_openid_message& inm,
	    basic_openid_message& oum) {
	op_checkid_hook(inm);
        setup_response(inm,oum);
	op_id_res_hook(oum);
    }

    void sreg_t::setup_response(const basic_openid_message& /* inm */,basic_openid_message& /* oum */) {
	setup_response();
    }
    void sreg_t::setup_response() {
	fields_response = (fields_required|fields_optional)&has_fields;
    }
}