summaryrefslogtreecommitdiffstatshomepage
path: root/ui-atom.c
blob: a6ea3eecf58ec6fd0db3aab018522ecca5e98acd (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
/* ui-atom.c: functions for atom feeds
 *
 * Copyright (C) 2008 Lars Hjemli
 *
 * Licensed under GNU General Public License v2
 *   (see COPYING for full license text)
 */

#include "cgit.h"
#include "html.h"
#include "ui-shared.h"

void add_entry(struct commit *commit, char *host)
{
	char delim = '&';
	char *hex;
	char *mail, *t, *t2;
	struct commitinfo *info;

	info = cgit_parse_commit(commit);
	hex = sha1_to_hex(commit->object.sha1);
	html("<entry>\n");
	html("<title>");
	html_txt(info->subject);
	html("</title>\n");
	html("<updated>");
	cgit_print_date(info->author_date, FMT_ATOMDATE, ctx.cfg.local_time);
	html("</updated>\n");
	html("<author>\n");
	if (info->author) {
		html("<name>");
		html_txt(info->author);
		html("</name>\n");
	}
	if (info->author_email) {
		mail = xstrdup(info->author_email);
		t = strchr(mail, '<');
		if (t)
			t++;
		else
			t = mail;
		t2 = strchr(t, '>');
		if (t2)
			*t2 = '\0';
		html("<email>");
		html_txt(t);
		html("</email>\n");
		free(mail);
	}
	html("</author>\n");
	html("<published>");
	cgit_print_date(info->author_date, FMT_ATOMDATE, ctx.cfg.local_time);
	html("</published>\n");
	if (host) {
		html("<link rel='alternate' type='text/html' href='http://");
		html_attr(host);
		html_attr(cgit_pageurl(ctx.repo->url, "commit", NULL));
		if (ctx.cfg.virtual_root)
			delim = '?';
		htmlf("%cid=%s", delim, hex);
		html("'/>\n");
	}
	htmlf("<id>%s</id>\n", hex);
	html("<content type='text'>\n");
	html_txt(info->msg);
	html("</content>\n");
	html("<content type='xhtml'>\n");
	html("<div xmlns='http://www.w3.org/1999/xhtml'>\n");
	html("<pre>\n");
	html_txt(info->msg);
	html("</pre>\n");
	html("</div>\n");
	html("</content>\n");
	html("</entry>\n");
	cgit_free_commitinfo(info);
}


void cgit_print_atom(char *tip, char *path, int max_count)
{
	char *host;
	const char *argv[] = {NULL, tip, NULL, NULL, NULL};
	struct commit *commit;
	struct rev_info rev;
	int argc = 2;

	if (!tip)
		argv[1] = ctx.qry.head;

	if (path) {
		argv[argc++] = "--";
		argv[argc++] = path;
	}

	init_revisions(&rev, NULL);
	rev.abbrev = DEFAULT_ABBREV;
	rev.commit_format = CMIT_FMT_DEFAULT;
	rev.verbose_header = 1;
	rev.show_root_diff = 0;
	rev.max_count = max_count;
	setup_revisions(argc, argv, &rev, NULL);
	prepare_revision_walk(&rev);

	host = cgit_hosturl();
	ctx.page.mimetype = "text/xml";
	ctx.page.charset = "utf-8";
	cgit_print_http_headers(&ctx);
	html("<feed xmlns='http://www.w3.org/2005/Atom'>\n");
	html("<title>");
	html_txt(ctx.repo->name);
	html("</title>\n");
	html("<subtitle>");
	html_txt(ctx.repo->desc);
	html("</subtitle>\n");
	if (host) {
		html("<link rel='alternate' type='text/html' href='http://");
		html_attr(host);
		html_attr(cgit_repourl(ctx.repo->url));
		html("'/>\n");
	}
	while ((commit = get_revision(&rev)) != NULL) {
		add_entry(commit, host);
		free(commit->buffer);
		commit->buffer = NULL;
		free_commit_list(commit->parents);
		commit->parents = NULL;
	}
	html("</feed>\n");
}
curl_mime *mime{parameters_to_curl_mime(uri, parameters)}; // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) curl_easy_setopt(_connection, CURLOPT_MIMEPOST, mime); } // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) code = curl_easy_setopt(_connection, CURLOPT_CUSTOMREQUEST, "PATCH"); if (code != CURLE_OK) { throw CURLException{code, "Failed to set URI", _curl_buffer_error}; } break; } case http_method::PUT: { if (!parameters.empty()) { curl_mime *mime{parameters_to_curl_mime(uri, parameters)}; // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) curl_easy_setopt(_connection, CURLOPT_MIMEPOST, mime); } // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) code = curl_easy_setopt(_connection, CURLOPT_CUSTOMREQUEST, "PUT"); if (code != CURLE_OK) { throw CURLException{code, "Failed to set URI", _curl_buffer_error}; } break; } case http_method::DELETE: { if (!parameters.empty()) { curl_mime *mime{parameters_to_curl_mime(uri, parameters)}; // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) curl_easy_setopt(_connection, CURLOPT_MIMEPOST, mime); } // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) code = curl_easy_setopt(_connection, CURLOPT_CUSTOMREQUEST, "DELETE"); if (code != CURLE_OK) { throw CURLException{code, "Failed to set URI", _curl_buffer_error}; } break; } } debuglog << "Making request to: " << uri << '\n'; // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) code = curl_easy_setopt(_connection, CURLOPT_URL, uri.data()); if (code != CURLE_OK) { throw CURLException{code, "Failed to set URI", _curl_buffer_error}; } answer_type answer; code = curl_easy_perform(_connection); if (code == CURLE_OK || (code == CURLE_ABORTED_BY_CALLBACK && _stream_cancelled)) { long http_status; // NOLINT(google-runtime-int) // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) curl_easy_getinfo(_connection, CURLINFO_RESPONSE_CODE, &http_status); answer.http_status = static_cast<uint16_t>(http_status); debuglog << "HTTP status code: " << http_status << '\n'; answer.headers = _curl_buffer_headers; answer.body = _curl_buffer_body; } else { answer.curl_error_code = static_cast<uint8_t>(code); answer.error_message = _curl_buffer_error; debuglog << "libcurl error: " << code << '\n'; debuglog << _curl_buffer_error << '\n'; } return answer; } void CURLWrapper::setup_connection_properties(const string_view proxy, const string_view access_token, const string_view cainfo, const string_view useragent) { if (!proxy.empty()) { set_proxy(proxy); } if (!access_token.empty()) { set_access_token(access_token); } if (!cainfo.empty()) { set_cainfo(cainfo); } if (!useragent.empty()) { set_useragent(useragent); } } void CURLWrapper::set_proxy(const string_view proxy) { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) CURLcode code{curl_easy_setopt(_connection, CURLOPT_PROXY, proxy.data())}; if (code != CURLE_OK) { throw CURLException{code, "Failed to set proxy", _curl_buffer_error}; } debuglog << "Set proxy to: " << proxy << '\n'; } void CURLWrapper::set_access_token(const string_view access_token) { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, hicpp-signed-bitwise) CURLcode code{curl_easy_setopt(_connection, CURLOPT_XOAUTH2_BEARER, access_token.data())}; if (code != CURLE_OK) { throw CURLException{code, "Could not set authorization token.", _curl_buffer_error}; } #if (LIBCURL_VERSION_NUM < 0x073d00) // libcurl < 7.61.0. #define CURLAUTH_BEARER CURLAUTH_ANY #endif // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, hicpp-signed-bitwise) code = curl_easy_setopt(_connection, CURLOPT_HTTPAUTH, CURLAUTH_BEARER); if (code != CURLE_OK) { throw CURLException{code, "Could not set authorization token.", _curl_buffer_error}; } debuglog << "Set authorization token.\n"; } void CURLWrapper::set_cainfo(const string_view path) { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) CURLcode code{curl_easy_setopt(_connection, CURLOPT_CAINFO, path.data())}; if (code != CURLE_OK) { throw CURLException{code, "Could not set CA info.", _curl_buffer_error}; } } void CURLWrapper::set_useragent(const string_view useragent) { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) CURLcode code{curl_easy_setopt(_connection, CURLOPT_USERAGENT, useragent.data())}; if (code != CURLE_OK) { throw CURLException{code, "Failed to set User-Agent", _curl_buffer_error}; } debuglog << "Set User-Agent to: " << useragent << '\n'; } size_t CURLWrapper::writer_body(char *data, size_t size, size_t nmemb) { if(data == nullptr) { return 0; } buffer_mutex.lock(); _curl_buffer_body.append(data, size * nmemb); buffer_mutex.unlock(); return size * nmemb; } size_t CURLWrapper::writer_header(char *data, size_t size, size_t nmemb) { if(data == nullptr) { return 0; } _curl_buffer_headers.append(data, size * nmemb); return size * nmemb; } int CURLWrapper::progress(void *, curl_off_t , curl_off_t , curl_off_t , curl_off_t ) { if (_stream_cancelled) { return 1; } return 0; } void CURLWrapper::setup_curl() { if (_connection == nullptr) { throw CURLException{CURLE_FAILED_INIT, "Failed to initialize curl."}; } // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) curl_easy_setopt(_connection, CURLOPT_ERRORBUFFER, _curl_buffer_error); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) curl_easy_setopt(_connection, CURLOPT_WRITEFUNCTION, writer_body_wrapper); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) curl_easy_setopt(_connection, CURLOPT_WRITEDATA, this); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) curl_easy_setopt(_connection, CURLOPT_HEADERFUNCTION, writer_header_wrapper); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) curl_easy_setopt(_connection, CURLOPT_HEADERDATA, this); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) curl_easy_setopt(_connection, CURLOPT_XFERINFOFUNCTION, progress_wrapper); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) curl_easy_setopt(_connection, CURLOPT_XFERINFODATA, this); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) curl_easy_setopt(_connection, CURLOPT_NOPROGRESS, 0L); CURLWrapper::set_useragent((string("mastodonpp/") += version)); // The next 2 only fail if HTTP is not supported. // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) CURLcode code{curl_easy_setopt(_connection, CURLOPT_FOLLOWLOCATION, 1L)}; if (code != CURLE_OK) { throw CURLException{code, "HTTP is not supported.", _curl_buffer_error}; } // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) curl_easy_setopt(_connection, CURLOPT_MAXREDIRS, 10L); } bool CURLWrapper::replace_parameter_in_uri(string &uri, const parameterpair &parameter) { static constexpr array replace { "id", "nickname", "nickname_or_id", "account_id", "list_id", "hashtag", "permission_group" }; if (any_of(replace.begin(), replace.end(), [&parameter](const auto &s) { return s == parameter.first; })) { const auto pos{uri.find('<')}; if (pos != string::npos) { uri.replace(pos, parameter.first.size() + 2, get<string_view>(parameter.second)); debuglog << "Replaced :" << parameter.first << " in URI with " << get<string_view>(parameter.second) << '\n'; return true; } } return false; } void CURLWrapper::add_parameters_to_uri(string &uri, const parametermap &parameters) { // Replace <ID> with the value of parameter “id” and so on. for (const auto &param : parameters) { if (replace_parameter_in_uri(uri, param)) { continue; } static bool first{true}; if (first) { uri += "?"; first = false; } else { uri += "&"; } if (holds_alternative<string_view>(param.second)) { ((uri += param.first) += "=") += get<string_view>(param.second); } else { for (const auto &arg : get<vector<string_view>>(param.second)) { ((uri += param.first) += "[]=") += arg; if (arg != *get<vector<string_view>>(param.second).rbegin()) { uri += "&"; } } } } } void CURLWrapper::add_mime_part(curl_mime *mime, string_view name, string_view data) { curl_mimepart *part{curl_mime_addpart(mime)}; if (part == nullptr) { throw CURLException{"Could not build HTTP form."}; } CURLcode code{curl_mime_name(part, name.data())}; if (code != CURLE_OK) { throw CURLException{code, "Could not build HTTP form."}; } if (data.substr(0, 6) == "@file:") { const string_view filename{data.substr(6)}; code = curl_mime_filedata(part, filename.data()); } else { code = curl_mime_data(part, data.data(), CURL_ZERO_TERMINATED); } if (code != CURLE_OK) { throw CURLException{code, "Could not build HTTP form."}; } debuglog << "Set form part: " << name << " = " << data << '\n'; } curl_mime *CURLWrapper::parameters_to_curl_mime(string &uri, const parametermap &parameters) { debuglog << "Building HTTP form.\n"; curl_mime *mime{curl_mime_init(_connection)}; for (const auto &param : parameters) { if (replace_parameter_in_uri(uri, param)) { continue; } if (holds_alternative<string_view>(param.second)) { add_mime_part(mime, param.first, get<string_view>(param.second)); } else { for (const auto &arg : get<vector<string_view>>(param.second)) { const string_view name{string(param.first) += "[]"}; add_mime_part(mime, name, arg); } } } return mime; } } // namespace mastodonpp