Compare commits

...

50 Commits
0.5.0 ... main

Author SHA1 Message Date
tastytea c48f1dc3d0
add maintenance mode notice
continuous-integration/drone/push Build is passing Details
2022-11-13 06:24:35 +01:00
tastytea 9a25cd9178
add Windows section to readme
Reported-by: Ben S.
2022-11-13 06:19:51 +01:00
tastytea e74828c19e
don't add -fsanitize=undefined if MinGW is used
Reported-by: Ben S.
2022-11-13 06:17:34 +01:00
tastytea 7255df01e0
add support for testing with catch 3
continuous-integration/drone/push Build is passing Details
2022-08-01 14:21:06 +02:00
tastytea 7de644d841
Add repology badge.
continuous-integration/drone/push Build is passing Details
2021-05-17 23:31:50 +02:00
tastytea 52a849870f
Make direction a string_view in answer_type::parse_pagination(). 2021-02-08 17:28:46 +01:00
tastytea aabcb46602
Update .clang-tidy. 2020-11-30 20:52:56 +01:00
tastytea cca3a4a239
Update Gentoo package installation.
continuous-integration/drone/push Build is passing Details
2020-11-29 05:07:04 +01:00
tastytea 5ec7a119dc
Add hyperlink to AUR.
continuous-integration/drone/push Build is passing Details
2020-11-27 14:46:58 +01:00
tastytea cf4302248f
Typo in CI recipe.
continuous-integration/drone/push Build is passing Details
2020-11-20 22:52:57 +01:00
tastytea cadf0d777e
Version bump 0.5.7.
Forgot to bump the version in the CMake recipe, packages couldn't be
built.
2020-11-13 15:07:41 +01:00
tastytea aeb7396961
Generate API documentation with CMake.
continuous-integration/drone/push Build is passing Details
2020-11-13 14:25:22 +01:00
tastytea f4bd5abd01
Fix some warnings.
Avoid copy, initialize members in header, initialize variables.
2020-11-13 14:17:44 +01:00
tastytea c9211e621e
Reformat examples. 2020-11-13 14:05:28 +01:00
tastytea 3a93aec941
Reformat tests. 2020-11-13 14:01:09 +01:00
tastytea 9efc8d2dfd
Reformat source files. 2020-11-13 14:00:03 +01:00
tastytea 8c7493e68e
Reformat header files. 2020-11-13 13:45:59 +01:00
tastytea 63d2497966
Update .clang-tidy, add .clang-format. 2020-11-13 13:41:13 +01:00
tastytea 4da6929392
Add note about scopes in documentation for ObtainToken::step_1().
continuous-integration/drone/push Build is passing Details
2020-11-13 13:33:24 +01:00
tastytea 24cb2d523d
ObtainToken: Change grant_type to authorization_code.
continuous-integration/drone/push Build is passing Details
client_credentials worked before but not anymore. 🤷
2020-11-13 13:03:49 +01:00
tastytea 41f470d2aa
Add read to scopes in obtain-token example.
Without that, we cannot very our credentials. Mastodon needs
read::accounts, Pleroma needs read.
2020-11-13 13:02:52 +01:00
tastytea e563731efe
Avoid copies, fix warnings. 2020-11-13 12:01:18 +01:00
tastytea ef11508ca1
Bump maximum CMake version to 3.16.
continuous-integration/drone/push Build is passing Details
2020-05-19 19:33:34 +02:00
tastytea c2bc1b5e7b
Only use add_compile_definitions with CMake >= 3.12. 2020-05-19 19:32:34 +02:00
tastytea 3c23f6d1a0
Version bump 0.5.5. 2020-05-19 19:26:51 +02:00
tastytea 3909187f7a
Add -DNDEBUG for non-debug builds.
continuous-integration/drone/push Build is failing Details
2020-05-19 19:24:21 +02:00
tastytea 2ada3b406b
Merge branch 'develop' into main
continuous-integration/drone/push Build is passing Details
2020-04-18 15:44:12 +02:00
tastytea d20818221d
Add Support for Pleroma 2.0.2.
continuous-integration/drone/push Build is passing Details
Add endpoint: /api/pleroma/admin/users/:nickname/update_credentials
2020-04-18 15:25:49 +02:00
tastytea f16e6d61e0
Remove unused using declaration. 2020-04-18 15:25:49 +02:00
tastytea 576c84aa36
Version bump 0.5.4.
continuous-integration/drone/push Build is passing Details
2020-03-21 12:26:42 +01:00
tastytea aea4b0f492
Merge branch 'develop' into main
continuous-integration/drone/push Build was killed Details
2020-03-21 12:25:54 +01:00
tastytea 5bf1e9bf25
Define copy constructor for instance.
continuous-integration/drone/push Build is passing Details
Needed because the underlying CURLWrapper is not copied but freshly created, so
access_token, proxy, cainfo and useragent have to be set.
2020-03-21 11:38:34 +01:00
tastytea 251d8a975c
Set access token in CURLWrapper too if it is set in Instance. 2020-03-21 11:26:23 +01:00
tastytea b5144fd9ce
Ensure that the first parameter in all GET calls is prefaced with ?.
continuous-integration/drone/push Build is passing Details
All calls to CURLWrapper::add_parameters_to_uri() but the first used & for all
parameters, because the boolean keeping track was implemented wrong.
2020-03-20 16:29:16 +01:00
tastytea 0bbc6ac4c7
Merge branch 'develop' into main
continuous-integration/drone/push Build is passing Details
2020-03-20 14:48:59 +01:00
tastytea abd7079c5a
Allow argument in constructor of Connection to be const.
continuous-integration/drone/push Build is running Details
2020-03-20 14:47:44 +01:00
tastytea ce27005e9c
Version bump 0.5.2.
continuous-integration/drone/push Build is passing Details
2020-03-20 13:53:13 +01:00
tastytea 778b7c3a5f
Merge branch 'develop' into main 2020-03-20 13:52:59 +01:00
tastytea 9d37bebdc8
Add missing constructors and assignment operators
continuous-integration/drone/push Build is passing Details
… to Instance and Connection.
2020-03-20 13:49:30 +01:00
tastytea fc32e8ac0a
Add copy constructor for CURLWrapper.
The copy constructor does the same as the constructor. A new CURL handle is used
for the “copy”.
2020-03-20 13:49:20 +01:00
tastytea d1b3455584
Version bump 0.5.1.
continuous-integration/drone/push Build is passing Details
2020-03-12 12:15:39 +01:00
tastytea f983acb910
Merge branch 'develop' into main 2020-03-12 12:15:19 +01:00
tastytea da1c2ba409
Handle more than one replacement in replace_parameter_in_uri().
continuous-integration/drone/push Build is passing Details
2020-03-12 12:12:31 +01:00
tastytea 00056c224e
Update parameter replacements for Pleroma 2.0.0.
continuous-integration/drone/push Build is passing Details
2020-03-12 11:40:11 +01:00
tastytea 854d2f67af
Updated Pleroma endpoints to version 2.0.0.
* v1::
  * pleroma_conversations_id_read
  * pleroma_accounts_id_scrobbles
  * pleroma_scrobble
  * pleroma_statuses_id_reactions_emoji
  * pleroma_statuses_id_reactions

* pleroma::
  * admin_users_nickname_toggle_activation
  * admin_users_permission_group_permission_group
  * admin_users_activate
  * admin_users_deactivate
  * admin_instances_instance_statuses
  * admin_statuses
  * admin_users_force_password_reset
  * admin_grouped_reports
  * admin_reports_id_notes
  * admin_reports_report_id_notes_id
  * admin_restart
  * admin_config_descriptions
  * admin_moderation_log
  * admin_reload_emoji
  * admin_users_confirm_email
  * admin_users_resend_confirm_email
  * admin_stats
  * emoji_packs
  * emoji_packs_name
  * emoji_packs_name_update_file
  * emoji_packs_name_update_metadata
  * emoji_packs_download_from
  * emoji_packs_list_from
  * emoji_packs_name_download_shared
2020-03-12 11:38:41 +01:00
tastytea e1a2b4e843
Merge branch 'develop' into main
continuous-integration/drone/push Build is passing Details
2020-02-16 14:09:01 +01:00
tastytea b80e40ada1
Update contribution guidelines.
continuous-integration/drone/push Build is passing Details
2020-02-16 14:08:17 +01:00
tastytea 1c3efd3589
Typo: A space too much. 2020-02-12 20:22:47 +01:00
tastytea ac07daddce
Make map of named entities into 2 columns.
continuous-integration/drone/push Build is passing Details
2020-02-12 17:13:17 +01:00
tastytea 58aebfc4c0
Mark unescape_html() nodiscard.
continuous-integration/drone/push Build is passing Details
2020-02-03 13:06:01 +01:00
41 changed files with 849 additions and 679 deletions

131
.clang-format Normal file
View File

@ -0,0 +1,131 @@
# -*- mode: yaml -*-
# Written for clang-format 10.
# https://releases.llvm.org/10.0.0/tools/clang/docs/ClangFormatStyleOptions.html
---
DisableFormat: false
Language: Cpp
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
# AlignConsecutiveBitFields: false # clang-format 11
AlignConsecutiveDeclarations: false
AlignConsecutiveMacros: false
AlignEscapedNewlines: DontAlign
AlignOperands: true # clang-format 11: Align
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Empty
AllowShortCaseLabelsOnASingleLine: false
# AllowShortEnumsOnASingleLine: false # clang-format 11
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: Inline
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: true
BinPackParameters: true
BraceWrapping: # If BreakBeforeBraces is set to Custom.
AfterCaseLabel: true
AfterClass: true
AfterControlStatement: Always
AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterStruct: true
AfterUnion: true
AfterExternBlock: true
BeforeCatch: true
BeforeElse: true
# BeforeLambdaBody: true # clang-format 11
# BeforeWhile: true # clang-format 11
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Custom
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeComma
BreakInheritanceList: BeforeComma
BreakStringLiterals: true
ColumnLimit: 80
# CommentPragmas: 'regex'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: false
FixNamespaceComments: true
ForEachMacros:
- FOREACH
- RANGES_FOR
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Regroup
IncludeCategories: # stdlib headers into own group.
- Regex: '^[^\.]+$'
Priority: 4
# IndentCaseBlocks: false # clang-format 11
IndentCaseLabels: false
# IndentExternBlock: NoIndent # clang-format 11
IndentGotoLabels: false
IndentPPDirectives: AfterHash
IndentWidth: 4
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: true
# MacroBlockBegin: 'string'
# MacroBlockEnd: 'string'
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
# NamespaceMacros: 'string'
PenaltyBreakAssignment: 250
PenaltyBreakBeforeFirstCallParameter: 300
# PenaltyBreakComment: 300
# PenaltyBreakFirstLessLess: 120
# PenaltyBreakString: 1000
# PenaltyBreakTemplateDeclaration: 10
# PenaltyExcessCharacter: 1000000
# PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
# RawStringFormats: # <YAML>
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Auto
# StatementMacros:
# - Q_UNUSED
# - QT_REQUIRE_VERSION
TabWidth: 4
# TypenameMacros:
# - STACK_OF
# - LIST
UseCRLF: false
UseTab: Never
# WhitespaceSensitiveMacros: ['string', 'string'] # clang-format 11
...

View File

@ -1,5 +1,5 @@
# -*- mode: conf; fill-column: 100; -*-
# Written for clang-tidy 9.
# Written for clang-tidy 11.
---
Checks: '*,
@ -25,19 +25,21 @@ Checks: '*,
-hicpp-vararg,
-fuchsia-statically-constructed-objects,
-google-readability-todo,
-modernize-use-trailing-return-type'
CheckOptions: - { key: readability-identifier-naming.ClassCase, value: CamelCase }
# Clashes with constant private member prefix. (const int _var;)
# - { key: readability-identifier-naming.ConstantCase, value: lower_case }
- { key: readability-identifier-naming.EnumCase, value: lower_case }
- { key: readability-identifier-naming.FunctionCase, value: lower_case }
-modernize-use-trailing-return-type,
-fuchsia-multiple-inheritance,
-llvmlibc*'
FormatStyle: file # Use .clang-format.
CheckOptions: # ↓ Clashes with static private member prefix. (static int _var;) ↓
- { key: readability-identifier-naming.VariableCase, value: lower_case }
- { key: readability-identifier-naming.MemberCase, value: lower_case }
- { key: readability-identifier-naming.ParameterCase, value: lower_case }
- { key: readability-identifier-naming.PrivateMemberCase, value: lower_case }
- { key: readability-identifier-naming.PrivateMemberPrefix, value: _ }
- { key: readability-identifier-naming.ProtextedMemberCase, value: lower_case }
- { key: readability-identifier-naming.ProtectedMemberCase, value: lower_case }
- { key: readability-identifier-naming.ProtectedMemberPrefix, value: _ }
- { key: readability-identifier-naming.ClassCase, value: CamelCase }
- { key: readability-identifier-naming.StructCase, value: lower_case }
# Clashes with static private member prefix. (static int _var;)
# - { key: readability-identifier-naming.VariableCase, value: lower_case }
- { key: readability-identifier-naming.EnumCase, value: lower_case }
- { key: readability-identifier-naming.FunctionCase, value: lower_case }
- { key: readability-identifier-naming.ParameterCase, value: lower_case }
...

View File

@ -141,7 +141,7 @@ steps:
- apt-get install -qq build-essential cmake lsb-release
- apt-get install -qq libcurl4-openssl-dev
- rm -rf build && mkdir -p build && cd build
- cmake -G "Unix Makefiles" -SCMAKE_INSTALL_PREFIX=/usr -DWITH_DEB=YES ..
- cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX=/usr -DWITH_DEB=YES ..
- make
- make install DESTDIR=install
- make package

1
.gitignore vendored
View File

@ -1,4 +1,3 @@
/build/
/doc/
/update_doc.sh
/examples/example99*

View File

@ -1,6 +1,6 @@
# Support version 3.9 and above, but use policy settings up to 3.14.
# 3.9 is needed for project description.
cmake_minimum_required(VERSION 3.9...3.14)
cmake_minimum_required(VERSION 3.9...3.16)
# Ranges are supported from 3.12, set policy to current for < 3.12.
if(${CMAKE_VERSION} VERSION_LESS 3.12)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
@ -10,8 +10,17 @@ endif()
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "The type of build.")
option(BUILD_SHARED_LIBS "Build shared libraries." YES)
# Not every non-debug build type adds -DNDEBUG.
if(NOT ${CMAKE_BUILD_TYPE} MATCHES "Debug")
if(${CMAKE_VERSION} VERSION_LESS 3.12)
add_definitions("-DNDEBUG")
else()
add_compile_definitions("NDEBUG")
endif()
endif()
project(mastodonpp
VERSION 0.5.0
VERSION 0.5.7
DESCRIPTION "C++ wrapper for the Mastodon and Pleroma APIs."
LANGUAGES CXX)
@ -20,6 +29,7 @@ list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
# Project build options.
option(WITH_TESTS "Compile tests." NO)
option(WITH_EXAMPLES "Compile examples." NO)
option(WITH_DOC "Generate API documentation." NO)
option(WITH_DEB "Prepare for the building of .deb packages." NO)
option(WITH_RPM "Prepare for the building of .rpm packages." NO)
option(WITH_CLANG-TIDY "Check sourcecode with clang-tidy while compiling." NO)
@ -51,4 +61,11 @@ if(WITH_EXAMPLES)
add_subdirectory(examples)
endif()
if(WITH_DOC)
include(cmake/Doxygen.cmake)
enable_doxygen(
"${CMAKE_CURRENT_SOURCE_DIR}/include"
"${CMAKE_CURRENT_SOURCE_DIR}/src")
endif()
include(cmake/packages.cmake)

View File

@ -25,7 +25,9 @@ don't want to open an account.
=== Pull requests
Please use similar coding conventions as the rest of the project. The basic rule
to remember is to write code in the same style as the existing/surrounding code.
to remember is to write code in the same style as the existing/surrounding
code. Add a copyright line with the year, your name and your email address to
the files you edited, unless you don't want to.
You can also send me your patches via mailto:{contact-email}[E-Mail], ideally
using link:{uri-git-format-patch}[git format-patch] or

View File

@ -1,27 +0,0 @@
# -*- mode: conf-unix -*-
INPUT = src/ include/
RECURSIVE = YES
STRIP_FROM_INC_PATH = "include"
EXAMPLE_PATH = examples/
EXAMPLE_RECURSIVE = YES
GENERATE_HTML = YES
HTML_OUTPUT = doc/html
GENERATE_LATEX = NO
ALLOW_UNICODE_NAMES = YES
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES
ALWAYS_DETAILED_SEC = YES
INLINE_INHERITED_MEMB = NO
INHERIT_DOCS = YES
SEPARATE_MEMBER_PAGES = NO
TAB_SIZE = 4
MARKDOWN_SUPPORT = YES
AUTOLINK_SUPPORT = YES
INLINE_SIMPLE_STRUCTS = NO
QUIET = NO
WARNINGS = YES
BUILTIN_STL_SUPPORT = YES
VERBATIM_HEADERS = YES
INLINE_SOURCES = YES
SEARCHENGINE = YES
SHOW_FILES = YES

View File

@ -22,6 +22,10 @@
link:{uri-pleroma}[Pleroma] APIs. It replaces
link:{uri-mastodon-cpp}[mastodon-cpp].
[IMPORTANT]
mastodonpp is in maintenance mode. I will continue to fix bugs, but won't add
new features. If you'd like to adopt this project, please get in touch.
We aim to create a library that is comfortable, yet minimal. All API endpoints
from Mastodon and Pleroma are stored in ``enum class``es, to counteract typos
and make your life easier. The network-facing code is built on
@ -79,16 +83,24 @@ link:{uri-reference}/examples.html[More examples] are included in the reference.
== Install
[alt="Packaging status" link=https://repology.org/project/mastodonpp/versions]
image::https://repology.org/badge/vertical-allrepos/mastodonpp.svg[]
=== Gentoo
[source,shell]
--------------------------------------------------------------------------------
eselect repository enable tastytea
eselect repository enable guru
echo 'dev-cpp/mastodonpp' >> /etc/portage/package.accept_keywords/mastodonpp
emaint sync -r tastytea
emaint sync -r guru
emerge -a dev-cpp/mastodonpp
--------------------------------------------------------------------------------
=== Arch
The git-version is available via the AUR:
<https://aur.archlinux.org/packages/mastodonpp-git/>.
=== Debian and Ubuntu
We automatically generate packages for Debian buster (10) and Ubuntu bionic
@ -151,6 +163,7 @@ cmake --build . -- -j$(nproc --ignore=1)
* `-DCMAKE_BUILD_TYPE=Debug` for a debug build.
* `-DWITH_TESTS=YES` if you want to compile the tests.
* `-DWITH_EXAMPLES=YES` if you want to compile the examples.
* `-DWITH_DOC=YES` if you want to generate the API documentation.
* `-DWITH_CLANG-TIDY=YES` to check the sourcecode with
link:{uri-clang-tidy}[clang-tidy] while compiling.
* One of:
@ -159,4 +172,10 @@ cmake --build . -- -j$(nproc --ignore=1)
To create a deb or rpm package, run `make package` after compiling.
===== Windows
mastodonpp has been reported to compile with MinGW GCC, but
`http_method::DELETE` has to be renamed, because Windows headers define a
`DELETE` macro.
include::{uri-base}/raw/branch/main/CONTRIBUTING.adoc[]

View File

@ -1,11 +0,0 @@
#!/bin/sh
project="$(realpath --relative-base=.. .)"
version="$(grep -Eo '[0-9]+.[0-9]+.[0-9]+$' CMakeLists.txt)"
if [[ -f Doxyfile ]]; then
mkdir -p doc
(doxygen -s -g - && cat Doxyfile &&
echo "PROJECT_NAME = ${project}" &&
echo "PROJECT_NUMBER = ${version}") | doxygen -
fi

43
cmake/Doxygen.cmake Normal file
View File

@ -0,0 +1,43 @@
include(GNUInstallDirs)
function(enable_doxygen)
find_package(Doxygen REQUIRED dot)
set(DOXYGEN_RECURSIVE YES)
set(DOXYGEN_STRIP_FROM_INC_PATH "include")
if (WITH_EXAMPLES)
set(DOXYGEN_EXAMPLE_PATH "examples/")
set(DOXYGEN_EXAMPLE_RECURSIVE YES)
endif()
set(DOXYGEN_GENERATE_HTML YES)
set(DOXYGEN_HTML_OUTPUT "doc/html")
set(DOXYGEN_GENERATE_LATEX NO)
set(DOXYGEN_ALLOW_UNICODE_NAMES YES)
set(DOXYGEN_BRIEF_MEMBER_DESC YES)
set(DOXYGEN_REPEAT_BRIEF YES)
set(DOXYGEN_ALWAYS_DETAILED_SEC YES)
set(DOXYGEN_INLINE_INHERITED_MEMB NO)
set(DOXYGEN_INHERIT_DOCS YES)
set(DOXYGEN_SEPARATE_MEMBER_PAGES NO)
set(DOXYGEN_TAB_SIZE 4)
set(DOXYGEN_MARKDOWN_SUPPORT YES)
set(DOXYGEN_AUTOLINK_SUPPORT YES)
set(DOXYGEN_INLINE_SIMPLE_STRUCTS NO)
set(DOXYGEN_QUIET YES)
set(DOXYGEN_WARNINGS YES)
set(DOXYGEN_WARN_IF_UNDOCUMENTED YES)
set(DOXYGEN_BUILTIN_STL_SUPPORT YES)
set(DOXYGEN_VERBATIM_HEADERS YES)
set(DOXYGEN_INLINE_SOURCES YES)
set(DOXYGEN_SEARCHENGINE YES)
set(DOXYGEN_SHOW_FILES YES)
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/doc")
doxygen_add_docs(${PROJECT_NAME}_doxygen "${ARGV}")
# Make sure doxygen is run with every build.
add_custom_target(${PROJECT_NAME}_docs ALL DEPENDS ${PROJECT_NAME}_doxygen)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc/html
DESTINATION "${CMAKE_INSTALL_DOCDIR}")
endfunction()

View File

@ -22,7 +22,6 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang"
"-Wdouble-promotion"
"-Wformat=2"
"-ftrapv"
"-fsanitize=undefined"
"-Og"
"-fno-omit-frame-pointer")
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
@ -40,15 +39,21 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang"
endif()
endif()
endif()
if(NOT MINGW)
list(APPEND DEBUG_CXXFLAGS
"-fsanitize=undefined")
endif()
add_compile_options("$<$<CONFIG:Debug>:${DEBUG_CXXFLAGS}>")
set(DEBUG_LDFLAGS
"-fsanitize=undefined")
# add_link_options was introduced in version 3.13.
if(${CMAKE_VERSION} VERSION_LESS 3.13)
set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${DEBUG_LDFLAGS}")
else()
add_link_options("$<$<CONFIG:Debug>:${DEBUG_LDFLAGS}>")
if(NOT MINGW)
set(DEBUG_LDFLAGS
"-fsanitize=undefined")
# add_link_options was introduced in version 3.13.
if(${CMAKE_VERSION} VERSION_LESS 3.13)
set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${DEBUG_LDFLAGS}")
else()
add_link_options("$<$<CONFIG:Debug>:${DEBUG_LDFLAGS}>")
endif()
endif()
else()
message(STATUS

View File

@ -16,7 +16,7 @@
// Print information about an instance (/api/v1/instance).
#if __has_include("mastodonpp.hpp")
# include "mastodonpp.hpp" // We're building mastodonpp.
# include "mastodonpp.hpp" // We're building mastodonpp.
#else
# include <mastodonpp/mastodonpp.hpp> // We're building outside mastodonpp.
#endif
@ -27,11 +27,11 @@
#include <vector>
namespace masto = mastodonpp;
using std::cout;
using std::cerr;
using std::cout;
using std::endl;
using std::to_string;
using std::string_view;
using std::to_string;
using std::vector;
int main(int argc, char *argv[])

View File

@ -16,7 +16,7 @@
// Print new public events (/api/v1/streaming/public).
#if __has_include("mastodonpp.hpp")
# include "mastodonpp.hpp" // We're building mastodonpp.
# include "mastodonpp.hpp" // We're building mastodonpp.
#else
# include <mastodonpp/mastodonpp.hpp> // We're building outside mastodonpp.
#endif
@ -30,14 +30,14 @@
namespace masto = mastodonpp;
using namespace std::chrono_literals;
using std::cout;
using std::cerr;
using std::cout;
using std::endl;
using std::to_string;
using std::string_view;
using std::thread;
using std::this_thread::sleep_for;
using std::to_string;
using std::vector;
using std::this_thread::sleep_for;
int main(int argc, char *argv[])
{
@ -61,8 +61,10 @@ int main(int argc, char *argv[])
if (answer && answer.body == "OK")
{
// Make a thread, get all public events.
// clang-format off
thread stream_thread{[&]
{
// clang-format on
answer = connection.get(masto::API::v1::streaming_public);
}};
@ -73,8 +75,8 @@ int main(int argc, char *argv[])
for (const auto &event : connection.get_new_events())
{
// Print type of event and the beginning of the data.
cout << event.type << ": "
<< event.data.substr(0, 70) << "" << endl;
cout << event.type << ": " << event.data.substr(0, 70)
<< "" << endl;
}
}

View File

@ -16,7 +16,7 @@
// Post a status (/api/v1/status).
#if __has_include("mastodonpp.hpp")
# include "mastodonpp.hpp" // We're building mastodonpp.
# include "mastodonpp.hpp" // We're building mastodonpp.
#else
# include <mastodonpp/mastodonpp.hpp> // We're building outside mastodonpp.
#endif
@ -27,11 +27,11 @@
#include <vector>
namespace masto = mastodonpp;
using std::cout;
using std::cerr;
using std::cout;
using std::endl;
using std::to_string;
using std::string_view;
using std::to_string;
using std::vector;
int main(int argc, char *argv[])
@ -51,12 +51,12 @@ int main(int argc, char *argv[])
// Set up the parameters.
constexpr auto poll_seconds{60 * 60 * 24 * 2}; // 2 days.
const masto::parametermap parameters
{
{"status", "How is the weather?"},
{"poll[options]", vector<string_view>{"Nice", "not nice"}},
{"poll[expires_in]", to_string(poll_seconds)}
};
const masto::parametermap parameters{{"status", "How is the weather?"},
{"poll[options]",
vector<string_view>{"Nice",
"not nice"}},
{"poll[expires_in]",
to_string(poll_seconds)}};
// Post the status.
auto answer{connection.post(masto::API::v1::statuses, parameters)};

View File

@ -16,7 +16,7 @@
// Post a status (/api/v1/status) with an attachment (/api/v1/media).
#if __has_include("mastodonpp.hpp")
# include "mastodonpp.hpp" // We're building mastodonpp.
# include "mastodonpp.hpp" // We're building mastodonpp.
#else
# include <mastodonpp/mastodonpp.hpp> // We're building outside mastodonpp.
#endif
@ -27,12 +27,12 @@
#include <vector>
namespace masto = mastodonpp;
using std::cout;
using std::cerr;
using std::cout;
using std::endl;
using std::string;
using std::to_string;
using std::string_view;
using std::to_string;
using std::vector;
int main(int argc, char *argv[])
@ -54,10 +54,8 @@ int main(int argc, char *argv[])
// Create attachment.
auto answer{connection.post(masto::API::v1::media,
{
{"file", string("@file:") += filename},
{"description", "Test."}
})};
{{"file", string("@file:") += filename},
{"description", "Test."}})};
// Get the ID of the attachment.
// You normally would use a JSON parser, of course. I don't use one
@ -69,11 +67,9 @@ int main(int argc, char *argv[])
// Post the status. Note that “media_ids” always has to be a vector.
answer = connection.post(masto::API::v1::statuses,
{
{"status", "Attachment test."},
{"media_ids",
vector<string_view>{media_id}}
});
{{"status", "Attachment test."},
{"media_ids",
vector<string_view>{media_id}}});
if (answer)
{
cout << "Successfully posted " << filename << ".\n";

View File

@ -16,7 +16,7 @@
// Update notification settings (/api/pleroma/notification_settings).
#if __has_include("mastodonpp.hpp")
# include "mastodonpp.hpp" // We're building mastodonpp.
# include "mastodonpp.hpp" // We're building mastodonpp.
#else
# include <mastodonpp/mastodonpp.hpp> // We're building outside mastodonpp.
#endif
@ -27,11 +27,11 @@
#include <vector>
namespace masto = mastodonpp;
using std::cout;
using std::cerr;
using std::cout;
using std::endl;
using std::to_string;
using std::string_view;
using std::to_string;
using std::vector;
int main(int argc, char *argv[])
@ -50,14 +50,14 @@ int main(int argc, char *argv[])
masto::Connection connection{instance};
// Update the settings.
const auto answer{connection.put(
masto::API::pleroma::notification_settings,
{
{"followers", "true"},
{"follows", "true"},
{"remote", "true"},
{"local", "true"},
})};
const auto answer{
connection.put(masto::API::pleroma::notification_settings,
{
{"followers", "true"},
{"follows", "true"},
{"remote", "true"},
{"local", "true"},
})};
if (answer)
{
cout << answer << endl;

View File

@ -16,7 +16,7 @@
// Update account display name settings (/api/v1/accounts/update_credentials).
#if __has_include("mastodonpp.hpp")
# include "mastodonpp.hpp" // We're building mastodonpp.
# include "mastodonpp.hpp" // We're building mastodonpp.
#else
# include <mastodonpp/mastodonpp.hpp> // We're building outside mastodonpp.
#endif
@ -27,11 +27,11 @@
#include <vector>
namespace masto = mastodonpp;
using std::cout;
using std::cerr;
using std::cout;
using std::endl;
using std::to_string;
using std::string_view;
using std::to_string;
using std::vector;
int main(int argc, char *argv[])
@ -52,11 +52,9 @@ int main(int argc, char *argv[])
masto::Connection connection{instance};
// Update the settings.
const auto answer{connection.patch(
masto::API::v1::accounts_update_credentials,
{
{"display_name", name}
})};
const auto answer{
connection.patch(masto::API::v1::accounts_update_credentials,
{{"display_name", name}})};
if (answer)
{
cout << "Successfully changed display name.\n";

View File

@ -16,7 +16,7 @@
// Post a status (/api/v1/status), then delete it (/api/v1/statuses/:id).
#if __has_include("mastodonpp.hpp")
# include "mastodonpp.hpp" // We're building mastodonpp.
# include "mastodonpp.hpp" // We're building mastodonpp.
#else
# include <mastodonpp/mastodonpp.hpp> // We're building outside mastodonpp.
#endif
@ -30,13 +30,13 @@
namespace masto = mastodonpp;
using namespace std::chrono_literals;
using std::cout;
using std::cerr;
using std::cout;
using std::endl;
using std::to_string;
using std::string_view;
using std::this_thread::sleep_for;
using std::to_string;
using std::vector;
using std::this_thread::sleep_for;
int main(int argc, char *argv[])
{

View File

@ -16,7 +16,7 @@
// Obtain an access token and verify that it works.
#if __has_include("mastodonpp.hpp")
# include "mastodonpp.hpp" // We're building mastodonpp.
# include "mastodonpp.hpp" // We're building mastodonpp.
#else
# include <mastodonpp/mastodonpp.hpp> // We're building outside mastodonpp.
#endif
@ -28,14 +28,14 @@
#include <vector>
namespace masto = mastodonpp;
using std::exit;
using std::cout;
using std::cerr;
using std::endl;
using std::cin;
using std::cout;
using std::endl;
using std::exit;
using std::string;
using std::to_string;
using std::string_view;
using std::to_string;
using std::vector;
void handle_error(const masto::answer_type &answer);
@ -57,7 +57,9 @@ int main(int argc, char *argv[])
// Create an “Application” (/api/v1/apps),
// and get URI for the authorization code (/oauth/authorize).
auto answer{token.step_1("Testclient", "read:blocks read:mutes",
// NOTE: Mastodon only needs read:accounts for verify_credentials but
// Pleroma needs the full read scope.
auto answer{token.step_1("Testclient", "read write:favourites",
"https://example.com/")};
if (!answer)
{
@ -107,8 +109,8 @@ void handle_error(const masto::answer_type &answer)
else
{
// Network errors like “Couldn't resolve host.”.
cerr << "libcurl error " << to_string(answer.curl_error_code)
<< ": " << answer.error_message << endl;
cerr << "libcurl error " << to_string(answer.curl_error_code) << ": "
<< answer.error_message << endl;
}
exit(1);

View File

@ -17,7 +17,7 @@
// nlohmann-json. <https://github.com/nlohmann/json>
#if __has_include("mastodonpp.hpp")
# include "mastodonpp.hpp" // We're building mastodonpp.
# include "mastodonpp.hpp" // We're building mastodonpp.
#else
# include <mastodonpp/mastodonpp.hpp> // We're building outside mastodonpp.
#endif
@ -26,19 +26,19 @@
#if __has_include(<nlohmann/json.hpp>)
# include <nlohmann/json.hpp>
#include <cstdlib>
#include <iostream>
#include <string>
#include <string_view>
#include <vector>
# include <cstdlib>
# include <iostream>
# include <string>
# include <string_view>
# include <vector>
namespace masto = mastodonpp;
using json = nlohmann::json;
using std::exit;
using std::cout;
using std::cerr;
using std::to_string;
using std::cout;
using std::exit;
using std::string_view;
using std::to_string;
using std::vector;
void handle_error(const masto::answer_type &answer);
@ -60,10 +60,7 @@ int main(int argc, char *argv[])
// Get the last 4 public statuses of the instance.
auto answer{connection.get(masto::API::v1::timelines_public,
{
{"limit", "4"},
{"local", "true"}
})};
{{"limit", "4"}, {"local", "true"}})};
if (answer)
{
// Parse JSON string.
@ -129,8 +126,8 @@ void handle_error(const masto::answer_type &answer)
else
{
// Network errors like “Couldn't resolve host.”.
cerr << "libcurl error " << to_string(answer.curl_error_code)
<< ": " << answer.error_message << '\n';
cerr << "libcurl error " << to_string(answer.curl_error_code) << ": "
<< answer.error_message << '\n';
}
exit(1);
@ -138,11 +135,11 @@ void handle_error(const masto::answer_type &answer)
#else
#include <iostream>
# include <iostream>
int main()
{
std::cout << "Example could not be compiled "
"because nlohmann-json was not found.\n";
"because nlohmann-json was not found.\n";
}
#endif // __has_include(<nlohmann/json.hpp>)
#endif // __has_include(<nlohmann/json.hpp>)

View File

@ -31,7 +31,7 @@ using std::variant;
/*!
* @brief Holds %API endpoints.
*
* Supported %API endpoints: Mastodon 3.0.1, Pleroma 1.1.7.
* Supported %API endpoints: Mastodon 3.0.1, Pleroma 2.0.2.
*
* @since 0.1.0
*
@ -43,7 +43,7 @@ public:
/*!
* @brief An enumeration of all v1 %API endpoints.
*
* The original `/` are substituted with `_`.
* The original `/` are substituted with `_`. `:` are omitted.
*
* @since 0.1.0
*/
@ -184,7 +184,6 @@ public:
admin_reports_id_reopen,
pleroma_notifications_read,
pleroma_accounts_id_subscribe,
pleroma_accounts_id_unsubscribe,
pleroma_accounts_id_favourites,
@ -192,17 +191,20 @@ public:
pleroma_accounts_update_banner,
pleroma_accounts_update_background,
pleroma_accounts_confirmation_resend,
pleroma_mascot,
pleroma_conversations_id_statuses,
pleroma_conversations_id,
pleroma_conversations_id_read,
pleroma_accounts_id_scrobbles,
pleroma_scrobble,
pleroma_statuses_id_reactions_emoji,
pleroma_statuses_id_reactions,
};
/*!
* @brief An enumeration of all v2 %API endpoints.
*
* The original `/` are substituted with `_`.
* The original `/` are substituted with `_`. `:` are omitted.
*
* @since 0.1.0
*/
@ -214,7 +216,7 @@ public:
/*!
* @brief An enumeration of all oauth %API endpoints.
*
* The original `/` are substituted with `_`.
* The original `/` are substituted with `_`. `:` are omitted.
*
* @since 0.1.0
*/
@ -230,7 +232,7 @@ public:
*
* These endpoints are directly under `/api/`.
*
* The original `/` are substituted with `_`.
* The original `/` are substituted with `_`. `:` are omitted.
*
* @since 0.1.0
*/
@ -243,7 +245,7 @@ public:
/*!
* @brief An enumeration of all pleroma %API endpoints.
*
* The original `/` are substituted with `_`.
* The original `/` are substituted with `_`. `:` are omitted.
*
* @since 0.1.0
*/
@ -252,38 +254,66 @@ public:
admin_users,
admin_users_follow,
admin_users_unfollow,
admin_users_nickname,
admin_users_nickname_toggle_activation,
admin_users_tag,
admin_users_nickname_permission_group,
admin_users_nickname_permission_group_permission_group,
admin_users_nickname_activation_status,
admin_users_permission_group_permission_group,
admin_users_activate,
admin_users_deactivate,
admin_users_nickname_or_id,
admin_users_nickname_or_id_statuses,
admin_instances_instance_statuses,
admin_statuses,
admin_relay,
admin_users_invite_token,
admin_users_invites,
admin_users_revoke_invite,
admin_users_email_invite,
admin_users_nickname_password_reset,
admin_users_nickname_update_credentials,
admin_users_force_password_reset,
admin_reports,
admin_grouped_reports,
admin_reports_id,
admin_reports_id_respond,
admin_reports_id_notes,
admin_reports_report_id_notes_id,
admin_statuses_id,
admin_restart,
admin_config,
admin_config_descriptions,
admin_moderation_log,
admin_reload_emoji,
admin_users_confirm_email,
admin_users_resend_confirm_email,
admin_stats,
// No longer documented for Pleroma 2.0.0.
admin_users_nickname,
admin_users_nickname_activation_status,
admin_reports_id_respond,
admin_config_migrate_to_db,
admin_config_migrate_from_db,
admin_config,
emoji,
follow_import,
captcha,
delete_account,
disable_account,
account_register,
notification_settings,
healthcheck,
change_email
change_email,
emoji_packs,
emoji_packs_name,
emoji_packs_name_update_file,
emoji_packs_name_update_metadata,
emoji_packs_download_from,
emoji_packs_list_from,
emoji_packs_name_download_shared,
// No longer documented for Pleroma 2.0.0.
account_register,
};
/*!
@ -292,7 +322,7 @@ public:
*
* @since 0.1.0
*/
using endpoint_type = variant<v1,v2,oauth,other,pleroma>;
using endpoint_type = variant<v1, v2, oauth, other, pleroma>;
/*!
* @brief Constructs an API object. You should never need this.
@ -309,17 +339,16 @@ public:
*
* @since 0.1.0
*/
[[nodiscard]]
inline string_view to_string_view() const
[[nodiscard]] inline string_view to_string_view() const
{
return _endpoint_map.at(_endpoint);
}
private:
const endpoint_type _endpoint;
static const map<endpoint_type,string_view> _endpoint_map;
static const map<endpoint_type, string_view> _endpoint_map;
};
} // namespace mastodonpp
#endif // MASTODONPP_API_HPP
#endif // MASTODONPP_API_HPP

View File

@ -40,7 +40,7 @@ using std::vector;
*
* @since 0.1.0
*/
using endpoint_variant = variant<API::endpoint_type,string_view>;
using endpoint_variant = variant<API::endpoint_type, string_view>;
/*!
* @brief A stream event.
@ -85,13 +85,32 @@ public:
*
* @since 0.1.0
*/
explicit Connection(Instance &instance)
explicit Connection(const Instance &instance)
: _instance{instance}
, _baseuri{instance.get_baseuri()}
{
_instance.copy_connection_properties(*this);
}
/*!
* @brief Copy constructor. A new CURLWrapper is constructed.
*
* @since 0.5.2
*/
Connection(const Connection &other) = default;
//! Move constructor
Connection(Connection &&other) noexcept = delete;
//! Destructor
~Connection() noexcept override = default;
//! Copy assignment operator
Connection &operator=(const Connection &other) = delete;
//! Move assignment operator
Connection &operator=(Connection &&other) noexcept = delete;
/*!
* @brief Make a HTTP GET call with parameters.
*
@ -110,9 +129,8 @@ public:
*
* @since 0.1.0
*/
[[nodiscard]]
answer_type get(const endpoint_variant &endpoint,
const parametermap &parameters);
[[nodiscard]] answer_type get(const endpoint_variant &endpoint,
const parametermap &parameters);
/*!
* @brief Make a HTTP GET call.
@ -126,8 +144,7 @@ public:
*
* @since 0.1.0
*/
[[nodiscard]]
inline answer_type get(const endpoint_variant &endpoint)
[[nodiscard]] inline answer_type get(const endpoint_variant &endpoint)
{
return get(endpoint, {});
}
@ -152,9 +169,8 @@ public:
*
* @since 0.1.0
*/
[[nodiscard]]
answer_type post(const endpoint_variant &endpoint,
const parametermap &parameters);
[[nodiscard]] answer_type post(const endpoint_variant &endpoint,
const parametermap &parameters);
/*!
* @brief Make a HTTP POST call.
@ -163,8 +179,7 @@ public:
*
* @since 0.1.0
*/
[[nodiscard]]
inline answer_type post(const endpoint_variant &endpoint)
[[nodiscard]] inline answer_type post(const endpoint_variant &endpoint)
{
return post(endpoint, {});
}
@ -178,9 +193,8 @@ public:
*
* @since 0.2.0
*/
[[nodiscard]]
answer_type patch(const endpoint_variant &endpoint,
const parametermap &parameters);
[[nodiscard]] answer_type patch(const endpoint_variant &endpoint,
const parametermap &parameters);
/*!
* @brief Make a HTTP PATCH call.
@ -189,8 +203,7 @@ public:
*
* @since 0.2.0
*/
[[nodiscard]]
inline answer_type patch(const endpoint_variant &endpoint)
[[nodiscard]] inline answer_type patch(const endpoint_variant &endpoint)
{
return patch(endpoint, {});
}
@ -204,9 +217,8 @@ public:
*
* @since 0.2.0
*/
[[nodiscard]]
answer_type put(const endpoint_variant &endpoint,
const parametermap &parameters);
[[nodiscard]] answer_type put(const endpoint_variant &endpoint,
const parametermap &parameters);
/*!
* @brief Make a HTTP PUT call.
@ -215,8 +227,7 @@ public:
*
* @since 0.2.0
*/
[[nodiscard]]
inline answer_type put(const endpoint_variant &endpoint)
[[nodiscard]] inline answer_type put(const endpoint_variant &endpoint)
{
return put(endpoint, {});
}
@ -230,9 +241,8 @@ public:
*
* @since 0.2.0
*/
[[nodiscard]]
answer_type del(const endpoint_variant &endpoint,
const parametermap &parameters);
[[nodiscard]] answer_type del(const endpoint_variant &endpoint,
const parametermap &parameters);
/*!
* @brief Make a HTTP DELETE call.
@ -241,8 +251,7 @@ public:
*
* @since 0.2.0
*/
[[nodiscard]]
inline answer_type del(const endpoint_variant &endpoint)
[[nodiscard]] inline answer_type del(const endpoint_variant &endpoint)
{
return del(endpoint, {});
}
@ -272,14 +281,15 @@ public:
{
CURLWrapper::cancel_stream();
}
private:
Instance &_instance;
const Instance &_instance;
const string_view _baseuri;
[[nodiscard]]
string endpoint_to_uri(const endpoint_variant &endpoint) const;
[[nodiscard]] string
endpoint_to_uri(const endpoint_variant &endpoint) const;
};
} // namespace mastodonpp
#endif // MASTODONPP_CONNECTION_HPP
#endif // MASTODONPP_CONNECTION_HPP

View File

@ -17,9 +17,8 @@
#ifndef MASTODONPP_CURL_WRAPPER_HPP
#define MASTODONPP_CURL_WRAPPER_HPP
#include "types.hpp"
#include "curl/curl.h"
#include "types.hpp"
#include <mutex>
#include <string>
@ -39,11 +38,11 @@ using std::string_view;
*/
enum class http_method
{
GET, // NOLINT(readability-identifier-naming)
POST, // NOLINT(readability-identifier-naming)
PATCH, // NOLINT(readability-identifier-naming)
PUT, // NOLINT(readability-identifier-naming)
DELETE // NOLINT(readability-identifier-naming)
GET, // NOLINT(readability-identifier-naming)
POST, // NOLINT(readability-identifier-naming)
PATCH, // NOLINT(readability-identifier-naming)
PUT, // NOLINT(readability-identifier-naming)
DELETE // NOLINT(readability-identifier-naming)
};
/*!
@ -70,8 +69,12 @@ public:
*/
CURLWrapper();
//! Copy constructor
CURLWrapper(const CURLWrapper &other) = delete;
/*!
* @brief Copy constructor. Does the same as the Constructor.
*
* @since 0.5.2
*/
CURLWrapper(const CURLWrapper &);
//! Move constructor
CURLWrapper(CURLWrapper &&other) noexcept = delete;
@ -88,10 +91,10 @@ public:
virtual ~CURLWrapper() noexcept;
//! Copy assignment operator
CURLWrapper& operator=(const CURLWrapper &other) = delete;
CURLWrapper &operator=(const CURLWrapper &other) = delete;
//! Move assignment operator
CURLWrapper& operator=(CURLWrapper &&other) noexcept = delete;
CURLWrapper &operator=(CURLWrapper &&other) noexcept = delete;
/*!
* @brief Returns pointer to the CURL easy handle.
@ -119,18 +122,17 @@ public:
*
* @since 0.3.0
*/
[[nodiscard]]
inline string escape_url(const string_view url) const
[[nodiscard]] inline string escape_url(const string_view url) const
{
char *cbuf{curl_easy_escape(_connection, url.data(),
static_cast<int>(url.size()))};
const string sbuf{cbuf};
string sbuf{cbuf};
curl_free(cbuf);
return sbuf;
}
/*!
* @brief URL decodes the given string .
* @brief URL decodes the given string.
*
* For more information consult [curl_easy_unescape(3)]
* (https://curl.haxx.se/libcurl/c/curl_easy_unescape.html).
@ -141,12 +143,11 @@ public:
*
* @since 0.3.0
*/
[[nodiscard]]
inline string unescape_url(const string_view url) const
[[nodiscard]] inline string unescape_url(const string_view url) const
{
char *cbuf{curl_easy_unescape(_connection, url.data(),
static_cast<int>(url.size()), nullptr)};
const string sbuf{cbuf};
string sbuf{cbuf};
curl_free(cbuf);
return sbuf;
}
@ -160,8 +161,7 @@ public:
*/
void setup_connection_properties(string_view proxy,
string_view access_token,
string_view cainfo,
string_view useragent);
string_view cainfo, string_view useragent);
protected:
/*!
@ -183,17 +183,16 @@ protected:
*
* @since 0.1.0
*/
[[nodiscard]]
answer_type make_request(const http_method &method, string uri,
const parametermap &parameters);
[[nodiscard]] answer_type make_request(const http_method &method,
string uri,
const parametermap &parameters);
/*!
* @brief Returns a reference to the buffer libcurl writes into.
*
* @since 0.1.0
*/
[[nodiscard]]
inline string &get_buffer()
[[nodiscard]] inline string &get_buffer()
{
return _curl_buffer_body;
}
@ -231,7 +230,6 @@ protected:
*/
void set_access_token(string_view access_token);
/*!
* @brief Set path to Certificate Authority (CA) bundle.
*
@ -247,11 +245,18 @@ protected:
virtual void set_useragent(string_view useragent);
private:
CURL *_connection;
char _curl_buffer_error[CURL_ERROR_SIZE];
CURL *_connection{nullptr};
char _curl_buffer_error[CURL_ERROR_SIZE]{'\0'};
string _curl_buffer_headers;
string _curl_buffer_body;
bool _stream_cancelled;
bool _stream_cancelled{false};
/*!
* @brief Initializes curl and sets up connection.
*
* @since 0.5.2
*/
void init();
/*!
* @brief libcurl write callback function.
@ -271,7 +276,7 @@ private:
static inline size_t writer_body_wrapper(char *data, size_t sz,
size_t nmemb, void *f)
{
return static_cast<CURLWrapper*>(f)->writer_body(data, sz, nmemb);
return static_cast<CURLWrapper *>(f)->writer_body(data, sz, nmemb);
}
//! @copydoc writer_body
@ -281,7 +286,7 @@ private:
static inline size_t writer_header_wrapper(char *data, size_t sz,
size_t nmemb, void *f)
{
return static_cast<CURLWrapper*>(f)->writer_header(data, sz, nmemb);
return static_cast<CURLWrapper *>(f)->writer_header(data, sz, nmemb);
}
/*!
@ -292,15 +297,15 @@ private:
* @since 0.1.0
*/
int progress(void *clientp, curl_off_t dltotal, curl_off_t dlnow,
curl_off_t ultotal, curl_off_t ulnow);
curl_off_t ultotal, curl_off_t ulnow) const;
//! @copydoc writer_body_wrapper
static inline int progress_wrapper(void *f, void *clientp,
curl_off_t dltotal, curl_off_t dlnow,
curl_off_t ultotal, curl_off_t ulnow)
{
return static_cast<CURLWrapper*>(f)->progress(clientp, dltotal, dlnow,
ultotal, ulnow);
return static_cast<CURLWrapper *>(f)->progress(clientp, dltotal, dlnow,
ultotal, ulnow);
}
/*!
@ -343,8 +348,8 @@ private:
*
* @since 0.2.0
*/
static void add_mime_part(curl_mime *mime,
string_view name, string_view data);
static void add_mime_part(curl_mime *mime, string_view name,
string_view data);
/*!
* @brief Convert parametermap to `*curl_mime`.
@ -366,4 +371,4 @@ private:
} // namespace mastodonpp
#endif // MASTODONPP_CURL_WRAPPER_HPP
#endif // MASTODONPP_CURL_WRAPPER_HPP

View File

@ -26,9 +26,9 @@
namespace mastodonpp
{
using std::uint16_t;
using std::exception;
using std::string;
using std::uint16_t;
/*!
* @brief Exception for libcurl errors.
@ -77,8 +77,7 @@ public:
*
* @since 0.1.0
*/
[[nodiscard]]
const char *what() const noexcept override;
[[nodiscard]] const char *what() const noexcept override;
private:
const string _message;
@ -87,4 +86,4 @@ private:
} // namespace mastodonpp
#endif // MASTODONPP_EXCEPTIONS_HPP
#endif // MASTODONPP_EXCEPTIONS_HPP

View File

@ -42,8 +42,8 @@ using std::string;
*
* @since 0.4.0
*/
string unescape_html(string html);
[[nodiscard]] string unescape_html(string html);
} // namespace mastodonpp
#endif // MASTODONPP_HELPERS_HPP
#endif // MASTODONPP_HELPERS_HPP

View File

@ -29,10 +29,9 @@
namespace mastodonpp
{
using std::uint64_t;
using std::string;
using std::string_view;
using std::move;
using std::uint64_t;
using std::vector;
/*!
@ -57,13 +56,26 @@ public:
*
* @since 0.1.0
*/
explicit Instance(const string_view hostname,
const string_view access_token)
: _hostname{hostname}
, _baseuri{"https://" + _hostname}
, _access_token{access_token}
, _max_chars{0}
{}
explicit Instance(string_view hostname, string_view access_token);
/*!
* @brief Copy constructor. A new CURLWrapper is constructed.
*
* @since 0.5.2
*/
Instance(const Instance &other);
//! Move constructor
Instance(Instance &&other) noexcept = delete;
//! Destructor
~Instance() noexcept override = default;
//! Copy assignment operator
Instance &operator=(const Instance &other) = delete;
//! Move assignment operator
Instance &operator=(Instance &&other) noexcept = delete;
/*!
* @brief Set the properties of the connection of the calling class up.
@ -75,7 +87,7 @@ public:
*
* @since 0.3.0
*/
inline void copy_connection_properties(CURLWrapper &curlwrapper)
inline void copy_connection_properties(CURLWrapper &curlwrapper) const
{
curlwrapper.setup_connection_properties(_proxy, _access_token, _cainfo,
_useragent);
@ -86,8 +98,7 @@ public:
*
* @since 0.1.0
*/
[[nodiscard]]
inline string_view get_hostname() const noexcept
[[nodiscard]] inline string_view get_hostname() const noexcept
{
return _hostname;
}
@ -99,8 +110,7 @@ public:
*
* @since 0.1.0
*/
[[nodiscard]]
inline string_view get_baseuri() const noexcept
[[nodiscard]] inline string_view get_baseuri() const noexcept
{
return _baseuri;
}
@ -110,8 +120,7 @@ public:
*
* @since 0.1.0
*/
[[nodiscard]]
inline string_view get_access_token() const noexcept
[[nodiscard]] inline string_view get_access_token() const noexcept
{
return _access_token;
}
@ -124,9 +133,10 @@ public:
*
* @since 0.1.0
*/
inline void set_access_token(string access_token)
inline void set_access_token(const string_view access_token)
{
_access_token = move(access_token);
_access_token = access_token;
CURLWrapper::set_access_token(access_token);
}
/*!
@ -140,8 +150,7 @@ public:
*
* @since 0.1.0
*/
[[nodiscard]]
uint64_t get_max_chars() noexcept;
[[nodiscard]] uint64_t get_max_chars() noexcept;
/*! @copydoc CURLWrapper::set_proxy(string_view)
*
@ -163,8 +172,7 @@ public:
*
* @since 0.3.0
*/
[[nodiscard]]
answer_type get_nodeinfo();
[[nodiscard]] answer_type get_nodeinfo();
/*!
* @brief Returns the allowed mime types for statuses.
@ -258,6 +266,9 @@ public:
* The `body` of the returned @link answer_type answer @endlink
* contains only the URI, not the whole JSON response.
*
* Note that the required scopes may be different between Mastodon and
* other implementations, like Pleroma.
*
* @param client_name The name of your application.
* @param scopes Space separated list of scopes. Defaults to
* read if empty.
@ -268,9 +279,9 @@ public:
*
* @since 0.3.0
*/
[[nodiscard]]
answer_type step_1(string_view client_name, string_view scopes,
string_view website);
[[nodiscard]] answer_type step_1(string_view client_name,
string_view scopes,
string_view website);
/*!
* @brief Creates a token via `/oauth/token`.
@ -287,8 +298,7 @@ public:
*
* @since 0.3.0
*/
[[nodiscard]]
answer_type step_2(string_view code);
[[nodiscard]] answer_type step_2(string_view code);
private:
Instance &_instance;
@ -311,4 +321,4 @@ private:
} // namespace mastodonpp
#endif // MASTODONPP_INSTANCE_HPP
#endif // MASTODONPP_INSTANCE_HPP

View File

@ -119,4 +119,4 @@
namespace mastodonpp
{} // namespace mastodonpp
#endif // MASTODONPP_HPP
#endif // MASTODONPP_HPP

View File

@ -31,13 +31,13 @@
namespace mastodonpp
{
using std::uint8_t;
using std::uint16_t;
using std::map;
using std::ostream;
using std::pair;
using std::string;
using std::string_view;
using std::pair;
using std::uint16_t;
using std::uint8_t;
using std::variant;
using std::vector;
@ -60,16 +60,16 @@ using std::vector;
*
* @since 0.1.0
*/
using parametermap =
map<string_view, variant<string_view, vector<string_view>>>;
using parametermap = map<string_view,
variant<string_view, vector<string_view>>>;
/*!
* @brief A single parameter of a parametermap.
*
* @since 0.1.0
*/
using parameterpair =
pair<string_view, variant<string_view, vector<string_view>>>;
using parameterpair = pair<string_view,
variant<string_view, vector<string_view>>>;
/*!
* @brief Return type for Request%s.
@ -138,7 +138,7 @@ struct answer_type
*
* @since 0.1.0
*/
friend ostream &operator <<(ostream &out, const answer_type &answer);
friend ostream &operator<<(ostream &out, const answer_type &answer);
/*!
* @brief Returns the value of a header field.
@ -149,8 +149,7 @@ struct answer_type
*
* @since 0.1.0
*/
[[nodiscard]]
string_view get_header(string_view field) const;
[[nodiscard]] string_view get_header(string_view field) const;
/*!
* @brief Returns the parameters needed for the next entries.
@ -159,8 +158,7 @@ struct answer_type
*
* @since 0.3.0
*/
[[nodiscard]]
inline parametermap next() const
[[nodiscard]] inline parametermap next() const
{
return parse_pagination(true);
}
@ -173,8 +171,7 @@ struct answer_type
*
* @since 0.3.0
*/
[[nodiscard]]
inline parametermap prev() const
[[nodiscard]] inline parametermap prev() const
{
return parse_pagination(false);
}
@ -188,10 +185,9 @@ private:
*
* @since 0.3.0
*/
[[nodiscard]]
parametermap parse_pagination(bool next) const;
[[nodiscard]] parametermap parse_pagination(bool next) const;
};
} // namespace mastodonpp
#endif // MASTODONPP_TYPES_HPP
#endif // MASTODONPP_TYPES_HPP

View File

@ -25,8 +25,7 @@ API::API(const endpoint_type &endpoint)
// TODO: look for a better way.
// NOLINTNEXTLINE(cert-err58-cpp)
const map<API::endpoint_type,string_view> API::_endpoint_map
{
const map<API::endpoint_type, string_view> API::_endpoint_map{
{v1::apps, "/api/v1/apps"},
{v1::apps_verify_credentials, "/api/v1/apps/verify_credentials"},
@ -67,7 +66,7 @@ const map<API::endpoint_type,string_view> API::_endpoint_map
{v1::follow_requests, "/api/v1/follow_requests"},
{v1::follow_requests_id_authorize,
"/api/v1/follow_requests/<ID>/authorize"},
"/api/v1/follow_requests/<ID>/authorize"},
{v1::follow_requests_id_reject, "/api/v1/follow_requests/<ID>/reject"},
{v1::endorsements, "/api/v1/endorsements"},
@ -165,7 +164,6 @@ const map<API::endpoint_type,string_view> API::_endpoint_map
{v1::admin_reports_id_reopen, "/api/v1/admin/reports/<ID>/reopen"},
{v1::pleroma_notifications_read, " /api/v1/pleroma/notifications/read"},
{v1::pleroma_accounts_id_subscribe,
"/api/v1/pleroma/accounts/<ID>/subscribe"},
{v1::pleroma_accounts_id_unsubscribe,
@ -180,12 +178,19 @@ const map<API::endpoint_type,string_view> API::_endpoint_map
"/api/v1/pleroma/accounts/update_background"},
{v1::pleroma_accounts_confirmation_resend,
"/api/v1/pleroma/accounts/confirmation_resend"},
{v1::pleroma_mascot, "/api/v1/pleroma/mascot"},
{v1::pleroma_conversations_id_statuses,
"/api/v1/pleroma/conversations/<ID>/statuses"},
{v1::pleroma_conversations_id, "/api/v1/pleroma/conversations/<ID>"},
{v1::pleroma_conversations_id_read,
"/api/v1/pleroma/conversations/<ID>/read"},
{v1::pleroma_accounts_id_scrobbles,
"/api/v1/pleroma/accounts/<ID>/scrobbles"},
{v1::pleroma_scrobble, "/api/v1/pleroma/scrobble"},
{v1::pleroma_statuses_id_reactions_emoji,
"/api/v1/pleroma/statuses/<ID>/reactions/<EMOJI>"},
{v1::pleroma_statuses_id_reactions,
"/api/v1/pleroma/statuses/<ID>/reactions"},
{v2::search, "/api/v2/search"},
@ -199,18 +204,25 @@ const map<API::endpoint_type,string_view> API::_endpoint_map
{pleroma::admin_users, "/api/pleroma/admin/users"},
{pleroma::admin_users_follow, "/api/pleroma/admin/users/follow"},
{pleroma::admin_users_unfollow, "/api/pleroma/admin/users/unfollow"},
{pleroma::admin_users_nickname, "/api/pleroma/admin/users/<NICKNAME>"},
{pleroma::admin_users_nickname_toggle_activation,
"/api/pleroma/admin/users/<NICKNAME>/toggle_activation"},
{pleroma::admin_users_tag, "/api/pleroma/admin/users/tag"},
{pleroma::admin_users_nickname_permission_group,
"/api/pleroma/admin/users/<NICKNAME>/permission_group"},
{pleroma::admin_users_nickname_permission_group_permission_group,
"/api/pleroma/admin/users/<NICKNAME>/permission_group/<PERMISSION_GROUP>"},
{pleroma::admin_users_nickname_activation_status,
"/api/pleroma/admin/users/<NICKNAME>/activation_status"},
"/api/pleroma/admin/users/<NICKNAME>"
"/permission_group/<PERMISSION_GROUP>"},
{pleroma::admin_users_permission_group_permission_group,
"/api/pleroma/admin/users/permission_group/<PERMISSION_GROUP>"},
{pleroma::admin_users_activate, "/api/pleroma/admin/users/activate"},
{pleroma::admin_users_deactivate, "/api/pleroma/admin/users/deactivate"},
{pleroma::admin_users_nickname_or_id,
"/api/pleroma/admin/users/<NICKNAME_OR_ID>"},
{pleroma::admin_users_nickname_or_id_statuses,
"/api/pleroma/admin/users/<NICKNAME_OR_ID>/statuses"},
{pleroma::admin_instances_instance_statuses,
"/api/pleroma/admin/instances/<INSTANCE>/statuses"},
{pleroma::admin_statuses, "/api/pleroma/admin/statuses"},
{pleroma::admin_relay, "/api/pleroma/admin/relay"},
{pleroma::admin_users_invite_token,
"/api/pleroma/admin/users/invite_token"},
@ -221,27 +233,60 @@ const map<API::endpoint_type,string_view> API::_endpoint_map
"/api/pleroma/admin/users/email_invite"},
{pleroma::admin_users_nickname_password_reset,
"/api/pleroma/admin/users/<NICKNAME>/password_reset"},
{pleroma::admin_users_nickname_update_credentials,
"/api/pleroma/admin/users/<NICKNAME>/update_credentials"},
{pleroma::admin_users_force_password_reset,
"/api/pleroma/admin/users/force_password_reset"},
{pleroma::admin_reports, "/api/pleroma/admin/reports"},
{pleroma::admin_grouped_reports, "/api/pleroma/admin/grouped_reports"},
{pleroma::admin_reports_id, "/api/pleroma/admin/reports/<ID>"},
{pleroma::admin_reports_id_notes, "/api/pleroma/admin/reports/<ID>/notes"},
{pleroma::admin_reports_report_id_notes_id,
"/api/pleroma/admin/reports/<REPORT_ID>/notes/<ID>"},
{pleroma::admin_statuses_id, "/api/pleroma/admin/statuses/<ID>"},
{pleroma::admin_restart, "/api/pleroma/admin/restart"},
{pleroma::admin_config, "/api/pleroma/admin/config"},
{pleroma::admin_config_descriptions,
"/api/pleroma/admin/config/descriptions"},
{pleroma::admin_moderation_log, "/api/pleroma/admin/moderation_log"},
{pleroma::admin_reload_emoji, "/api/pleroma/admin/reload_emoji"},
{pleroma::admin_users_confirm_email,
"/api/pleroma/admin/users/confirm_email"},
{pleroma::admin_users_resend_confirm_email,
"/api/pleroma/admin/users/resend_confirm_email"},
{pleroma::admin_stats, "/api/pleroma/admin/stats"},
{pleroma::admin_users_nickname, "/api/pleroma/admin/users/<NICKNAME>"},
{pleroma::admin_users_nickname_activation_status,
"/api/pleroma/admin/users/<NICKNAME>/activation_status"},
{pleroma::admin_reports_id_respond,
"/api/pleroma/admin/reports/<ID>/respond"},
{pleroma::admin_statuses_id, "/api/pleroma/admin/statuses/<ID>"},
{pleroma::admin_config_migrate_to_db,
"/api/pleroma/admin/config/migrate_to_db"},
{pleroma::admin_config_migrate_from_db,
"/api/pleroma/admin/config/migrate_from_db"},
{pleroma::admin_config, "/api/pleroma/admin/config"},
{pleroma::emoji, "/api/pleroma/emoji"},
{pleroma::follow_import, "/api/pleroma/follow_import"},
{pleroma::captcha, "/api/pleroma/captcha,"},
{pleroma::delete_account, "/api/pleroma/delete_account"},
{pleroma::disable_account, "/api/pleroma/disable_account"},
{pleroma::account_register, "/api/pleroma/account/register"},
{pleroma::notification_settings, "/api/pleroma/notification_settings"},
{pleroma::healthcheck, "/api/pleroma/healthcheck"},
{pleroma::change_email, "/api/pleroma/change_email"},
{pleroma::emoji_packs, "/api/pleroma/emoji/packs"},
{pleroma::emoji_packs_name, "/api/pleroma/emoji/packs/<NAME>"},
{pleroma::emoji_packs_name_update_file,
"/api/pleroma/emoji/packs/<NAME>/update_file"},
{pleroma::emoji_packs_name_update_metadata,
"/api/pleroma/emoji/packs/<NAME>/update_metadata"},
{pleroma::emoji_packs_download_from,
"/api/pleroma/emoji/packs/download_from"},
{pleroma::emoji_packs_list_from, "/api/pleroma/emoji/packs/list_from"},
{pleroma::emoji_packs_name_download_shared,
"/api/pleroma/emoji/packs/<NAME>/download_shared"},
{pleroma::account_register, "/api/pleroma/account/register"},
};
} // namespace mastodonpp

View File

@ -25,8 +25,8 @@ string Connection::endpoint_to_uri(const endpoint_variant &endpoint) const
{
if (holds_alternative<API::endpoint_type>(endpoint))
{
return string(_baseuri)
+= API{std::get<API::endpoint_type>(endpoint)}.to_string_view();
return string(_baseuri) += API{std::get<API::endpoint_type>(endpoint)}
.to_string_view();
}
return string(_baseuri) += std::get<string_view>(endpoint);
}
@ -34,43 +34,43 @@ string Connection::endpoint_to_uri(const endpoint_variant &endpoint) const
answer_type Connection::get(const endpoint_variant &endpoint,
const parametermap &parameters)
{
return make_request(http_method::GET,
endpoint_to_uri(endpoint), parameters);
return make_request(http_method::GET, endpoint_to_uri(endpoint),
parameters);
}
answer_type Connection::post(const endpoint_variant &endpoint,
const parametermap &parameters)
{
return make_request(http_method::POST,
endpoint_to_uri(endpoint), parameters);
return make_request(http_method::POST, endpoint_to_uri(endpoint),
parameters);
}
answer_type Connection::patch(const endpoint_variant &endpoint,
const parametermap &parameters)
{
return make_request(http_method::PATCH,
endpoint_to_uri(endpoint), parameters);
return make_request(http_method::PATCH, endpoint_to_uri(endpoint),
parameters);
}
answer_type Connection::put(const endpoint_variant &endpoint,
const parametermap &parameters)
{
return make_request(http_method::PUT,
endpoint_to_uri(endpoint), parameters);
return make_request(http_method::PUT, endpoint_to_uri(endpoint),
parameters);
}
answer_type Connection::del(const endpoint_variant &endpoint,
const parametermap &parameters)
{
return make_request(http_method::DELETE,
endpoint_to_uri(endpoint), parameters);
return make_request(http_method::DELETE, endpoint_to_uri(endpoint),
parameters);
}
string Connection::get_new_stream_contents()
{
_buffer_mutex.lock();
auto &buffer{get_buffer()};
const string buffer_copy{buffer};
string buffer_copy{buffer};
buffer.clear();
_buffer_mutex.unlock();
return buffer_copy;

View File

@ -15,6 +15,7 @@
*/
#include "curl_wrapper.hpp"
#include "exceptions.hpp"
#include "log.hpp"
#include "version.hpp"
@ -22,25 +23,26 @@
#include <algorithm>
#include <array>
#include <atomic>
#include <cctype>
#include <cstdint>
namespace mastodonpp
{
using std::any_of;
using std::array; // NOLINT(misc-unused-using-decls)
using std::atomic;
using std::get;
using std::holds_alternative;
using std::any_of;
using std::array; // NOLINT(misc-unused-using-decls)
using std::atomic;
using std::uint8_t;
using std::toupper;
using std::transform;
using std::uint16_t;
using std::uint8_t;
// No one will ever need more than 65535 connections. 😉
static atomic<uint16_t> curlwrapper_instances{0};
CURLWrapper::CURLWrapper()
: _curl_buffer_error{}
, _stream_cancelled{false}
void CURLWrapper::init()
{
if (curlwrapper_instances == 0)
{
@ -52,6 +54,17 @@ CURLWrapper::CURLWrapper()
_connection = curl_easy_init();
setup_curl();
}
CURLWrapper::CURLWrapper()
{
init();
}
CURLWrapper::CURLWrapper(const CURLWrapper &)
{
init();
}
CURLWrapper::~CURLWrapper() noexcept
{
curl_easy_cleanup(_connection);
@ -71,7 +84,7 @@ answer_type CURLWrapper::make_request(const http_method &method, string uri,
_curl_buffer_headers.clear();
_curl_buffer_body.clear();
CURLcode code;
CURLcode code{CURLE_OK};
switch (method)
{
case http_method::GET:
@ -167,7 +180,7 @@ answer_type CURLWrapper::make_request(const http_method &method, string uri,
if (code == CURLE_OK
|| (code == CURLE_ABORTED_BY_CALLBACK && _stream_cancelled))
{
long http_status; // NOLINT(google-runtime-int)
long http_status{0}; // 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);
@ -232,11 +245,11 @@ void CURLWrapper::set_access_token(const string_view access_token)
if (code != CURLE_OK)
{
throw CURLException{code, "Could not set authorization token.",
_curl_buffer_error};
_curl_buffer_error};
}
#if (LIBCURL_VERSION_NUM < 0x073d00) // libcurl < 7.61.0.
#define CURLAUTH_BEARER CURLAUTH_ANY
# define CURLAUTH_BEARER CURLAUTH_ANY
#endif
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, hicpp-signed-bitwise)
@ -244,7 +257,7 @@ void CURLWrapper::set_access_token(const string_view access_token)
if (code != CURLE_OK)
{
throw CURLException{code, "Could not set authorization token.",
_curl_buffer_error};
_curl_buffer_error};
}
debuglog << "Set authorization token.\n";
@ -262,20 +275,20 @@ void CURLWrapper::set_cainfo(const string_view path)
void CURLWrapper::set_useragent(const string_view useragent)
{
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
CURLcode code{curl_easy_setopt(_connection, CURLOPT_USERAGENT,
useragent.data())};
CURLcode code{
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
curl_easy_setopt(_connection, CURLOPT_USERAGENT, useragent.data())};
if (code != CURLE_OK)
{
throw CURLException{code, "Failed to set User-Agent",
_curl_buffer_error};
_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)
if (data == nullptr)
{
return 0;
}
@ -289,7 +302,7 @@ size_t CURLWrapper::writer_body(char *data, size_t size, size_t nmemb)
size_t CURLWrapper::writer_header(char *data, size_t size, size_t nmemb)
{
if(data == nullptr)
if (data == nullptr)
{
return 0;
}
@ -299,8 +312,8 @@ size_t CURLWrapper::writer_header(char *data, size_t size, size_t nmemb)
return size * nmemb;
}
int CURLWrapper::progress(void *, curl_off_t , curl_off_t ,
curl_off_t , curl_off_t )
int CURLWrapper::progress(void *, curl_off_t, curl_off_t, curl_off_t,
curl_off_t) const
{
if (_stream_cancelled)
{
@ -353,15 +366,28 @@ void CURLWrapper::setup_curl()
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"
};
static constexpr array replace{"id",
"nickname",
"nickname_or_id",
"account_id",
"list_id",
"hashtag",
"permission_group",
"instance",
"report_id",
"name",
"emoji"};
if (any_of(replace.begin(), replace.end(),
[&parameter](const auto &s) { return s == parameter.first; }))
{
const auto pos{uri.find('<')};
const string searchstring{[&parameter] {
string s{"<"};
s += parameter.first;
transform(s.begin(), s.end(), s.begin(),
[](const unsigned char c) { return toupper(c); });
return s;
}()};
const auto pos{uri.find(searchstring)};
if (pos != string::npos)
{
uri.replace(pos, parameter.first.size() + 2,
@ -378,6 +404,7 @@ bool CURLWrapper::replace_parameter_in_uri(string &uri,
void CURLWrapper::add_parameters_to_uri(string &uri,
const parametermap &parameters)
{
bool first{true};
// Replace <ID> with the value of parameter “id” and so on.
for (const auto &param : parameters)
{
@ -386,7 +413,6 @@ void CURLWrapper::add_parameters_to_uri(string &uri,
continue;
}
static bool first{true};
if (first)
{
uri += "?";
@ -414,8 +440,8 @@ void CURLWrapper::add_parameters_to_uri(string &uri,
}
}
void CURLWrapper::add_mime_part(curl_mime *mime,
string_view name, string_view data)
void CURLWrapper::add_mime_part(curl_mime *mime, string_view name,
string_view data)
{
curl_mimepart *part{curl_mime_addpart(mime)};
if (part == nullptr)

View File

@ -21,8 +21,8 @@
namespace mastodonpp
{
using std::to_string;
using std::move;
using std::to_string;
CURLException::CURLException(const CURLcode &error, string message)
: error_code{error}

View File

@ -26,15 +26,15 @@
namespace mastodonpp
{
using std::stoul;
using std::codecvt_utf8;
using std::wstring_convert;
using std::map;
using std::move;
using std::regex;
using std::regex_search;
using std::smatch;
using std::stoul;
using std::string_view;
using std::move;
using std::wstring_convert;
string unescape_html(string html)
{
@ -43,274 +43,100 @@ string unescape_html(string html)
// Source: https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_
// entity_references#Character_entity_references_in_HTML
const map<string_view, char32_t> names
{{
{ "exclamation", 0x0021 },
{ "quot", 0x0022 },
{ "percent", 0x0025 },
{ "amp", 0x0026 },
{ "apos", 0x0027 },
{ "add", 0x002B },
{ "lt", 0x003C },
{ "equal", 0x003D },
{ "gt", 0x003E },
{ "nbsp", 0x00A0 },
{ "iexcl", 0x00A1 },
{ "cent", 0x00A2 },
{ "pound", 0x00A3 },
{ "curren", 0x00A4 },
{ "yen", 0x00A5 },
{ "brvbar", 0x00A6 },
{ "sect", 0x00A7 },
{ "uml", 0x00A8 },
{ "copy", 0x00A9 },
{ "ordf", 0x00AA },
{ "laquo", 0x00AB },
{ "not", 0x00AC },
{ "shy", 0x00AD },
{ "reg", 0x00AE },
{ "macr", 0x00AF },
{ "deg", 0x00B0 },
{ "plusmn", 0x00B1 },
{ "sup2", 0x00B2 },
{ "sup3", 0x00B3 },
{ "acute", 0x00B4 },
{ "micro", 0x00B5 },
{ "para", 0x00B6 },
{ "middot", 0x00B7 },
{ "cedil", 0x00B8 },
{ "sup1", 0x00B9 },
{ "ordm", 0x00BA },
{ "raquo", 0x00BB },
{ "frac14", 0x00BC },
{ "frac12", 0x00BD },
{ "frac34", 0x00BE },
{ "iquest", 0x00BF },
{ "Agrave", 0x00C0 },
{ "Aacute", 0x00C1 },
{ "Acirc", 0x00C2 },
{ "Atilde", 0x00C3 },
{ "Auml", 0x00C4 },
{ "Aring", 0x00C5 },
{ "AElig", 0x00C6 },
{ "Ccedil", 0x00C7 },
{ "Egrave", 0x00C8 },
{ "Eacute", 0x00C9 },
{ "Ecirc", 0x00CA },
{ "Euml", 0x00CB },
{ "Igrave", 0x00CC },
{ "Iacute", 0x00CD },
{ "Icirc", 0x00CE },
{ "Iuml", 0x00CF },
{ "ETH", 0x00D0 },
{ "Ntilde", 0x00D1 },
{ "Ograve", 0x00D2 },
{ "Oacute", 0x00D3 },
{ "Ocirc", 0x00D4 },
{ "Otilde", 0x00D5 },
{ "Ouml", 0x00D6 },
{ "times", 0x00D7 },
{ "Oslash", 0x00D8 },
{ "Ugrave", 0x00D9 },
{ "Uacute", 0x00DA },
{ "Ucirc", 0x00DB },
{ "Uuml", 0x00DC },
{ "Yacute", 0x00DD },
{ "THORN", 0x00DE },
{ "szlig", 0x00DF },
{ "agrave", 0x00E0 },
{ "aacute", 0x00E1 },
{ "acirc", 0x00E2 },
{ "atilde", 0x00E3 },
{ "auml", 0x00E4 },
{ "aring", 0x00E5 },
{ "aelig", 0x00E6 },
{ "ccedil", 0x00E7 },
{ "egrave", 0x00E8 },
{ "eacute", 0x00E9 },
{ "ecirc", 0x00EA },
{ "euml", 0x00EB },
{ "igrave", 0x00EC },
{ "iacute", 0x00ED },
{ "icirc", 0x00EE },
{ "iuml", 0x00EF },
{ "eth", 0x00F0 },
{ "ntilde", 0x00F1 },
{ "ograve", 0x00F2 },
{ "oacute", 0x00F3 },
{ "ocirc", 0x00F4 },
{ "otilde", 0x00F5 },
{ "ouml", 0x00F6 },
{ "divide", 0x00F7 },
{ "oslash", 0x00F8 },
{ "ugrave", 0x00F9 },
{ "uacute", 0x00FA },
{ "ucirc", 0x00FB },
{ "uuml", 0x00FC },
{ "yacute", 0x00FD },
{ "thorn", 0x00FE },
{ "yuml", 0x00FF },
{ "OElig", 0x0152 },
{ "oelig", 0x0153 },
{ "Scaron", 0x0160 },
{ "scaron", 0x0161 },
{ "Yuml", 0x0178 },
{ "fnof", 0x0192 },
{ "circ", 0x02C6 },
{ "tilde", 0x02DC },
{ "Alpha", 0x0391 },
{ "Beta", 0x0392 },
{ "Gamma", 0x0393 },
{ "Delta", 0x0394 },
{ "Epsilon", 0x0395 },
{ "Zeta", 0x0396 },
{ "Eta", 0x0397 },
{ "Theta", 0x0398 },
{ "Iota", 0x0399 },
{ "Kappa", 0x039A },
{ "Lambda", 0x039B },
{ "Mu", 0x039C },
{ "Nu", 0x039D },
{ "Xi", 0x039E },
{ "Omicron", 0x039F },
{ "Pi", 0x03A0 },
{ "Rho", 0x03A1 },
{ "Sigma", 0x03A3 },
{ "Tau", 0x03A4 },
{ "Upsilon", 0x03A5 },
{ "Phi", 0x03A6 },
{ "Chi", 0x03A7 },
{ "Psi", 0x03A8 },
{ "Omega", 0x03A9 },
{ "alpha", 0x03B1 },
{ "beta", 0x03B2 },
{ "gamma", 0x03B3 },
{ "delta", 0x03B4 },
{ "epsilon", 0x03B5 },
{ "zeta", 0x03B6 },
{ "eta", 0x03B7 },
{ "theta", 0x03B8 },
{ "iota", 0x03B9 },
{ "kappa", 0x03BA },
{ "lambda", 0x03BB },
{ "mu", 0x03BC },
{ "nu", 0x03BD },
{ "xi", 0x03BE },
{ "omicron", 0x03BF },
{ "pi", 0x03C0 },
{ "rho", 0x03C1 },
{ "sigmaf", 0x03C2 },
{ "sigma", 0x03C3 },
{ "tau", 0x03C4 },
{ "upsilon", 0x03C5 },
{ "phi", 0x03C6 },
{ "chi", 0x03C7 },
{ "psi", 0x03C8 },
{ "omega", 0x03C9 },
{ "thetasym", 0x03D1 },
{ "upsih", 0x03D2 },
{ "piv", 0x03D6 },
{ "ensp", 0x2002 },
{ "emsp", 0x2003 },
{ "thinsp", 0x2009 },
{ "zwnj", 0x200C },
{ "zwj", 0x200D },
{ "lrm", 0x200E },
{ "rlm", 0x200F },
{ "ndash", 0x2013 },
{ "mdash", 0x2014 },
{ "horbar", 0x2015 },
{ "lsquo", 0x2018 },
{ "rsquo", 0x2019 },
{ "sbquo", 0x201A },
{ "ldquo", 0x201C },
{ "rdquo", 0x201D },
{ "bdquo", 0x201E },
{ "dagger", 0x2020 },
{ "Dagger", 0x2021 },
{ "bull", 0x2022 },
{ "hellip", 0x2026 },
{ "permil", 0x2030 },
{ "prime", 0x2032 },
{ "Prime", 0x2033 },
{ "lsaquo", 0x2039 },
{ "rsaquo", 0x203A },
{ "oline", 0x203E },
{ "frasl", 0x2044 },
{ "euro", 0x20AC },
{ "image", 0x2111 },
{ "weierp", 0x2118 },
{ "real", 0x211C },
{ "trade", 0x2122 },
{ "alefsym", 0x2135 },
{ "larr", 0x2190 },
{ "uarr", 0x2191 },
{ "rarr", 0x2192 },
{ "darr", 0x2193 },
{ "harr", 0x2194 },
{ "crarr", 0x21B5 },
{ "lArr", 0x21D0 },
{ "uArr", 0x21D1 },
{ "rArr", 0x21D2 },
{ "dArr", 0x21D3 },
{ "hArr", 0x21D4 },
{ "forall", 0x2200 },
{ "part", 0x2202 },
{ "exist", 0x2203 },
{ "empty", 0x2205 },
{ "nabla", 0x2207 },
{ "isin", 0x2208 },
{ "notin", 0x2209 },
{ "ni", 0x220B },
{ "prod", 0x220F },
{ "sum", 0x2211 },
{ "minus", 0x2212 },
{ "lowast", 0x2217 },
{ "radic", 0x221A },
{ "prop", 0x221D },
{ "infin", 0x221E },
{ "ang", 0x2220 },
{ "and", 0x2227 },
{ "or", 0x2228 },
{ "cap", 0x2229 },
{ "cup", 0x222A },
{ "int", 0x222B },
{ "there4", 0x2234 },
{ "sim", 0x223C },
{ "cong", 0x2245 },
{ "asymp", 0x2248 },
{ "ne", 0x2260 },
{ "equiv", 0x2261 },
{ "le", 0x2264 },
{ "ge", 0x2265 },
{ "sub", 0x2282 },
{ "sup", 0x2283 },
{ "nsub", 0x2284 },
{ "sube", 0x2286 },
{ "supe", 0x2287 },
{ "oplus", 0x2295 },
{ "otimes", 0x2297 },
{ "perp", 0x22A5 },
{ "sdot", 0x22C5 },
{ "lceil", 0x2308 },
{ "rceil", 0x2309 },
{ "lfloor", 0x230A },
{ "rfloor", 0x230B },
{ "lang", 0x2329 },
{ "rang", 0x232A },
{ "loz", 0x25CA },
{ "spades", 0x2660 },
{ "clubs", 0x2663 },
{ "hearts", 0x2665 },
{ "diams", 0x2666 }
}};
const map<string_view, char32_t>
names{{"exclamation", 0x0021}, {"quot", 0x0022}, {"percent", 0x0025},
{"amp", 0x0026}, {"apos", 0x0027}, {"add", 0x002B},
{"lt", 0x003C}, {"equal", 0x003D}, {"gt", 0x003E},
{"nbsp", 0x00A0}, {"iexcl", 0x00A1}, {"cent", 0x00A2},
{"pound", 0x00A3}, {"curren", 0x00A4}, {"yen", 0x00A5},
{"brvbar", 0x00A6}, {"sect", 0x00A7}, {"uml", 0x00A8},
{"copy", 0x00A9}, {"ordf", 0x00AA}, {"laquo", 0x00AB},
{"not", 0x00AC}, {"shy", 0x00AD}, {"reg", 0x00AE},
{"macr", 0x00AF}, {"deg", 0x00B0}, {"plusmn", 0x00B1},
{"sup2", 0x00B2}, {"sup3", 0x00B3}, {"acute", 0x00B4},
{"micro", 0x00B5}, {"para", 0x00B6}, {"middot", 0x00B7},
{"cedil", 0x00B8}, {"sup1", 0x00B9}, {"ordm", 0x00BA},
{"raquo", 0x00BB}, {"frac14", 0x00BC}, {"frac12", 0x00BD},
{"frac34", 0x00BE}, {"iquest", 0x00BF}, {"Agrave", 0x00C0},
{"Aacute", 0x00C1}, {"Acirc", 0x00C2}, {"Atilde", 0x00C3},
{"Auml", 0x00C4}, {"Aring", 0x00C5}, {"AElig", 0x00C6},
{"Ccedil", 0x00C7}, {"Egrave", 0x00C8}, {"Eacute", 0x00C9},
{"Ecirc", 0x00CA}, {"Euml", 0x00CB}, {"Igrave", 0x00CC},
{"Iacute", 0x00CD}, {"Icirc", 0x00CE}, {"Iuml", 0x00CF},
{"ETH", 0x00D0}, {"Ntilde", 0x00D1}, {"Ograve", 0x00D2},
{"Oacute", 0x00D3}, {"Ocirc", 0x00D4}, {"Otilde", 0x00D5},
{"Ouml", 0x00D6}, {"times", 0x00D7}, {"Oslash", 0x00D8},
{"Ugrave", 0x00D9}, {"Uacute", 0x00DA}, {"Ucirc", 0x00DB},
{"Uuml", 0x00DC}, {"Yacute", 0x00DD}, {"THORN", 0x00DE},
{"szlig", 0x00DF}, {"agrave", 0x00E0}, {"aacute", 0x00E1},
{"acirc", 0x00E2}, {"atilde", 0x00E3}, {"auml", 0x00E4},
{"aring", 0x00E5}, {"aelig", 0x00E6}, {"ccedil", 0x00E7},
{"egrave", 0x00E8}, {"eacute", 0x00E9}, {"ecirc", 0x00EA},
{"euml", 0x00EB}, {"igrave", 0x00EC}, {"iacute", 0x00ED},
{"icirc", 0x00EE}, {"iuml", 0x00EF}, {"eth", 0x00F0},
{"ntilde", 0x00F1}, {"ograve", 0x00F2}, {"oacute", 0x00F3},
{"ocirc", 0x00F4}, {"otilde", 0x00F5}, {"ouml", 0x00F6},
{"divide", 0x00F7}, {"oslash", 0x00F8}, {"ugrave", 0x00F9},
{"uacute", 0x00FA}, {"ucirc", 0x00FB}, {"uuml", 0x00FC},
{"yacute", 0x00FD}, {"thorn", 0x00FE}, {"yuml", 0x00FF},
{"OElig", 0x0152}, {"oelig", 0x0153}, {"Scaron", 0x0160},
{"scaron", 0x0161}, {"Yuml", 0x0178}, {"fnof", 0x0192},
{"circ", 0x02C6}, {"tilde", 0x02DC}, {"Alpha", 0x0391},
{"Beta", 0x0392}, {"Gamma", 0x0393}, {"Delta", 0x0394},
{"Epsilon", 0x0395}, {"Zeta", 0x0396}, {"Eta", 0x0397},
{"Theta", 0x0398}, {"Iota", 0x0399}, {"Kappa", 0x039A},
{"Lambda", 0x039B}, {"Mu", 0x039C}, {"Nu", 0x039D},
{"Xi", 0x039E}, {"Omicron", 0x039F}, {"Pi", 0x03A0},
{"Rho", 0x03A1}, {"Sigma", 0x03A3}, {"Tau", 0x03A4},
{"Upsilon", 0x03A5}, {"Phi", 0x03A6}, {"Chi", 0x03A7},
{"Psi", 0x03A8}, {"Omega", 0x03A9}, {"alpha", 0x03B1},
{"beta", 0x03B2}, {"gamma", 0x03B3}, {"delta", 0x03B4},
{"epsilon", 0x03B5}, {"zeta", 0x03B6}, {"eta", 0x03B7},
{"theta", 0x03B8}, {"iota", 0x03B9}, {"kappa", 0x03BA},
{"lambda", 0x03BB}, {"mu", 0x03BC}, {"nu", 0x03BD},
{"xi", 0x03BE}, {"omicron", 0x03BF}, {"pi", 0x03C0},
{"rho", 0x03C1}, {"sigmaf", 0x03C2}, {"sigma", 0x03C3},
{"tau", 0x03C4}, {"upsilon", 0x03C5}, {"phi", 0x03C6},
{"chi", 0x03C7}, {"psi", 0x03C8}, {"omega", 0x03C9},
{"thetasym", 0x03D1}, {"upsih", 0x03D2}, {"piv", 0x03D6},
{"ensp", 0x2002}, {"emsp", 0x2003}, {"thinsp", 0x2009},
{"zwnj", 0x200C}, {"zwj", 0x200D}, {"lrm", 0x200E},
{"rlm", 0x200F}, {"ndash", 0x2013}, {"mdash", 0x2014},
{"horbar", 0x2015}, {"lsquo", 0x2018}, {"rsquo", 0x2019},
{"sbquo", 0x201A}, {"ldquo", 0x201C}, {"rdquo", 0x201D},
{"bdquo", 0x201E}, {"dagger", 0x2020}, {"Dagger", 0x2021},
{"bull", 0x2022}, {"hellip", 0x2026}, {"permil", 0x2030},
{"prime", 0x2032}, {"Prime", 0x2033}, {"lsaquo", 0x2039},
{"rsaquo", 0x203A}, {"oline", 0x203E}, {"frasl", 0x2044},
{"euro", 0x20AC}, {"image", 0x2111}, {"weierp", 0x2118},
{"real", 0x211C}, {"trade", 0x2122}, {"alefsym", 0x2135},
{"larr", 0x2190}, {"uarr", 0x2191}, {"rarr", 0x2192},
{"darr", 0x2193}, {"harr", 0x2194}, {"crarr", 0x21B5},
{"lArr", 0x21D0}, {"uArr", 0x21D1}, {"rArr", 0x21D2},
{"dArr", 0x21D3}, {"hArr", 0x21D4}, {"forall", 0x2200},
{"part", 0x2202}, {"exist", 0x2203}, {"empty", 0x2205},
{"nabla", 0x2207}, {"isin", 0x2208}, {"notin", 0x2209},
{"ni", 0x220B}, {"prod", 0x220F}, {"sum", 0x2211},
{"minus", 0x2212}, {"lowast", 0x2217}, {"radic", 0x221A},
{"prop", 0x221D}, {"infin", 0x221E}, {"ang", 0x2220},
{"and", 0x2227}, {"or", 0x2228}, {"cap", 0x2229},
{"cup", 0x222A}, {"int", 0x222B}, {"there4", 0x2234},
{"sim", 0x223C}, {"cong", 0x2245}, {"asymp", 0x2248},
{"ne", 0x2260}, {"equiv", 0x2261}, {"le", 0x2264},
{"ge", 0x2265}, {"sub", 0x2282}, {"sup", 0x2283},
{"nsub", 0x2284}, {"sube", 0x2286}, {"supe", 0x2287},
{"oplus", 0x2295}, {"otimes", 0x2297}, {"perp", 0x22A5},
{"sdot", 0x22C5}, {"lceil", 0x2308}, {"rceil", 0x2309},
{"lfloor", 0x230A}, {"rfloor", 0x230B}, {"lang", 0x2329},
{"rang", 0x232A}, {"loz", 0x25CA}, {"spades", 0x2660},
{"clubs", 0x2663}, {"hearts", 0x2665}, {"diams", 0x2666}};
// Used to convert number to utf-8 char.
wstring_convert<codecvt_utf8<char32_t>, char32_t> u8c;
// Matches numbered entities between 1 and 8 digits, decimal or hexadecimal,
// or named entities.
const regex re_entity{"&(#(x)?([[:alnum:]]{1,8})"
"|[^;[:space:][:punct:]]+);"};
"|[^;[:space:][:punct:]]+);"};
smatch match;
while (regex_search(buffer, match, re_entity))
@ -318,8 +144,10 @@ string unescape_html(string html)
output += match.prefix().str();
try
{
// clang-format off
const char32_t codepoint{[&match, &names]
{
// clang-format on
// If it doesn't start with a '#' it is a named entity.
if (match[1].str()[0] != '#')
{
@ -328,12 +156,12 @@ string unescape_html(string html)
// 'x' after '#' means the number is hexadecimal.
if (match[2].length() == 1)
{
return static_cast<char32_t>(stoul(match[3].str(),
nullptr, 16));
return static_cast<char32_t>(
stoul(match[3].str(), nullptr, 16));
}
// '#' without 'x' means the number is decimal.
return static_cast<char32_t>(stoul(match[3].str(),
nullptr, 10));
return static_cast<char32_t>(
stoul(match[3].str(), nullptr, 10));
}()};
output += u8c.to_bytes(codepoint);
}

View File

@ -15,6 +15,7 @@
*/
#include "instance.hpp"
#include "log.hpp"
#include <algorithm>
@ -24,12 +25,35 @@
namespace mastodonpp
{
using std::sort;
using std::stoull;
using std::exception;
using std::regex;
using std::regex_search;
using std::smatch;
using std::sort;
using std::stoull;
Instance::Instance(const string_view hostname, const string_view access_token)
: _hostname{hostname}
, _baseuri{"https://" + _hostname}
, _max_chars{0}
{
set_access_token(access_token);
}
Instance::Instance(const Instance &other)
: CURLWrapper{other}
, _hostname{other._hostname}
, _baseuri{other._baseuri}
, _access_token{other._access_token}
, _max_chars{other._max_chars}
, _proxy{other._proxy}
, _post_formats{other._post_formats}
, _cainfo{other._cainfo}
, _useragent{other._useragent}
{
CURLWrapper::setup_connection_properties(_proxy, _access_token, _cainfo,
_useragent);
}
uint64_t Instance::get_max_chars() noexcept
{
@ -43,16 +67,18 @@ uint64_t Instance::get_max_chars() noexcept
try
{
debuglog << "Querying " << _hostname << " for max_toot_chars…\n";
const auto answer{make_request(http_method::GET,
_baseuri + "/api/v1/instance", {})};
const auto answer{
make_request(http_method::GET, _baseuri + "/api/v1/instance", {})};
if (!answer)
{
debuglog << "Could not get instance info.\n";
return default_max_chars;
}
// clang-format off
_max_chars = [&answer]
{
// clang-format on
const regex re_chars{R"("max_toot_chars"\s*:\s*([^"]+))"};
smatch match;
@ -77,8 +103,8 @@ uint64_t Instance::get_max_chars() noexcept
answer_type Instance::get_nodeinfo()
{
auto answer{make_request(http_method::GET,
_baseuri + "/.well-known/nodeinfo", {})};
auto answer{
make_request(http_method::GET, _baseuri + "/.well-known/nodeinfo", {})};
if (!answer)
{
debuglog << "NodeInfo not found.\n";
@ -154,11 +180,8 @@ answer_type Instance::ObtainToken::step_1(const string_view client_name,
const string_view scopes,
const string_view website)
{
parametermap parameters
{
{"client_name", client_name},
{"redirect_uris", "urn:ietf:wg:oauth:2.0:oob"}
};
parametermap parameters{{"client_name", client_name},
{"redirect_uris", "urn:ietf:wg:oauth:2.0:oob"}};
if (!scopes.empty())
{
_scopes = scopes;
@ -169,8 +192,8 @@ answer_type Instance::ObtainToken::step_1(const string_view client_name,
parameters.insert({"website", website});
}
auto answer{make_request(http_method::POST, _baseuri + "/api/v1/apps",
parameters)};
auto answer{
make_request(http_method::POST, _baseuri + "/api/v1/apps", parameters)};
if (answer)
{
const regex re_id{R"("client_id"\s*:\s*"([^"]+)\")"};
@ -187,9 +210,10 @@ answer_type Instance::ObtainToken::step_1(const string_view client_name,
}
string uri{_baseuri + "/oauth/authorize?scope=" + escape_url(scopes)
+ "&response_type=code"
"&redirect_uri=" + escape_url("urn:ietf:wg:oauth:2.0:oob")
+ "&client_id=" + _client_id};
+ "&response_type=code"
"&redirect_uri="
+ escape_url("urn:ietf:wg:oauth:2.0:oob")
+ "&client_id=" + _client_id};
if (!website.empty())
{
uri += "&website=" + escape_url(website);
@ -203,21 +227,18 @@ answer_type Instance::ObtainToken::step_1(const string_view client_name,
answer_type Instance::ObtainToken::step_2(const string_view code)
{
parametermap parameters
{
{"client_id", _client_id},
{"client_secret", _client_secret},
{"redirect_uri", "urn:ietf:wg:oauth:2.0:oob"},
{"code", code},
{"grant_type", "client_credentials"}
};
parametermap parameters{{"client_id", _client_id},
{"client_secret", _client_secret},
{"redirect_uri", "urn:ietf:wg:oauth:2.0:oob"},
{"code", code},
{"grant_type", "authorization_code"}};
if (!_scopes.empty())
{
parameters.insert({"scope", _scopes});
}
auto answer{make_request(http_method::POST, _baseuri + "/oauth/token",
parameters)};
auto answer{
make_request(http_method::POST, _baseuri + "/oauth/token", parameters)};
if (answer)
{
const regex re_token{R"("access_token"\s*:\s*"([^"]+)\")"};

View File

@ -23,13 +23,13 @@
namespace mastodonpp
{
using std::cerr; // NOLINT(misc-unused-using-decls)
using std::cerr; // NOLINT(misc-unused-using-decls)
using std::string_view;
//! @private
constexpr auto shorten_filename(const string_view &filename)
{
for (const string_view &dir : {"/src/", "/include/"})
for (const string_view dir : {"/src/", "/include/"})
{
const auto pos{filename.rfind(dir)};
if (pos != string_view::npos)
@ -41,17 +41,17 @@ constexpr auto shorten_filename(const string_view &filename)
}
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define commonlog cerr << '[' << shorten_filename(__FILE__) \
<< ':' << __LINE__ << ']'
#define commonlog \
cerr << '[' << shorten_filename(__FILE__) << ':' << __LINE__ << ']'
#ifndef NDEBUG
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define debuglog commonlog << " DEBUG: "
# define debuglog commonlog << " DEBUG: "
#else
#define debuglog false && cerr
# define debuglog false && cerr
#endif
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define errorlog commonlog << " ERROR: "
} // namespace mastodonpp
#endif // MASTODONPP_LOG_HPP
#endif // MASTODONPP_LOG_HPP

View File

@ -1,5 +1,5 @@
/* This file is part of mastodonpp.
* Copyright © 2020 tastytea <tastytea@tastytea.de>
* Copyright © 2020, 2021 tastytea <tastytea@tastytea.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@ -14,9 +14,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "log.hpp"
#include "types.hpp"
#include "log.hpp"
#include <algorithm>
#include <cctype>
@ -36,7 +37,7 @@ answer_type::operator string_view() const
return body;
}
std::ostream &operator <<(std::ostream &out, const answer_type &answer)
std::ostream &operator<<(std::ostream &out, const answer_type &answer)
{
out << answer.body;
return out;
@ -45,10 +46,11 @@ std::ostream &operator <<(std::ostream &out, const answer_type &answer)
string_view answer_type::get_header(const string_view field) const
{
const string_view searchstring{string(field) += ':'};
auto it{search(headers.begin(), headers.end(),
searchstring.begin(), searchstring.end(),
[](unsigned char a, unsigned char b)
// clang-format off
auto it{search(headers.begin(), headers.end(), searchstring.begin(),
searchstring.end(), [](unsigned char a, unsigned char b)
{ return tolower(a) == tolower(b); })};
// clang-format on
if (it != headers.end())
{
@ -69,7 +71,7 @@ parametermap answer_type::parse_pagination(const bool next) const
return {};
}
const auto direction{next ? R"(rel="next")" : R"(rel="prev")"};
const string_view direction{next ? R"(rel="next")" : R"(rel="prev")"};
auto endpos{link.find(direction)};
endpos = link.rfind('>', endpos);
auto startpos{link.rfind('?', endpos) + 1};

View File

@ -3,11 +3,16 @@ include(CTest)
file(GLOB sources_tests test_*.cpp)
find_package(Catch2 CONFIG)
if(Catch2_FOUND) # Catch 2.x
if(Catch2_FOUND) # Catch 2.x / 3.x
include(Catch)
add_executable(all_tests main.cpp ${sources_tests})
target_link_libraries(all_tests
PRIVATE Catch2::Catch2 ${PROJECT_NAME})
if(TARGET Catch2::Catch2WithMain) # Catch 3.x
target_link_libraries(all_tests
PRIVATE Catch2::Catch2WithMain ${PROJECT_NAME})
else() # Catch 2.x
target_link_libraries(all_tests
PRIVATE Catch2::Catch2 ${PROJECT_NAME})
endif()
target_include_directories(all_tests PRIVATE "/usr/include/catch2")
catch_discover_tests(all_tests EXTRA_ARGS "${EXTRA_TEST_ARGS}")
else() # Catch 1.x

View File

@ -1,5 +1,5 @@
/* This file is part of mastodonpp.
* Copyright © 2020 tastytea <tastytea@tastytea.de>
* Copyright © 2020, 2022 tastytea <tastytea@tastytea.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@ -16,4 +16,9 @@
#define CATCH_CONFIG_MAIN
#include <catch.hpp>
// catch 3 does not have catch.hpp anymore
#if __has_include(<catch.hpp>)
# include <catch.hpp>
#else
# include <catch_all.hpp>
#endif

View File

@ -1,5 +1,5 @@
/* This file is part of mastodonpp.
* Copyright © 2020 tastytea <tastytea@tastytea.de>
* Copyright © 2020, 2022 tastytea <tastytea@tastytea.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@ -14,22 +14,26 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "instance.hpp"
#include "connection.hpp"
#include "instance.hpp"
#include <catch.hpp>
// catch 3 does not have catch.hpp anymore
#if __has_include(<catch.hpp>)
# include <catch.hpp>
#else
# include <catch_all.hpp>
#endif
#include <exception>
namespace mastodonpp
{
SCENARIO ("mastodonpp::Connection.")
SCENARIO("mastodonpp::Connection.")
{
bool exception = false;
WHEN ("Connection is instantiated.")
WHEN("Connection is instantiated.")
{
try
{
@ -41,7 +45,7 @@ SCENARIO ("mastodonpp::Connection.")
exception = true;
}
THEN ("No exception is thrown")
THEN("No exception is thrown")
{
REQUIRE_FALSE(exception);
}

View File

@ -1,5 +1,5 @@
/* This file is part of mastodonpp.
* Copyright © 2020 tastytea <tastytea@tastytea.de>
* Copyright © 2020, 2022 tastytea <tastytea@tastytea.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@ -16,7 +16,12 @@
#include "helpers.hpp"
#include <catch.hpp>
// catch 3 does not have catch.hpp anymore
#if __has_include(<catch.hpp>)
# include <catch.hpp>
#else
# include <catch_all.hpp>
#endif
#include <exception>
#include <string>
@ -26,11 +31,11 @@ namespace mastodonpp
using std::string;
SCENARIO ("mastodonpp::html_unescape()")
SCENARIO("mastodonpp::html_unescape()")
{
bool exception = false;
WHEN ("html_unescape() is called.")
WHEN("html_unescape() is called.")
{
string result;
try
@ -42,8 +47,8 @@ SCENARIO ("mastodonpp::html_unescape()")
exception = true;
}
THEN ("No exception is thrown")
AND_THEN("Result is as expected.")
THEN("No exception is thrown")
AND_THEN("Result is as expected.")
{
REQUIRE_FALSE(exception);
REQUIRE(result == "2€ = 2€ = 2€");

View File

@ -1,5 +1,5 @@
/* This file is part of mastodonpp.
* Copyright © 2020 tastytea <tastytea@tastytea.de>
* Copyright © 2020, 2022 tastytea <tastytea@tastytea.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@ -16,7 +16,12 @@
#include "instance.hpp"
#include <catch.hpp>
// catch 3 does not have catch.hpp anymore
#if __has_include(<catch.hpp>)
# include <catch.hpp>
#else
# include <catch_all.hpp>
#endif
#include <exception>
#include <string>
@ -26,11 +31,11 @@ namespace mastodonpp
using std::string;
SCENARIO ("mastopp::Instance")
SCENARIO("mastopp::Instance")
{
bool exception = false;
WHEN ("Instance is instantiated.")
WHEN("Instance is instantiated.")
{
try
{
@ -41,13 +46,13 @@ SCENARIO ("mastopp::Instance")
exception = true;
}
THEN ("No exception is thrown")
THEN("No exception is thrown")
{
REQUIRE_FALSE(exception);
}
}
WHEN ("Variables are set.")
WHEN("Variables are set.")
{
constexpr auto hostname{"likeable.space"};
constexpr auto proxy{"socks4a://[::1]:9050"};
@ -64,10 +69,10 @@ SCENARIO ("mastopp::Instance")
exception = true;
}
THEN ("No exception is thrown")
AND_THEN ("get_access_token() returns the set value.")
AND_THEN ("get_hostname() returns the set value.")
AND_THEN ("get_baseuri() returns the expected value.")
THEN("No exception is thrown")
AND_THEN("get_access_token() returns the set value.")
AND_THEN("get_hostname() returns the set value.")
AND_THEN("get_baseuri() returns the expected value.")
{
REQUIRE_FALSE(exception);
REQUIRE(instance.get_access_token() == access_token);