Compare commits

...

113 Commits
0.3.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
tastytea 965780fbba
Version bump 0.5.0.
continuous-integration/drone/push Build is passing Details
2020-02-02 15:26:43 +01:00
tastytea 95696ba5ca
Merge branch 'develop' into main
continuous-integration/drone/push Build is passing Details
2020-02-02 15:18:31 +01:00
tastytea 3bbc24ba57
Decrease loops drastically in unescape_html().
continuous-integration/drone/push Build is passing Details
2020-02-02 15:02:19 +01:00
tastytea af1993c71f
Add hyperlinks to git commands in contributing guidelines. 2020-02-02 15:01:50 +01:00
tastytea fa1cbbeb91
Merge branch 'develop' into main
continuous-integration/drone/push Build is passing Details
2020-01-31 03:57:55 +01:00
tastytea 185ab91978
Rename buffer_mutex → _buffer_mutex.
continuous-integration/drone/push Build is passing Details
2020-01-28 08:11:56 +01:00
tastytea cdd7a465d2
Update .clang-tidy. 2020-01-28 08:03:16 +01:00
tastytea 2bbead14e5
Add .clang-tidy. 2020-01-27 03:19:47 +01:00
tastytea 56cabf48a8
Add clang-tidy exceptions for http_method. 2020-01-27 01:09:20 +01:00
tastytea 4dc7dbb4e6
CMake: We are already in the build directory. 2020-01-27 01:08:45 +01:00
tastytea 0ccef6773b
Merge branch 'develop' into main
continuous-integration/drone/push Build is passing Details
2020-01-26 23:26:36 +01:00
tastytea deeff83410
Make some private members of CURLWrapper static.
continuous-integration/drone/push Build is passing Details
replace_parameter_in_uri(), add_parameters_to_uri() and add_mime_part().
2020-01-26 08:50:05 +01:00
tastytea 3876cf3f01
Add WITH_CLANG-TIDY to CMake recipe. 2020-01-26 05:20:05 +01:00
tastytea 151103a9a1
Catch JSON exceptions in nlohmann-json example. 2020-01-26 05:19:58 +01:00
tastytea 25c75a6211
Setting -DNDEBUG is redundant. 2020-01-26 05:12:00 +01:00
tastytea b82d779119
Use brace initialization for _stream_cancelled. 2020-01-26 04:10:59 +01:00
tastytea fe5ac46d52
Explicitly call CURLWrapper::set_useragent() in setup_curl(). 2020-01-26 03:51:32 +01:00
tastytea 65ff76312b
Add nlohmann-json example.
continuous-integration/drone/push Build is passing Details
2020-01-26 00:49:49 +01:00
tastytea f0b38dae10
Make the introduction more true. 2020-01-25 20:23:52 +01:00
tastytea 28b2668426
Add Mastodon and Pleroma hyperlinks to readme. 2020-01-25 20:22:33 +01:00
tastytea e6c2db91fd
Merge branch 'develop' into main
continuous-integration/drone/push Build is passing Details
2020-01-25 03:47:05 +01:00
tastytea 02a22d773c
Make set_proxy(), set_cainfo(), set_useragent() virtual.
continuous-integration/drone/push Build is passing Details
2020-01-24 22:06:47 +01:00
tastytea bb088e6d70
Mark CURLWrapper::get_buffer() inline. 2020-01-24 21:57:15 +01:00
tastytea ac4150a3aa
Fix decumentation for CURLWrapper destructor. 2020-01-24 21:50:51 +01:00
tastytea 8955442b0d
Setting -g for debug builds is redundant.
continuous-integration/drone/push Build is passing Details
2020-01-24 21:23:48 +01:00
tastytea c9ea49ed91
Enhance documentation for Instance and Connection.
continuous-integration/drone/push Build is passing Details
2020-01-24 02:18:54 +01:00
tastytea 9523de8788
Mark buffer variables const.
continuous-integration/drone/push Build is passing Details
2020-01-20 02:46:00 +01:00
tastytea 2c13f03a1c
Clarify documentation for Instance::ObtainToken::step_2(). 2020-01-20 02:32:48 +01:00
tastytea 3d614afa72
Instance is not a function. 2020-01-20 02:29:01 +01:00
tastytea 60f26dc864
Report right headerfile location for Instance::ObtainToken. 2020-01-20 02:26:15 +01:00
tastytea cbdc438ccf
80 char rule. 2020-01-19 20:52:55 +01:00
tastytea 161554e677
Allow examples to build outside mastodonpp.
continuous-integration/drone/push Build is passing Details
2020-01-18 23:51:34 +01:00
tastytea 9bc3fe6fe2
Fix header location in readme example. 2020-01-18 23:38:11 +01:00
tastytea e40848fb1e
typo.
continuous-integration/drone/push Build is passing Details
2020-01-18 23:32:51 +01:00
tastytea e650e4c787
Merge branch 'develop' into main
continuous-integration/drone/push Build was killed Details
2020-01-18 23:25:08 +01:00
tastytea c56d8115e4
Mention “make package” in readme.
continuous-integration/drone/push Build is passing Details
2020-01-18 23:24:15 +01:00
tastytea 8e5e0435b6
Update feature list.
continuous-integration/drone/push Build is passing Details
2020-01-18 18:09:58 +01:00
tastytea 837524661c
Indentation fix. 2020-01-18 18:09:49 +01:00
tastytea 5d78051161
Merge branch 'develop' into main
continuous-integration/drone/push Build is passing Details
2020-01-17 17:32:13 +01:00
tastytea c75aefbe2a
Fix header names in documentation.
continuous-integration/drone/push Build was killed Details
2020-01-17 17:30:31 +01:00
tastytea 393c179c00
Make main page of reference more precise. 2020-01-17 17:30:31 +01:00
tastytea d8582f33c1
Mention the dependency on C++17.
continuous-integration/drone/push Build was killed Details
2020-01-17 17:29:44 +01:00
tastytea dc706ab41f
Fix clang URI.
continuous-integration/drone/push Build is passing Details
2020-01-16 23:50:09 +01:00
tastytea be71510316
Version bump 0.4.0.
continuous-integration/drone/push Build is passing Details
2020-01-16 18:25:32 +01:00
tastytea 9d44f30f8e
Merge branch 'develop' into main
continuous-integration/drone/push Build is passing Details
2020-01-16 18:21:07 +01:00
tastytea c01ba95f5a
Remove useless else. 2020-01-16 18:20:37 +01:00
tastytea bdd328ac91
Remove useless const. 2020-01-16 18:19:47 +01:00
tastytea 5e612210ef
Log proxy changes when debugging.
continuous-integration/drone/push Build is passing Details
2020-01-16 18:17:29 +01:00
tastytea 18c0b41a20
Add test for unescape_html(). 2020-01-16 18:16:02 +01:00
tastytea 8cfba1f16b
Add example to unescape_html() documentation. 2020-01-16 18:06:14 +01:00
tastytea cf2ff119b4
Revert "Add -fsanitize=address to debug flags."
This reverts commit fad9f87391.

Output was unusable.
2020-01-16 18:05:22 +01:00
tastytea 343fe2adca
Add unescape_html().
continuous-integration/drone/push Build is passing Details
2020-01-15 19:09:43 +01:00
tastytea fad9f87391
Add -fsanitize=address to debug flags.
continuous-integration/drone/push Build is passing Details
2020-01-15 14:25:36 +01:00
tastytea 11608abed8
Merge branch 'develop' into main
continuous-integration/drone/push Build is passing Details
2020-01-14 23:50:05 +01:00
tastytea 7ac55d6868
Update tested dpkg version.
continuous-integration/drone/push Build is passing Details
2020-01-14 23:49:15 +01:00
tastytea 99f00e331a
Silence false-positive clang-tidy warning,
continuous-integration/drone/push Build was killed Details
and one I can't do anything about.
2020-01-14 23:46:42 +01:00
tastytea 0596406113
Silence false positive clang-tidy warnings in log.hpp. 2020-01-14 23:45:39 +01:00
tastytea a237d74a81
Use bracket initialization in example code. 2020-01-14 23:34:50 +01:00
tastytea 9ccd724693
Mark parse_pagination() nodiscard. 2020-01-14 23:32:04 +01:00
tastytea be42f30631
Removed superfluous const from parameter for readability. 2020-01-14 23:31:39 +01:00
tastytea 7afc8d19a9
Add documentation for set_useragent().
continuous-integration/drone/push Build is passing Details
2020-01-14 23:28:20 +01:00
tastytea 968863301a
Mark ObtainToken constructor explicit. 2020-01-14 23:27:30 +01:00
tastytea df51d4fdcc
Change website in example08 to example.com. 2020-01-14 23:22:50 +01:00
41 changed files with 1327 additions and 425 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
...

45
.clang-tidy Normal file
View File

@ -0,0 +1,45 @@
# -*- mode: conf; fill-column: 100; -*-
# Written for clang-tidy 11.
---
Checks: '*,
-cppcoreguidelines-non-private-member-variables-in-classes,
-fuchsia-default-arguments-calls,
-fuchsia-default-arguments-declarations,
-fuchsia-default-arguments,
-llvm-include-order,
-llvm-header-guard,
-misc-non-private-member-variables-in-classes,
-fuchsia-overloaded-operator,
-cppcoreguidelines-avoid-magic-numbers,
-readability-magic-numbers,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
-hicpp-no-array-decay,
-modernize-avoid-c-arrays,
-cppcoreguidelines-avoid-c-arrays,
-hicpp-avoid-c-arrays,
-google-build-using-namespace,
-readability-named-parameter,
-google-runtime-references,
-hicpp-avoid-goto,
-hicpp-vararg,
-fuchsia-statically-constructed-objects,
-google-readability-todo,
-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.PrivateMemberCase, value: lower_case }
- { key: readability-identifier-naming.PrivateMemberPrefix, value: _ }
- { 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 }
- { 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.3.0
VERSION 0.5.7
DESCRIPTION "C++ wrapper for the Mastodon and Pleroma APIs."
LANGUAGES CXX)
@ -20,8 +29,10 @@ 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)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
@ -29,9 +40,11 @@ set(CMAKE_CXX_EXTENSIONS OFF)
include(debug_flags)
# Disable debug log.
if(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug")
add_definitions("-DNDEBUG")
if(WITH_CLANG-TIDY)
set(CMAKE_CXX_CLANG_TIDY
"clang-tidy"
"-header-filter=${PROJECT_SOURCE_DIR}"
"-quiet")
endif()
add_subdirectory(src)
@ -48,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

@ -4,6 +4,8 @@
:contact-email: tastytea@tastytea.de
:contact-xmpp: {contact-email}
:contact-fediverse: https://likeable.space/users/tastytea
:uri-git-format-patch: https://git-scm.com/docs/git-format-patch
:uri-git-send-email: https://git-scm.com/docs/git-send-email
== How to contribute
@ -23,7 +25,10 @@ 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 `git format-patch` or `git send-email`.
using link:{uri-git-format-patch}[git format-patch] or
link:{uri-git-send-email}[git send-email].

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

@ -3,37 +3,49 @@
:project: mastodonpp
:uri-base: https://schlomp.space/tastytea/{project}
:uri-branch-main: {uri-base}/src/branch/main
:uri-wp-mastodon: https://en.wikipedia.org/wiki/Mastodon_(software)
:uri-pleroma: https://pleroma.social/
:uri-mastodon-cpp: https://schlomp.space/tastytea/mastodon-cpp
:uri-reference: https://doc.schlomp.space/{project}/
:uri-gcc: https://gcc.gnu.org/
:uti-clang: https://clang.llvm.org/
:uri-clang: https://clang.llvm.org/
:uri-cmake: https://cmake.org/
:uri-doxygen: http://www.doxygen.nl/
:uri-catch: https://github.com/catchorg/Catch2
:uri-dpkg: https://packages.qa.debian.org/dpkg
:uri-rpm-build: http://www.rpm.org
:uri-libcurl: https://curl.haxx.se/libcurl/
:uri-nodeinfo: https://nodeinfo.diaspora.software/
:uri-clang-tidy: https://clang.llvm.org/extra/clang-tidy/
*{project}* is a C++ wrapper for the Mastodon and Pleroma APIs. It replaces
*{project}* is a C++ wrapper for the link:{uri-wp-mastodon}[Mastodon] and
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
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
link:{uri-libcurl}[libcurl], a mature and stable library that is available on
virtually every operating system. The library does not parse the responses
itself, but returns to you the raw data, because we know everyone has their
favorite JSON library and we don't want to impose our choice on you!
most operating systems. The library does not parse the responses itself, but
returns to you the raw data, because we know everyone has their favorite JSON
library and we don't want to impose our choice on you!
== Features
This is still a work in progress; here is a rough overview of the features:
Here is a rough overview of the features:
* [x] `GET`, Streaming `GET`, `POST`, `PATCH`, `PUT` and `DELETE` requests.
* [x] Report maximum allowed character per post.
* [x] `GET`, Streaming `GET`, `POST`, `PATCH`, `PUT` and `DELETE` requests.
* [x] Comfortable access to pagination headers.
* [x] Comfortable function to register a new “app” (get an access token).
* [x] Report maximum allowed character per post.
* [x] Simple function to register a new “app” (get an access token).
* [x] Report which mime types are allowed for posting statuses.
* [x] Find and retrieve link:{uri-nodeinfo}[NodeInfo].
* [x] Easy access to the libcurl handle for maximum configurability.
* [x] Set proxy server, User-Agent and the path to the CA bundle.
== Usage
@ -43,7 +55,7 @@ Have a look at the link:{uri-reference}[reference].
[source,cpp]
--------------------------------------------------------------------------------
#include "mastodonpp.hpp"
#include <mastodonpp/mastodonpp.hpp>
#include <iostream>
int main()
@ -71,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
@ -107,13 +127,14 @@ yum install ./libmastodonpp*.rpm
==== Dependencies
* Tested OS: Linux
* C++ compiler (tested: link:{uri-gcc}[GCC] 7/8/9, link:{uri-lang}[clang] 6/7)
* C\++ compiler with C++17 support (tested: link:{uri-gcc}[GCC] 7/8/9,
link:{uri-clang}[clang] 6/7)
* link:{uri-cmake}[CMake] (at least: 3.9)
* link:{uri-libcurl}[libcurl] (at least: 7.56)
* Optional
** Documentation: link:{uri-doxygen}[Doxygen] (tested: 1.8)
** Tests: link:{uri-catch}[Catch] (tested: 2.5 / 1.2)
** DEB package: link:{uri-dpkg}[dpkg] (tested: 1.18)
** DEB package: link:{uri-dpkg}[dpkg] (tested: 1.19)
** RPM package: link:{uri-rpm-build}[rpm-build] (tested: 4.11)
==== Get sourcecode
@ -142,8 +163,19 @@ 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:
** `-DWITH_DEB=YES` if you want to be able to generate a deb-package.
** `-DWITH_RPM=YES` if you want to be able to generate an rpm-package.
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,8 +22,6 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang"
"-Wdouble-promotion"
"-Wformat=2"
"-ftrapv"
"-fsanitize=undefined"
"-g"
"-Og"
"-fno-omit-frame-pointer")
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
@ -41,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

@ -15,7 +15,11 @@
// Print information about an instance (/api/v1/instance).
#include "mastodonpp.hpp"
#if __has_include("mastodonpp.hpp")
# include "mastodonpp.hpp" // We're building mastodonpp.
#else
# include <mastodonpp/mastodonpp.hpp> // We're building outside mastodonpp.
#endif
#include <iostream>
#include <string>
@ -23,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

@ -15,7 +15,11 @@
// Print new public events (/api/v1/streaming/public).
#include "mastodonpp.hpp"
#if __has_include("mastodonpp.hpp")
# include "mastodonpp.hpp" // We're building mastodonpp.
#else
# include <mastodonpp/mastodonpp.hpp> // We're building outside mastodonpp.
#endif
#include <chrono>
#include <iostream>
@ -26,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[])
{
@ -57,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);
}};
@ -69,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

@ -15,7 +15,11 @@
// Post a status (/api/v1/status).
#include "mastodonpp.hpp"
#if __has_include("mastodonpp.hpp")
# include "mastodonpp.hpp" // We're building mastodonpp.
#else
# include <mastodonpp/mastodonpp.hpp> // We're building outside mastodonpp.
#endif
#include <iostream>
#include <string>
@ -23,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[])
@ -47,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

@ -15,7 +15,11 @@
// Post a status (/api/v1/status) with an attachment (/api/v1/media).
#include "mastodonpp.hpp"
#if __has_include("mastodonpp.hpp")
# include "mastodonpp.hpp" // We're building mastodonpp.
#else
# include <mastodonpp/mastodonpp.hpp> // We're building outside mastodonpp.
#endif
#include <iostream>
#include <string>
@ -23,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[])
@ -50,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
@ -65,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

@ -15,7 +15,11 @@
// Update notification settings (/api/pleroma/notification_settings).
#include "mastodonpp.hpp"
#if __has_include("mastodonpp.hpp")
# include "mastodonpp.hpp" // We're building mastodonpp.
#else
# include <mastodonpp/mastodonpp.hpp> // We're building outside mastodonpp.
#endif
#include <iostream>
#include <string>
@ -23,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[])
@ -46,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

@ -15,7 +15,11 @@
// Update account display name settings (/api/v1/accounts/update_credentials).
#include "mastodonpp.hpp"
#if __has_include("mastodonpp.hpp")
# include "mastodonpp.hpp" // We're building mastodonpp.
#else
# include <mastodonpp/mastodonpp.hpp> // We're building outside mastodonpp.
#endif
#include <iostream>
#include <string>
@ -23,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[])
@ -48,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

@ -15,7 +15,11 @@
// Post a status (/api/v1/status), then delete it (/api/v1/statuses/:id).
#include "mastodonpp.hpp"
#if __has_include("mastodonpp.hpp")
# include "mastodonpp.hpp" // We're building mastodonpp.
#else
# include <mastodonpp/mastodonpp.hpp> // We're building outside mastodonpp.
#endif
#include <chrono>
#include <iostream>
@ -26,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

@ -15,7 +15,11 @@
// Obtain an access token and verify that it works.
#include "mastodonpp.hpp"
#if __has_include("mastodonpp.hpp")
# include "mastodonpp.hpp" // We're building mastodonpp.
#else
# include <mastodonpp/mastodonpp.hpp> // We're building outside mastodonpp.
#endif
#include <cstdlib>
#include <iostream>
@ -24,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);
@ -53,8 +57,10 @@ 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",
"https://tastytea.de/")};
// 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)
{
handle_error(answer);
@ -103,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

@ -0,0 +1,145 @@
/* This file is part of mastodonpp.
* Copyright © 2020 tastytea <tastytea@tastytea.de>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
// Get the last 4 public statuses of an instance and process them with
// nlohmann-json. <https://github.com/nlohmann/json>
#if __has_include("mastodonpp.hpp")
# include "mastodonpp.hpp" // We're building mastodonpp.
#else
# include <mastodonpp/mastodonpp.hpp> // We're building outside mastodonpp.
#endif
// Don't compile if nlohmann-json can't be found.
#if __has_include(<nlohmann/json.hpp>)
# include <nlohmann/json.hpp>
# include <cstdlib>
# include <iostream>
# include <string>
# include <string_view>
# include <vector>
namespace masto = mastodonpp;
using json = nlohmann::json;
using std::cerr;
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);
int main(int argc, char *argv[])
{
const vector<string_view> args(argv, argv + argc);
if (args.size() <= 1)
{
cerr << "Usage: " << args[0] << " <instance hostname>\n";
return 1;
}
try
{
// Initialize Instance and Connection.
masto::Instance instance{args[1], {}};
masto::Connection connection{instance};
// Get the last 4 public statuses of the instance.
auto answer{connection.get(masto::API::v1::timelines_public,
{{"limit", "4"}, {"local", "true"}})};
if (answer)
{
// Parse JSON string.
auto statuses{json::parse(answer.body)};
for (const auto &status : statuses)
{
// Extract the info we want and print it.
const auto acct{status["account"]["acct"].get<string_view>()};
const auto content{status["content"].get<string_view>()};
const auto id{status["id"].get<string_view>()};
cout << acct << " wrote status " << id << ": \n";
cout << " " << content.substr(0, 76) << "\n";
// Print tags if there are any.
const auto tags{status["tags"]};
if (!tags.empty())
{
cout << " Tags: ";
for (const auto &tag : tags)
{
cout << '#' << tag["name"].get<string_view>() << " ";
}
cout << '\n';
}
// Print the number of attachments.
const auto n_attachments{status["media_attachments"].size()};
if (n_attachments > 0)
{
cout << " " << n_attachments << " attachment";
if (n_attachments > 1)
{
cout << "s";
}
cout << ".\n";
}
}
}
else
{
handle_error(answer);
}
}
catch (const masto::CURLException &e)
{
// Only libcurl errors that are not network errors will be thrown.
// There went probably something wrong with the initialization.
cerr << e.what() << '\n';
}
catch (const nlohmann::detail::exception &e)
{
cerr << "JSON exception: " << e.what() << '\n';
}
}
void handle_error(const masto::answer_type &answer)
{
if (answer.curl_error_code == 0)
{
// If it is no libcurl error, it must be an HTTP error.
cerr << "HTTP status: " << answer.http_status << '\n';
}
else
{
// Network errors like “Couldn't resolve host.”.
cerr << "libcurl error " << to_string(answer.curl_error_code) << ": "
<< answer.error_message << '\n';
}
exit(1);
}
#else
# include <iostream>
int main()
{
std::cout << "Example could not be compiled "
"because nlohmann-json was not found.\n";
}
#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.
@ -68,6 +68,9 @@ struct event_type
/*!
* @brief Represents a connection to an instance. Used for requests.
*
* Do not make 2 requests with the same Connection at the same time. You can
* create as many Connection%s as you want from one Instance.
*
* @since 0.1.0
*
* @headerfile connection.hpp mastodonpp/connection.hpp
@ -82,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.
*
@ -107,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.
@ -123,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, {});
}
@ -149,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.
@ -160,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, {});
}
@ -175,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.
@ -186,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, {});
}
@ -201,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.
@ -212,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, {});
}
@ -227,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.
@ -238,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, {});
}
@ -269,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,
POST,
PATCH,
PUT,
DELETE
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;
@ -79,7 +82,7 @@ public:
/*!
* @brief Cleans up curl and connection.
*
* Calls `curl_global_cleanup`, which is not thread-safe. For more
* May call `curl_global_cleanup`, which is not thread-safe. For more
* information consult [curl_global_cleanup(3)]
* (https://curl.haxx.se/libcurl/c/curl_global_cleanup.html).
*
@ -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,8 +122,7 @@ 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()))};
@ -130,7 +132,7 @@ public:
}
/*!
* @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,8 +143,7 @@ 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)};
@ -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:
/*!
@ -172,7 +172,7 @@ protected:
*
* @since 0.1.0
*/
mutex buffer_mutex;
mutex _buffer_mutex;
/*!
* @brief Make a HTTP request.
@ -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]]
string &get_buffer()
[[nodiscard]] inline string &get_buffer()
{
return _curl_buffer_body;
}
@ -222,31 +221,42 @@ protected:
*
* @since 0.1.0
*/
void set_proxy(string_view proxy);
virtual void set_proxy(string_view proxy);
/*!
* @brief Set OAuth 2.0 Bearer Access Token.
*
* @since 0.1.0
*/
void set_access_token(const string_view access_token);
void set_access_token(string_view access_token);
/*!
* @brief Set path to Certificate Authority (CA) bundle.
*
* @since 0.3.0
*/
void set_cainfo(string_view path);
virtual void set_cainfo(string_view path);
void set_useragent(string_view useragent);
/*!
* @brief Sets the User-Agent.
*
* @since 0.3.0
*/
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.
@ -266,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
@ -276,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);
}
/*!
@ -287,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);
}
/*!
@ -315,7 +325,8 @@ private:
*
* @since 0.1.0
*/
bool replace_parameter_in_uri(string &uri, const parameterpair &parameter);
static bool replace_parameter_in_uri(string &uri,
const parameterpair &parameter);
/*!
* @brief Add parameters to URI.
@ -325,7 +336,8 @@ private:
*
* @since 0.1.0
*/
void add_parameters_to_uri(string &uri, const parametermap &parameters);
static void add_parameters_to_uri(string &uri,
const parametermap &parameters);
/*!
* @brief Add `*curl_mimepart` to `*curl_mime`.
@ -336,8 +348,8 @@ private:
*
* @since 0.2.0
*/
void add_mime_part(curl_mime *mime,
string_view name, string_view data) const;
static void add_mime_part(curl_mime *mime, string_view name,
string_view data);
/*!
* @brief Convert parametermap to `*curl_mime`.
@ -359,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

49
include/helpers.hpp Normal file
View File

@ -0,0 +1,49 @@
/* This file is part of mastodonpp.
* Copyright © 2020 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
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MASTODONPP_HELPERS_HPP
#define MASTODONPP_HELPERS_HPP
#include <string>
#include <string_view>
//! @headerfile helpers.hpp mastodonpp/helpers.hpp
namespace mastodonpp
{
using std::string;
/*!
* @brief Replaces HTML entities with UTF-8 characters.
*
* Supports named and numbered entities, decimal and hexadecimal.
*
* Example:
* @code
* // Will output: 2€ = 2€ = 2€
* std::cout << mastodonpp::unescape_html("2&euro; = 2&#8364; = 2&#x20ac;");
* @endcode
*
* @param html The HTML to unescape.
*
* @since 0.4.0
*/
[[nodiscard]] string unescape_html(string html);
} // namespace mastodonpp
#endif // MASTODONPP_HELPERS_HPP

View File

@ -29,15 +29,18 @@
namespace mastodonpp
{
using std::uint64_t;
using std::string;
using std::string_view;
using std::move;
using std::uint64_t;
using std::vector;
/*!
* @brief Holds the access data of an instance.
*
* Instance%s are needed to initialize Connection%s. All properties you set
* here (with set_proxy(), set_useragent() and so on) are copied to every
* Connection you initialize afterwards.
*
* @since 0.1.0
*
* @headerfile instance.hpp mastodonpp/instance.hpp
@ -53,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.
@ -71,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);
@ -82,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;
}
@ -95,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;
}
@ -106,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;
}
@ -120,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);
}
/*!
@ -136,15 +150,14 @@ 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)
*
* Sets also the proxy for all Connection%s that are initialized with this
* Instance afterwards.
*/
void set_proxy(const string_view proxy)
void set_proxy(const string_view proxy) override
{
_proxy = proxy;
CURLWrapper::set_proxy(proxy);
@ -159,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.
@ -183,13 +195,21 @@ public:
*
* @since 0.3.0
*/
void set_cainfo(string_view path)
void set_cainfo(string_view path) override
{
_cainfo = path;
CURLWrapper::set_cainfo(path);
}
void set_useragent(const string_view useragent)
/*!
* @brief Sets the User-Agent.
*
* Sets also the User-Agent for all Connection%s that are initialized with
* this Instance afterwards.
*
* @since 0.3.0
*/
void set_useragent(const string_view useragent) override
{
_useragent = useragent;
CURLWrapper::set_useragent(useragent);
@ -198,15 +218,15 @@ public:
/*!
* @brief Simplifies obtaining an OAuth 2.0 Bearer Access Token.
*
* * Create an Instance() and initialize this class with it.
* * Create an Instance and initialize this class with it.
* * Call step_1() to get the URI your user has to visit.
* * Get the authorization code from your user.
* * Call step_2() with the code.
*
* Example:
* @code
* mastodonpp::Instance instance("example.com", {});
* mastodonpp::Instance::ObtainToken token(instance);
* mastodonpp::Instance instance{"example.com", {}};
* mastodonpp::Instance::ObtainToken token{instance};
* auto answer{token.step1("Good program", "read:blocks read:mutes", "")};
* if (answer)
* {
@ -222,11 +242,18 @@ public:
* @endcode
*
* @since 0.3.0
*
* @headerfile instance.hpp mastodonpp/instance.hpp
*/
class ObtainToken : public CURLWrapper
{
public:
ObtainToken(Instance &instance)
/*!
* @brief Constructor.
*
* @since 0.3.0
*/
explicit ObtainToken(Instance &instance)
: _instance{instance}
, _baseuri{instance.get_baseuri()}
{
@ -239,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.
@ -249,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`.
@ -259,7 +289,8 @@ public:
* The `body` of the returned @link answer_type answer @endlink
* contains only the access token, not the whole JSON response.
*
* The access token will be set in the parent Instance.
* The access token will be set in the Instance you initialized
* this ObtainToken with.
*
* @param code The authorization code you got from the user.
*
@ -267,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;
@ -291,4 +321,4 @@ private:
} // namespace mastodonpp
#endif // MASTODONPP_INSTANCE_HPP
#endif // MASTODONPP_INSTANCE_HPP

View File

@ -20,12 +20,11 @@
#include "api.hpp"
#include "connection.hpp"
#include "exceptions.hpp"
#include "helpers.hpp"
#include "instance.hpp"
#include "types.hpp"
/*!
* @headerfile mastodonpp.hpp mastodonpp/mastodonpp.hpp
*
* @mainpage mastodonpp Reference
*
* @section using Using the library
@ -45,6 +44,9 @@
*
* Or compile your code with `g++ $(pkg-config --cflags --libs mastodonpp)`.
*
* Since we use C++17 features in the headers of this library, your program
* needs to be compiled as C++17 or higher too.
*
* @subsection example Example
*
* @code
@ -53,7 +55,7 @@
*
* int main()
* {
* mastodonpp::Instance instance{"example.com", ""};
* mastodonpp::Instance instance{"example.com", {}};
* std::cout << "Maximum characters per post: "
* << instance.get_max_chars() << std::endl;
*
@ -81,12 +83,10 @@
*
* @section thread_safety Thread safety
*
* The first time you construct an @link mastodonpp::Instance Instance @endlink
* or @link mastodonpp::Connection Connection @endlink, [curl_global_init(3)]
* (https://curl.haxx.se/libcurl/c/curl_global_init.html) is called. When the
* last @link mastodonpp::Instance Instance @endlink or @link
* mastodonpp::Connection Connection @endlink is destroyed,
* [curl_global_cleanup(3)]
* The first time you construct an @link mastodonpp::Instance Instance@endlink,
* [curl_global_init(3)](https://curl.haxx.se/libcurl/c/curl_global_init.html)
* is called. When the last @link mastodonpp::Instance Instance @endlink is
* destroyed, [curl_global_cleanup(3)]
* (https://curl.haxx.se/libcurl/c/curl_global_cleanup.html) is called. Both
* are not thread safe.
*
@ -106,14 +106,17 @@
* @example example06_update_name.cpp
* @example example07_delete_status.cpp
* @example example08_obtain_token.cpp
* @example example09_nlohmann_json.cpp
*/
/*!
* @brief C++ wrapper for the Mastodon %API.
*
* @since 0.1.0
*
* @headerfile mastodonpp.hpp mastodonpp/mastodonpp.hpp
*/
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,24 +60,21 @@ 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.
*
* @since 0.1.0
*
* @headerfile answer.hpp mastodonpp/answer.hpp
*
*/
struct answer_type
{
@ -88,6 +85,8 @@ struct answer_type
* [libcurl-errors(3)](https://curl.haxx.se/libcurl/c/libcurl-errors.html).
*
* @since 0.1.0
*
* @headerfile types.hpp mastodonpp/types.hpp
*/
uint8_t curl_error_code{0};
@ -139,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.
@ -150,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.
@ -160,8 +158,7 @@ struct answer_type
*
* @since 0.3.0
*/
[[nodiscard]]
inline parametermap next() const
[[nodiscard]] inline parametermap next() const
{
return parse_pagination(true);
}
@ -174,8 +171,7 @@ struct answer_type
*
* @since 0.3.0
*/
[[nodiscard]]
inline parametermap prev() const
[[nodiscard]] inline parametermap prev() const
{
return parse_pagination(false);
}
@ -189,9 +185,9 @@ private:
*
* @since 0.3.0
*/
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,51 +34,51 @@ 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();
_buffer_mutex.lock();
auto &buffer{get_buffer()};
auto buffer_copy{buffer};
string buffer_copy{buffer};
buffer.clear();
buffer_mutex.unlock();
_buffer_mutex.unlock();
return buffer_copy;
}
vector<event_type> Connection::get_new_events()
{
buffer_mutex.lock();
_buffer_mutex.lock();
auto &buffer{get_buffer()};
vector<event_type> events;
@ -103,7 +103,7 @@ vector<event_type> Connection::get_new_events()
buffer.erase(0, endpos);
}
buffer_mutex.unlock();
_buffer_mutex.unlock();
return events;
}

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;
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);
@ -221,6 +234,7 @@ void CURLWrapper::set_proxy(const string_view proxy)
{
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)
@ -231,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)
@ -243,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";
@ -251,6 +265,7 @@ void CURLWrapper::set_access_token(const string_view access_token)
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)
{
@ -260,34 +275,34 @@ 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;
}
buffer_mutex.lock();
_buffer_mutex.lock();
_curl_buffer_body.append(data, size * nmemb);
buffer_mutex.unlock();
_buffer_mutex.unlock();
return size * nmemb;
}
size_t CURLWrapper::writer_header(char *data, size_t size, size_t nmemb)
{
if(data == nullptr)
if (data == nullptr)
{
return 0;
}
@ -297,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)
{
@ -335,7 +350,7 @@ void CURLWrapper::setup_curl()
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
curl_easy_setopt(_connection, CURLOPT_NOPROGRESS, 0L);
set_useragent((string("mastodonpp/") += version));
CURLWrapper::set_useragent((string("mastodonpp/") += version));
// The next 2 only fail if HTTP is not supported.
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
@ -351,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,
@ -376,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)
{
@ -384,7 +413,6 @@ void CURLWrapper::add_parameters_to_uri(string &uri,
continue;
}
static bool first{true};
if (first)
{
uri += "?";
@ -412,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) const
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}

178
src/helpers.cpp Normal file
View File

@ -0,0 +1,178 @@
/* This file is part of mastodonpp.
* Copyright © 2020 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
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "helpers.hpp"
#include <codecvt>
#include <locale>
#include <map>
#include <regex>
#include <stdexcept>
#include <string_view>
namespace mastodonpp
{
using std::codecvt_utf8;
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::wstring_convert;
string unescape_html(string html)
{
string buffer{move(html)};
string output;
// 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}};
// 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:]]+);"};
smatch match;
while (regex_search(buffer, match, re_entity))
{
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] != '#')
{
return names.at(match[1].str());
}
// 'x' after '#' means the number is hexadecimal.
if (match[2].length() == 1)
{
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));
}()};
output += u8c.to_bytes(codepoint);
}
catch (const std::out_of_range &) // Named entity could not be found.
{
output += match.str();
}
buffer = match.suffix().str();
}
output += buffer;
return output;
}
} // namespace mastodonpp

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;
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)
@ -40,15 +40,18 @@ constexpr auto shorten_filename(const string_view &filename)
return filename;
}
#define commonlog cerr << '[' << shorten_filename(__FILE__) \
<< ':' << __LINE__ << ']'
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define commonlog \
cerr << '[' << shorten_filename(__FILE__) << ':' << __LINE__ << ']'
#ifndef NDEBUG
#define debuglog commonlog << " DEBUG: "
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
# 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

@ -0,0 +1,59 @@
/* This file is part of mastodonpp.
* 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
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "helpers.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>
namespace mastodonpp
{
using std::string;
SCENARIO("mastodonpp::html_unescape()")
{
bool exception = false;
WHEN("html_unescape() is called.")
{
string result;
try
{
result = unescape_html("2&euro; = 2&#8364; = 2&#x20ac;");
}
catch (const std::exception &e)
{
exception = true;
}
THEN("No exception is thrown")
AND_THEN("Result is as expected.")
{
REQUIRE_FALSE(exception);
REQUIRE(result == "2€ = 2€ = 2€");
}
}
}
} // namespace mastodonpp

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);