2 * rs-serve - (c) 2013 Niklas E. Cathor
4 * This program is distributed in the hope that it will be useful,
5 * but WITHOUT ANY WARRANTY; without even the implied warranty of
6 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
7 * GNU Affero General Public License for more details.
9 * You should have received a copy of the GNU Affero General Public License
10 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 #define ADD_CORS_HEADERS(req) \
16 ADD_RESP_HEADER(req, "Access-Control-Allow-Origin", RS_ALLOW_ORIGIN); \
17 ADD_RESP_HEADER(req, "Access-Control-Allow-Headers", RS_ALLOW_HEADERS); \
18 ADD_RESP_HEADER(req, "Access-Control-Allow-Methods", "GET")
20 char *lrdd_template = NULL;
21 char *storage_uri_format = NULL;
22 size_t storage_uri_format_len = 0;
24 void init_webfinger() {
25 lrdd_template = malloc(strlen(RS_SCHEME) + strlen(RS_HOSTNAME) + 40 + 1);
26 if(lrdd_template == NULL) {
27 log_error("malloc() failed: %s", strerror(errno));
30 sprintf(lrdd_template, "%s://%s/.well-known/webfinger?resource={uri}",
31 RS_SCHEME, RS_HOSTNAME);
32 storage_uri_format_len = strlen(RS_SCHEME) + strlen(RS_HOSTNAME) + 13;
33 storage_uri_format = malloc(storage_uri_format_len + 2 + 1);
34 sprintf(storage_uri_format, "%s://%s/storage/%%s/%%s", RS_SCHEME, RS_HOSTNAME);
37 static size_t json_writer(char *buf, size_t count, void *arg) {
38 return evbuffer_add((struct evbuffer*)arg, buf, count);
42 static int process_resource(const char *resource, char **storage_uri, char **auth_uri) {
44 static int process_resource(const char *resource, char **storage_uri) {
47 size_t resource_len = strlen(resource);
48 char *resource_buf = malloc(resource_len + 1);
49 if(resource_buf == NULL) {
50 log_error("malloc() failed: %s", strerror(errno));
54 memset(resource_buf, 0, resource_len + 1);
57 if(evhtp_unescape_string((unsigned char**)&resource_buf, (unsigned char*)resource, resource_len) != 0) {
58 log_error("unescaping string failed (errno: %s)", strerror(errno));
63 if(strncmp(resource_buf, "acct:", 5) == 0) {
65 log_debug("scheme matches");
68 char *local_part = resource_buf + 5;
70 for(ptr = local_part; *ptr && *ptr != '@'; ptr++);
72 // '@' found, host starts (otherwise invalid)
74 log_debug("local-part: %s", local_part);
75 char *hostname = ptr + 1;
76 log_debug("hostname: %s", hostname);
78 if(strcmp(hostname, RS_HOSTNAME) == 0) {
80 uid_t uid = user_get_uid(local_part);
81 log_debug("got uid: %d (RS_MIN_UID: %d, allowed: %d)", uid,
82 RS_MIN_UID, UID_ALLOWED(uid));
83 // check if user is valid
84 if(UID_ALLOWED(uid)) {
86 *storage_uri = malloc(storage_uri_format_len + strlen(hostname) + strlen(local_part) + 1);
87 sprintf(*storage_uri, storage_uri_format, hostname, local_part);
89 *auth_uri = malloc(RS_AUTH_URI_LEN + strlen(local_part) + 1);
90 sprintf(*auth_uri, RS_AUTH_URI, local_part);
106 void reject_webfinger(evhtp_request_t *req, void *arg) {
107 evhtp_send_reply(req, EVHTP_RES_NOTIMPL);
110 void handle_webfinger(evhtp_request_t *req, void *arg) {
112 switch(evhtp_request_get_method(req)) {
114 ADD_CORS_HEADERS(req);
115 ADD_RESP_HEADER(req, "Content-Type", "application/json");
117 json = new_json(json_writer, req->buffer_out);
118 json_start_object(json);
120 const char *resource;
121 if(req->uri->query && (resource = evhtp_kv_find(req->uri->query, "resource"))) {
123 char *storage_uri = NULL, *auth_uri = NULL;
125 char *storage_uri = NULL;
128 if(process_resource(resource, &storage_uri, &auth_uri) != 0) {
130 if(process_resource(resource, &storage_uri) != 0) {
132 req->status = EVHTP_RES_NOTFOUND;
134 json_write_key_val(json, "subject", resource);
135 json_write_key(json, "links");
136 json_start_array(json);
137 json_start_object(json);
138 json_write_key_val(json, "rel", "remotestorage");
139 json_write_key_val(json, "type", RS_STORAGE_API);
140 json_write_key_val(json, "href", storage_uri);
142 json_write_key(json, "properties");
143 json_start_object(json);
145 json_write_key_val(json, RS_AUTH_METHOD, auth_uri);
146 // (begin legacy support [<= remotestorage-00])
147 json_write_key_val(json, "auth-method", RS_AUTH_METHOD);
148 json_write_key_val(json, "auth-endpoint", auth_uri);
149 // (end legacy support)
152 json_end_object(json); // properties
153 json_end_object(json); // link rel=remotestorage]
154 json_end_array(json); // links
155 req->status = EVHTP_RES_OK;
158 json_write_key_val(json, "subject", RS_HOSTNAME);
159 json_write_key(json, "links");
160 json_start_array(json);
161 json_start_object(json);
162 json_write_key_val(json, "rel", "lrdd");
163 json_write_key_val(json, "template", lrdd_template);
164 json_end_object(json);
165 json_end_array(json);
166 req->status = EVHTP_RES_OK;
169 json_end_object(json);
173 case htp_method_OPTIONS:
174 ADD_CORS_HEADERS(req);
175 req->status = EVHTP_RES_NOCONTENT;
178 req->status = EVHTP_RES_METHNALLOWED;
181 evhtp_send_reply(req, req->status);